/* Snap! Sponsor POS. Brand blue #2c59a5, navy #394255, from the supplied logo.
   Sized for a phone first: a rep uses this standing in someone's shop. */

:root {
  --blue: #2c59a5;
  --blue-dark: #21458a;
  --blue-wash: #eef3fb;
  --navy: #394255;
  --ink: #1d2433;
  --muted: #6b7486;
  --line: #dde2ea;
  --surface: #fff;
  --bg: #f4f6fa;
  --ok: #1a7f52;
  --ok-wash: #e6f4ed;
  --warn: #9a6500;
  --warn-wash: #fdf3e0;
  --bad: #b3261e;
  --bad-wash: #fdeceb;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(29,36,51,.06), 0 4px 16px rgba(29,36,51,.05);
}

* { box-sizing: border-box; }

/* The browser's own [hidden] rule is a bare attribute selector, so ANY class
   that sets display outranks it — .waiting { display: inline-flex } silently
   wins and el.hidden = true does nothing you can see. This makes the attribute
   mean what everyone assumes it means. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ---------- shell ---------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 30;
}
.logo { height: 32px; width: auto; display: block; }
.topbar .who { margin-left: auto; display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--muted); }
.avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; background: var(--blue-wash); }

.tabs {
  display: flex;
  gap: 4px;
  padding: 10px 16px 0;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  position: sticky;
  top: 57px;
  z-index: 29;
}
.tab {
  appearance: none; border: 0; background: none; font: inherit;
  font-size: 14px; font-weight: 600; color: var(--muted);
  padding: 9px 14px; border-radius: 8px 8px 0 0; cursor: pointer; white-space: nowrap;
  border-bottom: 2px solid transparent;
}
.tab:hover { color: var(--blue-dark); background: var(--blue-wash); }
.tab.is-active { color: var(--blue); border-bottom-color: var(--blue); }

main { padding: 16px; max-width: 1100px; margin: 0 auto; }
.view { display: none; }
.view.is-active { display: block; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px;
  margin-bottom: 16px;
}
.card h2 { font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--blue); margin: 0 0 12px; }
.card h3 { font-size: 16px; margin: 0 0 10px; color: var(--navy); }

.centered { max-width: 26rem; margin: 8vh auto; }
.centered .logo { height: 44px; margin: 0 auto 24px; }

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

label { display: block; font-size: 13px; font-weight: 600; color: var(--navy); margin-bottom: 10px; }
input, select, textarea {
  width: 100%; font: inherit; color: inherit; background: #fff;
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; margin-top: 4px;
  /* 16px minimum or iOS Safari zooms the page on focus. */
  font-size: 16px;
}
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible {
  outline: 2px solid var(--blue); outline-offset: 1px;
}
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.row-3 { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 0 12px; }
@media (max-width: 560px) { .row, .row-3 { grid-template-columns: 1fr; } }

.btn {
  appearance: none; font: inherit; font-weight: 600; font-size: 15px;
  border-radius: 8px; padding: 12px 18px; border: 1px solid transparent; cursor: pointer;
}
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:not(:disabled):hover { background: var(--blue-dark); }
.btn-secondary { background: #fff; color: var(--blue); border-color: var(--blue); }
.btn-secondary:not(:disabled):hover { background: var(--blue-wash); }
.btn-ghost { background: none; color: var(--muted); border-color: var(--line); }
.btn-block { width: 100%; }
.btn-sm { padding: 6px 12px; font-size: 13px; }
.actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }

/* The collapsed item form: just its button until someone wants it. */
.form-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.form-head h2 { margin: 0; flex: 1; }

.link { appearance: none; border: 0; background: none; padding: 0; font: inherit; font-size: 13px;
        font-weight: 600; color: var(--blue); cursor: pointer; text-decoration: underline; }

.note { font-size: 13px; color: var(--muted); margin: 8px 0 0; }
/* A note explains the field ABOVE it, so it stays close to that one and the
   gap goes before the next. Without this the two touch — the hint under
   "Printer email" sat directly on the "Fallback tax rate" label. */
.note + label, .note + fieldset, .note + .row, .note + .row-3 { margin-top: 18px; }
.note.bad { color: var(--bad); }
.note.ok  { color: var(--ok); }

