/* generated by build.py from theme.py — do not edit by hand */
:root{--font-head:'Josefin Sans','Inter',system-ui,sans-serif;}
:root{
  --brand:#24584E;
  --brand-dark:#17352F;
  --brand-tint:#E7EFEC;
  --brand-line:#C3D6D0;
  --surface-1:#FFFFFF;
  --page:#FAF7F2;
  --panel:#F1EADE;
  --border:#E4DED2;
  --gridline:#EFE9DE;
  --text-primary:#16211E;
  --text-secondary:#4E5B56;
  --text-muted:#67736E;
  --owe:#B3372C;
  --clear:#127A4C;
  --headline:#B3372C;
  --owe-bg:#FBEDEB;
  --clear-bg:#E7F4EE;
  --accent:#EDB92E;
  --accent-text:#8A6410;
  --accent-bg:#FBF3DF;
  --sage:#8FA79B;
  --zari:#C7A988;
  --series-1:#24584E;
  --series-2:#EDB92E;
  --series-3:#8FA79B;
  --series-4:#C7A988;
  --series-5:#17352F;
  --gr-grey:#A9AFAC;
  --good:#127A4C;
  --critical:#B3372C;
  --warning:#8A6410;
  --serious:#B3372C;
  --delta-good:#127A4C;
  --baseline:#D8D0C2;
  --radius:14px;
  --radius-sm:10px;
  --shadow:0 1px 2px rgba(22,33,30,.04);
  --topbar-h:54px;
  /* How much of the bottom of the screen something else already owns. The
     vendor portal has a tab bar down there and raises this; the admin app has
     nothing, so it stays zero. Declared here so a page that fills the screen
     can do the arithmetic on either side without knowing which side it is. */
  --tab-clear:0px;
}
*{box-sizing:border-box;}
/* clip, not hidden: `overflow-x:hidden` turns the page into a scroll container,
   which silently breaks every `position:sticky` header inside it. */
html,body{overflow-x:clip;max-width:100%;}
body{
  margin:0;font-family:'Inter','Noto Sans Devanagari',system-ui,-apple-system,sans-serif;font-size:15px;line-height:1.45;
  background:var(--page);color:var(--text-primary);
  -webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;
}
a{color:var(--brand);text-decoration:none;}
h1,h2,h3{margin:0;font-weight:700;letter-spacing:-.015em;}
svg{display:block;}
.num,.money{font-variant-numeric:tabular-nums;}
.hidden{display:none !important;}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);}

/* ---------------- cards & tiles ---------------- */
.card{
  background:var(--surface-1);border:1px solid var(--border);
  border-radius:var(--radius);padding:16px;box-shadow:var(--shadow);
}
.card.flush{padding:0;overflow:hidden;}
.card-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:12px;}
.card-head h2{font-size:15px;}
.card-head .cap{font-size:11.5px;color:var(--text-muted);font-weight:500;}

.tiles{display:grid;gap:10px;margin-bottom:16px;}
.tiles.c2{grid-template-columns:repeat(2,1fr);}
.tiles.c3{grid-template-columns:repeat(3,1fr);}
.tiles.c4{grid-template-columns:repeat(4,1fr);}
.tiles.c5{grid-template-columns:repeat(5,1fr);}
@media(max-width:900px){.tiles.c4,.tiles.c5,.tiles.c3{grid-template-columns:repeat(2,1fr);}}
@media(max-width:420px){.tiles.c3{grid-template-columns:1fr;}}
.tile{
  background:var(--surface-1);border:1px solid var(--border);
  border-radius:var(--radius);padding:13px 14px;box-shadow:var(--shadow);min-width:0;
}
.tile .label{font-size:11.5px;color:var(--text-muted);font-weight:500;}
.tile .value{font-size:20px;font-weight:700;letter-spacing:-.025em;margin-top:3px;
  font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.tile .sub{font-size:11px;color:var(--text-muted);margin-top:3px;}
.val-bad{color:var(--owe);}
.val-good{color:var(--clear);}
.tile.accent{border-color:var(--brand-line);background:var(--brand-tint);}

/* ---------------- section heading ---------------- */
.sec-head{display:flex;justify-content:space-between;align-items:baseline;
  gap:14px;margin:22px 2px 9px;}
.sec-head:first-child{margin-top:0;}
.sec-head h2{font-size:15.5px;min-width:0;}
.sec-head .cap{
  font-size:11.5px;color:var(--text-muted);white-space:nowrap;flex:none;
}
@media(max-width:480px){.sec-head .cap{display:none;}}
.sec-head a,.sec-head button.more{
  font-size:12.5px;color:var(--brand);font-weight:600;cursor:pointer;
  background:none;border:none;padding:0;font-family:inherit;
}

/* ---------------- controls ---------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  background:var(--brand);color:#fff;border:1px solid var(--brand);
  padding:10px 16px;border-radius:var(--radius-sm);font-size:14px;font-weight:600;
  cursor:pointer;font-family:inherit;text-decoration:none;line-height:1.2;
  transition:background .12s,border-color .12s;
}
.btn:hover{background:var(--brand-dark);border-color:var(--brand-dark);}
.btn:active{transform:translateY(1px);}
.btn svg{width:17px;height:17px;}
.btn.secondary{background:var(--surface-1);color:var(--text-primary);border-color:var(--border);}
.btn.secondary:hover{background:var(--brand-tint);border-color:var(--brand-line);color:var(--brand-dark);}
.btn.danger{background:var(--surface-1);color:var(--owe);border-color:#E7C4BF;}
.btn.danger:hover{background:var(--owe-bg);border-color:var(--owe);}
.btn.small{padding:7px 12px;font-size:12.5px;border-radius:8px;}
.btn.block{width:100%;}
/* A disabled button should read as inert, not as a differently-coloured button. */
.btn[disabled],.btn[disabled]:hover{
  background:var(--surface-1);color:var(--text-muted);border-color:var(--border);
  cursor:not-allowed;
}
input[type=checkbox],input[type=radio]{accent-color:var(--brand);}

input[type=text],input[type=tel],input[type=number],input[type=date],
input[type=password],input[type=search],select,textarea{
  width:100%;padding:11px 12px;border:1px solid var(--border);border-radius:var(--radius-sm);
  font-size:15px;font-family:inherit;background:var(--surface-1);color:var(--text-primary);
}
input:focus,select:focus,textarea:focus{outline:2px solid var(--brand);outline-offset:-1px;}
label{display:block;font-size:12px;color:var(--text-muted);font-weight:600;margin-bottom:5px;}
.field{margin-bottom:13px;}
/* A line under a field saying what it wants, before you get it wrong. Cheaper
   than an error message, because it arrives in time to be useful. */
.hint{display:block;font-size:11.5px;color:var(--text-muted);margin-top:4px;}
/* The same line, when what it is saying is a refusal. Kept as one element that
   changes rather than two that swap, so the form does not jump as you type. */
.hint.bad{color:var(--owe);font-weight:600;}
input.bad,select.bad,textarea.bad{border-color:var(--owe);background:var(--owe-bg);}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
.toolbar{display:flex;justify-content:space-between;align-items:center;gap:10px;
  margin-bottom:13px;flex-wrap:wrap;}
.search{max-width:280px;}

/* ---------------- badges ---------------- */
.badge{
  display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:999px;
  font-size:11px;font-weight:700;letter-spacing:.01em;white-space:nowrap;
}
.badge.ok,.badge.matched,.badge.resolved{background:var(--clear-bg);color:var(--clear);}
.badge.open,.badge.check{background:var(--owe-bg);color:var(--owe);}
.badge.bad{background:var(--owe-bg);color:var(--owe);}

/* ---------------- a vendor with no mobile number ---------------- */
/* Such a vendor cannot be given the portal — the account is half-made. This is
   the mark that says so wherever they are listed, and it is a button: tapping
   it goes to the field that fixes it, because a warning that only warns makes
   somebody go hunting.
   The WORD carries the meaning and the dot is decoration, not the other way
   round. Roughly one man in twelve cannot separate this red from the text
   around it, and for him a bare dot is nothing at all. */
.nophone{
  display:inline-flex;align-items:center;gap:5px;margin-left:8px;
  background:var(--owe-bg);color:var(--owe);border:1px solid #EBC9C4;
  border-radius:999px;padding:2px 9px;font-size:11px;font-weight:700;
  letter-spacing:.02em;vertical-align:middle;cursor:pointer;font-family:inherit;
}
.nophone-dot{width:7px;height:7px;border-radius:50%;background:var(--owe);flex:none;}
.nophone:hover{background:#F6DED9;}
/* Beside a heading it can be a little bigger; in a dense list it stays small. */
.vh-name .nophone{font-size:12px;padding:3px 10px;}
@media(max-width:520px){
  .nophone{min-height:26px;}
}
/* On a vendor's own page the mark gets its own line under the header. Squeezed
   in beside Back, the name and Delete it lost the end of its word on a phone,
   and the word is the half that carries the meaning. */
.vhead-flag{display:flex;margin:-2px 0 12px;}
.vhead-flag .nophone{margin-left:0;font-size:12px;padding:4px 11px;min-height:28px;}

/* An amount written both ways; the screen picks one. See amt() in the app. */
.n-lite{display:none;}
@media(max-width:520px){
  .n-full{display:none;}
  .n-lite{display:inline;}
}
.badge.note{background:var(--accent-bg);color:var(--accent-text);}
.badge svg{width:13px;height:13px;}
.dot-new{width:8px;height:8px;border-radius:50%;background:var(--owe);display:inline-block;flex:none;}

/* ---------------- tables ---------------- */
table{width:100%;border-collapse:collapse;font-size:13.5px;}
th,td{text-align:left;padding:11px 12px;border-bottom:1px solid var(--gridline);}
th{
  color:var(--text-muted);font-weight:600;font-size:11.5px;letter-spacing:0;
  white-space:nowrap;background:var(--surface-1);
}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;}
/* the first column of a table is a date or a month — never worth wrapping */
tbody td:first-child{white-space:nowrap;}
tbody tr:last-child td{border-bottom:none;}
tr.clickable{cursor:pointer;}
tr.clickable:hover{background:var(--brand-tint);}
/* A month that opens has to look like it opens. Without the chevron the row is
   indistinguishable from a row that does nothing, and nobody taps it. */
.m-open{color:var(--brand);font-weight:600;white-space:nowrap;}
.m-chev{margin-left:5px;opacity:.55;font-weight:400;}
/* The portal's month rows: the same five columns as the list they sit in, but
   a link rather than a panel that opens in place. Built on .elist.mlist's grid
   so the columns still line up with the header above them. */
.mlist .mrow{
  display:grid;grid-template-columns:minmax(72px,1fr) repeat(4,minmax(0,1fr));
  align-items:center;gap:6px;padding:11px 12px;
  border-top:1px solid var(--gridline);text-decoration:none;color:inherit;
  min-height:46px;
}
.mlist .mrow:hover,.mlist .mrow:active{background:var(--brand-tint);}
.mlist .mrow .e-item{color:var(--brand);font-weight:600;}
.month-title{font-weight:650;font-size:15px;}

/* ---------------- saved reports ---------------- */
.rcard{margin-bottom:12px;}
/* The report opens inside its own card. The page it used to lead to held
   nothing the card could not hold — three figures and a balance — so it was a
   tap and a page-load to reach what was one line away. */
.rcard>summary{display:flex;align-items:center;gap:10px;cursor:pointer;
  list-style:none;padding:2px 0;}
.rcard>summary::-webkit-details-marker{display:none;}
.rcard>summary::marker{content:"";}
.rcard .rchev{margin-left:auto;color:var(--text-muted);display:inline-flex;
  transition:transform .15s;}
.rcard[open] .rchev{transform:rotate(90deg);}
.rcard .w-amt{font-size:16px;font-weight:700;}
.rcard .card.flush{margin-top:10px;box-shadow:none;}

/* ---- ask the studio, at the top of the queries page ---- */
.ask{margin-bottom:12px;}
.ask textarea{width:100%;margin-top:6px;font-size:16px;padding:10px 12px;resize:none;}
.ask-act{display:flex;justify-content:flex-end;margin-top:8px;}
.qfilter{margin-bottom:10px;}
.rhead{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:10px;}
.rhead b{font-size:15px;}
/* Buttons in two groups: what changes the report on the left, what takes a copy
   of it away on the right. Mixing Delete in among the downloads is how somebody
   presses it reaching for Excel. */
.ract{display:flex;align-items:center;gap:7px;margin-top:11px;flex-wrap:wrap;}
.ract .rspace{flex:1;min-width:8px;}
.tickline{display:flex;gap:9px;align-items:flex-start;font-size:13.5px;
  background:var(--panel);border-radius:9px;padding:11px 12px;margin:4px 0 12px;}
.tickline input{margin-top:2px;width:17px;height:17px;flex:none;}
.tickline .hint{margin-top:3px;}
/* The vendor's list of shared reports: name, the figure, and a way in. */
.rlink{display:grid;grid-template-columns:1fr auto;gap:4px 12px;
  align-items:center;text-decoration:none;color:inherit;margin-bottom:10px;
  min-height:56px;}
.rlink b{font-size:15px;}
.rlink .w-amt{font-size:16px;font-weight:700;text-align:right;}
.rlink .cap{grid-column:1 / -1;color:var(--brand);font-weight:600;}
.rlink:hover{border-color:var(--brand-line);background:var(--brand-tint);}

/* ---------------- month by month: three bars, one baseline ----------------
   Stock, Payment and Goods Return stand side by side from the same floor, so
   two months can be compared by eye without measuring from wherever a segment
   below happened to end. They are deliberately thin — six months is eighteen
   bars, and on a 390px phone a fat bar means fewer months or a chart that has
   to be scrolled to be read at all. */
.towers-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;
  scroll-snap-type:x proximity;scrollbar-width:none;}
