/* ============================================================
   stackview — app specific components. Tokens only, solid fills,
   no gradients, no blur, no glow. Loaded after app.css.
   ============================================================ */

/* == 0. accent: clear azure ==
   stackview is the infrastructure screen, so its accent is blue. The kit's
   token NAMES are kept — renaming --amber across every rule in app.css and
   this file would be a large diff for nothing visible — and only the values
   move. app.css is the shared kit copy and is never edited.

   The structure is the kit's, unchanged: --amber-fill is a FILL, --amber-deep
   and --amber-darker are the readable shades on white, --amber-soft and
   --amber-line are the tint and hairline of a selected state, --on-amber is
   what sits on the fill.

   Two things had to change rather than simply shift hue.

   1. --on-amber. Ink on #0B70C8 is 3.59:1 and fails; white is 5.04:1, so the
      fill now carries white text. Everything that reads --on-amber (primary
      button, brand mark, assistant avatar and launcher) follows automatically.

   2. Distance from --info (#1F5C9E). The kit's info blue is a muted navy at
      L*38.6 with 67% saturation; the accent is a clear azure at L*46.8 with
      90% saturation, and its tint (#E4F2FE, b* -7.4) and hairline (#9ACBF2,
      b* -25.2) are markedly cooler and stronger than --info-soft (b* -5.3)
      and --info-line (b* -13.3). Two places where the two blues would have
      sat side by side are re-pointed at other status tokens further down:
      the AWS provider badge and the "warning" alert rule.

   Contrast, computed not eyeballed:
     white on --amber-fill ................ 5.04:1
     --amber-deep on white ................ 5.50:1
     --amber-deep on --amber-soft ......... 4.82:1
     --amber-deep on --surface-2 .......... 5.26:1
     --amber-darker on white .............. 9.25:1
     --ink on --amber-soft ................ 15.6:1
   Dark theme:
     --amber-deep on --surface ............ 8.03:1
     --amber-deep on --amber-soft ......... 7.30:1
     --ink on --amber-soft ................ 14.1:1
     white on --amber-fill ................ 5.04:1 (the fill does not move) */
:root{
  --amber:#0B70C8;       --amber-deep:#0C6ABE;   --amber-darker:#08497F;
  --amber-fill:#0B70C8;  --amber-fill-hi:#0A5CA8;
  --amber-soft:#E4F2FE;  --amber-line:#9ACBF2;   --on-amber:#FFFFFF;
  /* the kit's accent-tinted hover, moved off yellow with everything else */
  --hover:#F0F6FD;

  /* The accent is also used as a large FIELD in two places — the sidebar and
     the phone-preview surround. A 5:1 fill leaves no room for a second level
     of text on it, and both of those fields need one (group headings, counts,
     the tenant line). So the field is one step deeper than the fill: white on
     it is 6.75:1, which buys a quiet tint at 4.89:1. Same hue, same family,
     solid. Nothing else in the app uses these. */
  --sv-field:#0A5CA8;    --sv-field-hi:#084E8E;
  --sv-field-ink:#FFFFFF;
  --sv-field-ink-2:#C7DEF6;  /* 4.89:1 on the field */
  --sv-field-line:#3B7FC4;   /* hairline on the field */
  --sv-field-mark:#BFD9F5;   /* quiet glyphs on the field, 4.71:1 */
  --sv-field-btn:#E4EFFB;    /* the pale controls on the field */
}
[data-theme="dark"]{
  --amber-soft:#0E2437;  --amber-line:#1D4B75;   --amber-deep:#6FB6F5;  --amber-darker:#A3D0F8;
  --hover:#16222E;
}
/* the kit selects with ink on the fill, which was right on yellow (10.8:1)
   and is 3.59:1 on azure — the selection takes --on-amber like every other
   text that lands on the fill */
::selection{background:var(--amber-fill);color:var(--on-amber)}

/* == shell extras == */
.sv-org{margin-bottom:10px}
.sv-org__name{font-weight:600;font-size:13.5px;margin-top:4px}
.sv-reset{justify-content:flex-start;color:var(--muted);font-weight:500}
.sv-reset:hover{color:var(--ink)}
.shell.is-rail .sv-reset{justify-content:center}
/* the footer is About across the top, then two paired rows: the links, then
   install beside reset. The rows carry the rhythm and centre what is in them,
   so no member needs a margin or an alignment of its own. */