.banner { border-radius: 8px; padding: 12px 14px; font-size: 14px; margin-bottom: 14px; }
.banner-warn { background: var(--warn-wash); color: var(--warn); }
.banner-bad  { background: var(--bad-wash);  color: var(--bad); }
.banner-ok   { background: var(--ok-wash);   color: var(--ok); }
.banner code { word-break: break-all; font-size: 12px; }

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

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
  text-align: left; font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--line); padding: 0 10px 8px 0; white-space: nowrap;
}
td { padding: 9px 10px 9px 0; border-bottom: 1px solid #f0f2f6; vertical-align: top; }
tbody tr:hover { background: var(--blue-wash); }
.num { text-align: right; font-variant-numeric: tabular-nums; padding-right: 14px; }
.name { font-weight: 600; color: var(--navy); }
.sub { display: block; font-size: 12px; color: var(--muted); }
/* .sub is normally a second line under something, hence display:block — but it
   is also used on whole <td>s to make a column quiet, and a table cell set to
   block drops out of the table's column layout entirely. The browser then
   generates an anonymous cell in its place, so every column after it lands one
   slot to the left of its own header. That is what made the Ordered/Paid/Value
   columns of these tables line up under the wrong headings. */
td.sub { display: table-cell; white-space: nowrap; }
.empty { color: var(--muted); font-size: 14px; padding: 24px 0; text-align: center; }

.pill { display: inline-block; font-size: 11px; font-weight: 700; border-radius: 20px; padding: 2px 9px; white-space: nowrap; }
.pill-ok   { background: var(--ok-wash);   color: var(--ok); }
.pill-warn { background: var(--warn-wash); color: var(--warn); }
.pill-bad  { background: var(--bad-wash);  color: var(--bad); }
.pill-muted{ background: #eef0f4;          color: var(--navy); }

/* ---------- order builder ---------- */

.line {
  display: grid; grid-template-columns: 80px 1fr auto auto; gap: 10px;
  align-items: center; padding: 10px 0; border-bottom: 1px solid #f0f2f6;
}
.line input[type=number] { margin: 0; }
.line select { margin: 0; }
.line .line-total { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--navy); }
.line .art { font-size: 12px; color: var(--muted); }
@media (max-width: 560px) {
  .line { grid-template-columns: 70px 1fr; grid-template-areas: "qty item" "tot tot"; }
  .line .line-total { grid-area: tot; text-align: right; }
  .line .remove { position: absolute; right: 0; }
}

.totals { margin-top: 14px; font-variant-numeric: tabular-nums; }
.totals div { display: flex; justify-content: space-between; padding: 5px 0; font-size: 14px; }
.totals .grand { font-size: 22px; font-weight: 700; color: var(--navy); border-top: 2px solid var(--line); margin-top: 6px; padding-top: 10px; }

/* ---------- QR / payment ---------- */

.pay-stage { text-align: center; padding: 8px 0 4px; }
.qr-frame {
  display: inline-block; background: #fff; padding: 16px;
  border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow);
}
.qr-frame img { display: block; width: min(320px, 70vw); height: auto; image-rendering: pixelated; }
.pay-amount { font-size: 34px; font-weight: 700; color: var(--navy); font-variant-numeric: tabular-nums; }
.pay-order  { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 15px; color: var(--muted); letter-spacing: .04em; }
.pay-url    { font-size: 12px; color: var(--muted); word-break: break-all; margin-top: 10px; }

.paid-banner {
  background: var(--ok); color: #fff; border-radius: var(--radius);
  padding: 28px 20px; text-align: center; margin-bottom: 16px;
}
.paid-banner .big { font-size: 28px; font-weight: 800; letter-spacing: .02em; }
.paid-banner .amt { font-size: 20px; margin-top: 6px; opacity: .95; font-variant-numeric: tabular-nums; }
.waiting { display: inline-flex; align-items: center; gap: 9px; color: var(--muted); font-size: 14px; margin-top: 14px; }
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--blue); animation: pulse 1.3s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: .25; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .dot { animation: none; opacity: .8; } }

/* ---------- photo capture ---------- */

.photo-row { display: flex; align-items: center; gap: 16px; margin-bottom: 8px; }
.photo-preview {
  width: 88px; height: 88px; border-radius: 50%; object-fit: cover;
  background: var(--blue-wash); border: 1px solid var(--line); flex: none;
}
.photo-empty {
  width: 88px; height: 88px; border-radius: 50%; background: var(--blue-wash);
  border: 1px dashed var(--blue); flex: none;
  display: flex; align-items: center; justify-content: center; color: var(--blue); font-size: 26px;
}

/* ---------- autocomplete ---------- */

/* Above the map. Leaflet stacks its own panes and controls up to z-index 1000
   in the page's root stacking context, and the map card comes after this one
   in the DOM — so a suggestion list at z-index 10 rendered behind the map and
   was simply invisible. The whole combo is lifted, not just the list, so the
   input keeps its own dropdown. */
.combo { position: relative; z-index: 1100; }
.suggestions {
  position: absolute; z-index: 10; left: 0; right: 0; top: calc(100% + 4px);
  margin: 0; padding: 4px; list-style: none; background: #fff;
  border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow);
  max-height: 260px; overflow-y: auto;
}
/* Lifted out of the row it belongs to, because the orders table scrolls
   horizontally and `overflow-x: auto` clips both axes. Fixed positioning is
   the only thing an ancestor's overflow does not cut off. */
.suggestions.floating { position: fixed; left: 0; top: 0; z-index: 1200; }
.suggestions li { padding: 9px; border-radius: 6px; cursor: pointer; font-size: 14px; }
.suggestions li:hover { background: var(--blue-wash); }
.suggestions b { display: block; color: var(--navy); }
.suggestions span { color: var(--muted); font-size: 12px; }

/* ---------- outbox ---------- */