.towers-wrap::-webkit-scrollbar{display:none;}
.towers{display:flex;align-items:flex-end;gap:10px;min-height:150px;
  padding:6px 0 0;}
/* 46px, not 40: "Mar 26" is 45px wide at this size, and a month whose own
   name is clipped is a column nobody can place. The bars stay thin inside it. */
.tcol{flex:1 0 46px;min-width:46px;display:flex;flex-direction:column;
  align-items:center;gap:6px;background:none;border:0;padding:0 0 2px;
  cursor:pointer;font-family:inherit;scroll-snap-align:end;}
.trio{display:flex;align-items:flex-end;justify-content:center;gap:3px;
  width:100%;min-height:118px;}
/* A bar for a month with almost nothing in it still has to be visible, or the
   month reads as missing rather than as quiet. */
.trio i{display:block;width:8px;min-height:2px;border-radius:3px 3px 0 0;}
.t-stock{background:var(--series-1);}
.t-paid{background:var(--series-2);}
.t-gr{background:var(--gr-grey);}
.tlab{font-size:11.5px;color:var(--text-muted);white-space:nowrap;}
.tcol[aria-current="true"] .tlab{color:var(--brand);font-weight:700;}

.tlegend{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:4px;
  font-size:12px;color:var(--text-secondary);}
.tlegend span{display:inline-flex;align-items:center;gap:6px;}
.sw{width:11px;height:11px;border-radius:3px;flex:none;}
.sw-gr{background:var(--gr-grey);}
.sw-paid{background:var(--series-2);}
.sw-stock{background:var(--series-1);}

/* Tapping a month gives its figures. Three amounts cannot be printed over one
   month's width on a phone; under the chart they can. */
.tread{margin-top:12px;background:var(--panel);border-radius:10px;
  padding:11px 13px;font-size:13.5px;}
.tread b{display:block;margin-bottom:6px;font-size:14px;}
.tread .trow{display:flex;justify-content:space-between;gap:12px;padding:3px 0;}
.tread .trow span:last-child{font-weight:700;font-variant-numeric:tabular-nums;}
.tread .tnote{margin-top:7px;color:var(--accent-text);font-size:12.5px;}
.card.flush:has(table){overflow-x:auto;}
.empty{color:var(--text-muted);padding:22px 12px;text-align:center;font-size:13.5px;}

/* ---------------- chikankari in the empty space ---------------- */
/* The studio's own craft, worked into the app the way it is worked into cloth:
   only where there is ground to work on. Two weights, both set by the studio —
   about 17% where a page is genuinely empty, about 10% where there is content.
   Never behind a word or an amount: this is a money app, and ₹12,48,605 has to
   read clean the first time, a hundred times a day. See motifs.py. */
.empty::before{
  content:"";display:block;width:54px;height:54px;margin:2px auto 12px;
  background:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http://www.w3.org/2000/svg%22%20viewBox%3D%220%200%2064%2064%22%20fill%3D%22none%22%20stroke%3D%22%2324584E%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M32%2062V40%22/%3E%3Cpath%20d%3D%22M32%2049c-7%200-12-5-12-12%208%200%2012%205%2012%2012z%22/%3E%3Cpath%20d%3D%22M32%2049c7%200%2012-5%2012-12-8%200-12%205-12%2012z%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%280%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%2872%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%28144%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%28216%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%28288%2032%2025%29%22/%3E%3Ccircle%20cx%3D%2232%22%20cy%3D%2225%22%20r%3D%223.4%22/%3E%3C/svg%3E") center/contain no-repeat;
  opacity:.17;
}
/* The run-out below the last card — a tepchi hem along the end of the page.
   It is the one place on a working screen that is reliably empty. */
main::after{
  content:"";display:block;height:24px;margin:20px auto 6px;max-width:820px;
  background:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http://www.w3.org/2000/svg%22%20viewBox%3D%220%200%2096%2024%22%20fill%3D%22none%22%20stroke%3D%22%2324584E%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M0%2021h96%22%20stroke-dasharray%3D%226%204.5%22%20stroke-width%3D%221.3%22/%3E%3Cpath%20d%3D%22M48%2021v-5%22/%3E%3Cellipse%20cx%3D%2248%22%20cy%3D%226%22%20rx%3D%222.7%22%20ry%3D%225.2%22%20transform%3D%22rotate%280%2048%2011%29%22/%3E%3Cellipse%20cx%3D%2248%22%20cy%3D%226%22%20rx%3D%222.7%22%20ry%3D%225.2%22%20transform%3D%22rotate%2872%2048%2011%29%22/%3E%3Cellipse%20cx%3D%2248%22%20cy%3D%226%22%20rx%3D%222.7%22%20ry%3D%225.2%22%20transform%3D%22rotate%28144%2048%2011%29%22/%3E%3Cellipse%20cx%3D%2248%22%20cy%3D%226%22%20rx%3D%222.7%22%20ry%3D%225.2%22%20transform%3D%22rotate%28216%2048%2011%29%22/%3E%3Cellipse%20cx%3D%2248%22%20cy%3D%226%22%20rx%3D%222.7%22%20ry%3D%225.2%22%20transform%3D%22rotate%28288%2048%2011%29%22/%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2211%22%20r%3D%221.9%22/%3E%3Cpath%20d%3D%22M20%2021c-5%200-8-3-8-7%205%200%208%203%208%207z%22/%3E%3Cpath%20d%3D%22M76%2021c5%200%208-3%208-7-5%200-8%203-8%207z%22/%3E%3Cellipse%20cx%3D%228%22%20cy%3D%2215%22%20rx%3D%221.5%22%20ry%3D%223%22%20transform%3D%22rotate%28-30%208%2015%29%22/%3E%3Cellipse%20cx%3D%2288%22%20cy%3D%2215%22%20rx%3D%221.5%22%20ry%3D%223%22%20transform%3D%22rotate%2830%2088%2015%29%22/%3E%3C/svg%3E") center/96px 24px repeat-x;
  opacity:.10;pointer-events:none;
}
/* The blank the sidebar keeps between its last link and the account box. */
.side-spacer{
  background:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http://www.w3.org/2000/svg%22%20viewBox%3D%220%200%2064%2064%22%20fill%3D%22none%22%20stroke%3D%22%2324584E%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M32%2062V40%22/%3E%3Cpath%20d%3D%22M32%2049c-7%200-12-5-12-12%208%200%2012%205%2012%2012z%22/%3E%3Cpath%20d%3D%22M32%2049c7%200%2012-5%2012-12-8%200-12%205-12%2012z%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%280%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%2872%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%28144%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%28216%2032%2025%29%22/%3E%3Cellipse%20cx%3D%2232%22%20cy%3D%2214%22%20rx%3D%224.6%22%20ry%3D%229.4%22%20transform%3D%22rotate%28288%2032%2025%29%22/%3E%3Ccircle%20cx%3D%2232%22%20cy%3D%2225%22%20r%3D%223.4%22/%3E%3C/svg%3E") center bottom 18px/44px 44px no-repeat;
  opacity:.10;
}
/* Not on a page that already fills the screen. The chat page pins its message
   box to the bottom edge; a hem under it is 50px of nothing between the box and
   the thumb, which is exactly the fault that was fixed to put it there. */
