/* ═══════════════════════════════════════════════════════════════
   DASHBOARD — Styles specific to index.php (dashboard page)
   Extracted from inline <style> blocks
═══════════════════════════════════════════════════════════════ */

/* ── Table value colors (match chart bars) ── */
.val-amount  { font-size: 12px; font-weight: 600; color: #1a5fa8; white-space: nowrap; }
.val-cost-on { font-size: 12px; font-weight: 600; color: #c85028; white-space: nowrap; }
.val-cost-off{ font-size: 12px; font-weight: 500; color: rgba(200,80,40,0.38); white-space: nowrap; }
.val-profit  { font-size: 12px; font-weight: 600; color: #008a78; white-space: nowrap; }
.val-profit-neg { font-size: 12px; font-weight: 600; color: #b52020; white-space: nowrap; }
.profit-pct { font-size: 10.5px; font-weight: 600; color: #008a78; margin-left: 4px; }

/* ── Product thumbnail, and its fallback ──
   The icon ships in every row and stays hidden. Revealing it is then a class
   toggle rather than markup built inside an onerror attribute, and there is no
   second request to fail — see the note in sales_ajax.php.

   Two ways it shows: `is-shown` from the server when the row has no image at
   all, and the sibling rule when a real path 404s. */
.product-img-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 35px;
  height: 35px;
}
/* Scoped through the wrapper on purpose. `.pos-icon` in index.css sets
   display:inline-block at the same specificity as a bare .product-img-fallback
   would have, so the hide would depend on index.css being loaded first — true
   today, and not something a stylesheet reorder should be able to break. */
.product-img-wrap .product-img-fallback {
  display: none;
  color: rgba(90, 105, 130, 0.55);
}
.product-img-wrap .product-img-fallback.is-shown { display: block; }
.product-img-wrap .product-img.is-broken { display: none; }
.product-img-wrap .product-img.is-broken ~ .product-img-fallback { display: block; }

/* ── Pay type column ── */
.pay-type-ico {
  width: 26px;
  height: 26px;
  object-fit: contain;
  vertical-align: middle;
  display: inline-block;
}
/* The pill hangs off the icon, not off the cell, so it is centred on the mark
   rather than on the column. line-height:0 keeps the inline box tight to the
   26px image — without it the host is a text line tall and the pill floats
   clear of the icon it names.

   Pills elsewhere are two words and never wrap; the Acima one is a full
   sentence, and nowrap would run it out past the column on both sides. Here
   they wrap to a second line instead, which is why this rule exists at all. */
.pay-type-host { display: inline-block; line-height: 0; }
.pay-type-host > .pos-pop {
  /* width, not just max-width. The pill is absolutely positioned, so it
     shrink-to-fits against its containing block — and that block is the host,
     which is only as wide as the 26px icon. Left to itself with white-space
     normal it wraps to one word per line. max-content gives it its natural
     width and max-width is then what actually bites. */
  width: max-content;
  max-width: 190px;
  white-space: normal;
  line-height: 1.35;
  text-align: center;
}

/* NULL pay_type on historical rows.
 Quiet on purpose — an unknown value
   shouldn't compete with the rows that actually recorded one. */
.pay-type-none {
  font-size: 13px;
  color: rgba(120, 140, 170, 0.45);
}
body.dark-mode .pay-type-none { color: rgba(148, 163, 184, 0.45); }

/* ── Deductibles: weekly funding strip ──
   One dot per trading day, Mon–Sat, showing which days this category received
   money. Sunday is omitted — the shop is closed, and a dot that can never fill
   would read as a permanent miss. */
.ded-cat-row {
  padding: 9px 10px;
  border-bottom: 1px solid var(--glass-border);
}
/* Six EQUAL columns rather than a flex row: equal tracks keep the letters in
   the same six positions on every category, so the week reads as a column down
   the card. Sizing to content would let a $1.2k Thursday shove that row's
   letters out of line with the one above. */
.ded-week {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 2px;
  /* Indented to clear the ✕ and status dot, so the strip starts under the
     category name rather than under the row edge. */
  margin: 7px 0 1px 26px;
}
.ded-day {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--font-ui);
  line-height: 1;
  color: var(--text-secondary);
}
.ded-day b {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: .04em;
  opacity: .55;
}
.ded-day em {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 9.5px;
  font-style: normal;
  font-weight: 600;
  opacity: .4;
}
/* Funded: the same green the amounts use, so the strip and the figure agree.
   #008a5a is the + button's green and the money-in arrow's green — a day with
   money put aside is money in from where the operator sits, and it should wear
   the colour the rest of the card already gives that. */
.ded-day.on b,
.ded-day.on em { color: #008a5a; opacity: 1; }
/* Missed: a day that has been and gone with nothing put aside. Red, but only
   the hue changes — the .55/.4 opacities above still do the muting, so a week
   of blanks stays quiet instead of turning into a wall of alarm beside the one
   green day that matters.

   Colour only, deliberately — the .55/.4 above is already the muting we want,
   and this selector outranks every rule that sets opacity on these elements, so
   any opacity declared here would silently win over all of them.

   :not(.today) carries real meaning. Red means missed, and today is not missed
   at 4pm — it is unfinished. Painting it red would say the day was blown before
   it is over, which is exactly the distinction the underline below exists to
   draw. Today goes red on its own tomorrow. */
.ded-day:not(.on):not(.future):not(.today) b,
.ded-day:not(.on):not(.future):not(.today) em { color: #b52020; }

/* Not yet reachable — distinct from a day that was missed, hence no red. */
.ded-day.future b,
.ded-day.future em { opacity: .22; }
/* Underline marks today, so an empty cell reads as "not done YET" rather than
   "missed" — very different things at 4pm on a Friday. */
.ded-day.today b {
  opacity: 1;
  border-bottom: 1.5px solid rgba(0, 138, 90, 0.55);   /* #008a5a */
  padding-bottom: 1px;
}

/* ── Seller rows ── */
.seller-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--glass-border);
}
.seller-row:last-child { border-bottom: none; }
.seller-rank {
  width: 20px; height: 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700;
  flex-shrink: 0;
}
.seller-rank-1 { background: rgba(255,185,0,0.18); color: #b38600; }
.seller-rank-2 { background: rgba(160,170,180,0.18); color: #6b7280; }
.seller-rank-3 { background: rgba(180,100,50,0.18); color: #a0522d; }
.seller-rank-4 { background: rgba(100,100,100,0.10); color: #888; }
.seller-avatar {
  width: 32px; height: 32px; border-radius: 50%;
  object-fit: cover; flex-shrink: 0;
}
.seller-avatar-placeholder {
  width: 32px; height: 32px; border-radius: 50%;
  background: rgba(26,95,168,0.10); color: #1a5fa8;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; flex-shrink: 0;
}
.seller-info { flex: 1; min-width: 0; }
.seller-name { font-size: 12.5px; font-weight: 500; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.seller-stats { font-size: 11px; color: var(--text-secondary); }
.seller-amount { font-size: 12.5px; font-weight: 600; color: #1a5fa8; white-space: nowrap; }

/* ── Payments card extras ── */
.pmt-pay-chip {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 6px;
  background: rgba(26,95,168,0.08);
  color: #1a5fa8;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .02em;
}
.pmt-pager-btn {
  width: 24px; height: 24px;
  border-radius: 6px;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,0.55);
  color: var(--text-primary);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all .15s;
}
.pmt-pager-btn:hover:not(:disabled) { background: rgba(26,95,168,0.10); }
.pmt-pager-btn:disabled { opacity: 0.35; cursor: not-allowed; }

/* ── Totals row: 3 cards side by side ── */
.totals-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 16px;
}
/* .totals-row-4 kept: nothing on the dashboard uses it since Sales/Cost/Profit
   merged, but it is a generic 4-up row and other pages may still want it. */
.totals-row-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin-bottom: 16px;
}

/* ── Totals row: Total Sales, then everything else on one surface ──
   Rearranged 2026-09-18 (Luis). Sales used to be the first of three merged
   columns with Card Payments alone on the right; they have swapped roles.

   It started at 1fr / 4fr, which held every column within a pixel or two of
   the width it had before the regroup. Widened to 1 / 3.1 (Luis, 2026-09-18)
   now that Total Sales is a card in its own right rather than the first of
   three columns — it carries four bars and their labels in the space the
   others use for the same, and it was the one paying for the merge.

   The floor rose with it: at 1920 the old 180px was the minmax bottoming out,
   so the ratio alone would not have reached that far. */
.totals-row-3plus1 {
  display: grid;
  grid-template-columns: minmax(215px, 1fr) 3.1fr;
  gap: 12px;
  margin-bottom: 16px;
}
.totals-merged {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* No gap: the divider IS the separation. A gap plus a rule reads as two
     different kinds of gap. */
  gap: 0;
}
.totals-merged .tm-col { padding: 0 16px; }
.totals-merged .tm-col:first-child { padding-left: 0; }
.totals-merged .tm-col:last-child  { padding-right: 0; }
/* Divider between columns only — never on the outside edges. */
.totals-merged .tm-col + .tm-col {
  border-left: 1px solid var(--glass-border);
}

@media (max-width: 1100px) {
  /* Total Sales drops above the rest rather than being squeezed into a sliver
     beside four columns. Nothing later in this file touches
     .totals-row-3plus1, so this one lands from here.

     The merged card's own columns are NOT set here. A later unconditioned
     .totals-merged rule further down restates grid-template-columns, and an
     unconditioned rule that comes after a media query wins on source order —
     so a stacking rule written at this point is silently ignored while its
     padding and border rules still apply, which leaves four columns wearing a
     two-column divider pattern. They live beside that later rule instead. */
  .totals-row-3plus1 { grid-template-columns: 1fr; }
}
@media (max-width: 700px) {
  /* Stacked: the divider has to move to the top edge, or a vertical rule
     appears alongside content it no longer separates. */
  .totals-merged { grid-template-columns: 1fr; }
  .totals-merged .tm-col { padding: 14px 0; }
  .totals-merged .tm-col:first-child { padding-top: 0; }
  .totals-merged .tm-col:last-child  { padding-bottom: 0; }
  .totals-merged .tm-col + .tm-col {
    border-left: none;
    border-top: 1px solid var(--glass-border);
  }
}

/* ── The Total Sales card's glowing border (Luis, 2026-09-19) ────────────────
 *
 * Brought back after the gauge cable was removed, and rebuilt rather than
 * restored: the old version leaned on flow_lines.php for both halves of itself.
 * That file published --fl-wire (the exact colour it had stroked the cable
 * with) and added a .fl-wired class to this card only while a cable was
 * actually drawn. With the cable gone, an unloaded stylesheet would have
 * supplied neither — no colour, and a class that nothing ever adds, so the
 * treatment would simply never have appeared.
 *
 * So the colours are written out here. They are the same two values, which
 * were already the fallbacks in the old rule: rgb(180,157,164) is house plum
 * washed against the card, and rgb(118,70,82) is the brighter one the arc
 * used. Nothing publishes them now, so nothing can take them away.
 *
 * The names lost their fl- prefix with the flow lines. Leaving them would have
 * pointed the next reader at a file that no longer has anything to do with it.
 *
 * Three parts. The border is the plum. ::before is a soft outer glow that
 * breathes — its box-shadow falls entirely outside the card's edge, so it never
 * fogs the figure inside. ::after is a single bright arc travelling the
 * perimeter: a conic gradient masked down to the border itself, spun by
 * animating its angle.
 *
 * !important on the border because apple.css sets `border` on .card with
 * !important; an id outranks it. The glow is a pseudo-element and NOT a
 * box-shadow on the card, deliberately — !important is ignored inside
 * @keyframes, so an animated box-shadow would lose to apple's
 * `box-shadow: none !important` and never appear at all. */
@property --sc-ang { syntax: '<angle>'; initial-value: 0deg; inherits: false; }

#salesCard {
  position: relative;
  border-color: rgb(180, 157, 164) !important;
}
#salesCard::before,
#salesCard::after {
  content: '';
  position: absolute;
  border-radius: inherit;
  pointer-events: none;
}
#salesCard::before {
  inset: 0;
  box-shadow: 0 0 16px 1px rgba(107, 55, 68, 0.28);
  animation: scGlowPulse 3.8s ease-in-out infinite;
}
#salesCard::after {
  inset: -1px;
  padding: 1px;
  background: conic-gradient(from var(--sc-ang),
              transparent 0deg,
              rgb(118, 70, 82) 16deg,
              transparent 54deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  animation: scBorderRun 7s linear infinite;
  opacity: .85;
}
@keyframes scGlowPulse { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
@keyframes scBorderRun { to { --sc-ang: 360deg; } }

/* The border and its colour stay — they are the card's edge; the travel and the
   breathing are the decoration. */
@media (prefers-reduced-motion: reduce) {
  #salesCard::before { animation: none; opacity: .5; }
  #salesCard::after  { animation: none; opacity: .5; }
}

.card-compact {
  padding: 18px 16px !important;
}
.card-compact .card-icon { font-size: 1.1rem; margin-bottom: 4px; }
.card-compact .card-label { font-size: 11px; }
.card-compact .card-value { font-size: 1.8rem; margin-bottom: 4px; }
.card-compact .card-value sup { font-size: 0.7rem; }
.card-compact .card-desc { font-size: 10.5px; }
.card-compact .progress-label { font-size: 10px; }
.card-compact .rev-stat-icon { font-size: 0.85rem; width: 26px; height: 26px; }
.card-compact .rev-stat-label { font-size: 10.5px; }
.card-compact .rev-stat-val { font-size: 12px; }

/* ── Vertical bar pills ── */
.vbar-row {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  justify-content: center;
}
.vbar-pill {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.vbar-amount {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
}
.vbar-track {
  width: 100%;
  height: 90px;
  border-radius: 14px;
  background: rgba(0,0,0,0.06);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,0.15);
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
}
.vbar-fill {
  width: 100%;
  border-radius: 0 0 13px 13px;
  min-height: 4px;
  transition: height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
}
.vbar-fill-blue {
  background: linear-gradient(180deg, rgba(26,95,168,0.7) 0%, rgba(26,95,168,0.25) 100%);
}
.vbar-fill-orange {
  background: linear-gradient(180deg, rgba(200,80,40,0.7) 0%, rgba(200,80,40,0.25) 100%);
}
.vbar-fill-teal {
  background: linear-gradient(180deg, rgba(0,138,120,0.7) 0%, rgba(0,138,120,0.25) 100%);
}
.vbar-fill-gray {
  background: linear-gradient(180deg, rgba(90,98,112,0.7) 0%, rgba(90,98,112,0.25) 100%);
}
.vbar-fill::after {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: repeating-linear-gradient(
    -45deg,
    transparent,
    transparent 4px,
    rgba(255,255,255,0.08) 4px,
    rgba(255,255,255,0.08) 8px
  );
  border-radius: inherit;
}
.vbar-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
}

@media (max-width: 1100px) {
  .totals-row { grid-template-columns: repeat(3, 1fr); }
  .totals-row-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 700px) {
  .totals-row { grid-template-columns: 1fr; }
  .totals-row-4 { grid-template-columns: 1fr; }
}

/* ── Table pagination ── */
.table-pagination {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--glass-border);
}
.pag-btn {
  height: 28px;
  min-width: 28px;
  padding: 0 8px;
  border-radius: 8px;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,0.45);
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 12px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.pag-btn:hover:not(:disabled) { background: rgba(255,255,255,0.8); color: var(--accent); }
.pag-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.pag-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.pag-info { font-size: 11px; color: var(--text-secondary); margin-right: 4px; }

/* ── Skeleton loading animation ── */
@keyframes shimmer {
  0%   { background-position: -400px 0; }
  100% { background-position: 400px 0; }
}
.skeleton {
  background: linear-gradient(90deg, rgba(0,0,0,0.04) 25%, rgba(0,0,0,0.08) 50%, rgba(0,0,0,0.04) 75%);
  background-size: 400px 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-radius: 6px;
}
.skeleton-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
}
.skeleton-circle {
  width: 35px; height: 35px;
  border-radius: 50%;
  flex-shrink: 0;
}
.skeleton-line {
  height: 12px;
  border-radius: 6px;
}
.skeleton-line-sm { width: 60px; }
.skeleton-line-md { width: 100px; }
.skeleton-line-lg { width: 140px; }

/* ── Weekly Stats Card ── */
.weekly-stats { display: flex; }
.ws-col { flex: 1; padding: 0 18px; }
.ws-col:first-child { padding-left: 0; }
.ws-col:last-child { padding-right: 0; }
.ws-divider { width: 1px; background: var(--glass-border); align-self: stretch; }
.ws-header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.ws-icon {
  width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.ws-icon svg { width: 17px; height: 17px; }
.ws-icon-img { width: 32px; height: 32px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.ws-icon-img img { width: 32px; height: 32px; object-fit: contain; display: block; }

/* SVG replacements for the four "By Week" PNGs.
   Each takes its column's accent — the same blue / orange / teal the value
   below it uses — so the icon and the figure read as one unit. The PNGs were
   four unrelated illustrations; these are one family in four colours. */
.ws-icon-svg { display: block; opacity: 0.9; }
.weekly-stats > :nth-child(1) .ws-icon-svg { color: #1a5fa8; }
.weekly-stats > :nth-child(3) .ws-icon-svg { color: #c85028; }
.weekly-stats > :nth-child(5) .ws-icon-svg { color: #008a78; }
/* Taxes has no accent of its own — it is a deduction, not a performance
   figure, so it stays neutral rather than borrowing a colour that would imply
   good or bad. */
.weekly-stats > :nth-child(7) .ws-icon-svg { color: var(--text-secondary); }
.ws-icon-blue   { background: rgba(26,95,168,0.10); color: #1a5fa8; border: 1px solid rgba(26,95,168,0.18); }
.ws-icon-orange { background: rgba(200,80,40,0.10); color: #c85028; border: 1px solid rgba(200,80,40,0.18); }
.ws-icon-teal   { background: rgba(0,138,120,0.10); color: #008a78; border: 1px solid rgba(0,138,120,0.18); }
.ws-label { font-size: 12px; color: var(--text-secondary); font-weight: 400; line-height: 1.2; }
/* font-family deliberately absent: the figures block further down assigns
   var(--font-num). A 'DM Sans' here was dead weight — overridden by that later
   rule — but it read as if these numbers were meant to be proportional. */
.ws-value { font-size: 1.2rem; font-weight: 600; }
.weekly-stats > :nth-child(1) .ws-value { color: #1a5fa8; }
.weekly-stats > :nth-child(3) .ws-value { color: #c85028; }
.weekly-stats > :nth-child(5) .ws-value { color: #008a78; }
/* The week's figure, what it beat, and by how much — one line.
   This used to be two: the figure in the header and a .ws-compare row beneath
   it. Splitting them meant the comparison sat a line away from the number it
   compares, and cost the card a whole row to say so.

   Baseline alignment, not centre: a 1.2rem figure next to 11px text lines up on
   the letters' feet or it looks like the small type is floating.

   The delta keeps its right edge via margin-left:auto rather than
   justify-content:space-between, which would also have pushed the vs-text away
   from the figure — the exact adjacency this change exists to create. */
.ws-valrow {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.ws-vs { font-size: 11px; color: var(--text-secondary); white-space: nowrap; }
.ws-pct { font-size: 11px; font-weight: 600; margin-left: auto; white-space: nowrap; }
.ws-pct-up   { color: #008a5a; }
.ws-pct-down { color: #b52020; }
/* ── Category rings ──
   Replaced seven unlabelled daily bars per card. Those showed shape without
   magnitude; these show the week's actual money per category, which is the
   question the card exists to answer.

   The arc is scaled to the biggest category on that card, NOT to a share of
   the headline total — the buckets overlap, so shares would not sum to it. See
   ws_ring() in index.php.

   The value is absolutely centred rather than laid out in flow: it has to sit
   in the ring's hole regardless of how many digits it runs to, and its size is
   chosen per value in PHP. */
.ws-rings {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  margin: 10px 0 8px;
}
.ws-ring {
  position: relative;
  flex: 1;
  min-width: 0;
  text-align: center;
}
/* Square, and the SVG and the figure both fill it. That is what keeps the value
   centred on the ring rather than on the tile — the caption below sits outside
   the face, and a fixed 72px overlay would drift the moment a narrow column
   shrank the SVG. */
.ws-ring-face {
  position: relative;
  width: 100%;
  max-width: 72px;
  aspect-ratio: 1;
  margin: 0 auto;
}
.ws-ring-svg { width: 100%; height: 100%; display: block; }
.ws-ring-track { stroke: rgba(0, 0, 0, 0.06); }
/* The arc's stroke is a gradient built in week_strip.php (a `stroke="url(#…)"`
   attribute whose stops are currentColor), so each column's accent below
   still drives its own rings — but it must NOT be set here: a CSS stroke
   would beat the attribute and paint the arc flat again. The halo is the same
   arc, wider, faint and blurred, sitting underneath. */
.ws-ring-arc   { transition: stroke-dashoffset .5s ease; }
.ws-ring-halo  { opacity: .22; filter: blur(2.5px); pointer-events: none; transition: stroke-dashoffset .5s ease; }
.ws-ring-val {
  position: absolute;
  inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-ui);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.02em;
  pointer-events: none;
}
/* The arc and the centre figure take the column's accent, matching the icon and
   the headline above them. Taxes stays neutral for the reason given there. */
.weekly-stats > :nth-child(1) .ws-ring { color: #1a5fa8; }
.weekly-stats > :nth-child(3) .ws-ring { color: #c85028; }
.weekly-stats > :nth-child(5) .ws-ring { color: #008a78; }
.weekly-stats > :nth-child(7) .ws-ring { color: var(--text-secondary); }
.ws-ring-val { color: inherit; }

.ws-ring-lab {
  margin-top: 3px;
  font-family: var(--font-ui);
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Badge modal (stat chip click) ── */
.badge-click { cursor: pointer; }

.badge-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 20, 60, 0.35);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.badge-modal-overlay.open { display: flex; }

.badge-modal {
  width: 94%;
  max-width: 580px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  background: rgba(255,255,255,0.82);
  border: 1px solid rgba(100,160,220,0.25);
  border-radius: 20px;
  box-shadow: 0 16px 48px rgba(60,100,180,0.18), inset 0 1px 0 rgba(255,255,255,0.8);
  animation: pinSlideUp 0.35s cubic-bezier(0.34,1.26,0.64,1) both;
}

.badge-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 28px 14px;
  border-bottom: 1px solid rgba(100,160,220,0.15);
}

.badge-modal-eyebrow {
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.badge-modal-title {
  font-family: var(--font-ui);
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--text-primary);
}

.badge-modal-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid rgba(100,160,220,0.2);
  background: rgba(255,255,255,0.5);
  color: var(--text-secondary);
  font-size: 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
}
.badge-modal-close:hover { background: rgba(200,40,40,0.08); color: #b52020; }

.badge-modal-body {
  overflow-y: auto;
  padding: 16px 28px 24px;
  flex: 1;
}

.badge-modal-loading {
  text-align: center;
  padding: 32px 0;
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--text-secondary);
}

.badge-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(100,160,220,0.10);
}
.badge-item:last-child { border-bottom: none; }

.badge-item-img {
  width: 48px;
  height: 48px;
  border-radius: 10px;
  object-fit: cover;
  background: rgba(230,238,250,0.5);
  flex-shrink: 0;
}

.badge-item-placeholder {
  width: 48px;
  height: 48px;
  border-radius: 10px;
  background: rgba(230,238,250,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.badge-item-info {
  flex: 1;
  min-width: 0;
}

.badge-item-name {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.badge-item-imei {
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.badge-check {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1.5px solid rgba(100,160,220,0.3);
  background: rgba(230,238,250,0.3);
  color: #b0b8c4;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 0.2s ease;
}
.badge-check:hover {
  border-color: rgba(0,184,122,0.4);
  color: #80c4a8;
}
.badge-check.active {
  background: rgba(0,184,122,0.12);
  border-color: #00b87a;
  color: #00b87a;
}

/* ── Icon action buttons (add/minus/category) ──
   The artwork used to be two PNGs, and icons/minus.png never existed — every
   Subtract button rendered a broken-image glyph. Both are sprite icons now.

   Colour is semantic rather than decorative: these buttons sit directly above a
   transaction log that already reads green for money in and red for money out.
   Painting the controls in the colours of the rows they produce means the button
   says what it does before you read its tooltip.

   One channel drives both the icon and its hover shadow, so a variant can't end
   up with a green glyph throwing a blue glow. Held as bare RGB components
   because rgba() needs to mix in its own alpha for the shadow. */
.icon-btn {
  --icon-btn-ink: 138, 180, 248;   /* fallback: add.png's old light blue */
  width: 30px;
  height: 30px;
  border: none;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
  transition: filter 0.2s;
}
.icon-btn--add { --icon-btn-ink: 0, 138, 90; }    /* the log's "add" green */
.icon-btn--sub { --icon-btn-ink: 181, 32, 32; }   /* the log's "subtract" red */

.icon-btn img,
.icon-btn .pos-icon {
  width: 24px;
  height: 24px;
  opacity: 1;
  transition: all 0.2s;
}
/* Set on the icon, not the button: body.neu-mode paints every button
   var(--neu-mid), and inheriting that would drain the semantic colour away in
   the one theme where these controls are hardest to pick out. */
.icon-btn .pos-icon { color: rgb(var(--icon-btn-ink)); }

.icon-btn:hover img,
.icon-btn:hover .pos-icon {
  opacity: 1;
  filter: drop-shadow(2px 3px 2px rgba(var(--icon-btn-ink), 0.35));
}

/* ============================================================
   House type stack for the dashboard.
   Tokens (--font-ui / --font-num) live in index.css.

   These overrides live HERE, not in index.css, because index.css is loaded
   by every page — editing it would restyle the whole app. dashboard.css is
   only loaded by index.php, so the change stays on this page.

   Text = Apple SF Pro. Figures = SF Mono. "Figures" means money, counts,
   IMEIs and percentages. Word-valued fields keep the UI face.
   ============================================================ */

body {
    font-family: var(--font-ui);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* ── Text: everything previously DM Sans or Cormorant Garamond ── */
header h1,
header .eyebrow,
.card-title,
.card-tab,
.table-title,
.topbar-title,
.topbar-brand-name,
.topbar-brand-sub,
.topbar-action-label,
.topbar-search-input,
.topbar-imei-input,
.topbar-imei-select,
.modal-title,
.modal-tab,
.modal-input,
.modal-btn-primary,
.imei-status-badge,
.cal-month-year,
.pag-btn,
.badge-modal-title,
.badge-modal-eyebrow,
.badge-modal-loading,
.badge-item-name,
.user-dropdown-item.user-dropdown-logout[type="submit"] {
    font-family: var(--font-ui);
}

/* ── Figures ──────────────────────────────────────────────────────────────
   Money and quantities: the UI face with TABULAR figures.

   What a column of money needs is equal-width digits so the decimals stack —
   that's `tabular-nums`, not monospace. SF Pro (and Inter, Segoe UI, Roboto
   further down the stack) all carry real tabular figures, so the column aligns
   AND the numbers look like the rest of the product. Monospace aligns too, but
   it's a code face: $1,398.60 ends up reading like terminal output in a sales
   report, and its wider digits cost real space in an 11-column table.

   Monospace is kept for IDENTIFIERS — see the block below. */
.card-value,
.amount-val,
.foot-total,
.rev-stat-val,
.ws-value,
.chip-num,
.cal-day,
.topbar-badge,
/* All five val-* classes, not just two.
   .val-cost-on and .val-profit-neg were listed here but their siblings were not,
   so a Cost cell rendered monospace when it had a value and proportional when it
   was $0.00 — and a Profit cell switched font depending on whether it was
   negative. Same column, two fonts, digits drifting out of line as the data
   changed. They all belong to the same figures. */
.val-amount,
.val-cost-on,
.val-cost-off,
.val-profit,
.val-profit-neg,
.profit-pct,
.seller-amount,
.vbar-amount,
/* these carried an inline font-family:'Inter' that silently beat the token */
#paymentsTotal,
#expGrandTotal,
#bankBalance,
#othersBalance {
    font-family: var(--font-ui);
    font-variant-numeric: tabular-nums;
}

/* ── Identifiers ──────────────────────────────────────────────────────────
   Order numbers and IMEIs are read character by character and checked against
   a box or a screen, not compared by magnitude. Monospace earns its place here:
   it separates 0/O and 1/l, and fixed advance makes it easy to keep your place
   part-way through a 15-digit string. */
.search-tag,
.badge-item-imei {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
}

/* ============================================================
   Dark glass cards — radial tint, blur retained.

   dark.css paints these a flat rgba(15,23,42,0.55). Swapping in a radial
   whose stops are ALSO translucent keeps the alpha below 1, so the
   backdrop-filter blur still shows the page through — you get the bluish
   dome from the embossed theme without losing the glass.

   `html body.dark-mode` outranks dark.css's own `body.dark-mode` (one extra
   element in the selector), so this wins without needing !important —
   dark.css loads later, since topbar.php pulls it in from the body.
   ============================================================ */
html body.dark-mode .card,
html body.dark-mode .card-compact,
html body.dark-mode .card-wide,
html body.dark-mode .ws-card {
    background: radial-gradient(circle at 30% 25%,
                rgba(48, 62, 86, 0.62) 0%,
                rgba(15, 23, 42, 0.55) 72%);
}

/* ============================================================
   Totals row — value-first stat cards.

   Replaces the floating pill that used to sit half-off the top edge of each
   card holding the icon + label. That chip was the loudest thing on a card
   whose content is a number, and it forced position:relative, overflow:visible
   and a 25px top margin on all four cards just to make room for it.

   Now: icon quietly top-right, number large and centred, label beneath it.
   ============================================================ */

.totals-card { position: relative; }

/* Peripheral identifier — the eye enters top-left, where the number is,
   so the icon sits opposite and stays muted. */
.tc-icon {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 20px;
    height: 20px;
    object-fit: contain;
    opacity: 0.5;
    pointer-events: none;
}

/* SVG version of the card icon. Inherits position/size from .tc-icon above and
   only overrides what a vector needs.

   Lower opacity than the PNG: a flat stroke at 0.5 reads heavier than a pale
   illustration at the same value, because there's no light tint inside it to
   soften the mass. */
.tc-icon-svg {
    object-fit: initial;
    color: var(--text-secondary);
    /* Higher than the 0.42 the outline version used. Solid shapes carry their
       own weight, so they can sit closer to the card's own text colour without
       shouting — and below about 0.5 the .32-opacity background bars vanish
       entirely, which is what makes it read as a climb. */
    opacity: 0.62;
    /* A touch larger than the PNG it replaces: the bars are the whole icon, so
       every pixel of height is legibility. */
    width: 22px;
    height: 22px;
}
/* The card's own accent shows through on hover — the PNG could never do this. */
.totals-card:hover .tc-icon-svg,
.tm-col:hover .tc-icon-svg { opacity: 0.72; }

.tc-head { text-align: center; }

/* The badge is gone, so the number takes the space it was leaving free */
.totals-card .card-value {
    font-size: 2.8rem;
    font-weight: 500;
    line-height: 1;
    margin-bottom: 4px;
}
.totals-card .card-value sup { font-size: 0.45em; top: -0.55em; }

.tc-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-secondary);
    line-height: 1.2;
}

/* Live status line sits under the label as a quieter caption */
.totals-card .card-desc {
    font-size: 11px;
    margin-top: 4px;
    opacity: 0.75;
    min-height: 16px;
}

/* Card Payments: same value treatment as the other three (was 1.6rem) */
#squareTotal { color: var(--accent); }

@media (max-width: 1400px) {
    .totals-card .card-value { font-size: 2.45rem; }
}

/* Caption under the Daily Bank / Daily Others balance.
   Moved below the figure and quietened down — it was above it at 10px in
   --text-secondary, which gave the label more presence than the number. */
.bal-caption {
    font-size: 9.5px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-secondary);
    opacity: 0.62;
    margin-top: 5px;
    line-height: 1.2;
}

/* ── Daily Bank view switch ────────────────────────────────────────────────
   Balance / Count / Log are three peer views of one card. They used to be
   two "go to" buttons in the header plus a back arrow inside each panel —
   an asymmetric pattern that needed three separate headers. A segmented
   control makes them peers, so the headers and back arrows are gone. */
.bank-seg {
    display: flex;
    gap: 3px;
    padding: 3px;
    margin-top: 14px;
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.05);
    border: 1px solid var(--glass-border);
}
.bank-seg-btn {
    flex: 1;
    padding: 7px 8px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-secondary);
    font-family: var(--font-ui);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.bank-seg-btn:hover { color: var(--text-primary); }
.bank-seg-btn.on {
    background: var(--accent);
    color: #fff;
}

body.dark-mode .bank-seg { background: rgba(255, 255, 255, 0.05); }
body.dark-mode .bank-seg-btn { color: rgba(226, 232, 240, 0.65); }
body.dark-mode .bank-seg-btn:hover { color: #fff; }

body.embossed-mode .bank-seg {
    background: var(--emb-bg);
    border: none;
    box-shadow: var(--emb-in);
}
body.embossed-mode .bank-seg-btn { color: var(--emb-mid); }
body.embossed-mode .bank-seg-btn.on {
    background: var(--emb-accent);
    color: #fff;
    box-shadow: var(--emb-accent-shadow);
}

/* ============================================================
   Card fields + inner boxes.

   These carried inline background:rgba(255,255,255,.35/.55) — a white overlay
   that works on a light card but renders as flat opaque grey on a dark one,
   which is why the Cash Count panel looked solid. The inline values are gone;
   the surface is defined here so each theme can pitch it correctly.
   ============================================================ */
#bankCard input, #bankCard select,
#othersCard input, #othersCard select,
#expensesCard input, #expensesCard select,
#deductiblesCard input, #deductiblesCard select,
.bank-count-box,
#bankCountTodayStatus, #bankCountStatus {
    background: rgba(255, 255, 255, 0.55);
}
.bank-count-box { background: rgba(255, 255, 255, 0.35); }
#bankCountTodayStatus, #bankCountStatus { background: rgba(120, 120, 120, 0.06); }

/* Dark: a light overlay on the glass, not an opaque slab */
body.dark-mode #bankCard input, body.dark-mode #bankCard select,
body.dark-mode #othersCard input, body.dark-mode #othersCard select,
body.dark-mode #expensesCard input, body.dark-mode #expensesCard select,
body.dark-mode #deductiblesCard input, body.dark-mode #deductiblesCard select {
    /* !important because dark.css forces #othersCard's fields to a heavier
       rgba(30,41,59,.7); without this that one card would sit at a different
       weight from the other three. */
    background: rgba(255, 255, 255, 0.06) !important;
    border-color: rgba(100, 160, 220, 0.18) !important;
    color: #e2e8f0 !important;
}
body.dark-mode #bankCard input::placeholder,
body.dark-mode #othersCard input::placeholder,
body.dark-mode #expensesCard input::placeholder,
body.dark-mode #deductiblesCard input::placeholder { color: rgba(148, 163, 184, 0.7); }

body.dark-mode .bank-count-box {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(100, 160, 220, 0.15);
}
body.dark-mode #bankCountTodayStatus,
body.dark-mode #bankCountStatus { background: rgba(255, 255, 255, 0.05); }

/* Embossed: fields are wells, boxes are shallow wells */
body.embossed-mode #bankCard input, body.embossed-mode #bankCard select,
body.embossed-mode #othersCard input, body.embossed-mode #othersCard select,
body.embossed-mode #expensesCard input, body.embossed-mode #expensesCard select,
body.embossed-mode #deductiblesCard input, body.embossed-mode #deductiblesCard select,
body.embossed-mode .bank-count-box,
body.embossed-mode #bankCountTodayStatus,
body.embossed-mode #bankCountStatus {
    background: var(--emb-surface-in);
    border: none;
    box-shadow: var(--emb-in);
    color: var(--emb-ink);
}

/* ══════════════════════════════════════════════════════════════════════════
   POWER / BALANCE HERO PANEL
   Ported from analytics.php and placed under the Calendar / Bank / Deductibles
   row. Deliberately dark in EVERY theme: the sparklines and gauge use white
   and amber strokes, which vanish on a light surface. A data-viz panel with
   its own ground is a normal pattern — the alternative is maintaining a second
   light palette for every series colour.
   ══════════════════════════════════════════════════════════════════════════ */
.hero-card {
  background: rgba(18, 14, 11, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 20px;
  padding: 22px;
  margin-bottom: 16px;
  position: relative;
  overflow: hidden;
}
/* The warm pool behind the panel — the same trick the reference uses to stop
   near-black reading as flat. */
.hero-card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(760px 380px at 22% 96%, rgba(232, 135, 58, 0.20), transparent 62%),
    radial-gradient(560px 300px at 82% 6%,  rgba(232, 135, 58, 0.10), transparent 60%);
}
.hero-card > * { position: relative; z-index: 1; }

.hero-grid {
  display: grid;
  grid-template-columns: 290px minmax(0, 1fr) 330px;
  gap: 28px;
  /* stretch, not start: the sparkline column needs the row's full height to
     distribute its three rows down it. The gauge is the tallest thing here and
     sets that height. */
  align-items: stretch;
}

/* Sparkline column: heading and axis take their natural height, the rows take
   what's left and share it evenly. */
.hero-col-left { display: flex; flex-direction: column; }
.hero-col-left #heroSparks {
  flex: 1;
  display: flex;
  flex-direction: column;
  /* space-evenly, not space-between: space-between puts every gap BETWEEN rows
     and none after the last one, so the third graph ended up flush against the
     card's inner edge. .hero-card is overflow:hidden for its glow layer, so
     anything that reaches that edge is clipped rather than merely crowded.
     space-evenly leaves the same breathing room above the first row and below
     the last. */
  justify-content: space-evenly;

  /* No min-height. A floor here could make the column TALLER than the card and
     the excess would simply be cut off — the row it protected against crushing
     would disappear instead. Letting the rows size to the space available is
     the safer failure. */
  min-height: 0;
}
/* Each row keeps its own height so the rows never compress into each other when
   the column is short. */
.hero-col-left .hero-spark-row { flex: 0 0 auto; }

.hero-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.hero-h    { font-family: var(--font-ui); font-size: 14px; font-weight: 600; color: #F4EFE9; margin: 0 0 8px; }
.hero-sub  { font-family: var(--font-ui); font-size: 11.5px; color: #8B8075; }
.hero-dots {
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid rgba(255,255,255,0.09); background: transparent;
  color: #8B8075; cursor: pointer; font-size: 13px; line-height: 1;
}

.hero-kpi      { display: flex; align-items: center; gap: 12px; margin: 12px 0 4px; }
.hero-kpi-ico  { width: 32px; height: 32px; border-radius: 50%; display: flex;
                 align-items: center; justify-content: center;
                 background: rgba(232,135,58,0.16); color: #E8873A; font-size: 15px; }
.hero-kpi-val  { font-family: var(--font-num); font-size: 30px; font-weight: 700; color: #E8873A; line-height: 1; }
.hero-kpi-unit { font-family: var(--font-ui); font-size: 12px; color: #8B8075; align-self: flex-end; margin-bottom: 3px; }

.hero-spark-axis { display: flex; justify-content: space-between; font-family: var(--font-ui);
                   font-size: 9.5px; color: #8B8075; margin-bottom: 8px; }
/* margin-bottom removed: spacing now comes from the column's space-between, so
   a margin here would add to it and reintroduce the uneven gap at the bottom. */
.hero-spark-row  { margin-bottom: 0; }
.hero-spark-top  { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; }
.hero-spark-name { font-family: var(--font-ui); font-size: 11px; color: #8B8075; }
.hero-spark-val  { font-family: var(--font-num); font-size: 12.5px; font-weight: 700; color: #F4EFE9; }
.hero-spark-val small { font-family: var(--font-ui); font-size: 9.5px; color: #8B8075; margin-left: 2px; }

/* flex-start, not center: the arc sits at the top of its column so the Cash
   and Card figures have room to hang BELOW its two ends rather than beside
   them (Luis, 2026-09-18). There were 42px of slack above and below. */
.hero-gauge-wrap { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; }
/* justify-content:center keeps the pair balanced under the needle now that the
   columns are wider than the values they hold. */
/* The stage is the svg's own box, so the three figures can be placed against
   the arc in percentages and track it at every width. */
/* A DEFINITE width, not 100%. .hero-gauge-wrap is an auto-sized flex column,
   so a percentage here resolved against nothing and the whole gauge collapsed
   to zero — the svg's own intrinsic width was what held the column open before
   this stage existed. 440px is the max-width the svg used to carry inline. */
.hero-gauge-stage { position: relative; width: 440px; max-width: 100%; }
.hero-gauge-stage > svg { display: block; width: 100%; height: auto; }
.hero-gauge-nums { position: absolute; inset: 0; margin: 0; pointer-events: none; }

/* Average Ticket, in the middle of the half-disc the arc encloses. */
.hero-g-mid {
  position: absolute;
  left: 50%;
  top: 46%;
  transform: translateX(-50%);
  text-align: center;
  white-space: nowrap;
}

/* The arc's two ends. gauge() draws cx 230, cy 230, r 175 in a 460x250
   viewBox, so the tips sit at x 55 and x 405 — 12% and 88% across. The blocks
   are pulled INWARD from dead centre on the tip (translateX -30%/-70% rather
   than -50%) so a long figure cannot hang off the side of the card. */
/* Hung BELOW the stage, in the room freed above. The arc's tips sit at 92% of
   the svg's height, so a block at bottom 0 was level with them; -46px drops it
   a clear 24px underneath (Luis, 2026-09-18: "a bit more lower"). */
.hero-g-end  { position: absolute; bottom: -46px; width: auto; max-width: 40%; padding: 0; }
/* Pulled back OUT toward the tips (Luis, 2026-09-18). -40%/-60% rather than
   -30%/-70%: each figure moves ~14px further out, sitting closer to the end
   it belongs to while still leaning inward enough that a long amount cannot
   hang off the card. */
/* Opened up (Luis, 2026-09-18): -44%/-56% instead of -40%/-60% pushes each
   figure about 6px further out, so the pair sits nearer the ends it belongs
   to. Still leaning inward, because a long amount would otherwise hang off
   the side of the card. */
.hero-g-cash { left: 12%;  transform: translateX(-44%); text-align: center; }
.hero-g-card { left: 88%;  transform: translateX(-56%); text-align: center; }

/* display beats [hidden] — .hero-gcol is not display:block, so the attribute
   alone would not hide it. The same trap the pin pad and the chip both hit. */
.hero-g-acima[hidden],
.hero-g-zelle[hidden] { display: none !important; }
.hero-g-acima,
.hero-g-zelle { left: 88%; transform: translateX(-60%); text-align: center; }

/* THREE figures — the day had Acima or Zelle, one of them but not both. The
   extra one takes the empty middle and Cash and Card keep their own ends (Luis,
   2026-09-18). There is no room to put it AFTER Card: the arc's right tip sits
   at 353 of a 401px stage, leaving 48px, and "984.84" alone needs 53. The
   middle is free — nothing lives under the arc between the two ends — so that
   is where it goes, and neither of the other two has to move. Blocks stay
   capped so three can never touch.

   Only the LABEL size is set here. The figures are sized inline by the gauge
   script, from the longest amount on the row, and inline beats any rule a class
   could carry — a font-size for .hero-gval lived in this block for months
   without ever taking effect. */
.hero-gauge-nums.has-acima .hero-gcol,
.hero-gauge-nums.has-zelle .hero-gcol    { max-width: 30%; }
.hero-gauge-nums.has-acima .hero-glab,
.hero-gauge-nums.has-zelle .hero-glab    { font-size: 11px; }
.hero-gauge-nums.has-acima .hero-g-cash,
.hero-gauge-nums.has-zelle .hero-g-cash  { left: 10%; transform: translateX(-40%); }
.hero-gauge-nums.has-acima .hero-g-card,
.hero-gauge-nums.has-zelle .hero-g-card  { left: 88%; transform: translateX(-53%); }
.hero-gauge-nums.has-acima .hero-g-acima { left: 50%; transform: translateX(-50%); }
.hero-gauge-nums.has-zelle .hero-g-zelle { left: 50%; transform: translateX(-50%); }

/* FOUR — both on the same day (Luis, 2026-09-22: "in case we have both them
   shrink them so they fit"). The middle that carried one now carries two, so
   the pair splits it and the whole row closes up: Cash and Card come further in
   off the tips, the blocks are capped tighter again, and the script caps the
   figures at 16px to match. Four amounts at 24% of a 401px stage leave about
   20px of air between neighbours on the longest day this shop has had. */
.hero-gauge-nums.has-acima.has-zelle .hero-gcol    { max-width: 24%; }
.hero-gauge-nums.has-acima.has-zelle .hero-glab    { font-size: 9.5px; }
.hero-gauge-nums.has-acima.has-zelle .hero-g-cash  { left: 8%;  transform: translateX(-34%); }
.hero-gauge-nums.has-acima.has-zelle .hero-g-zelle { left: 37%; transform: translateX(-50%); }
.hero-gauge-nums.has-acima.has-zelle .hero-g-acima { left: 63%; transform: translateX(-50%); }
.hero-gauge-nums.has-acima.has-zelle .hero-g-card  { left: 92%; transform: translateX(-62%); }

/* Acima in the house sky blue (Luis, 2026-09-18) — the fourth accent, beside
   plum for sales, green for cash and silver for cost. Zelle takes its own brand
   purple, the one the checkout key and the table icon already carry, so the
   figure is tied to the mark the sale was rung with rather than borrowing an
   accent that means something else on this dashboard.

   The fill as well as the colour. .hero-gauge-nums paints a gradient clipped to
   its text with -webkit-text-fill-color: transparent, and a child that only
   sets `color` inherits the transparent fill and renders as nothing — which is
   exactly what happened to the Cash/Card LABELS a few changes ago. */
.hero-gacima .hero-gval { color: #5b8def; -webkit-text-fill-color: #5b8def; }
.hero-gzelle .hero-gval { color: #6D1ED4; -webkit-text-fill-color: #6D1ED4; }

/* A small muted $ in front of each gauge figure. In em so it follows the size
   gauge() gives the box, and in currentColor at 45% so it belongs to the figure
   it sits on rather than adding a third colour to the row. The fill is spelled
   out for the same reason as everything else in here. */
.hero-gval .hero-cur {
  /* Raised, like a currency mark rather than a character on the line (Luis,
     2026-09-18). Same idiom the totals cards use — apple.css lifts the hero
     footer's $ with position/top for exactly this reason; vertical-align:super
     would also grow the line box and push the figure down. */
  position: relative;
  top: -0.52em;
  font-size: 0.52em;
  color: currentColor;
  -webkit-text-fill-color: currentColor;
  opacity: 0.45;
  margin-right: 1px;
}
.hero-g-end .hero-gval { font-size: 19px; }
/* The label needs its fill back. It sits inside .hero-gauge-nums, which
   apple.css and liquid.css paint with a gradient clipped to the text and
   -webkit-text-fill-color: transparent. The VALUES have colours of their
   own so they survive that; the labels did not, and came out invisible. */
.hero-g-end .hero-glab { margin-top: 3px; font-size: 12px;
                         -webkit-text-fill-color: currentColor; }
/* 42px was sized for the reference's "1.03". Real money runs to "$3,117.70" —
   nine glyphs — which overflowed the column and collided with the other side.
   Narrower type, wider column, and a hard clamp so an unusually big day can't
   break the layout. */
/* The reference sized these for "1.03". Real money runs to "$3,117.70", which
   overflowed and collided across the centre divider.
   font-size here is only the fallback — gauge() in index.php sets it from the
   length of the longer value so the pair can never overlap at any amount. */
.hero-gcol       { width: 150px; max-width: 44%; text-align: center; overflow: hidden;
                   padding: 0 6px; box-sizing: border-box; }
.hero-gval       { font-family: var(--font-num); font-size: 22px; font-weight: 600;
                   line-height: 1; letter-spacing: -0.02em; white-space: nowrap; }
.hero-glab       { font-family: var(--font-ui); font-size: 13px; color: #8B8075; margin-top: 6px; }
.hero-gprod .hero-gval { color: #E8873A; }
.hero-gcons .hero-gval { color: #F4EFE9; }
.hero-gdiv       { width: 1px; background: rgba(255,255,255,0.09); margin: 6px 0; }

/* .hero-gfoot is gone with Total Collected (Luis, 2026-09-18). The rules that
   themed it in apple.css and liquid.css are harmless leftovers — they match
   nothing now — and are left alone rather than chased across three sheets.
   .hero-gfcol stays: Average Ticket still wears it. */
.hero-gfcol { text-align: center; }
.hero-gfval { font-family: var(--font-num); font-size: 24px; font-weight: 600; color: #F4EFE9; }
.hero-cur   { font-size: 16px; color: #8B8075; }
.hero-gflab { font-family: var(--font-ui); font-size: 12px; color: #8B8075; margin-top: 4px; }
.hero-up    { color: #4ADE80; font-size: 13px; }
.hero-down  { color: #F0568A; font-size: 13px; }

/* ── Today in context (right column of the hero card) ──
   Replaced the Cash/Card donuts. Deliberately typographic rather than another
   chart: the card already carries a gauge and three sparklines, and a fourth
   graphic would compete instead of adding. */
.hero-vs { padding-top: 4px; }
.hero-vs-lab {
  font-family: var(--font-ui);
  font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: #8B8075;
}
.hero-vs-delta {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 34px; font-weight: 700; line-height: 1.15;
  margin-top: 8px; color: #8B8075;
}
.hero-vs-delta.is-up   { color: #4ADE80; }
.hero-vs-delta.is-down { color: #F0568A; }
.hero-vs-sub {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 11.5px; color: #8B8075; margin-top: 4px;
}
.hero-vs-rows { margin-top: 20px; border-top: 1px solid rgba(255,255,255,0.09); }
.hero-vs-row {
  display: grid;
  /* Three fixed tracks so the middle column lines up down the list — a flex row
     would let each label push its own value out of alignment. */
  grid-template-columns: 1fr auto auto;
  align-items: baseline; gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  font-family: var(--font-ui); font-size: 12px; color: #8B8075;
}
.hero-vs-row:last-child { border-bottom: none; }
.hero-vs-k {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 12px; font-weight: 700; color: #F4EFE9;
}
.hero-vs-v {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 12px; color: #8B8075; min-width: 62px; text-align: right;
}

/* Biggest ticket of each kind */
.hero-top-rows { margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.09); }
.hero-top-row  {
  display: grid;
  grid-template-columns: 34px 1fr auto;   /* matches .hero-top-icowrap */
  align-items: center; gap: 10px;
  padding: 7px 0;
}
/* Fixed box so the row never shifts when a photo replaces the icon, and so both
   products read the same size whatever their source image dimensions are. */
.hero-top-icowrap {
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.hero-top-ico  { width: 18px; height: 18px; color: #8B8075; }
.hero-top-img  {
  /* No plate, no rounding — the product photo on its own.
     `contain` rather than `cover`: cover filled the square by cropping, which
     sliced the top and bottom off a phone. contain letterboxes instead, so the
     whole product shows and a tall phone and a wide part share one box size. */
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}
.hero-top-txt  { min-width: 0; }
.hero-top-lab  {
  font-family: var(--font-ui); font-size: 10px; font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase; color: #8B8075;
}
/* Names run long — clip rather than wrap, so a row never becomes two. */
.hero-top-name {
  font-family: var(--font-ui); font-size: 12.5px; font-weight: 600; color: #F4EFE9;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hero-top-amt  {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 12.5px; font-weight: 700; color: #F4EFE9; white-space: nowrap;
}

.hero-dist      { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin: 12px 0 22px; }
.hero-dist-cell { background: rgba(255,255,255,0.055); border: 1px solid rgba(255,255,255,0.09);
                  border-radius: 14px; padding: 12px 8px 8px; text-align: center; }
.hero-dist-name { font-family: var(--font-ui); font-size: 12px; color: #F4EFE9; margin-bottom: 6px; }

.hero-legend   { display: flex; gap: 14px; flex-wrap: wrap; margin: 8px 0 6px; }
.hero-legend span { font-family: var(--font-ui); font-size: 10.5px; color: #8B8075;
                    display: flex; align-items: center; gap: 5px; }
.hero-legend i { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }

@media (max-width: 1250px) {
  .hero-grid { grid-template-columns: 1fr; }
  /* Nothing to pull up any more: the figures are positioned against the arc
     itself now, so they follow it when the grid stacks. */
  /* Stacked, the column no longer sits beside the gauge, so there is no height
     to fill — spreading would stretch three rows down an arbitrarily tall box.
     Back to a plain gap. */
  .hero-col-left #heroSparks {
    display: block;
    min-height: 0;
    flex: none;
  }
  .hero-spark-row { margin-bottom: 14px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   GROUPED ORDERS + RECEIPT
   ═══════════════════════════════════════════════════════════════════════════
   A repair ticket with three parts used to draw three rows carrying the same
   customer and the same order number — one event, read as three. It now draws
   one row, and the whole order opens as a receipt.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The item count on a grouped row. A chip rather than plain text, because the
   cell it sits in normally holds a product name and "3 items" needs to read as
   a different KIND of thing, not as a product called "3 items". */
.order-count-chip {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-blue, #1a5fa8);
  background: rgba(26, 95, 168, 0.10);
}

/* ── The View button ──────────────────────────────────────────────────────── */
.row-receipt-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  color: var(--brand-blue, #1a5fa8);
  background: rgba(26, 95, 168, 0.10);
  transition: background .15s ease, color .15s ease;
}
.row-receipt-btn:hover { background: rgba(26, 95, 168, 0.20); }
.row-receipt-btn svg { width: 14px; height: 14px; flex: 0 0 auto; }

/* The receipt modal moved to receipt_modal.css on 2026-09-15 — the drawer log
   opens the same one, so it is no longer the dashboard's alone. */

/* Two-part column headers on the sales table (Amount over Tax, Cost over
   Bank, Customer over Order, Product over IMEI): the second word on its own
   line, smaller and quieter, the shape the cells beneath them have. */
.table-wrap th .th-sub { display: block; font-size: 0.82em; opacity: 0.7; margin-top: 2px; }

/* ── Payments column on the totals card (2026-09-14) ─────────────────────────
   A fourth, narrower term after Profit: carrier payments (Vida Pay) are inside
   the day's Total Sales since today, and this says how much of it they were
   and how they were taken. The bars run sideways, one per method, each the
   method's share of the payments, so the pair reads as one whole. */
/* Cost · Profit · Payments · Card Payments. The last two are minmax'd rather
   than free: Payments has to hold two labelled bars and Card Payments a
   four-line breakdown plus a sparkline, and an even quarter each starves the
   one with the most to say while leaving Cost room it does not need.
 *
 * Those two floors are also the only part of this row's minimum width that is
 * a choice rather than min-content, so they are where widening Total Sales is
 * paid for: 155/200 dropped to 142/180 gives back the 35px the wider card and
 * its raised floor took, and the row's total minimum comes out where it was.
 * It still overflows .dashboard-right on a narrow window — see the note on the
 * 1100px block, which is measured against the VIEWPORT while this row lives in
 * a column roughly half that wide, so it stacks far too late to help. That is
 * older than this change and is not made worse by it. */
.totals-merged { grid-template-columns: 1fr 1fr minmax(142px, 0.7fr) minmax(180px, 1.05fr); }
.tm-col-pay .card-value { font-size: 2.1rem; }
.tc-icon-pay { width: 26px; height: 26px; opacity: 0.9; border-radius: 6px; }
.tm-col-pay:hover .tc-icon-pay { opacity: 1; }
.hbar-list { display: flex; flex-direction: column; gap: 12px; padding: 6px 0 2px; }
.hbar { display: grid; grid-template-columns: 36px 1fr auto; align-items: center; gap: 8px; }
.hbar-k { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-secondary); }
.hbar-track { display: block; height: 8px; border-radius: 6px; background: rgba(0,0,0,0.06); overflow: hidden; }
.hbar-fill { display: block; height: 100%; width: 0; border-radius: 6px; transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1); }
.hbar-fill-blue { background: linear-gradient(90deg, rgba(26,95,168,0.75), rgba(26,95,168,0.35)); }
.hbar-fill-gray { background: linear-gradient(90deg, rgba(90,98,112,0.75), rgba(90,98,112,0.35)); }
.hbar-v { font-size: 11px; font-weight: 700; color: var(--text-primary); font-family: var(--font-num); white-space: nowrap; min-width: 52px; text-align: right; }
/* Two by two: four columns across a narrow window leaves Card Payments too thin
   for the breakdown it exists to show. Placed HERE, after the unconditioned
   rule above, because source order decides it — see the note at the 1100px
   block. The dividers have to follow the wrap: a left rule on the column that
   starts the second row would separate it from the edge of the card, and the
   two rows need a rule between them instead. */
@media (max-width: 1100px) {
  .totals-merged { grid-template-columns: 1fr minmax(200px, 1.05fr); }
  .totals-merged .tm-col { padding: 16px; }
  .totals-merged .tm-col:nth-child(-n+2) { padding-top: 0; }
  .totals-merged .tm-col:nth-child(n+3)  { padding-bottom: 0; }
  .totals-merged .tm-col:nth-child(odd)  { padding-left: 0;  border-left: none; }
  .totals-merged .tm-col:nth-child(even) { padding-right: 0; }
  .totals-merged .tm-col:nth-child(n+3)  { border-top: 1px solid var(--glass-border); }
}
@media (max-width: 700px) {
  .totals-merged { grid-template-columns: 1fr; }
  /* One column: every divider is now a horizontal one between stacked rows. */
  .totals-merged .tm-col { padding: 14px 0; }
  .totals-merged .tm-col:first-child { padding-top: 0; }
  .totals-merged .tm-col:last-child  { padding-bottom: 0; }
  .totals-merged .tm-col:nth-child(n) { border-left: none; border-top: 1px solid var(--glass-border); }
  .totals-merged .tm-col:first-child  { border-top: none; }
}
/* In the narrow column the corner icon collided with the figure; it sits
   centred above the number instead, as the column's badge. */
.tm-col-pay .tc-icon-pay { position: static; display: block; margin: 0 auto 6px; width: 28px; height: 28px; }
.tm-col-pay .card-value { font-size: 2rem; }
/* Larger badge, and the cash/card rows spaced out (Luis, 2026-09-14). */
.tm-col-pay .tc-icon-pay { width: 44px; height: 44px; margin-bottom: 10px; border-radius: 10px; }
.tm-col-pay .hbar-list { gap: 22px; padding-top: 14px; }
/* The badge came off again (Luis, 2026-09-14): the figure leads, with a
   little air above it where the icon was. */
.tm-col-pay .tc-head { padding-top: 6px; }
/* The divider and the bars sit lower, level with the bars beside them
   (Luis, 2026-09-14). */
.tm-col-pay .card-divider { margin-top: 38px !important; }
.tm-col-pay .hbar-list { padding-top: 30px; }

/* ── Seller avatar ring on row hover (2026-09-14) ────────────────────────────
   The comments button's expanding ring from Active Repairs, quieter: a thin
   green ring that grows out of the avatar and fades, only while the pointer
   is on the row. Still for anyone who asked their system for less motion. */
.rep-avatar-wrap { position: relative; display: inline-block; border-radius: 50%; vertical-align: middle; line-height: 0; }
.rep-avatar-wrap > .rep-avatar { display: inline-flex; }
.rep-avatar-wrap::after {
  content: ''; position: absolute; inset: -3px; border-radius: 50%;
  border: 1.5px solid rgba(63, 122, 104, 0.55);
  opacity: 0; pointer-events: none; transform: scale(1);
}
#salesTableBody tr:hover .rep-avatar-wrap::after { animation: rep-ring 1.6s ease-out infinite; }
@keyframes rep-ring { 0% { transform: scale(1); opacity: 0.6; } 100% { transform: scale(1.55); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { #salesTableBody tr:hover .rep-avatar-wrap::after { animation: none; opacity: 0.5; } }
/* Engraved (Luis, 2026-09-14): the avatar sits in a shallow well — a dark
   edge top-left and a light one bottom-right on the well, and a thin inner
   rim over the photo so it reads as set into the surface, not laid on it. */
.rep-avatar-wrap {
  padding: 3px;
  background: rgba(23, 54, 59, 0.045);
  box-shadow: inset 2px 2px 4px rgba(23, 54, 59, 0.22), inset -2px -2px 4px rgba(255, 255, 255, 0.9);
}
.rep-avatar-wrap::before {
  content: ''; position: absolute; inset: 3px; border-radius: 50%; pointer-events: none;
  box-shadow: inset 1px 1px 2px rgba(0, 0, 0, 0.3), inset -1px -1px 1px rgba(255, 255, 255, 0.35);
}
.rep-avatar-wrap > .rep-avatar-img, .rep-avatar-wrap > .rep-avatar { display: block; }

/* ── The Deductibles chip under Total Profit (Luis, 2026-09-17) ──────────────
 *
 * "not to big so it doesn't distord the panel size at all" — so it is built to
 * the size of the line it sits in rather than to the size of a button. 10px
 * icon, 9.5px caps, 2px of vertical padding: 16px tall, plus a 2px margin — 18px,
 * which is the slack the card already had. Measured headless before and after:
 * the totals row stays 292px and all four columns stay 254px. It does not move.
 *
 * A chip, not a pill button: the panel already has one loud thing in it, and
 * that is the figure. This has to be findable without competing with it.
 */
.tc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 18px;
  padding: 5px 13px;
  border: 0;
  border-radius: 999px;
  /* Cut in, like the segmented controls on the repairs checkout. The hairline
     is the third shadow rather than a border, so nothing sits proud of the
     groove. */
  background: rgba(226, 232, 240, 0.50);
  box-shadow: inset 2px 2px 5px rgba(23, 54, 59, 0.16),
              inset -2px -2px 5px rgba(255, 255, 255, 0.95),
              0 0 0 1px rgba(31, 45, 61, 0.10);
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
/* Hover lightens what is IN the groove. A thing set into a surface does not
   float up when you point at it — the same argument the calendar makes. */
.tc-chip:hover {
  background: rgba(255, 255, 255, 0.72);
  color: var(--text-primary);
}
.tc-chip:active { transform: scale(0.96); }
/* OUTSIDE the pill, to its left (Luis, 2026-09-18). Still a child of the
   button, so it is still what you press to open the modal — an absolutely
   positioned descendant takes clicks perfectly well even when it hangs outside
   its parent's box.

   Absolute for the same reason the transform was: out of the layout entirely,
   so the pill, the profit card and the totals row do not move a pixel. Being
   out there also means it can be as big as it likes; it was capped at 22px
   while it had to fit a 26px pill.

   --di-ring paints the ring and the dollar in the in-house green. It crosses
   into the icon's <use> shadow tree the same way --di-run does. The ARROW is
   deliberately not covered by it: the symbol pins the arrow back to
   currentColor, so it keeps wearing the chip's own colour — plum on a day that
   set money aside, green on one that took it back. */
.tc-chip-ico {
  position: absolute;
  right: calc(100% + 7px);
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  flex: none;
  opacity: 0.95;
  --di-ring: #3f7a68;
  /* Cut into the page: a dark edge up and left, a white one down and right.
     box-shadow does nothing to an SVG stroke, so this is the drop-shadow form.
     Spelled out rather than read from --ap-engrave-filter, which only exists
     under .apple-mode while this chip is drawn in every theme. */
  filter: drop-shadow(-1px -1px 1px rgba(23, 54, 59, 0.30))
          drop-shadow(1px 1px 1px rgba(255, 255, 255, 0.95));
}

/* display beats [hidden]. The script hides this chip when the card it jumps to
   is not on the page, and inline-flex would quietly ignore that — the same
   trap the pin pad and the phone diagram both fell into. */
.tc-chip[hidden] { display: none !important; }

/* ── The three bar graphs share a baseline (Luis, 2026-09-17) ────────────────
 *
 * The columns are grid cells and already stretch to equal height, but each
 * one's graph began wherever its own heading finished — and the headings are
 * not the same number of lines. Cost carries no "vs. last week" line, so its
 * bars sat 19px above the other two at every width measured, 1280 through
 * 1920. It was not a width bug; it was structural.
 *
 * Rather than padding one head to match another — which needs redoing every
 * time a card gains or loses a line, as Total Profit just did when it gained
 * the Deductibles chip — the divider and the bars beneath it are pushed to the
 * BOTTOM of the column. Whatever the heads do above them, the graphs line up.
 *
 * !important because the divider carries an inline `margin:10px 0`, and an
 * inline style beats an ordinary rule. Only the TOP margin is taken over; the
 * 10px beneath it still separates the rule from the bars.
 *
 * Scoped to columns that actually hold a graph, so the Payments column keeps
 * its horizontal list exactly where it is.
 */
/* #salesCard is in this list even though it is no longer a column (Luis,
   2026-09-18). It used to match as a .tm-col and inherited the alignment for
   free; giving it its own card dropped it out of the selector, and its graph
   floated up a row while Cost's and Profit's stayed pinned to the bottom.
   It still works on it for the same reason it works on them — the outer grid
   stretches it to the row's height, and both surfaces carry the same 18px of
   vertical padding, so the two content boxes start and end level. */
.totals-merged .tm-col:has(.vbar-row),
#salesCard:has(.vbar-row) {
  display: flex;
  flex-direction: column;
}
.totals-merged .tm-col:has(.vbar-row) > .card-divider,
#salesCard:has(.vbar-row) > .card-divider {
  margin-top: auto !important;
}
/* The 10px the divider's inline margin-top used to contribute to the column's
   NATURAL height. An auto margin absorbs free space instead of adding height,
   so without this the whole totals row came out 10px shorter than it was.
   It cannot go back on the divider as padding — that element is a 1px line
   with a background, and padding would draw it as an 11px block. */
.totals-merged .tm-col:has(.vbar-row) > .tc-head,
#salesCard:has(.vbar-row) > .tc-head { margin-bottom: 10px; }

/* ── The Deductibles modal (Luis, 2026-09-18) ────────────────────────────────
 *
 * The card it holds is the one that used to sit in the bottom row, moved here
 * whole rather than rebuilt — its tabs, its form and its transaction list are
 * all still addressed by the same ids from the script further down the page.
 *
 * position:fixed is doing two jobs. It puts the sheet over the page, and it
 * takes the card out of the bottom row's grid so the back counter can have
 * that third slot. Shut, the wrapper is display:none and occupies nothing.
 *
 * There is no close-on-click-outside. Deductibles are typed in here and a
 * stray click on the page behind should not discard a half-entered amount, so
 * the × is the only way out. (The rule below still stops clicks reaching the
 * page, which is what makes that safe rather than merely inconvenient.)
 */
.ded-modal {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 40px 20px;
  overflow-y: auto;
  background: rgba(16, 24, 40, 0.42);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

/* display:flex above beats the [hidden] attribute — the trap the pin pad, the
   phone diagram and the Deductibles chip all fell into. Spell it out. */
.ded-modal[hidden] { display: none !important; }

/* The page behind stops scrolling while the sheet is up. */
body.ded-modal-open { overflow: hidden; }

/* The sheet arrives on the house liquid curve — var(--ease-liquid), the same
   motion the repairs panels and the parts search use. See index.css for what
   the curve is for and the durations that suit it. */
.ded-modal-sheet {
  position: relative;
  /* Was 560px; widened (Luis, 2026-09-27) so the category rows and their
     week strip have room, with the type scaled up to match below. */
  width: min(820px, 100%);
  margin: auto;
  animation: dedSheetIn 0.42s var(--ease-liquid) both;
}
/* The larger sheet: bigger rows, name, amount and week strip. */
.ded-modal .ded-cat-row { padding: 13px 16px; }
/* The name, amount and time carry inline sizes in both renderers, hence the
   positional selectors and !important. */
.ded-modal .ded-cat-row > div > div:first-child > span:last-child { font-size: 15.5px !important; }
.ded-modal .ded-cat-row > div > div:last-child  > span:first-child { font-size: 17px !important; }
.ded-modal .ded-cat-row > div > div:last-child  > span:last-child:not(:first-child) { font-size: 11.5px !important; }
.ded-modal .ded-day b  { font-size: 11px; }
.ded-modal .ded-day em { font-size: 12px; }
.ded-modal .ded-week   { margin: 9px 0 2px 28px; gap: 4px; }
.ded-modal #deductiblesCard input, .ded-modal #deductiblesCard select { font-size: 15px; }
@keyframes dedSheetIn {
  from { opacity: 0; transform: translateY(18px) scale(0.975); }
  to   { opacity: 1; transform: none; }
}

/* Leaving is quicker and does NOT overshoot: a panel settling into place has
   weight, a panel bouncing on its way out just looks undecided. */
.ded-modal.is-closing .ded-modal-sheet {
  animation: dedSheetOut 0.18s cubic-bezier(0.4, 0, 1, 1) both;
}
@keyframes dedSheetOut {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(10px) scale(0.985); }
}

/* The dimmed ground fades rather than snapping, so the page behind does not
   flash back at full strength while the sheet is still on screen. */
.ded-modal            { transition: opacity 0.18s ease; opacity: 1; }
.ded-modal.is-closing { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .ded-modal-sheet,
  .ded-modal.is-closing .ded-modal-sheet { animation: none; }
  .ded-modal { transition: none; }
}

/* The card fills the sheet. It carried an inline margin:0 in the row; here it
   needs its own ground, since the row's background is no longer behind it. */
.ded-modal .card {
  margin: 0 !important;
  max-height: calc(100vh - 60px);
  overflow-y: auto;
}

/* The only way out. Sits on the sheet's top-right corner, outside the card's
   own padding so it never collides with the card's Daily Log button. */
.ded-modal-x {
  position: absolute;
  top: -14px;
  right: -14px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px 7px 11px;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.10));
  border-radius: 999px;
  background: #ffffff;
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(16, 24, 40, 0.18);
  transition: background 0.18s ease, color 0.18s ease;
}
.ded-modal-x span { font-size: 10px; }
.ded-modal-x:hover  { background: #f5f5f7; color: var(--text-primary); }
.ded-modal-x:active { transform: scale(0.96); }

@media (max-width: 560px) {
  .ded-modal   { padding: 56px 12px 20px; }
  .ded-modal-x { top: -10px; right: 0; }
}

/* The Deductibles chip once the day has some. It stops being a label and
   becomes a figure — what came off the profit above it (Luis, 2026-09-18).
   Plum is the house colour for money owed or going out; the rare negative day,
   where more came out of the pot than went in, takes the in-house green. */
/* The coloured states tint the groove and change the ink; the cut is the same
   in all three, so the chip never looks like it changed shape. */
.tc-chip.has-ded {
  background: rgba(107, 55, 68, 0.10);
  color: #6b3744;
  letter-spacing: 0.04em;
}
.tc-chip.has-ded:hover { background: rgba(107, 55, 68, 0.16); color: #4a2531; }
.tc-chip.has-ded-back {
  background: rgba(63, 122, 104, 0.11);
  color: #3f7a68;
  letter-spacing: 0.04em;
}
.tc-chip.has-ded-back:hover { background: rgba(63, 122, 104, 0.17); color: #2f6152; }

/* The chip's arrow only falls on a day that HAS deductibles. --di-run crosses
   into the icon's <use> shadow tree, where the keyframes are declared; see the
   'deductibles' entry in icons.php for why it has to be done that way. */
.tc-chip.has-ded,
.tc-chip.has-ded-back { --di-run: running; }

/* Trade-in credit given today, under the gauge figures and in none of them. */
.hero-tradein { position: absolute; left: 50%; bottom: -68px; transform: translateX(-50%); white-space: nowrap; margin: 0; text-align: center; font-size: 11.5px; font-weight: 600; color: #0b6b4f; letter-spacing: .01em; }
.hero-tradein[hidden] { display: none; }
.hero-tradein b { font-weight: 800; }
.hero-tradein small { display: block; font-size: 10px; font-weight: 600; opacity: .75; margin-top: 1px; }
/* Discounts given today, under the trade-in line, in the house plum; it links
   to the report (2026-09-26). */
.hero-discounts { bottom: -86px; color: #6b3744; text-decoration: none; }
.hero-discounts:hover { text-decoration: underline; }
.hero-discounts b { font-weight: 800; }
/* The Total Cost card's Refunds chip (Luis, 2026-09-28): the Deductibles
   chip's shape, red once the day has refunds. A link, so no button reset. */
.tc-chip.tc-chip-refunds { text-decoration: none; color: inherit; }
.tc-chip.tc-chip-refunds .tc-chip-svg { width: 18px; height: 18px; color: #86868b; }
.tc-chip.tc-chip-refunds.has-rf { background: rgba(179, 20, 29, 0.10); color: #b3141d; }
.tc-chip.tc-chip-refunds.has-rf .tc-chip-svg { color: #b3141d; }
.tc-chip.tc-chip-refunds.has-rf:hover { background: rgba(179, 20, 29, 0.16); }

/* ── The sales filter list in the house motion (Luis, 2026-10-15) ──────────
   One plum wash glides to the option under the pointer and rests on the one
   chosen (motion.js); the option it is on gives up its own. Apple dark draws
   the chosen one as an orange ring with !important, so its hand-over is too. */
.pm-glide.sf-glide {
  background: rgba(107, 55, 68, 0.09);
  box-shadow: none;
}
.sf-panel.pm-track .sf-opt.pm-on,
.sf-panel.pm-track .sf-opt:hover { background: transparent; }
body.apple-mode.apple-dark .pm-glide.sf-glide {
  background: rgba(236, 124, 34, 0.14);
  box-shadow: 0 0 0 1px #ec7c22, 0 0 0 3px rgba(236, 124, 34, 0.16);
}
body.apple-mode.apple-dark .sf-panel.pm-track .sf-opt.pm-on {
  background: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
}