.msg { border: 1px solid var(--line); border-radius: 8px; padding: 12px; margin-bottom: 10px; }
.msg-head { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; margin-bottom: 6px; }
.msg-head .to { font-weight: 600; color: var(--navy); }
.msg-head .when { margin-left: auto; font-size: 12px; color: var(--muted); }
.msg pre {
  white-space: pre-wrap; word-break: break-word; font-size: 12px; color: var(--muted);
  background: var(--bg); border-radius: 6px; padding: 10px; margin: 8px 0 0; max-height: 200px; overflow-y: auto;
}

/* ---------- brochure ---------- */

.brochure-head { display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.brochure-head h3 { font-size: 26px; line-height: 1.15; margin: 4px 0 6px; letter-spacing: -.01em; }

/* The printed version of this page, for a rep who is about to hand something
   over or send a follow-up. A link rather than a button: it opens a file. */
.sellsheet-link {
  display: flex; align-items: center; gap: 12px; flex: none;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px;
  text-decoration: none; color: var(--navy); background: var(--surface);
}
.sellsheet-link:hover { border-color: var(--blue); background: var(--blue-wash); }
.sellsheet-link .ss-icon {
  flex: none; background: var(--bad); color: #fff; font-size: 10px; font-weight: 800;
  letter-spacing: .06em; padding: 5px 7px; border-radius: 4px;
}
.sellsheet-link .ss-text { display: flex; flex-direction: column; line-height: 1.35; }
.sellsheet-link .ss-text b { font-size: 14px; }
.sellsheet-link .ss-text span { font-size: 12px; color: var(--muted); }
@media (max-width: 560px) { .sellsheet-link { width: 100%; } }

.brochure-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 16px;
}
@media (max-width: 560px) { .brochure-grid { grid-template-columns: 1fr; } }