body.chatting main::after{display:none;}
/* Paper is a working document, not a decorated one. */
@media print{
  .empty::before,main::after,.side-spacer{background-image:none;}
}

/* ---------------- avatar & party list ---------------- */
.av{
  display:inline-flex;align-items:center;justify-content:center;border-radius:50%;
  color:#fff;font-weight:700;flex:none;letter-spacing:.01em;
}
.plist{
  border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;
  background:var(--surface-1);box-shadow:var(--shadow);
}
.prow{
  display:flex;align-items:center;gap:12px;padding:12px 14px;
  border-bottom:1px solid var(--gridline);cursor:pointer;
}
.prow:last-child{border-bottom:none;}
.prow:hover{background:var(--brand-tint);}
.pmain{flex:1;min-width:0;}
.pname{font-size:14.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.pmeta{font-size:11.5px;color:var(--text-muted);margin-top:2px;}
.pamt{text-align:right;white-space:nowrap;}
.pamt b{font-size:15px;font-weight:700;font-variant-numeric:tabular-nums;}
.pamt .tag{display:block;font-size:10.5px;font-weight:600;margin-top:1px;}
.owe b,.owe .tag{color:var(--owe);}
.clear b,.clear .tag{color:var(--clear);}

/* ---------------- one-line expandable rows ---------------- */
/* item · qty · rate · amount on one line; tap opens the full detail.
   The date used to lead every row. It is gone: rows now sit inside a card that
   is already the date, so repeating it on each line was spending width that
   long item names — "Cotton Plazzo White" — were being cut off for. */
.elist{
  border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;
  background:var(--surface-1);box-shadow:var(--shadow);
}
.ehead,.erow{
  display:grid;
  grid-template-columns:minmax(0,1fr) 46px 62px 84px;
  gap:9px;align-items:center;padding:11px 13px;
}
.ehead{
  font-size:11px;font-weight:600;color:var(--text-muted);
  border-bottom:1px solid var(--border);background:var(--surface-1);
  white-space:nowrap;letter-spacing:0;
}
.erow{
  border-bottom:1px solid var(--gridline);cursor:pointer;font-size:13.5px;
  background:none;border-left:none;border-right:none;border-top:none;
  width:100%;text-align:left;font-family:inherit;color:inherit;
}
.erow:hover{background:var(--brand-tint);}
.erow.open{background:var(--brand-tint);}
.e-date{color:var(--text-muted);font-size:12.5px;font-variant-numeric:tabular-nums;white-space:nowrap;}
.e-item{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
.e-qty,.e-rate,.e-amt{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;}
.e-amt{font-weight:700;}
.ehead .e-qty,.ehead .e-rate,.ehead .e-amt{text-align:right;}
.edet{
  padding:2px 13px 15px;border-bottom:1px solid var(--gridline);
  background:var(--brand-tint);
}
.edet dl{display:grid;grid-template-columns:auto 1fr;gap:7px 16px;margin:0 0 13px;font-size:13.5px;}
.edet dt{color:var(--text-muted);font-size:12.5px;}
.edet dd{margin:0;font-weight:500;overflow-wrap:anywhere;}
.edet .acts{display:flex;gap:8px;}
.etot{
  display:flex;justify-content:space-between;align-items:center;
  padding:12px 13px;font-size:13.5px;font-weight:700;background:var(--surface-1);
}
.etot span{color:var(--text-secondary);font-weight:600;}
.etot b{font-variant-numeric:tabular-nums;color:var(--brand);font-size:15px;}
@media(max-width:400px){
  .ehead,.erow{grid-template-columns:minmax(0,1fr) 38px 54px 74px;gap:7px;padding:11px 10px;font-size:13px;}
  .edet,.etot{padding-left:10px;padding-right:10px;}
}

/* Month by month keeps five columns: the month is the grouping, so it never
   moved inside a date card and never lost its first column.
   The four amount columns are minmax(0,1fr) rather than a bare 1fr on purpose.
   A bare 1fr is minmax(auto,1fr), so a long figure like −₹99.9K sets its own
   floor and takes the width from the only column that can give — the month —
   which on a 360px phone left rows reading "Aug …" and "M…". The month is the
   row's name; it gets a floor of its own and the amounts share what is left. */
.mlist .ehead,.mlist .erow,.mlist .mrow,.mlist summary{
  grid-template-columns:minmax(72px,1fr) repeat(4,minmax(0,1fr));
}
.mlist .ehead span:first-child,.mlist .e-item{text-align:left;}
/* Every figure in the month table was bold, so the table read as one solid
   block and the eye had nowhere to land. Bold is kept for the two places it
   means something: the month's name, and the total at the foot. */
.mlist .mrow .e-amt,.mlist .erow .e-amt{font-weight:500;}
@media(max-width:400px){
  .mlist .ehead,.mlist .erow,.mlist .mrow,.mlist summary{
    gap:5px;font-size:12px;
    grid-template-columns:minmax(62px,.9fr) repeat(4,minmax(0,1fr));
  }
}

/* ---------------- one card per date ---------------- */
/* The header carries the date, the number of entries, the pieces and the day's
   total, so a day reads without being opened. Everything starts closed: a
   vendor with 111 dates would otherwise open onto thirty phone screens. */
.dcards{display:flex;flex-direction:column;gap:8px;}
.dcard{
  border:1px solid var(--border);border-radius:var(--radius);
  background:var(--surface-1);box-shadow:var(--shadow);overflow:hidden;
}
/* The server pages use <details>/<summary> so they work with no JavaScript at
   all; the single-page app uses a <button> because it re-renders anyway. Both
   get the same header. */
.dcard>summary,.dcard>.dc-head{
  display:grid;grid-template-columns:14px auto 1fr auto;gap:10px;align-items:center;
  padding:13px 14px;cursor:pointer;list-style:none;width:100%;text-align:left;
  background:none;border:none;font-family:inherit;color:inherit;
}
.dcard>summary::-webkit-details-marker{display:none;}
.dcard>summary::marker{content:"";}
.dcard>summary:hover,.dcard>.dc-head:hover{background:var(--brand-tint);}
.dcard[open]>summary,.dcard.is-open>.dc-head{
  background:var(--brand-tint);border-bottom:1px solid var(--border);
}
.dc-chev{
  width:8px;height:8px;border-right:2px solid var(--text-muted);
  border-bottom:2px solid var(--text-muted);transform:rotate(-45deg);
  transition:transform .15s;margin-left:2px;
}
.dcard[open] .dc-chev,.dcard.is-open .dc-chev{transform:rotate(45deg);}
.dc-date{font-weight:700;font-size:14px;font-variant-numeric:tabular-nums;white-space:nowrap;}
.dc-meta{font-size:12px;color:var(--text-muted);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;min-width:0;}
.dc-amt{font-weight:700;font-size:14.5px;font-variant-numeric:tabular-nums;white-space:nowrap;}
.dc-body .elist{border:none;border-radius:0;box-shadow:none;}
/* Open All on the left, Add on the right, one line. Two rows of buttons both
   pinned right read as an accident; a bar with a control at each end reads as
   a bar. Kept as its own rule rather than reusing .toolbar because this one
   has to survive an empty list, where the left half is deliberately blank. */
/* A vendor page's header: Back, the name, Delete — in that order across.
   Three columns with the two sides forced to the SAME width, because Delete is
   wider than Back and a plain space-between would push the name off centre by
   exactly that difference. Centred means centred or it looks like a mistake. */
/* The two side slots are `flex:1 1 0` with `min-width:0` so they come out the
   SAME width whatever is in them. That is the whole trick: Delete is wider than
   Back, and any layout that lets the sides size themselves pushes the name off
   centre by exactly that difference — 7px on a 390px phone, which is small
   enough to look like sloppiness rather than a decision. Equal slots, real
   centre. A button wider than its slot leans into the gap; it does not move
   the name. */
.vhead{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);
  align-items:center;gap:10px;margin:0 0 14px;}
.vh-l{justify-self:start;}
.vh-r{justify-self:end;}
/* Long names wrap rather than truncate. A vendor's name cut to "Mohammed
   Shahnawaz Ahmed Chikankar…" on the page that is about them is worse than a
   header one line taller. */
.vh-name{font-size:18px;margin:0;text-align:center;min-width:0;
  overflow-wrap:anywhere;line-height:1.3;}
/* On a narrow phone the word "Delete" is the difference between a centred name
   and a name with a button sitting on top of it. The icon says the same thing
   — a bin, in red — and the tap target does not shrink with the label. */
/* At 360px the three pieces do not fit at full size — Back ran 6px under the
   name. Rather than let them touch, each gives up a little: the name drops a
   step, the buttons lose some side padding, and Delete becomes its icon. The
   bin in red says "delete" on its own, and none of this touches the 42px the
   thumb needs. */
@media(max-width:430px){
  .vhead .btn-word{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap;}
  .vhead .btn.danger{min-width:44px;padding:10px 12px;}
  .vhead .btn.small{padding:10px 11px;}
  .vh-name{font-size:16px;}
}
/* Below 340px — an old SE, and the narrowest thing anyone still carries — even
   "Back" has to go to its arrow, or it climbs under the name. */
@media(max-width:340px){
  .vhead #backToVendors{font-size:0;min-width:44px;padding:10px 12px;}
  .vhead #backToVendors svg{font-size:1rem;}
}

.listbar{display:flex;justify-content:space-between;align-items:center;
  gap:10px;margin-bottom:10px;min-height:36px;}
.listbar > span{flex:1;}
.toggle-all{display:flex;justify-content:flex-end;margin-bottom:10px;}
.gtot{
  display:flex;align-items:center;gap:10px;margin-top:10px;padding:13px 14px;
  border:1px solid var(--brand-line);background:var(--brand-tint);
  border-radius:var(--radius);font-size:13.5px;
}
.gtot span{color:var(--text-secondary);font-weight:600;}
.gtot b{margin-left:auto;font-size:16px;color:var(--brand);font-variant-numeric:tabular-nums;}
.gtot .gtot-qty{flex-basis:100%;order:3;font-weight:500;color:var(--text-muted);font-size:12px;}
/* On a phone the four parts do not fit on one line, so the header becomes two:
   the date and the day's total on the first, the count and pieces underneath.
   The amount stays on the right where the eye already goes for it — the first
   attempt dropped it to its own line on the left, which read as a stray number
   rather than the total of the day above it. */
@media(max-width:520px){
  .dcard>summary,.dcard>.dc-head{
    grid-template-columns:14px minmax(0,1fr) auto;gap:2px 9px;padding:12px 12px;
  }
  .dc-date{grid-column:2;grid-row:1;}
  .dc-amt{grid-column:3;grid-row:1;text-align:right;}
  .dc-meta{grid-column:2 / -1;grid-row:2;}
}

/* ---------------- the outstanding working ---------------- */
/* The minus sign belongs on the number, where it reads as a subtraction. In
   front of the label it reads as a bullet point. */
.working{display:flex;flex-direction:column;}
.wrow{
  display:grid;grid-template-columns:1fr auto;gap:2px 12px;align-items:baseline;
  padding:14px 16px;border-bottom:1px solid var(--gridline);
  color:inherit;text-decoration:none;
}
a.wrow:hover,button.wrow:hover{background:var(--brand-tint);}
button.wrow{width:100%;background:none;border:none;border-bottom:1px solid var(--gridline);
  font-family:inherit;cursor:pointer;text-align:left;}
.w-label{font-weight:600;font-size:14.5px;}
.w-sub{grid-row:2;font-size:12px;color:var(--text-muted);}
.w-amt{grid-row:1 / span 2;align-self:center;font-size:16px;font-weight:700;
  font-variant-numeric:tabular-nums;white-space:nowrap;}
.wrow.wtotal{border-bottom:none;border-top:2px solid var(--text-primary);
  background:var(--brand-tint);}
.wrow.wtotal .w-amt{font-size:19px;}

/* ---------------- documents ---------------- */
.doclist{display:flex;flex-direction:column;gap:8px;}
.docrow{
  display:flex;align-items:center;gap:13px;padding:14px 15px;
  background:var(--surface-1);border:1px solid var(--border);border-radius:var(--radius);
  color:inherit;text-decoration:none;
}
.docrow:hover{background:var(--brand-tint);border-color:var(--brand-line);}
.docrow b{font-size:14.5px;display:block;}
.docrow .nav-ico{color:var(--brand);flex-shrink:0;}
.docrow .chev{margin-left:auto;color:var(--text-muted);flex-shrink:0;}
.doc{line-height:1.65;font-size:14.5px;}
.doc h2{font-family:var(--font-head);font-size:20px;margin:0 0 6px;}
.doc h3{font-size:15px;margin:22px 0 6px;color:var(--brand);
  border-top:1px solid var(--gridline);padding-top:16px;}
.doc h3:first-of-type{border-top:none;padding-top:6px;}
.doc p{margin:0 0 10px;}
.doc p.lead{color:var(--text-secondary);font-size:15px;margin-bottom:4px;}
.doc ul{margin:0 0 12px;padding-left:20px;}
.doc li{margin-bottom:6px;}
.doc pre{
  background:var(--panel);border:1px solid var(--border);border-radius:8px;
  padding:12px 14px;overflow-x:auto;font-size:13px;line-height:1.6;
  font-variant-numeric:tabular-nums;white-space:pre;margin:0 0 12px;
}
.doc .ver{color:var(--text-muted);font-size:12.5px;margin-top:18px;}

/* Who said it, above the message. Only ever on the other side's bubbles — your
   own messages do not need telling you who you are. */
.who-said{
  font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--brand);margin-bottom:3px;opacity:.85;
}
/* Both bubble styles sit on a pale ground — `.me` is the brand tint, not a solid
   fill — so the label takes the brand colour on both. It was white on `.me`,
   which on mint is very nearly invisible. */