.side__foot .side__pair{margin-top:8px}
.side__pair .btn{justify-content:center}

/* back to the author's site — the one dark control in the sidebar, so it reads
   as a deliberate way out on either tone. White on ink is 15.4:1. */
.sv-site{background:var(--ink);border-color:var(--ink);color:#FFFFFF}
.sv-site:hover{background:var(--night-2);border-color:var(--night-2);color:#FFFFFF}
.sv-site svg{color:#FFFFFF}
/* source link: an ordinary outline control, so nasvih.in stays the only
   inverted element in the footer */
.sv-about__src{border-top:1px solid var(--line);padding-top:14px}
.sv-about__src .sv-src{margin-top:10px}

/* == the accent sidebar is the default, so everything in it is checked
      against the field, never against white. ==
   Yellow was a LIGHT surface: app.css therefore pins the ink palette inside
   this subtree — ink text, ink focus ring, ink hairlines — and re-pins it a
   second time for dark mode. Azure is a DARK surface, so every one of those
   rules has to come out the other way round. Re-pointing the same tokens
   inside the subtree flips all of them at once, in both themes, without
   touching app.css and without duplicating a single rule.

   Checked against --sv-field (#0A5CA8):
     white (nav links, brand, tenant name) ........ 6.75:1
     --sv-field-ink-2 (tag, group labels, counts) . 4.89:1
     --sv-field-mark (nav icons) .................. 4.71:1
     white on --sv-field-hi (nav hover) ........... 8.28:1
     ink on --sv-field-btn (the pale controls) .... 15.7:1
     --amber-deep on the white selected pill ...... 5.50:1 */
.side[data-tone="amber"],
[data-theme="dark"] .side[data-tone="amber"]{
  --amber-fill:var(--sv-field); --amber-fill-hi:var(--sv-field-hi);
  --ink:var(--sv-field-ink);   --ink-2:var(--sv-field-ink-2);
  --muted:var(--sv-field-ink-2); --faint:var(--sv-field-ink-2);
  --line:var(--sv-field-line); --line-2:var(--sv-field-line);
  /* the selected nav item is a white pill on the field in both themes, so the
     subtree keeps a white --bg and a text-on-white --amber-deep even when the
     page palette is dark */
  --bg:#FFFFFF;
  --amber-deep:#0C6ABE;
  /* quiet glyphs sitting ON the field, where app.css reads --amber-darker */
  --amber-darker:var(--sv-field-mark);
  --night-2:#2E3033;
}
/* The four kit rules that pair a var(--ink) surface with var(--ink) text.
   On yellow each was dark-on-light; on azure the two halves swap. */
.side[data-tone="amber"] .navlink.is-active{background:#FFFFFF;color:#17181A}
/* the alert count rides inside that pill, and the tint it takes on the field
   would be 1.4:1 on the white of it */
.side[data-tone="amber"] .navlink.is-active .navlink__count{color:#5A5F66}
.side[data-tone="amber"] .navlink:hover{background:var(--sv-field-hi)}
.side[data-tone="amber"] .btn,
.side[data-tone="amber"] .side__brandbtns .btn{background:var(--sv-field-btn);border-color:var(--sv-field-btn);color:#17181A}
.side[data-tone="amber"] .btn:hover,
.side[data-tone="amber"] .side__brandbtns .btn:hover{background:#FFFFFF;border-color:#FFFFFF}
/* and the hairlines, which app.css draws as translucent ink — invisible on a
   dark field, so they are drawn in the field's own lighter step instead */
.side[data-tone="amber"] .side__brand,
.side[data-tone="amber"] .side__foot,
.side[data-tone="amber"] .navgroup+.navgroup{border-color:var(--sv-field-line)}

/* stackview's own footer bits, held to the same rule. --sv-reset is a control
   on the pale button surface, so it takes ink like the rest of them. */
.side[data-tone="amber"] .sv-org__name{color:var(--ink)}
.side[data-tone="amber"] .sv-org .label,
.side[data-tone="amber"] .sv-org .faint{color:var(--ink-2)}
.side[data-tone="amber"] ::selection{background:var(--ink);color:var(--amber-fill)}
/* the dark site link keeps its own fill against the sidebar button styling —
   the one dark control on the field, in both themes */
.side[data-tone="amber"] .sv-site,
[data-theme="dark"] .side[data-tone="amber"] .sv-site{background:#17181A;border-color:#17181A;color:#FFFFFF}
.side[data-tone="amber"] .sv-site:hover,
[data-theme="dark"] .side[data-tone="amber"] .sv-site:hover{background:#2E3033;border-color:#2E3033;color:#FFFFFF}
.side[data-tone="amber"] .sv-site svg,
[data-theme="dark"] .side[data-tone="amber"] .sv-site svg{color:#FFFFFF}
.sv-topttl{min-width:0}
.sv-navscrim{position:fixed;inset:0;background:rgba(23,24,26,.34);z-index:60}
.sv-navscrim--drawer{z-index:66}
.sv-drawer__ttl{font-weight:700;font-size:15px;letter-spacing:-.015em}

.sv-demopill{cursor:pointer;font-family:var(--mono)}
.sv-demopill:hover{background:var(--amber-fill);color:var(--on-amber);border-color:var(--amber-fill)}

/* == topbar controls: notifications, device preview, theme == */
.sv-topbtns{display:flex;align-items:center;gap:6px;flex:none}
.sv-seg{display:inline-flex;align-items:center;gap:2px;border:1px solid var(--line);border-radius:var(--r);padding:2px}
.sv-seg .btn{width:30px;height:30px;padding:0}
.sv-devbtn[aria-pressed="true"]{background:var(--amber-soft);color:var(--amber-deep)}

.sv-notifwrap{position:relative;flex:none}
.sv-bell{position:relative;overflow:visible}
.sv-bell__n{position:absolute;top:-3px;right:-3px;min-width:18px;height:18px;padding:0 4px;border-radius:var(--r-pill);
  background:var(--bad);color:var(--surface);font-size:10px;font-weight:600;line-height:1;
  display:grid;place-items:center;border:2px solid var(--surface)}
.sv-notif{position:absolute;top:calc(100% + 8px);right:0;z-index:40;width:min(370px,calc(100vw - 24px));
  max-height:min(560px,72vh);overflow-y:auto;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--shadow-3)}
.sv-notif__head{display:flex;align-items:center;gap:8px;padding:12px 12px 10px;border-bottom:1px solid var(--line);
  position:sticky;top:0;background:var(--surface);z-index:1}
.sv-notif__ttl{font-weight:700;font-size:14.5px;letter-spacing:-.015em}
.sv-notif__list{display:block}
.sv-notif__i{display:flex;align-items:flex-start;gap:4px;border-bottom:1px solid var(--line);padding:2px 6px 2px 0}
.sv-notif__i:last-child{border-bottom:0}
.sv-notif__i.is-read{background:var(--surface-2)}
.sv-notif__i.is-read .sv-notif__t{font-weight:500;color:var(--muted)}
.sv-notif__go{flex:1;min-width:0;text-align:left;background:none;border:0;cursor:pointer;padding:10px 8px 11px 12px;border-radius:var(--r-sm)}
.sv-notif__go:hover{background:var(--hover)}
.sv-notif__t{font-size:13.5px;font-weight:600;line-height:1.4;margin:6px 0 3px}
.sv-notif__dot{width:7px;height:7px;border-radius:50%;background:var(--bad);flex:none}
.sv-notif__read{width:30px;height:30px;padding:0;margin-top:9px;flex:none}
.sv-notif__empty{padding:26px 18px;text-align:center}
.sv-notif__empty h4{margin-bottom:6px}

/* == phone preview ==
   The app is loaded again inside an iframe at 390x844 so the real
   breakpoints apply. Solid accent surround, white text on it, dark bezel.
   It is the second place the accent is a full field and it carries two levels
   of text, so it takes --sv-field like the sidebar rather than the fill. */
.sv-phone{position:fixed;inset:0;z-index:90;background:var(--sv-field);overflow:auto;
  display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px}
.sv-phone__bar{display:flex;align-items:center;gap:16px;width:100%;max-width:430px;color:var(--sv-field-ink)}
.sv-phone__bar>div{flex:1}
.sv-phone__name{font-weight:700;font-size:15px;letter-spacing:-.02em}
.sv-phone__size{font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--sv-field-ink-2)}
.sv-phone__bar .btn{background:var(--sv-field-btn);border-color:var(--sv-field-btn);color:#17181A}
.sv-phone__bar .btn:hover{background:#FFFFFF;border-color:#FFFFFF}
.sv-phone__bezel{width:414px;flex:none;background:var(--night);border:1px solid var(--night-line);
  border-radius:34px;padding:12px}
.sv-phone__screen{width:390px;height:844px;border:0;border-radius:24px;background:#FFFFFF;display:block}
body.is-phoneview{overflow:hidden}
body.is-phoneview .assist-fab,body.is-phoneview .assist,body.is-phoneview .toasts{display:none!important}
@media (max-height:960px){
  .sv-phone__bezel{transform:scale(.84);transform-origin:top center;margin-bottom:-140px}
}
@media (max-height:820px){
  .sv-phone__bezel{transform:scale(.72);margin-bottom:-246px}
}

/* == about: the worked examples == */
.sv-about__ex{display:grid;gap:12px;margin-top:10px}
.sv-about__ex li{border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;background:var(--surface-2)}
.sv-about__ask{font-size:12.5px;font-weight:600;color:var(--ink)}
.sv-about__does{margin:5px 0 6px;line-height:1.55}
.sv-about__b{margin-top:16px}
.sv-about__b h4{margin-bottom:4px}
.sv-about__b p{line-height:1.6}
.sv-about__list{list-style:none;margin-top:6px;display:grid;gap:8px}
.sv-about__list li{position:relative;padding-left:15px;font-size:13px;line-height:1.6;color:var(--muted)}
.sv-about__list li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;border-radius:50%;background:var(--amber-fill)}
.sv-about__list strong{color:var(--ink);font-weight:600}

/* == small shared bits == */
.sv-stat--fit{font-size:22px;overflow-wrap:anywhere}
.sv-neg{color:var(--bad);font-weight:600}
.sv-count{color:var(--faint);white-space:nowrap}
.sv-inline{display:inline-flex;align-items:center;gap:8px}
.select--sm{width:auto;min-width:118px;padding:6px 30px 6px 10px;font-size:13px}
.sv-filters{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.sv-filters .search{min-width:200px}

.sv-kv{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
.sv-kv--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.sv-kv__v{font-size:13.5px;margin-top:4px;overflow-wrap:anywhere}
.sv-detailhead{display:flex;gap:8px;flex-wrap:wrap}

.sv-badge{font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  border:1px solid var(--line-2);border-radius:var(--r-xs);padding:2px 6px;color:var(--muted);background:var(--surface-2);flex:none}
/* Provider badges have to be told apart at a glance in a dense table, and the
   accent is now the same family as --info, which Azure already uses. AWS
   moves off the accent onto the warn family so the three stay three colours
   rather than two blues and a grey. */
.sv-badge--aws{border-color:var(--warn-line);background:var(--warn-soft);color:var(--warn)}
.sv-badge--azure{border-color:var(--info-line);background:var(--info-soft);color:var(--info)}
.sv-badge--onprem{border-color:var(--line-2);background:var(--surface-2);color:var(--ink-2)}

.sv-env{font-family:var(--mono);font-size:11px;font-weight:600;border-radius:var(--r-xs);padding:2px 7px;
  border:1px solid var(--line-2);background:var(--surface-2);color:var(--muted);white-space:nowrap}
.sv-env--prod{border-color:var(--ok-line);background:var(--ok-soft);color:var(--ok)}
.sv-env--staging{border-color:var(--warn-line);background:var(--warn-soft);color:var(--warn)}
.sv-env--dev{border-color:var(--info-line);background:var(--info-soft);color:var(--info)}
.sv-env--shared{border-color:var(--line-2);background:var(--surface-2);color:var(--muted)}
.sv-envname{font-family:var(--mono);font-size:12.5px;font-weight:600}

/* == overview == */
.view .grid.g2{align-items:start}
.sv-prov{display:block}
.sv-prov__v{font-size:20px;margin-top:6px}
.sv-prov__ico svg{width:20px;height:20px;color:var(--amber-deep)}
.sv-alertlist{display:flex;flex-direction:column;gap:12px}
.sv-alertlist li{display:flex;gap:10px;align-items:flex-start}
.sv-alertlist__t{font-size:13.5px;font-weight:500;line-height:1.45}

/* == resources table == */
.sv-th{padding:0!important}
.sv-sort{display:flex;align-items:center;gap:5px;width:100%;background:none;border:0;cursor:pointer;
  font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);padding:11px 14px}
.data th.right .sv-sort{justify-content:flex-end}
.sv-sort:hover{color:var(--amber-deep)}
.sv-sort__i{font-size:8px;line-height:1}
.sv-row{cursor:pointer}
.sv-row:focus-visible{outline:2px solid var(--amber);outline-offset:-2px}
.sv-res{display:flex;align-items:center;gap:9px;min-width:0}
.sv-res__n{font-size:12.5px;font-weight:600}
.sv-util+.sv-util{margin-top:12px}
.sv-util .between{margin-bottom:5px}
.sv-tags{display:flex;flex-wrap:wrap;gap:6px}
.sv-tag{font-size:11px;border:1px solid var(--line-2);background:var(--surface-2);border-radius:var(--r-xs);padding:3px 7px;color:var(--muted)}
.sv-waste{border-color:var(--amber-line)}

/* == cost == */
.sv-savings{border-color:var(--amber-line);background:var(--amber-soft)}
.sv-savings__v{font-size:30px;font-weight:600;letter-spacing:-.02em;margin:6px 0 4px}
.sv-savings__plan{text-align:right}
.sv-savings__pv{font-size:20px;margin:6px 0 4px}
.sv-wastelist{display:grid;gap:var(--gutter);grid-template-columns:repeat(2,minmax(0,1fr))}
.sv-wastecard.is-planned{border-color:var(--amber-line);background:var(--amber-soft)}
.sv-wastecard__n{font-size:13px;font-weight:600}
.sv-wastecard__save{font-size:15px;font-weight:600;white-space:nowrap;color:var(--amber-deep)}
.sv-why{font-size:13px;line-height:1.55;margin:12px 0 8px}

/* == access == */
.sv-user{display:flex;align-items:center;gap:9px;min-width:0}
.sv-user__n{font-weight:600;font-size:13.5px}
.sv-ownlist li{display:flex;justify-content:space-between;gap:10px;padding:6px 0;border-bottom:1px solid var(--line)}
.sv-ownlist li:last-child{border-bottom:0}
.sv-check li+li{margin-top:8px}
.sv-check__row{display:flex;gap:9px;align-items:flex-start;font-size:13.5px;cursor:pointer}
.sv-check__row input{margin-top:3px;accent-color:var(--amber);width:15px;height:15px;flex:none}
.sv-check__done{color:var(--faint);text-decoration:line-through}

/* == alerts == */
.sv-alerts{display:flex;flex-direction:column;gap:10px}
.sv-alert{display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap;
  border:1px solid var(--line);border-left:3px solid var(--line-2);border-radius:var(--r-lg);
  background:var(--surface);padding:14px 16px}
/* The severity rules follow the severity pills exactly — critical and high
   both take the bad pill, warning the warn pill, info the info pill. That was
   already true of three of the four; "warning" used to borrow the accent,
   which on azure would have read as the same bar as an info alert directly
   below it in the sorted list. */
.sv-alert--critical{border-left-color:var(--bad)}
.sv-alert--high{border-left-color:var(--bad)}
.sv-alert--warning{border-left-color:var(--warn)}
.sv-alert--info{border-left-color:var(--info)}
.sv-alert__main{flex:1;min-width:260px}
.sv-alert__main .row{margin-bottom:7px}
.sv-alert__title{display:block;text-align:left;background:none;border:0;padding:0;cursor:pointer;
  font-size:14.5px;font-weight:600;letter-spacing:-.01em;line-height:1.35;margin-bottom:5px;color:var(--ink)}
.sv-alert__title:hover{color:var(--amber-deep)}
.sv-alert__meta{margin-top:8px}
.sv-alert__acts{align-items:flex-start}

/* == uptime == */
.sv-legend{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.sv-legend__i{display:inline-flex;align-items:center;gap:7px}
.sv-svc+.sv-svc{margin-top:0}
.sv-svc__head{display:flex;justify-content:space-between;gap:14px;align-items:flex-start;flex-wrap:wrap;margin-bottom:14px}
.sv-svc__nums{display:flex;align-items:center;gap:10px}
.sv-svc__up{font-size:16px}
.sv-strip{display:flex;gap:3px}
.sv-day{flex:1;min-width:5px;height:34px;border-radius:var(--r-xs);background:var(--ok);border:0;display:block}
.sv-day--deg{background:var(--warn)}
.sv-day--down{background:var(--bad)}
.sv-day--maint{background:var(--line-2)}
.sv-day--btn{cursor:pointer;padding:0}
.sv-day--btn:hover{outline:2px solid var(--ink);outline-offset:1px}
.sv-legend .sv-day{flex:none;width:14px;height:14px}
.sv-svc__foot{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:12px}

/* == reports == */
.sv-notes{display:grid;gap:14px}
.sv-note p{margin-top:4px;line-height:1.55}
.sv-dl{display:grid;gap:8px}
.sv-dlitem{display:flex;align-items:center;gap:11px;width:100%;text-align:left;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r);background:var(--surface);padding:10px 12px}
.sv-dlitem:hover{background:var(--hover);border-color:var(--amber-line)}
.sv-dlitem__i svg{width:17px;height:17px;color:var(--amber-deep)}
.sv-dlitem__t{display:block;font-size:13.5px;font-weight:600}
.sv-dlitem .small{display:block}

/* == dark mode ==
   app.css carries the palette; these are stackview's own pieces. The accent
   sidebar does not need a second block here any more: azure is a dark surface
   in both themes, so the one token block up in section 0 already covers the
   dark theme, and it overrides app.css's yellow-era dark pinning on the same
   selector. */
:root{color-scheme:light}
[data-theme="dark"]{color-scheme:dark}

/* the one inverted control in the sidebar: on the default (grey) sidebar in
   dark mode it is a light block with dark text — the accent sidebar keeps its
   own ink block, set alongside the rest of the tone rules above */
[data-theme="dark"] .sv-site{background:var(--ink);border-color:var(--ink);color:var(--bg)}
[data-theme="dark"] .sv-site svg{color:var(--bg)}
[data-theme="dark"] .sv-site:hover{background:var(--ink-2);border-color:var(--ink-2);color:var(--bg)}
/* the select chevron is drawn in the light muted grey — lift it on dark */
[data-theme="dark"] .select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23A2A8B0' stroke-width='1.7'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E")}
/* uptime blocks: the palette already lifts ok/warn/bad, but the strip is the
   one place colour carries meaning on its own, so the day buttons keep a
   hairline and every strip stays paired with the legend words. */
[data-theme="dark"] .sv-day{box-shadow:inset 0 0 0 1px rgba(0,0,0,.28)}
[data-theme="dark"] .sv-day--btn:hover{outline-color:var(--ink)}
[data-theme="dark"] .sv-phone__screen{background:var(--bg)}
[data-theme="dark"] .sv-navscrim{background:rgba(0,0,0,.6)}

/* == assistant message tables ==
   The bubble is a flex item beside the avatar, so its automatic minimum is the
   table's own minimum: four money columns will not fold, and the bubble grows
   past the panel. A scrolling wrapper drops that minimum to zero, so the
   bubble stays inside the panel and the table scrolls on its own. */
.msg,.msg__body{min-width:0}
.msg__body{max-width:100%;overflow-wrap:anywhere}
.msg__tablewrap{margin-top:10px;max-width:100%;overflow-x:auto;
  border:1px solid var(--line);border-radius:var(--r-sm)}
/* natural width, never less than the bubble: a money column that wraps reads
   as two numbers, so the table keeps its own size and the wrapper scrolls */
.msg__tablewrap .msg__table{margin-top:0;border:0;border-radius:0;width:max-content;min-width:100%}
.msg__tablewrap .msg__table th,.msg__tablewrap .msg__table td{white-space:nowrap}

/* A prose column cannot survive being squeezed: inside a narrow card the
   three column waste table collapses to a few characters a line. Giving the
   tables in a scroll wrapper a floor turns that squeeze into a scroll. Every
   other data table is already wider than this, so nothing else moves. */
.tablewrap--scroll>table.data{min-width:460px}

/* == responsive == */
@media (max-width:1080px){
  .sv-wastelist{grid-template-columns:minmax(0,1fr)}
  .sv-kv{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:900px){
  /* the sidebar is a drawer down here, so the rail control has nothing to do */
  .sv-railbtn{display:none}
  /* and a phone frame inside a phone-sized window is nothing to look at:
     the device control belongs to the desktop layout */
  .sv-seg{display:none}
  /* == touch sizing ==
     Below 900px the whole shell is driven by a thumb, so the chrome controls
     grow to a 40px target. Nothing changes above this width, where the same
     controls are pointed at with a cursor. */
  .btn{min-height:41px}
  /* the small button stays visibly the quieter of the two sizes, but 31px is
     not a thumb target — 38px is the most it can take without reading as an
     ordinary button beside a card heading */
  .btn--sm{min-height:38px}
  .btn--icon,.side__brandbtns .btn{width:40px;height:40px;min-height:40px;padding:9px}
  .navlink{padding:10px}
  .side__foot .btn{min-height:40px}
  .sv-notif__go{padding-top:12px;padding-bottom:13px}
  .sv-notif__read{width:38px;height:38px;margin-top:7px}
  /* a pill is a 24px shape by design; as the one topbar control that is a
     pill it still has to be tappable, so only its vertical padding grows */
  .sv-demopill{padding-top:8px;padding-bottom:8px}
}
@media (max-width:640px){
  /* the topbar carries five controls at this width — the title gives way
     first, then the subtitle, so nothing wraps and nothing overflows */
  .topbar{gap:8px;padding:0 12px}
  /* the spacer would take the free room and leave the title truncating at a
     few characters — down here the title itself is the elastic element */
  .topbar .spacer{display:none}
  .sv-topttl{flex:1;min-width:0}
  .sv-topttl .topbar__title,.sv-topttl .topbar__sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sv-demopill{padding:8px 8px;font-size:9.5px;letter-spacing:.04em}
  .sv-topbtns{gap:2px}
  /* the panel hangs off the bell, and the bell is near the right edge of a
     phone: a 370px panel anchored there runs 140px off the left of the screen
     with no way to scroll to it. Down here it stops being a dropdown and
     becomes a sheet pinned to both edges of the viewport. */
  .sv-notif{position:fixed;top:calc(var(--bar) + 6px);left:12px;right:12px;width:auto;
    max-height:calc(100vh - var(--bar) - 24px)}
  /* app.css already gives the page head its own row for the buttons at this
     width; here they share that row evenly rather than bunching to the left */
  .page-head .btnrow>*{flex:1;justify-content:center}
  .sv-kv,.sv-kv--2{grid-template-columns:minmax(0,1fr)}
  .sv-filters{align-items:stretch}
  .sv-inline{width:100%;justify-content:space-between}
  .select--sm{flex:1;min-width:0}
  .sv-savings__plan{text-align:left;margin-top:14px}
  .sv-savings .between{flex-direction:column;align-items:flex-start}
  .sv-day{height:30px}
  .sv-alert{flex-direction:column;gap:12px}
  .sv-alert__main{min-width:0}
}
@media (max-width:400px){
  /* five controls in a 320px bar: the pill drops to one word so the view
     title keeps a readable share of the row rather than one syllable */
  .sv-demopill__long{display:none}
}
@media (max-width:390px){
  .sv-strip{gap:2px}
  .sv-day{min-width:4px}
}
@media (max-width:359px){
  /* seven months of a five figure sum across 320px: the value above each bar
     was ellipsing to "$5,9". A tighter gap and a smaller figure buy the two
     characters back — the labels stay whole rather than half true. */
  .bars{gap:4px}
  .bars__x{font-size:9px;letter-spacing:-.02em}
}