.product {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.product-img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  background: var(--blue-wash); border-bottom: 1px solid var(--line);
}
.product-img-empty {
  width: 100%; aspect-ratio: 4 / 3; background: var(--blue-wash);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--blue); font-size: 13px; font-weight: 600; text-align: center; padding: 12px;
}
.product-body { padding: 14px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.product-body h4 { margin: 0; font-size: 15px; color: var(--navy); }
.product-body .ref { font-size: 11px; letter-spacing: .05em; color: var(--muted); text-transform: uppercase; }
.product-body .blurb { font-size: 13px; color: var(--muted); margin: 0; flex: 1; }
.product-price { font-size: 26px; font-weight: 800; color: var(--blue); font-variant-numeric: tabular-nums; }
.product-meta { font-size: 12px; color: var(--muted); display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.product .actions { margin-top: 8px; }

/* Floating "view order", up from the bottom edge and clear of a phone's home
   indicator. Fixed rather than sticky so it stays in reach wherever the rep has
   scrolled to. */
.brochure-bar {
  position: fixed;
  left: 0; right: 0;
  bottom: calc(18px + env(safe-area-inset-bottom));
  display: flex;
  /* Right-aligned on a wide screen, where a centred pill lands squarely on a
     card's own Add to order button. Centred on a phone, where one column means
     nothing to collide with and the thumb is in the middle. */
  justify-content: flex-end;
  padding: 0 24px;
  pointer-events: none;   /* only the button itself is clickable */
  z-index: 40;
}
@media (max-width: 560px) {
  .brochure-bar { justify-content: center; padding: 0 16px; }
}
.fab {
  pointer-events: auto;
  appearance: none; border: 0; cursor: pointer;
  background: var(--blue); color: #fff;
  font: inherit; text-align: center;
  padding: 12px 26px;
  border-radius: 999px;
  box-shadow: 0 6px 20px rgba(44, 89, 165, .38), 0 2px 6px rgba(29, 36, 51, .18);
  display: flex; flex-direction: column; gap: 1px;
  animation: fab-in .18s ease-out;
}
.fab:hover { background: var(--blue-dark); }
.fab-main { font-weight: 700; font-size: 15px; letter-spacing: .01em; }
.fab-sub  { font-size: 12px; opacity: .92; font-variant-numeric: tabular-nums; }

@keyframes fab-in { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .fab { animation: none; } }

/* The button floats over the grid, so the last row needs room beneath it. */
#pane-brochure { padding-bottom: 92px; }

/* A card already on the order reads as chosen without being mistaken for the
   primary action on the page. */
.btn-on-order {
  background: var(--ok-wash);
  color: var(--ok);
  border-color: var(--ok);
}
.btn-on-order:hover { background: #d8ece2; }

.req {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--blue); background: var(--blue-wash);
  border-radius: 20px; padding: 1px 7px; margin-left: 6px;
}
input.missing { border-color: var(--bad); background: var(--bad-wash); }

/* ---------- full-size product picture ---------- */

.product-img-btn {
  display: block; width: 100%; padding: 0; border: 0; background: none;
  position: relative; cursor: zoom-in; overflow: hidden;
  border-bottom: 1px solid var(--line);
}
.product-img-btn .product-img { border-bottom: 0; transition: transform .25s ease; }

/* Only where a pointer can actually hover. On a phone this rule never applies
   and the tap target is the whole image. */
@media (hover: hover) and (pointer: fine) {
  .product-img-btn:hover .product-img { transform: scale(1.06); }
  .product-img-btn:hover .zoom-hint { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .product-img-btn .product-img { transition: none; }
  .product-img-btn:hover .product-img { transform: none; }
}

.zoom-hint {
  position: absolute; right: 8px; bottom: 8px;
  width: 30px; height: 30px; border-radius: 50%;
  background: rgba(29, 36, 51, .72); color: #fff;
  font-size: 15px; line-height: 30px; text-align: center;
  opacity: 0; transition: opacity .2s ease;
}
/* Without hover there is no way to discover it, so it is simply always shown. */
@media (hover: none), (pointer: coarse) { .zoom-hint { opacity: .85; } }

.lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(15, 20, 30, .88);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  animation: lb-in .15s ease-out;
}
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .lightbox { animation: none; } }

.lightbox figure {
  margin: 0; max-width: 100%; max-height: 100%;
  display: flex; flex-direction: column; gap: 10px; align-items: center;
}
.lightbox img {
  /* contain, not cover: showing the whole picture is the entire point. */
  max-width: 100%; max-height: calc(100vh - 140px);
  object-fit: contain; border-radius: 10px; background: #fff;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .45);
}
.lightbox figcaption { color: #fff; text-align: center; font-size: 14px; }
.lightbox figcaption b { display: block; font-size: 16px; }
.lightbox figcaption span { opacity: .8; font-size: 13px; }

.lightbox-close {
  position: absolute; top: 16px; right: 16px;
  width: 42px; height: 42px; border-radius: 50%;
  border: 0; cursor: pointer;
  background: rgba(255, 255, 255, .16); color: #fff; font-size: 26px; line-height: 1;
}
.lightbox-close:hover { background: rgba(255, 255, 255, .28); }

/* ---------- orders filters & action column ---------- */

.order-filters {
  display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin-bottom: 10px;
}
.order-filters label { margin: 0; display: flex; flex-direction: column; gap: 2px; }
.order-filters label span { font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.order-filters select, .order-filters input[type="date"] { width: auto; margin: 0; padding: 7px 10px; font-size: 14px; }
.order-filters .link { padding-bottom: 9px; }
.order-filters .btn { margin-left: auto; }
@media (max-width: 620px) {
  .order-filters { gap: 8px; }
  .order-filters .btn { margin-left: 0; width: 100%; }
}

/* The action column: the button was inheriting the row's top alignment and the
   cell's right padding, so it sat low and pushed past the table's edge. */
th.act { width: 1%; }
td.act {
  vertical-align: middle;
  text-align: right;
  white-space: nowrap;
  padding-right: 0;
}

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

.avatar-btn { border: 0; background: none; padding: 0; cursor: pointer; border-radius: 50%; line-height: 0; }
.avatar-btn:hover { box-shadow: 0 0 0 3px var(--blue-wash); }
.avatar-initials {
  display: flex; align-items: center; justify-content: center;
  background: var(--blue); color: #fff; font-size: 13px; font-weight: 700; letter-spacing: .02em;
}

.profile-sheet {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(15, 20, 30, .6);
  display: flex; align-items: center; justify-content: center; padding: 20px;
  overflow-y: auto;
}
.profile-card {
  position: relative;
  background: var(--surface); border-radius: 16px; box-shadow: var(--shadow);
  padding: 28px; max-width: 30rem; width: 100%;
}
.profile-head { display: flex; gap: 18px; align-items: center; margin-bottom: 18px; }
.profile-photo {
  width: 92px; height: 92px; border-radius: 50%; object-fit: cover; flex: none;
  border: 3px solid var(--blue-wash);
}
.profile-lede { font-size: 15px; line-height: 1.55; color: var(--ink); margin: 0 0 20px; }

.profile-contact {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  border-top: 1px solid var(--line); padding-top: 18px; font-size: 14px; color: var(--navy);
}
@media (max-width: 460px) { .profile-contact { grid-template-columns: 1fr; } }
.profile-contact .lbl {
  display: block; font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 4px; font-weight: 700;
}
.profile-contact a { color: var(--blue); }

.profile-own { border-top: 1px dashed var(--line); margin-top: 18px; padding-top: 16px; }

/* ---------- schools map ---------- */

.school-controls { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

.map-wrap { position: relative; }
#schoolMap {
  height: 58vh; min-height: 340px; width: 100%;
  background: var(--blue-wash);
}
@media (max-width: 560px) { #schoolMap { height: 50vh; } }

/* Shown when someone tries to scroll the page and the map takes it instead.
   Saying what to do beats a page that has silently stopped moving. */
.map-hint {
  position: absolute; inset: 0; z-index: 900;
  display: grid; place-items: center; pointer-events: none;
  background: rgba(29, 36, 51, .45);
  color: #fff; font-size: 17px; font-weight: 700; text-align: center; padding: 20px;
  opacity: 0; transition: opacity .18s ease;
}
.map-hint.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .map-hint { transition: none; } }

/* Above the map, not under it. These two numbers ARE the pitch — a rep turns
   the screen round to show someone "1,240 schools, 611,000 students" — and
   under the map they were read last if at all. */
.map-summary {
  padding: 14px 18px; color: var(--navy);
  background: var(--blue-wash); border-bottom: 1px solid var(--line);
  display: flex; gap: 32px; align-items: flex-end; flex-wrap: wrap;
}
.ms-stat { display: flex; flex-direction: column; }
.ms-stat .v {
  font-size: 34px; font-weight: 800; line-height: 1.05;
  color: var(--blue); font-variant-numeric: tabular-nums;
}
.ms-stat .k {
  font-size: 11px; letter-spacing: .07em; text-transform: uppercase;
  font-weight: 700; color: var(--muted); margin-top: 3px;
}
.ms-scope {
  margin-left: auto; font-size: 13px; font-weight: 600; color: var(--muted);
  display: flex; flex-direction: column; align-items: flex-end; gap: 3px;
}
.ms-empty { font-size: 14px; color: var(--muted); }
.map-summary .warn { color: var(--warn); font-weight: 600; }

@media (max-width: 560px) {
  .map-summary { gap: 18px; padding: 12px 14px; }
  .ms-stat .v { font-size: 26px; }
  .ms-scope { margin-left: 0; align-items: flex-start; width: 100%; }
}

/* ---------- several businesses at once ---------- */

.places { margin-top: 12px; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.places-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px; background: var(--bg); border-bottom: 1px solid var(--line);
  font-size: 13px;
}
.places-head .sub { color: var(--muted); }
.places-head .link { margin-left: auto; }
.place-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-bottom: 1px solid var(--line); font-size: 14px;
}
.place-row:last-child { border-bottom: 0; }
/* Numbered to match nothing on the map yet — the pins are identical — but it
   gives the rep a way to say "the third one" out loud. */
.place-row .pin-no {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  background: #d92b1f; color: #fff; font-size: 12px; font-weight: 700;
  display: grid; place-items: center;
}
.place-row .pn { flex: 1; font-weight: 600; min-width: 8rem; }
.place-row .pn .sub, .place-row .pc .sub { display: block; font-weight: 400; font-size: 12px; color: var(--muted); }
.place-row .pc { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .place-row { flex-wrap: wrap; }
  .place-row .pc { text-align: left; }
}

.level-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px; margin: 0;
  border: 1px solid var(--line); border-radius: 999px; padding: 6px 12px;
  font-size: 13px; font-weight: 600; color: var(--navy); cursor: pointer;
}
.chip span { font-weight: 400; color: var(--muted); font-size: 12px; }
.chip input { width: auto; margin: 0; accent-color: var(--blue); }
.chip:has(input:checked) { border-color: var(--blue); background: var(--blue-wash); }

/* Leaflet's popup, in this app's voice rather than its own. */
.leaflet-popup-content { margin: 12px 14px; font: inherit; }
.leaflet-popup-content b { display: block; color: var(--navy); font-size: 14px; }
.leaflet-popup-content .meta { font-size: 12px; color: var(--muted); display: block; margin-top: 3px; }
.leaflet-container { font: inherit; }

/* The corporate mark sits above the rep, because the question this page
   answers is "who am I paying" before "who are you". Capped in width rather
   than height: it is a one-line lockup and a height cap on a 8:1 aspect makes
   it either hairline or overflowing. */
.profile-brand {
  display: block;
  width: 190px; max-width: 55%;
  height: auto;
  margin-bottom: 20px;
}

/* A divIcon carries Leaflet's default white box; the pin is the whole icon. */
.anchor-pin { background: none; border: 0; }
.anchor-pin svg { filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .35)); }