/* ---------------- terms tick ---------------- */
.tickline{
  display:flex;align-items:flex-start;gap:10px;margin:4px 0 14px;
  font-size:13.5px;line-height:1.5;color:var(--text-primary);
  text-transform:none;letter-spacing:0;font-weight:500;cursor:pointer;
}
.tickline input{width:auto;margin:2px 0 0;flex-shrink:0;}
.terms-peek{margin-bottom:14px;}
/* The browser's default disclosure triangle is a raw ▸ that matches nothing
   else on the page; the same chevron the date cards use goes here instead. */
.terms-peek summary{
  display:flex;align-items:center;gap:8px;list-style:none;
  font-size:13px;font-weight:600;color:var(--brand);cursor:pointer;padding:5px 0;
}
.terms-peek summary::-webkit-details-marker{display:none;}
.terms-peek summary::marker{content:"";}
.terms-peek summary::before{
  content:"";width:7px;height:7px;flex:none;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:rotate(-45deg);transition:transform .15s;margin-left:2px;
}
.terms-peek[open] summary::before{transform:rotate(45deg);}
.terms-peek .doc{
  max-height:46vh;overflow-y:auto;padding:12px 14px;margin-top:8px;
  border:1px solid var(--border);border-radius:8px;background:var(--page);
  font-size:13.5px;
}
.accept-box{margin-top:14px;}

/* ---------------- date range ---------------- */
.rangebar{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 14px;
  padding:12px 14px;background:var(--surface-1);border:1px solid var(--border);
  border-radius:var(--radius);
}
.rangebar .label{font-size:11.5px;color:var(--text-muted);font-weight:600;}
/* One line, always. Five chips wrapping to two lines put "All" alone on a row
   of its own, which reads as a mistake rather than a choice. They stay on one
   line and the strip slides sideways on the narrowest phones instead — a line
   that breaks looks broken; a line that slides does not. */
.range-opts{
  display:flex;flex-wrap:nowrap;overflow-x:auto;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none;
  padding-bottom:1px;
}
.range-opts::-webkit-scrollbar{display:none;}
/* Five chips, one line, all of them whole.
   They had a floor width, so on a phone the fifth one hung over the edge of the
   card and "All" was cut in half — a chip you can see half of reads as a fault,
   not as a strip you can slide. They share the width instead: equal parts, and
   the type comes down a little on a narrow screen rather than the last chip
   falling off it. */
/* The strip takes the width it needs inside the bar. Left to the default it
   was handed whatever was spare after the label and the date boxes, and on a
   computer the five words were squeezed until each one was clipped. */
.range-opts{overflow-x:visible;flex:1 1 330px;min-width:0;}
/* One word on a wide screen, its short form on a narrow one. Both are in the
   page; only the screen decides. */
.lbl-short{display:none;}
/* Same reasoning as the row below: the space between chips is a margin, so a
   browser without flex `gap` still shows five separate chips. */
.range-opts .chip + .chip{margin-left:6px;}
.range-opts .chip{flex:1 1 0;min-width:0;text-align:center;justify-content:center;
  padding-left:4px;
  padding-right:4px;}