/* ---------- orders totals & tax report ---------- */

.totals-strip {
  display: flex; gap: 22px; flex-wrap: wrap;
  padding: 12px 14px; margin-bottom: 12px;
  background: var(--blue-wash); border-radius: 8px;
}
.totals-strip div { display: flex; flex-direction: column; }
.totals-strip .k {
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); font-weight: 700;
}
.totals-strip .v { font-size: 19px; font-weight: 700; color: var(--navy); font-variant-numeric: tabular-nums; }
.totals-strip .v.tax { color: var(--blue); }

/* A collapsible table tucked under a totals strip. Named for the sales-tax
   breakdown it was built for; that one is gone and the By product rollup on
   the Assets page is what uses it now. */
.tax-block { margin-bottom: 14px; border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px; }
.tax-block summary { font-size: 13px; font-weight: 700; color: var(--blue); cursor: pointer; }
.tax-block table { margin-top: 10px; }

/* ---------- live / test mode ---------- */

.mode-badge {
  font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  border-radius: 20px; padding: 3px 9px;
}
.mode-badge.test { background: var(--warn-wash); color: var(--warn); }
.mode-badge.live { background: var(--bad); color: #fff; }

.pill-test { background: var(--warn-wash); color: var(--warn); }
.pill-live { background: var(--bad-wash); color: var(--bad); }

/* ---------- test / live switch ----------

   A real switch rather than a clickable label. This is the control that
   decides whether a rep's next order charges a live card, so it has to read as
   something you operate, and its current position has to be legible across a
   desk. Both labels stay visible — a single-word badge makes you guess whether
   the word is the state or the action.
*/

.mode-toggle {
  position: relative;
  display: inline-flex; align-items: center;
  width: 108px; height: 30px; padding: 0;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--warn-wash);
  cursor: pointer; font: inherit;
  transition: background .18s ease, border-color .18s ease;
  -webkit-tap-highlight-color: transparent;
}
.mode-toggle.is-live { background: var(--bad-wash); border-color: var(--bad); }

.mt-knob {
  position: absolute; top: 2px; left: 2px;
  width: 52px; height: 24px; border-radius: 999px;
  background: var(--warn); 
  box-shadow: 0 1px 3px rgba(29, 36, 51, .25);
  transition: transform .18s ease, background .18s ease;
}
.mode-toggle.is-live .mt-knob { transform: translateX(50px); background: var(--bad); }

.mt-label {
  position: relative; z-index: 1;
  width: 54px; text-align: center;
  font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  transition: color .18s ease;
}
/* The label on the knob is reversed out; the other sits on the track. */
.mt-test { color: #fff; }
.mt-live { color: var(--muted); }
.mode-toggle.is-live .mt-test { color: var(--muted); }
.mode-toggle.is-live .mt-live { color: #fff; }

.mode-toggle:hover { border-color: var(--navy); }
.mode-toggle:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .mode-toggle, .mt-knob, .mt-label { transition: none; }
}

/* ---------- invoice ----------
   A document, not a screen. It is read on a phone, forwarded to whoever pays
   the bills, and printed — so it is a single column with generous type, and
   the print rules below strip the page furniture rather than leaving a button
   and a background colour in the middle of someone's accounts file. */

.invoice-body { background: var(--bg); }
.invoice-wrap { max-width: 46rem; margin: 0 auto; padding: 24px 16px 64px; }

.invoice-sheet {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 32px;
}
@media (max-width: 640px) { .invoice-sheet { padding: 20px; } }

.inv-head { display: flex; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.inv-logo { height: 34px; width: auto; display: block; margin-bottom: 14px; }
.inv-from { font-size: 13px; line-height: 1.6; color: var(--muted); }
.inv-from b { color: var(--ink); }

.inv-meta { text-align: right; margin-left: auto; }
.inv-meta h1 { margin: 0; font-size: 26px; letter-spacing: .04em; text-transform: uppercase; color: var(--navy); }
.inv-no { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; margin-top: 2px; }
.inv-meta dl {
  display: grid; grid-template-columns: auto auto; gap: 2px 12px;
  justify-content: end; margin: 10px 0 0; font-size: 13px;
}
.inv-meta dt { color: var(--muted); }
.inv-meta dd { margin: 0; }

/* The one thing someone must be able to read without looking for it: whether
   they owe money, and how much. */
.inv-stamp {
  display: inline-block; margin-top: 14px; padding: 8px 14px;
  border-radius: 8px; font-weight: 800; letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
}
.inv-stamp.is-due  { background: var(--warn-wash); color: var(--warn); border: 2px solid var(--warn); }
.inv-stamp.is-paid { background: var(--ok-wash);   color: var(--ok);   border: 2px solid var(--ok); }

.inv-testnote {
  margin: 20px 0 0; padding: 10px 12px; border-radius: 8px;
  background: var(--warn-wash); color: var(--warn); font-size: 13px; font-weight: 600;
}

.inv-parties { display: flex; gap: 40px; flex-wrap: wrap; margin: 28px 0 8px; }
.inv-parties h2 {
  margin: 0 0 6px; font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.inv-parties p { margin: 0; font-size: 14px; line-height: 1.6; }

.inv-table { width: 100%; border-collapse: collapse; margin-top: 18px; }
.inv-table th {
  text-align: left; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--line); padding: 0 0 8px;
}
.inv-table td { padding: 12px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
/* Gutters between columns, and none after the last one, so the amounts line up
   with the right edge of the sheet. Without this the quantity runs straight
   into the description and "Total due" into the figure it labels. */
.inv-table th, .inv-table td { padding-right: 18px; }
.inv-table th:last-child, .inv-table td:last-child { padding-right: 0; }
.inv-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.inv-table th:first-child, .inv-table td:first-child { width: 1%; }
.inv-table .name { font-weight: 600; }
.inv-table .name .sub { display: block; font-weight: 400; font-size: 13px; color: var(--muted); }
.inv-table tfoot td { border-bottom: none; padding-top: 8px; padding-bottom: 2px; }
.inv-table tfoot .grand td { font-size: 19px; color: var(--navy); border-top: 2px solid var(--line); padding-top: 12px; }

/* On a phone the two halves of the header stack, and a right-aligned block
   sitting under a left-aligned one reads as two documents. One column, one
   edge. */
@media (max-width: 640px) {
  .inv-meta { text-align: left; margin-left: 0; }
  .inv-meta dl { justify-content: start; }
  .inv-parties { gap: 22px; }
}

.inv-pay { margin-top: 26px; text-align: center; }
.btn-lg { padding: 16px 28px; font-size: 18px; font-weight: 700; }
.inv-pay .note { margin-top: 10px; }

.inv-foot {
  margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--line);
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  font-size: 12px; color: var(--muted);
}
.inv-foot span { flex: 1; min-width: 14rem; }

@media print {
  .invoice-body { background: #fff; }
  .invoice-wrap { max-width: none; padding: 0; }
  .invoice-sheet { border: none; box-shadow: none; border-radius: 0; padding: 0; }
  /* A printed invoice is a record of what is owed, so the amount-due stamp
     stays. The button that pays it cannot be clicked on paper. */
  .inv-pay .btn, .inv-foot .btn { display: none; }
  .inv-stamp { border-width: 2px; }
}

/* The invoice alternative, under the QR code. Separated by a rule rather than
   sat next to the code: it is what a rep reaches for when the customer is NOT
   going to scan, so it should read as the other option, not a second step. */
.pay-alt {
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--line);
}
.pay-alt .note { margin: 8px 0 0; }

/* Links styled as buttons need what a <button> gets for free. */
a.btn { display: inline-block; text-decoration: none; text-align: center; }

/* Two controls in the actions column of the orders table. */
td.act .btn + .btn { margin-left: 6px; }

/* ---------- placement ----------
   The expander on an order row, and the per-asset placement editor it opens.
   The row is a disclosure rather than a column because the orders table is
   already wide, and because a school name is longer than any cell that would
   fit beside eleven others. */

.row-expand {
  appearance: none; background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--navy);
  display: inline-flex; align-items: center; gap: 7px; text-align: left;
}
.row-expand:hover { color: var(--blue); }
.row-expand:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: 4px; }
/* A CSS triangle, so there is no glyph to render differently per platform. */
.row-expand .chev {
  width: 0; height: 0; flex: none;
  border-left: 6px solid currentColor;
  border-top: 4.5px solid transparent;
  border-bottom: 4.5px solid transparent;
  transition: transform .15s ease;
}
.row-expand.is-open .chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .row-expand .chev { transition: none; } }