.chip{
  flex:0 0 auto;
  padding:5px 11px;border-radius:999px;font-size:12.5px;font-weight:600;
  border:1px solid var(--border);background:var(--surface-1);
  color:var(--text-secondary);text-decoration:none;white-space:nowrap;
}
.chip:hover{border-color:var(--brand-line);background:var(--brand-tint);}
.chip.on{background:var(--brand);border-color:var(--brand);color:#fff;}
/* The two typed dates wrap rather than run off the edge. A month input is wider
   than it looks — on a 390px phone the pair plus Apply does not fit on one line,
   and without this the button sits past the right edge of the card. */
/* The two date boxes, each under its own word. They used to be two blank
   rectangles beside Apply: on an iPhone a month field draws nothing at all
   until it holds a value, so nobody could tell what they were for — and they
   were empty until somebody guessed. They carry the period's own months now,
   and they say which end they are. */
/* The space between the three boxes is a margin, not `gap`. Flexbox gap is a
   newer property than the rest of this row, and where a browser does not honour
   it the three boxes end up touching — one long box divided by two hairlines,
   which is not what any of them is. A margin is understood everywhere. */
.range-custom{display:flex;align-items:flex-end;margin-left:auto;flex-wrap:wrap;}
.range-field + .range-field,.range-custom .btn{margin-left:8px;}
/* margin-bottom:0 overrides the 5px every label carries. With it, the two date
   boxes sat 5px higher than Apply — three boxes on one line, none of them
   level. */
.range-field{display:flex;flex-direction:column;gap:3px;flex:1 1 130px;min-width:0;
  margin-bottom:0;}
.range-field span{font-size:11px;color:var(--text-muted);font-weight:600;}
.range-custom input{
  width:100%;min-width:0;padding:6px 9px;font-size:12.5px;margin:0;
  border:1px solid var(--border);border-radius:8px;background:var(--surface-1);
  /* An iPhone gives a month field a mind of its own: a built-in width wide
     enough for "September 2026", which it keeps whatever the box around it
     says. Three of those on a 390px screen do not fit, so each one spilled a
     few pixels over its neighbour and the row read as one long box divided by
     hairlines. Turning the native control off lets the field be the size it is
     given, on that phone as everywhere else. */
  -webkit-appearance:none;appearance:none;
}
/* Belt and braces: the space between the boxes lives inside the label as well,
   so even a field that insists on being wide has somewhere to spill that is not
   the next box. */
.range-field{padding-right:2px;}
.range-custom .btn{flex:0 0 auto;}
@media(max-width:700px){
  .rangebar{flex-direction:column;align-items:stretch;}
  /* From, To and Apply stay on one line on a phone too. Three boxes on one
     row and a wrapped Apply below them are the same three controls; the second
     reads as the button having fallen off. */
  .range-custom{margin-left:0;width:100%;flex-wrap:nowrap;}
  .range-field + .range-field,.range-custom .btn{margin-left:6px;}
  .range-field{flex:1 1 0;}
  .range-custom input{padding-left:5px;padding-right:5px;}
  /* The box keeps its 16px — anything smaller and iOS zooms the whole page on
     a tap — while the month itself is set a little smaller inside it, which is
     what lets "September 2026" fit a third of a 360px screen whole. */
  .range-custom input::-webkit-datetime-edit{font-size:12.5px;}
  /* A 360px phone has 30px less to share out, and "September 2026" is the
     longest month there is: the type inside the box comes down again rather
     than the year being cut off. */
  @media(max-width:400px){
    .range-field + .range-field,.range-custom .btn{margin-left:5px;}
    .range-custom input{padding-left:3px;padding-right:3px;}
    .range-custom input::-webkit-datetime-edit{font-size:11px;}
    .range-custom .btn{padding-left:8px;padding-right:8px;}
  }
  /* The little calendar glyph is 24px of a 110px box and only Chrome on a
     computer needs it: on a phone the field itself opens the picker. */
  .range-custom input::-webkit-calendar-picker-indicator{display:none;}
  .range-custom .btn{padding-left:11px;padding-right:11px;}
  /* The bar stacks here, so its main axis is the height. The 330px basis that
     shares the width on a computer would set a 330px-TALL strip on a phone —
     five chips the height of the screen. On this axis the strip takes only the
     height it needs; the chips still share the width between them. */
  .range-opts{flex:0 0 auto;width:100%;}
}
@media(max-width:430px){
  /* Five whole words do not fit a 360px screen at any readable size, so the
     chips carry their short form instead — 3M, 6M, 9M, 12M — rather than
     shrinking the type past reading or letting the last one hang off the edge. */
  .range-opts .chip{padding:6px 2px;}
  .range-opts .lbl-long{display:none;}
  .range-opts .lbl-short{display:inline;}
}

/* ---------------- vendor mark ---------------- */
/* A colour and a chikankari motif per vendor. The shape is what makes a vendor
   findable in a list — three of them reduce to the initials "AC". */
.av.mark{position:relative;overflow:visible;}
/* The motif rides on a white disc at the corner, in the vendor's own colour.
   Behind the initials it was a texture you could not name; out here it is a
   shape you can find in a list without reading anything. */
.av.mark .mark-motif{
  position:absolute;right:-3px;bottom:-3px;
  width:48%;height:48%;min-width:15px;min-height:15px;
  background:var(--surface-1);border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 0 0 1.5px var(--surface-1),0 1px 2px rgba(22,33,30,.18);
}
.av.mark .mark-motif svg{width:74%;height:74%;}
.av.mark .mark-ini{position:relative;z-index:1;}

/* ---------------- tabs rendered as links ---------------- */
.tabs a.tab{
  border-bottom:2px solid transparent;margin-bottom:-1px;padding:11px 13px;
  font-size:14px;font-weight:500;color:var(--text-muted);text-decoration:none;
  white-space:nowrap;flex:0 0 auto;display:flex;align-items:center;gap:6px;
}
.tabs a.tab.active{color:var(--brand);border-bottom-color:var(--brand);font-weight:700;}

/* ---------------- clickable summary cards ---------------- */
/* The four cards on the home screen open the page behind them. They were always
   the thing people tapped first; until now nothing happened. */
a.tile,button.tile{
  text-decoration:none;color:inherit;cursor:pointer;font-family:inherit;
  text-align:left;transition:border-color .12s,transform .12s;
}
a.tile:hover,button.tile:hover{border-color:var(--brand-line);transform:translateY(-1px);}
a.tile:active,button.tile:active{transform:none;}

/* ---------------- tabs + totals strip ---------------- */
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--border);overflow-x:auto;
  scrollbar-width:none;-webkit-overflow-scrolling:touch;}
/* A vendor's tab strip stays put.
   It used to be an ordinary row halfway down the page: scroll the ledger and it
   was gone, tap a tab and the page jumped back to the top, so the strip appeared
   to leap down the screen. Worse, the strip's own sideways position reset on
   every tap — you slid right to reach Cycle, tapped it, and the strip snapped
   back to the left with the tab you had just chosen out of sight.
   Now it sticks under the header, and the app keeps its sideways position and
   the page's place. Only the list underneath changes. */
.tabs.vtabs{position:sticky;top:0;z-index:20;background:var(--page);}
/* A finger on this strip slides the strip, and nothing else.
   Without this the browser reads a drag on the tabs as a page scroll: the row
   went sideways and the whole page went up and down under it at the same time,
   so the tabs appeared to wander and slip behind the cards. pan-x says: this
   strip takes horizontal drags, the page keeps vertical ones — and a drag that
   starts here never becomes a page scroll. */
.tabs{touch-action:pan-x;overscroll-behavior-x:contain;}
@media(max-width:860px){
  .tabs.vtabs{top:var(--topbar-h);}
}
.tabs::-webkit-scrollbar{display:none;}
.tabs button{
  background:none;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;
  padding:11px 13px;font-size:14px;font-weight:500;color:var(--text-muted);
  cursor:pointer;font-family:inherit;white-space:nowrap;flex:0 0 auto;
}
.tabs button.active{color:var(--brand);border-bottom-color:var(--brand);font-weight:700;}
.tabtot{
  display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:11px 2px 14px;font-size:13.5px;color:var(--text-secondary);
}
.tabtot b{font-size:16px;font-weight:700;color:var(--text-primary);font-variant-numeric:tabular-nums;}

/* ---------------- charts ---------------- */
.chart-wrap{position:relative;}
/* Only set when the renderer decided the months will not fit — see
   renderMonthlyBars. Without the class the svg fills the box as before. */