.order-detail > td { background: var(--blue-wash); padding: 14px 12px; }
/* The detail cell spans a table that is wider than the screen, so its contents
   are pinned to the left edge of the scroll container rather than stretched
   across it. Otherwise the save indicator sits off-screen and the rep has to
   scroll sideways to find out whether their typing was kept. */
/* Capped by the viewport as well as by the cell: on a phone the cell is far
   wider than the screen, so "100% of the cell" would run the input off the
   right edge along with the indicator that says whether it saved. */
.placements { position: sticky; left: 12px; width: min(100%, 44rem, calc(100vw - 3.5rem)); }
.placements h4 {
  margin: 0 0 10px; font-size: 11px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.placement-row {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  padding: 7px 0; border-top: 1px solid rgba(29,36,51,.08);
}
.placement-row:first-of-type { border-top: 0; }
.pl-asset { min-width: 13rem; font-weight: 600; font-size: 14px; }
.pl-asset .sub { display: block; font-weight: 400; font-size: 12px; color: var(--muted); }
.pl-input { flex: 1; min-width: 15rem; max-width: 24rem; }
.pl-input input { width: 100%; margin: 0; }
.pl-state { font-size: 12px; color: var(--muted); min-width: 4.5rem; }
.pl-state.is-ok  { color: var(--ok); font-weight: 600; }
.pl-state.is-bad { color: var(--bad); }

@media (max-width: 560px) {
  .placement-row { align-items: stretch; flex-direction: column; gap: 6px; }
  .pl-input { min-width: 0; }
}

/* ---------- verified on snapraise.com ----------
   The profile page's whole job is credibility, and everything else on it was
   typed in by the person it describes. A link through to the company's own
   page for that rep is the only thing on the screen they could not have
   written, so it is marked rather than left to be discovered. */

.profile-photo-link { display: block; flex: none; border-radius: 50%; position: relative; }
/* Only interactive when it has somewhere to go. */
.profile-photo-link[href] { cursor: pointer; }
.profile-photo-link.is-verified .profile-photo { border-color: var(--blue); }
.profile-photo-link[href]:hover .profile-photo { border-color: var(--blue-dark); }
.profile-photo-link:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

/* A tick in the corner of the photo, the way a verified account is marked
   everywhere else — recognisable without a caption. */
.profile-photo-link.is-verified::after {
  content: "✓";
  position: absolute; right: 0; bottom: 2px;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--blue); color: #fff;
  font-size: 14px; font-weight: 800; line-height: 26px; text-align: center;
  border: 2px solid var(--surface);
}

.snap-verify { margin: 6px 0 0; font-size: 13px; font-weight: 600; }
.snap-verify a { color: var(--blue); }

/* ---------- price list order ----------
   Up/down buttons rather than drag and drop: this is edited once in a while,
   sometimes on a phone, and a drag target is the one interaction that is worse
   on touch AND unusable from a keyboard. */
th.ord { width: 1%; white-space: nowrap; }
td.ord { white-space: nowrap; vertical-align: middle; }
.ord-btn {
  appearance: none; background: none; border: 1px solid var(--line);
  border-radius: 6px; padding: 2px 6px; cursor: pointer;
  font-size: 11px; line-height: 1.4; color: var(--blue);
}
.ord-btn + .ord-btn { margin-left: 3px; }
.ord-btn:not(:disabled):hover { background: var(--blue-wash); border-color: var(--blue); }
.ord-btn:disabled { opacity: .3; cursor: default; }
.ord-btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* ---------- bundles ----------
   A bundle carries the price and hangs nowhere; the things inside it are what
   actually get placed. The editor and the placement list both show that shape:
   a heading, then the contents indented under it. */

.bundle-editor { border-top: 1px solid var(--line); margin-top: 14px; padding-top: 4px; }
.bundle-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 14px;
}
.bundle-row .bq { font-weight: 700; color: var(--navy); min-width: 2.5rem; font-variant-numeric: tabular-nums; }
.bundle-row .bn { flex: 1; font-weight: 600; }
.bundle-row .bn .sub { display: block; font-weight: 400; }
.bundle-add { display: flex; gap: 8px; align-items: center; margin-top: 10px; flex-wrap: wrap; }
.bundle-add select { flex: 1; min-width: 12rem; margin: 0; }
.bundle-add input { width: 5rem; margin: 0; }

.placement-bundle {
  margin-top: 8px; padding: 8px 0 6px;
  font-weight: 700; font-size: 14px; color: var(--navy);
  border-top: 1px solid rgba(29,36,51,.08);
}
.placement-bundle .sub { display: block; font-weight: 400; font-size: 12px; color: var(--muted); }
/* Indented and ruled to the left, so it reads as "part of the thing above"
   rather than as another item bought separately. */
.placement-row.is-child {
  border-top: 0; margin-left: 14px; padding-left: 12px;
  border-left: 2px solid var(--blue-wash);
}

/* A line in the suggestion list that is not a choice — "Already on your list."
   Must not look clickable, because it isn't. */
.suggestions li.is-note {
  color: var(--muted); font-style: italic; cursor: default; padding: 10px 9px;
}
.suggestions li.is-note:hover { background: none; }

/* Multi-select in the suggestion list. The whole row is the target — a 14px
   checkbox is a poor thing to hit on a phone. */
.suggestions li.is-multi { display: flex; align-items: flex-start; gap: 10px; }
.suggestions li.is-multi input[type="checkbox"] {
  width: auto; margin: 2px 0 0; flex: none; accent-color: var(--blue);
}
.suggestions li.is-multi .txt { flex: 1; min-width: 0; }
.suggestions li.is-multi:has(input:checked) { background: var(--blue-wash); }
.suggestions li.is-actions {
  display: flex; align-items: center; gap: 10px;
  border-top: 1px solid var(--line); margin-top: 4px; padding-top: 9px;
  position: sticky; bottom: -4px; background: #fff; cursor: default;
}
.suggestions li.is-actions:hover { background: #fff; }
.suggestions li.is-actions .sub { color: var(--muted); font-size: 12px; }

/* ---------- importing a list ---------- */

.import-panel {
  margin-top: 12px; padding: 14px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg);
}
.import-panel textarea {
  font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre; overflow-wrap: normal; overflow-x: auto;
}
.import-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.import-actions .note { margin: 0; }
.import-actions .note.bad { color: var(--bad); font-weight: 600; }
/* The primary action sits at the right, away from Cancel. */
.import-actions #runImport { margin-left: auto; }
@media (max-width: 560px) {
  .import-actions #runImport { margin-left: 0; }
}

/* The radius slider, on its own row under the business list header — inline in
   the header it was squeezed to a couple of centimetres, which is not a
   control anyone can aim. */
.radius-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-bottom: 1px solid var(--line);
  background: var(--bg); font-size: 13px;
}
.radius-row label { margin: 0; font-weight: 700; color: var(--navy); }
.radius-row input[type="range"] {
  flex: 1; min-width: 8rem; margin: 0; padding: 0;
  accent-color: var(--blue); background: none; border: 0;
}
.radius-row output {
  min-width: 5.5rem; text-align: right; font-weight: 700;
  color: var(--blue); font-variant-numeric: tabular-nums;
}

/* ---------- artwork requirement ----------
   A size or a resolution. They are alternatives, so they look like a choice
   rather than two sets of fields both waiting to be filled in. */
.art-spec { border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px 14px; margin: 14px 0 0; }
.art-spec legend {
  font-size: 11px; letter-spacing: .07em; text-transform: uppercase;
  font-weight: 700; color: var(--muted); padding: 0 6px;
}
.art-kind { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.art-kind .chip { font-size: 13px; }
.art-kind .chip input { accent-color: var(--blue); }
#artDpi input { max-width: 10rem; }

/* ---------- local business ---------- */

#bizMap { height: 52vh; min-height: 320px; width: 100%; background: var(--blue-wash); }
@media (max-width: 560px) { #bizMap { height: 46vh; } }

.kind-group { margin-top: 10px; }
.kind-group h4 {
  margin: 0 0 6px; font-size: 11px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
/* Dozens of trades, so the chips are quieter than the level filters on the
   Schools tab — those are four choices, these are sixty-five. */
.kind-group .chip { font-size: 12px; padding: 4px 10px; }
#bizCost.bad { color: var(--bad); font-weight: 600; }