.chart-wrap.chart-scrolls{overflow-x:auto;-webkit-overflow-scrolling:touch;}
.legend{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:10px;}
.legend-item{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);}
.legend-swatch{width:9px;height:9px;border-radius:2px;}
.bar-row{display:flex;align-items:center;gap:10px;margin-bottom:10px;}
.bar-row .name{width:120px;flex:0 0 120px;font-size:12.5px;color:var(--text-secondary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.bar-track{flex:1;height:20px;position:relative;background:var(--gridline);border-radius:5px;}
.bar-fill{position:absolute;top:0;left:0;height:20px;background:var(--brand);border-radius:5px;cursor:pointer;}
.bar-value{font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap;}
.chart-tooltip{
  position:absolute;background:var(--brand-dark);color:#fff;padding:7px 10px;
  border-radius:8px;font-size:12px;pointer-events:none;opacity:0;transition:opacity .1s;
  white-space:nowrap;z-index:60;line-height:1.5;
}
.chart-tooltip.show{opacity:1;}
@media(max-width:520px){
  .bar-row{flex-wrap:wrap;gap:4px 8px;}
  /* on a phone the name gets its own line, so long vendor names stay readable
     instead of all truncating to the same few letters */
  .bar-row .name{width:100%;flex:1 0 100%;font-size:12.5px;color:var(--text-primary);font-weight:500;}
  .bar-row .bar-track{height:14px;}
  .bar-row .bar-fill{height:14px;}
}

/* ---------------- modal & toast ---------------- */
/* The form arrives from below rather than appearing on top of the page.
   Two things move and only two — the darkening behind it, and the card itself
   — because those are the only two a phone can move without measuring the page
   again. 190ms, easing out: long enough to be seen going somewhere, short
   enough that a person opening twenty of these in a morning never waits for it.
   Anybody who has asked their phone for less movement gets none. */
.modal-backdrop{
  position:fixed;inset:0;background:rgba(22,33,30,.5);display:flex;
  align-items:center;justify-content:center;padding:18px;z-index:80;
  opacity:0;transition:opacity .19s ease-out;
}
.modal-backdrop.in{opacity:1;}
.modal{
  background:var(--surface-1);border-radius:var(--radius);padding:20px;
  width:100%;max-width:440px;max-height:90vh;overflow:auto;
  transform:translateY(14px);opacity:0;
  transition:transform .19s cubic-bezier(.22,.9,.3,1),opacity .19s ease-out;
}
.modal-backdrop.in .modal{transform:none;opacity:1;}
@media(prefers-reduced-motion:reduce){
  .modal-backdrop,.modal{transition:none;}
}
.modal h3{margin:0 0 15px;font-size:17px;}
.modal .acts{display:flex;gap:8px;justify-content:flex-end;margin-top:4px;}
.toast{
  position:fixed;bottom:20px;left:50%;transform:translate(-50%,10px);
  background:var(--brand-dark);color:#fff;padding:11px 18px;border-radius:10px;
  font-size:13.5px;opacity:0;transition:all .22s;pointer-events:none;z-index:99;max-width:90vw;
}
.toast.show{opacity:1;transform:translate(-50%,0);}

/* ---------------- server-rendered page bits ---------------- */
h1{font-size:19px;font-weight:700;letter-spacing:-.02em;}
.muted{color:var(--text-muted);font-size:12.5px;}
.scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;}
input.sm,select.sm{padding:8px 10px;font-size:13.5px;border-radius:8px;}
code{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em;
  background:var(--panel);padding:1px 5px;border-radius:5px;
}
.note{
  background:var(--panel);border:1px solid var(--border);border-left:3px solid var(--brand);
  border-radius:var(--radius-sm);padding:12px 14px;font-size:13px;line-height:1.55;
  color:var(--text-secondary);margin-bottom:14px;
}
.note.warn{border-left-color:var(--accent);}
/* The count above the vendors list is a link, so it must not read as body text
   somebody then has to hunt a button for. */
.nophone-note{display:block;text-decoration:none;color:var(--text-secondary);
  border-left-color:var(--owe);margin-bottom:12px;}
.nophone-note b{color:var(--owe);}
.nophone-note:hover{background:var(--owe-bg);}

/* ---------------- flash ---------------- */
.flash{
  border-radius:var(--radius-sm);padding:11px 14px;font-size:13.5px;margin-bottom:14px;
  border:1px solid var(--border);background:var(--surface-1);
}
.flash.error{background:var(--owe-bg);border-color:#E7C4BF;color:var(--owe);}
.flash.success{background:var(--clear-bg);border-color:#BFDDCE;color:var(--clear);}

.shell{display:flex;align-items:flex-start;min-height:100vh;}
.side{
  width:236px;flex:0 0 236px;min-height:100dvh;position:sticky;top:0;
  background:var(--surface-1);border-right:1px solid var(--border);
  display:flex;flex-direction:column;padding:16px 12px;z-index:40;
}
/* The brand is a link on every panel. Tapping the studio name goes home, from
   anywhere — the one thing everybody already tries. */
.brand{display:flex;flex-direction:column;padding:6px 10px 18px;
  text-decoration:none;color:inherit;}
.brand b{font-family:var(--font-head);font-size:15px;font-weight:600;letter-spacing:.02em;line-height:1.25;}
.brand span{font-size:11.5px;color:var(--text-muted);margin-top:2px;}

/* The account manager, in the vendor's own menu. Read-only. */
.mgr-card{margin:14px 10px 0;padding:12px;border-radius:10px;
  background:var(--brand-tint);border:1px solid var(--brand-line);}
.mgr-card .label{font-size:10.5px;color:var(--text-muted);font-weight:600;
  letter-spacing:.04em;text-transform:uppercase;}
.mgr-card b{display:block;font-size:14px;margin-top:3px;}
.mgr-num{font-size:13px;color:var(--text-secondary);font-variant-numeric:tabular-nums;
  margin-top:1px;}
.mgr-btns{display:flex;gap:6px;margin-top:9px;}
.mgr-btns .btn{flex:1;justify-content:center;}
/* Scoped to .side on purpose. A bare `nav` selector here also caught the vendor
   portal's bottom tab bar — which is a <nav> too — and stacked its four icons in
   a column. Sidebar rules stay inside the sidebar. */
.side nav{display:flex;flex-direction:column;gap:2px;}
.side nav a,.side nav button{
  display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:none;border:none;cursor:pointer;font-family:inherit;
  padding:11px 12px;border-radius:var(--radius-sm);
  font-size:14px;font-weight:500;color:var(--text-secondary);text-decoration:none;
}
.side nav a:hover,.side nav button:hover{background:var(--page);color:var(--text-primary);}
.side nav a.active,.side nav button.active{
  background:var(--brand-tint);color:var(--brand);font-weight:700;
}
.side nav .nav-ico{display:inline-flex;width:19px;flex:none;}
.side nav .nav-ico svg{width:19px;height:19px;}
.nav-badge{margin-left:auto;background:var(--owe);color:#fff;font-size:10.5px;
  font-weight:700;border-radius:999px;padding:1px 7px;}
.side-spacer{flex:1;}
.side-foot{border-top:1px solid var(--border);margin-top:12px;padding-top:12px;}
.side-foot .who{padding:0 12px 8px;}
.side-foot .who b{display:block;font-size:13px;}
.side-foot .who span{display:block;font-size:11.5px;color:var(--text-muted);text-transform:capitalize;}

.main-col{flex:1;min-width:0;}
main{max-width:1120px;margin:0 auto;padding:22px 20px 64px;}

/* the green header bar — phone only on the admin, always on the portal */
.topbar{
  display:none;align-items:center;gap:12px;height:var(--topbar-h);
  padding:0 14px;background:var(--brand);color:#fff;
  position:sticky;top:0;z-index:45;
}
.topbar b{font-family:var(--font-head);font-size:16px;font-weight:600;letter-spacing:.02em;}
.topbar .tb-ico{display:inline-flex;color:#fff;background:none;border:none;padding:6px;
  margin:-6px;cursor:pointer;}
.topbar .tb-ico svg{width:21px;height:21px;}
.topbar .right{margin-left:auto;}
.tb-title,.tb-brand{display:flex;flex-direction:column;line-height:1.18;min-width:0;}
.tb-title span,.tb-brand span{font-size:11px;opacity:.8;margin-top:1px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;}
.tb-brand{text-decoration:none;color:inherit;}

/* ---- the header carries the page ----
   Away from Home the green bar stops saying the studio's name — which is the
   one thing nobody needs told twice — and says where you are instead: the way
   back on the left, the page's own name in the middle, and the page's one
   destructive action on the right. Those three used to sit in the page itself,
   under the bar, and cost an inch of screen on every screen. */
/* On a computer the green bar is normally hidden — the sidebar says everything
   it would. A page header is different: it carries the way back and the page's
   one action, and those have to exist at every width, so this bar shows even
   where the plain one does not. */
.topbar.tb-page{display:flex;gap:8px;}
.tb-home{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto;}
/* These slots are swapped by hiding them, and every one of them is given a
   display by a rule further up — which beats the browser's own [hidden] and
   left Home wearing both headers at once. */
.topbar [hidden]{display:none !important;}
.topbar .tb-back{flex:0 0 auto;}
.topbar .tb-name{
  flex:1 1 auto;min-width:0;text-align:center;
  font-family:var(--font-head);font-size:16px;font-weight:600;letter-spacing:.02em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* A second line under the name, for the one thing a page's name cannot say —
   whether a query is open or settled. Same shape as the studio's own name and
   "Admin" on Home. */
.topbar .tb-name span{display:block;font-family:var(--font-body);font-size:11px;
  font-weight:500;opacity:.8;letter-spacing:0;margin-top:1px;}
/* The right slot keeps its width even when it holds nothing, so the name is
   centred on the screen rather than centred on what is left over. */
.topbar .tb-act{flex:0 0 33px;display:flex;justify-content:flex-end;gap:10px;}
.topbar .tb-act:has(> * + *){flex:0 0 auto;}
.topbar .tb-act form{display:flex;}
.topbar .tb-act .tb-ico{color:#fff;}
/* Delete is the one action that cannot be undone by tapping again. Red on
   green does not read, so it keeps the icon and loses the colour; the confirm
   step is what actually protects the ledger. */
.topbar .tb-ico.tb-danger svg{width:20px;height:20px;}

.scrim{position:fixed;inset:0;background:rgba(22,33,30,.45);z-index:49;
  opacity:0;pointer-events:none;transition:opacity .18s;}
body.drawer-open .scrim{opacity:1;pointer-events:auto;}

@media(max-width:860px){
  .shell{flex-direction:column;}
  .topbar{display:flex;}
  .side{
    /* 100dvh, not 100vh. On a phone browser 100vh counts the space behind the
       address bar as well, so the drawer was taller than the screen and its
       last line — Log out — sat behind that bar with no way to scroll to it. */
    position:fixed;top:0;left:0;bottom:0;width:270px;flex:none;min-height:100dvh;
    transform:translateX(-100%);z-index:50;overflow-y:auto;
    box-shadow:0 0 40px rgba(22,33,30,.16);
    /* The page is drawn under the status bar (viewport-fit=cover), so a drawer
       that starts at top:0 starts UNDER the clock — the studio name and the
       time print on top of each other and neither can be read. The phone tells
       us how much room to leave; take it. Same at the bottom, where the home
       bar would otherwise sit on top of Log out. */
    padding-top:calc(16px + env(safe-area-inset-top));
    /* Room for the browser's own bar as well as the phone's home bar. */
    padding-bottom:calc(56px + env(safe-area-inset-bottom));
    /* visibility keeps the closed drawer out of the tab order, not just off-screen */
    visibility:hidden;transition:transform .2s ease,visibility .2s;
  }
  body.drawer-open .side{transform:none;visibility:visible;}
  .main-col{width:100%;max-width:100vw;min-width:0;}
  main{max-width:100%;padding:15px 13px 56px;}
}

/* ---- the conversation itself ---- */
.thread{display:flex;flex-direction:column;gap:8px;padding:4px 0 12px;}
.daysep{align-self:center;background:var(--panel);border:1px solid var(--border);
  color:var(--text-muted);font-size:11px;font-weight:600;padding:3px 11px;border-radius:999px;margin:8px 0 2px;}
.bub{
  max-width:82%;padding:9px 12px 7px;border-radius:14px;font-size:14.5px;line-height:1.45;
  overflow-wrap:anywhere;
}
.bub .meta{display:flex;align-items:center;justify-content:flex-end;gap:4px;
  font-size:10.5px;color:var(--text-muted);margin-top:3px;}
.bub.them{background:var(--surface-1);border:1px solid var(--border);align-self:flex-start;
  border-bottom-left-radius:5px;}
.bub.me{background:var(--brand-tint);border:1px solid var(--brand-line);align-self:flex-end;
  border-bottom-right-radius:5px;}
.tick{display:inline-flex;}
.tick svg{width:15px;height:15px;}
.tick.sent,.tick.delivered{color:var(--text-muted);}
.tick.read{color:#1E88E5;}
.composer{
  /* Held above whatever owns the bottom of the screen — the portal's tab bar,
     or nothing at all on the studio's side. One variable, so this page does
     not have to know which side it is on. */
  position:sticky;bottom:0;
  display:flex;gap:8px;align-items:flex-end;padding:10px 0 4px;background:var(--page);
  /* Pushed to the bottom of a full-height page. Sticky alone was not enough:
     sticky only holds something back that would otherwise scroll away, and a
     conversation with one message in it has nothing to scroll — so the box sat
     just under that message, halfway up the screen, with the rest of the page
     blank beneath it. */
  margin-top:auto;
}
/* 16px, not 15. Below 16 an iPhone zooms the whole page the moment you tap a
   field, and then the studio is looking at a magnified corner of their own
   app. The rest of the app was fixed for this; this one box kept its 15px and
   is more specific, so it won. One pixel. */
.composer textarea{flex:1;min-height:44px;max-height:130px;resize:none;padding:11px 13px;font-size:16px;}
.composer .btn{flex:none;padding:12px 14px;}

/* ---- a conversation fills the screen ---- */
/* The box belongs at the bottom of the screen whether the conversation has one
   message in it or a hundred. That needs a page that is at least a screen
   tall, which is what this chain of flex boxes buys. */
body.chatting .main-col{display:flex;flex-direction:column;min-height:100dvh;}
/* The page keeps the bar's height as padding rather than as a shorter box. Two
   faults fall out of getting this wrong, and both have happened: a short
   conversation leaves its message box 80px above the bar, and a long one ends
   underneath the bar with its last inch unreachable. */
body.chatting main{flex:1 1 auto;display:flex;flex-direction:column;
  padding-bottom:calc(8px + var(--tab-clear));}
/* The conversation grows from the bottom. With one message in it the thread
   used to sit at the top of a screen-tall page and the box was stranded below
   a field of nothing; every chat anybody has ever used puts the newest words
   by the box you type in. */
body.chatting .thread{flex:1 1 auto;justify-content:flex-end;}

/* ---- the PIN, on the card it belongs to ---- */
/* Shown once, beside the vendor it is for. It used to be a banner at the top
   of a page of twelve cards, which meant reading a name off the banner and
   then hunting for the card — with the one number that must not be misread. */
.freshpin{background:var(--brand-tint);border:1px solid var(--brand-line);
  border-radius:10px;padding:10px 12px;margin:0 0 12px;}
.freshpin b{display:block;font-size:23px;letter-spacing:.14em;color:var(--brand);
  font-variant-numeric:tabular-nums;margin:2px 0 4px;}
.freshpin .muted{font-size:12px;}
.freshpin code{overflow-wrap:anywhere;}

/* ---- Back, in the same corner of every page ---- */
/* One habit instead of one control per screen: upper left, same word, same
   shape, everywhere but Home. It goes back to wherever you came from, so
   opening something from a list returns you to that list rather than to the
   top of the app. */
.pagehead{display:flex;align-items:center;gap:10px;margin:0 0 12px;}
.pagehead .btn{flex:none;}

/* ---- a conversation uses the whole width ---- */
/* Back should touch the left edge and Delete the right one, and the message box
   should run from edge to edge. The page's own side margin was holding all
   three inside an inch of cream, which on a 390px screen is width the
   conversation could have used. The bubbles and cards keep their inset; only
   the two bars give it up. */
@media(max-width:700px){
  /* .portal main is declared later in the portal's own sheet, so this has to
     name it too — otherwise the vendor's side keeps its margin and only the
     studio's side goes edge to edge. */
  /* width:100% as well: with the padding gone, `margin:0 auto` on a flex item
     lets it shrink to its own content, and the whole conversation ended up in a
     250px column down the middle of the screen. */
  body.chatting main,body.portal.chatting main{padding-left:0;padding-right:0;
    width:100%;max-width:100%;margin-left:0;margin-right:0;}
  body.chatting main > *:not(.chead):not(.composer){padding-left:13px;padding-right:13px;}
  /* A hair of padding, not a margin. At a flat zero the outer edge of Back, of
     Delete and of the message box sat exactly on the screen edge and their own
     borders were shaved off, which reads as a broken box rather than a wide
     one. 5px keeps the border whole and gives up none of the width that
     mattered. */
  body.chatting .chead,body.chatting .composer{padding-left:5px;padding-right:5px;}
}

/* ---- the header of a conversation ---- */
/* Back, who, where it stands, and the actions — one line. Mark Resolved used
   to take a row to itself, which on a phone is a whole strip of screen for one
   button. Below 430px the buttons drop their words and keep their icons: the
   line holds, which is what matters when you are looking for the same control
   in the same place every time. */
.chead{flex-wrap:nowrap;gap:8px;}
.chead .chead-name{font-size:15.5px;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.chead .chead-end{margin-left:auto;display:flex;gap:6px;flex:none;}
.chead form{display:flex;}
@media(max-width:430px){
  .chead .btn-word{position:absolute;width:1px;height:1px;overflow:hidden;
    clip:rect(0 0 0 0);white-space:nowrap;}
  .chead .btn{padding:9px 11px;min-width:42px;justify-content:center;}
}
/* Resolving must not move the page.
   The flash that follows it — "Query marked resolved" — used to push the whole
   conversation down by its own height, so a tap that changes one word appeared
   to redraw the screen. It floats now: same message, no layout. */
body.chatting .flash{position:fixed;left:50%;transform:translateX(-50%);
  /* Below the green bar, never over it. It used to be measured from the bar's
     own height alone, which forgets the strip the phone keeps for its clock —
     so on a phone the note landed on the studio's name. */
  top:calc(var(--topbar-h) + env(safe-area-inset-top) + 10px);
  z-index:60;margin:0;max-width:88vw;
  box-shadow:0 3px 10px rgba(22,33,30,.14);}

/* ---- a message that was taken down, and one that was corrected ---- */
.bub.gone{background:var(--panel);border:1px dashed var(--border);color:var(--text-muted);
  font-style:italic;}
.bub .edited{color:var(--text-muted);font-size:10.5px;}
.bub-act{display:flex;gap:10px;align-items:center;margin-top:4px;justify-content:flex-end;}
.linky{background:none;border:none;padding:0;font:inherit;font-size:11.5px;font-weight:600;
  color:var(--brand);cursor:pointer;text-decoration:underline;text-underline-offset:2px;}
.bub-edit{margin-top:6px;}
.bub-edit textarea{width:100%;font-size:16px;padding:8px 10px;resize:none;}
@media(max-width:860px){
  body.chatting .main-col{
    min-height:calc(100dvh - var(--topbar-h) - env(safe-area-inset-top));}
}

.tabbar{
  position:fixed;left:0;right:0;bottom:0;z-index:35;
  display:flex;flex-direction:row;   /* stated outright so nothing can flip it */
  background:var(--surface-1);border-top:1px solid var(--border);
  padding-bottom:env(safe-area-inset-bottom);
}
/* 12px, not 10.5. This is the main navigation and it is on every screen
   anybody ever sees — it was the smallest type in the whole app. */
.tabbar a{
  flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:9px 2px 9px;font-size:12px;color:var(--text-muted);min-width:0;text-decoration:none;
  min-height:56px;justify-content:center;
}
.tabbar a svg{width:22px;height:22px;}
.tabbar a.on{color:var(--brand);font-weight:700;}
.tabbar a span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* Five names on a narrow phone: the type comes down a touch rather than the
   words being cut. */
@media(max-width:400px){
  .tabbar a{font-size:11px;gap:2px;}
  .tabbar a svg{width:20px;height:20px;}
}
/* Whoever has the bar owes it the room at the bottom of the page. */
body.portal,body.has-tabs{--tab-clear:calc(74px + env(safe-area-inset-bottom));
  padding-bottom:var(--tab-clear);}
/* A conversation page already reserves the bar's height inside its own
   min-height. Counted twice it made the page 74px taller than the screen, so
   every thread opened scrolled down with its own header pushed out of sight.
   The page gives the padding back and the message box clears the bar itself. */
body.chatting{padding-bottom:0;}
body.chatting .composer{bottom:var(--tab-clear);}

/* The studio's own bar is for the phone. On a computer the sidebar is right
   there and a bar across the bottom of a wide screen is furniture. */
@media(min-width:861px){
  body.has-tabs{--tab-clear:0px;padding-bottom:0;}
  body.has-tabs .tabbar{display:none;}
}

/* Clearance for the fixed tab bar, which is 56px of touch target plus its
   border. Kept a few pixels ahead of it so the last row on a page is never
   half-hidden behind it. */
/* The portal's tab bar: 56px of touch target plus its border, kept a few
   pixels ahead of itself so the last row on a page is never half-hidden.
   Scoped to `body.portal`, not to every body: the studio's own pages load this
   same stylesheet and have no tab bar, so a blanket rule gave every admin page
   74px of dead space at the bottom — and told a full-height page to leave room
   for something that was not there. */

.portal .topbar{display:flex;padding-top:env(safe-area-inset-top);
  height:calc(var(--topbar-h) + env(safe-area-inset-top));}
/* Fixed to the SCREEN, never to the page — and clear of the status bar at the
   top and the home bar at the bottom. `height:100dvh` rather than 100vh so the
   drawer does not grow behind the browser's own bars as they collapse and
   reappear, which is what made it look like it drifted when you came back to a
   long page like Home. */
.portal .side{position:fixed;top:0;left:0;bottom:0;height:100dvh;
  width:272px;flex:none;z-index:50;
  transform:translateX(-100%);visibility:hidden;transition:transform .2s ease,visibility .2s;
  box-shadow:0 0 40px rgba(22,33,30,.16);overflow-y:auto;overscroll-behavior:contain;
  padding-top:calc(16px + env(safe-area-inset-top));
  padding-bottom:calc(16px + env(safe-area-inset-bottom));}
body.drawer-open .side{transform:none;visibility:visible;}
.portal main{max-width:640px;padding:15px 13px 24px;}
.lang-row{display:flex;gap:6px;padding:0 12px 4px;}
.lang-row a{
  flex:1;text-align:center;padding:8px;border-radius:9px;font-size:13px;font-weight:600;
  border:1px solid var(--border);color:var(--text-secondary);
}
.lang-row a.on{background:var(--brand);border-color:var(--brand);color:#fff;}


/* ---- server-rendered expandable rows: <details> does the work, no JS ---- */
.elist details{border-bottom:1px solid var(--gridline);}
.elist details:last-of-type{border-bottom:none;}
/* Four columns, matching the four cells the row actually renders.
   This had five, left over from when every row began with its own date. The
   date moved up into the card header, the cell was removed from the markup,
   and this rule was missed — so every cell sat one column to the left of where
   it belonged: the item name was squeezed into the old date column (56px on a
   phone, enough for "Mul Print…"), the quantity sprawled across the old item
   column, and 74px of dead space sat on the right. It is the single worst
   thing on the vendor's most-used screen and it is invisible in the markup. */
.elist summary{
  display:grid;grid-template-columns:minmax(0,1fr) 46px 62px 84px;
  gap:9px;align-items:center;padding:11px 13px;font-size:13.5px;
  cursor:pointer;list-style:none;
}
.elist summary::-webkit-details-marker{display:none;}
.elist summary::marker{content:"";}
.elist details[open] summary{background:var(--brand-tint);}
.elist summary:hover{background:var(--brand-tint);}
@media(max-width:400px){
  .elist summary{grid-template-columns:minmax(0,1fr) 36px 52px 72px;gap:6px;padding:12px 10px;}
}
/* Month by month keeps five, because there the month IS the first column. The
   selector has to out-rank the four-column rule above, which the portal sheet
   appends after the base one. */
.elist.mlist summary{grid-template-columns:minmax(0,1fr) 1fr 1fr 1fr 1fr;}

/* ---- query list ---- */
.qrow{
  display:flex;gap:11px;align-items:flex-start;padding:13px 14px;
  border-bottom:1px solid var(--gridline);text-decoration:none;color:inherit;
}
.qrow:last-child{border-bottom:none;}
.qrow:hover{background:var(--brand-tint);}
.qrow .qmain{flex:1;min-width:0;}
.qrow .qtop{display:flex;align-items:center;gap:8px;margin-bottom:3px;}
.qrow .qsub{font-size:11.5px;color:var(--text-muted);}
.qrow .qlast{
  font-size:13.5px;color:var(--text-secondary);margin-top:3px;
  display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;
}
.qrow .qtime{font-size:11px;color:var(--text-muted);white-space:nowrap;text-align:right;}

/* ---- simple column chart (server-rendered, no library) ----
   Every column carries its own amount and its own month, and each one has a
   floor width. Past that the chart scrolls sideways.

   It used to divide the width by however many months there were. At six that
   was fine. At twenty-seven — which is what "All" gives a vendor who has been
   with the studio two years — each column fell to 5px, the amounts printed
   above them overlapped into an unreadable smear, and the months underneath
   were clipped to single letters: "J. J. A. S. C. N. C. J. F.". The chart got
   worse the more it had to show, which is exactly backwards.

   The month label also moved INSIDE the column. It used to live in a second
   row underneath, which meant two independently-sized lists that only lined up
   while the counts happened to match — the label could never follow its bar
   into a scroller. */
/* Snap so a swipe lands on whole columns rather than leaving a sliced one at
   the edge. `proximity`, not `mandatory` — a short flick should still be able
   to nudge the strip rather than being yanked to the next stop. */
.mchart-wrap{
  overflow-x:auto;-webkit-overflow-scrolling:touch;margin-top:4px;
  scroll-snap-type:x proximity;
}
.mcol{scroll-snap-align:end;}
.mchart{display:flex;align-items:flex-end;gap:7px;min-height:140px;padding-bottom:2px;}
.mcol{
  flex:1 0 46px;min-width:46px;display:flex;flex-direction:column;
  align-items:center;justify-content:flex-end;gap:5px;
}
.mcol i{width:100%;max-width:30px;border-radius:4px 4px 0 0;display:block;}
.mcol b{font-size:11px;font-weight:600;color:var(--text-secondary);white-space:nowrap;}
.mcol span{font-size:11.5px;color:var(--text-muted);white-space:nowrap;}

/* ---- chat ---- */
/* ---- rating ---- */
.stars{display:flex;gap:6px;margin:4px 0 12px;}
.stars input{position:absolute;opacity:0;width:0;height:0;}
.stars label{margin:0;cursor:pointer;color:var(--border);padding:2px;}
.stars label svg{width:31px;height:31px;}
.stars:hover label svg{color:inherit;}
.stars label.on{color:var(--accent);}
.stars label.on svg{fill:var(--accent);}
.rating-done{display:flex;align-items:center;gap:8px;font-size:13.5px;}
.rating-done .filled{color:var(--accent);display:inline-flex;}
.rating-done .filled svg{width:17px;height:17px;fill:var(--accent);}
.rating-done .hollow{color:var(--border);display:inline-flex;}
.rating-done .hollow svg{width:17px;height:17px;}

@media(max-width:520px){

  /* The conversation header stays one line even here. .toolbar wraps by
     design; this one must not, or Resolve drops onto a row of its own again. */
  .toolbar.chead{flex-wrap:nowrap;}

  /* ---- tables give back their side padding ---- */
  /* 12px each side of five columns is 120px of a 390px screen spent on air,
     and it was the last column — the month's own total — that fell off the
     edge for it. */
  .card.flush table th,.card.flush table td{padding-left:7px;padding-right:7px;}

  /* ---- less chikankari on a small screen ---- */
  /* A phone has little room to spare, so the hem becomes one buti in the
     middle rather than a run of them, and the empty-page flower comes down a
     size. On a computer there is real blank to fill and the run stays. */
  main::after{background-repeat:no-repeat;}
  .empty::before{width:46px;height:46px;margin-bottom:10px;}

  /* ---- readable secondary type ---- */
  .tile .label{font-size:13px;}
  .tile .sub{font-size:12.5px;}
  .tile .value{font-size:22px;}
  .pmeta,.pname + .pmeta{font-size:12.5px;}
  .pamt .tag{font-size:12.5px;}
  .badge{font-size:12px;padding:4px 10px;}
  .brand span,.tb-brand span,.tb-title span{font-size:12.5px;}
  .muted,.cap,.doc .ver{font-size:13px;}
  label{font-size:13px;}
  .edet dt{font-size:13px;}
  .dc-meta{font-size:12.5px;}
  .w-sub{font-size:12.5px;}
  .gtot .gtot-qty{font-size:12.5px;}
  .legend-item,.bar-value{font-size:12.5px;}
  .qrow .qtime{font-size:12px;}
  .daysep{font-size:12px;}
  .mcol b{font-size:11.5px;}
  .mfoot span{font-size:11.5px;}
  .nav-badge{font-size:11.5px;}
  .who span,.who-said,.bub .meta{font-size:12px;}
  th,th.num{font-size:12.5px;}

  /* The item column was still losing long names in the admin app's own rows —
     the same four columns, but the fixed three had never been trimmed for a
     phone the way the portal's were. */
  .ehead,.erow{grid-template-columns:minmax(0,1fr) 36px 52px 72px;gap:6px;}
  /* .tile .label is deliberately NOT in this list — it is set to 13px above,
     and repeating it here at a smaller size silently undid that. */
  .mgr-card .label,.rangebar .label{font-size:12.5px;}
  .doc{font-size:15px;}
  .doc p.lead{font-size:15.5px;}

  /* ---- controls a thumb can actually hit ---- */
  .btn.small{min-height:42px;padding:10px 15px;font-size:13.5px;}
  .btn{min-height:46px;}
  /* justify-content as well as align-items: the chip becomes a flex box here to
     hold a 40px thumb target, and inside a flex box `text-align:center` does
     nothing — the word sat against the left edge of every chip. */
  .chip{min-height:40px;display:inline-flex;align-items:center;justify-content:center;
    font-size:13.5px;padding:0 14px;}
  /* The five period chips share one line and give up padding rather than
     height — the thumb target stays 40px either way. Below 430px the type
     comes down too: "12 Months" has to fit inside its own chip, and a chip
     with its name cut off is worse than a slightly smaller word. */
  .range-opts .chip{font-size:12.5px;padding:0 6px;}
  @media(max-width:430px){
    .range-opts .chip{font-size:12.5px;padding:0 2px;}
    .range-opts .lbl-long{display:none;}
    .range-opts .lbl-short{display:inline;}
  }
  input[type=text],input[type=tel],input[type=number],input[type=date],
  input[type=month],input[type=password],input[type=search],select,textarea{
    min-height:46px;font-size:16px;   /* 16px stops iOS zooming the page on focus */
  }
  /* 16px, not 14: a date field under 16px makes iOS zoom the page the moment
     it is tapped, and the month inside it is sized separately above. */
  .range-custom input{min-height:42px;font-size:16px;}
  .tb-ico{min-width:44px;min-height:44px;display:inline-flex;align-items:center;
    justify-content:center;margin:0 -8px;}
  .sec-head a,.sec-head button.more{min-height:40px;display:inline-flex;
    align-items:center;font-size:13.5px;}
  .tabs a.tab,.tabs button{min-height:46px;}
  .elist summary,.erow{min-height:48px;}
  .side nav a,.side nav button{min-height:46px;}
  .tickline{min-height:44px;align-items:center;}
  .tickline input,.acc-item input{width:22px;height:22px;}
  .acc-item{min-height:40px;}

  /* ---- a long vendor name wraps rather than being cut off ---- */
  .pname{white-space:normal;overflow:visible;text-overflow:clip;line-height:1.3;}

  /* ---- worked examples wrap instead of scrolling sideways ---- */
  .doc pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12.5px;line-height:1.7;}
}
