/* ══════════════════════════════════════════════════════════════════════════════════════════════
   THE ANALYTICS BOARD — one drawing, two frames
   ══════════════════════════════════════════════════════════════════════════════════════════════

   "please jsut copy the exact page we have for our clay.sites like demo.clay.site .. and add the
    numbers for our main site.. the fitlers we have some extra numbers.. that one wokrs"

   These rules were the tail of site.css, which is the TENANT stylesheet — so the one screen Clay
   shows its owners about their own site could only ever be drawn on a tenant page. /dashboard had
   a second report of its own, and the answer to "make that one look like this one" was a second
   copy of a drawing, which is the mistake this repo has made once already with the sections.

   So the board is its own sheet and its own script (public/stats-board.js), linked by both, and
   there is one drawing. Every class here is `st-`, which means something nowhere else, and every
   colour is an `--st-` token — the same trick `public/insights.css` uses to sit on two grounds.

   THE TOKENS ARE ON THE BOARD, NOT ON THE BODY. They were declared on `body.stats-on`, which is
   the tenant page turning into the dashboard; the same board inside /dashboard has no such body,
   so `.st-scope` declares them too and either frame carries its own palette.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
/* ============================================================================================
   ANALYTICS — the site becomes the dashboard
   ============================================================================================
   This layer deliberately does NOT wear the tenant's theme. Everything else on the page is
   painted by whatever the owner picked; this is Clay talking to its owner about their site, so
   it is Clay's own surface — near-black ground, one orange, and colour used only where it means
   something (green up, red down, amber warning). The ground travelling from the theme's
   background to this black is the whole reason it reads as ARRIVING somewhere rather than as a
   panel dropped on top.
   ============================================================================================ */
/* THE GROUND IS BLACK, and the cards stand on it. `#08080a` is a blue-tinted near-black, and on a
   product whose whole design language is black, white and one accent it reads as neither: "analytics
   has strange background.. feel like should e black? full..". The cards keep their own lift, so the
   board still reads as a board rather than as one flat sheet. */
/* THE PALETTE, WHEREVER THE BOARD IS DRAWN. `body.stats-on` is the tenant page turning into the
   dashboard; `.st-scope` is any other frame that draws the same board — /dashboard has no such
   body, and without this every card came out on the browser's own colours. */
body.stats-on, .st-scope { --st-bg: #000000; --st-card: #131316; --st-card2: #17171b; --st-line: rgba(255,255,255,.09);
  --st-ink: #f4f2ee; --st-dim: rgba(244,242,238,.56); --st-faint: rgba(244,242,238,.34);
  --st-accent: #C87F5E; --st-up: #5FD08A; --st-down: #FF7A72; --st-warn: #F5B544; }
body.stats-on { background: var(--st-bg); }
/* (`html { background: var(--vt-ground, transparent) }` stayed in site.css — it is about the
   TENANT page's view transition, and a bare `html` rule in a sheet /dashboard also links would
   repaint a screen this board is only a guest on.) */
/**
 * THE BOARD REPLACES THE SITE, AND THE SITE'S GROUND GOES WITH IT.
 *
 * A v23 theme paints the page's paper onto the ROOT as well as the page — deliberately, so the
 * strip under a short page and the flash a phone shows when it overscrolls are the site's colour
 * and not the browser's white. It states it as `:root[data-mode="light"], :root[data-mode="light"]
 * body`, which is two classes to `body.stats-on`'s one — so it won, and the dashboard came up
 * standing on the site's paper: black cards in a column with the business's porcelain either side
 * of them. "analytics background should be full black.. was like this before" — and it was, before
 * the themes started painting the root, which is a thing only they can see a reason for.
 *
 * Clay's own chrome is not the site and does not wear the site's colours, so this is stated with
 * the same `!important` the rest of the board's overrides use. Both selectors, because the theme
 * paints both: the canvas AND the body. `#000` rather than `var(--st-bg)` on the root, because the
 * root is outside `body` and cannot read a variable declared on it.
 */
html:has(> body.stats-on) { background: #000 !important; }
body.stats-on { background: var(--st-bg) !important; }
/* The filter bar starts where the site menu was — so the morph lands true — then closes the gap the
   editor's device toggle occupies, which is not on screen here. After the transition, and gently. */
/* `.is-owner` is in the selectors these override (the editor's own top offsets), so it has to be in
   these too — without it they lose on specificity and the gap never closes. */
body.stats-on .wrap { transition: max-width .45s cubic-bezier(.32,.72,0,1), padding-top .45s cubic-bezier(.32,.72,0,1); }
body.stats-on #site-nav { transition: top .45s cubic-bezier(.32,.72,0,1); }
body.is-owner.stats-on.stats-tight .wrap { padding-top: 16px; }
body.is-owner.stats-on.stats-tight #site-nav { top: 14px; }
/* a refresh updates in place: the numbers travel, the bars and lines do not redraw */
.st.st-instant .st-bar i, .st.st-instant .st-track i { transition: none !important; }
.st.st-instant .st-sparkline, .st.st-instant .st-arealine { transition: none !important; }
.st.st-instant .st-sparkfill, .st.st-instant .st-areafill, .st.st-instant .st-areadot { animation: none !important; opacity: 1 !important; }
.st.st-instant .st-card { animation: none !important; }
body.stats-on .wrap { background: var(--st-bg) !important; }
body.stats-on .wrap::before, body.stats-on .wrap::after { display: none !important; }
/* `#clay-page` is the clay renderer's page, and it is hidden here for the same reason `#grid` is:
   the dashboard REPLACES the site. Left out, the board was appended under a page that stayed put
   and the whole morph read as broken. */
/* THE BADGE, BACK WHERE IT BELONGS — and ABOVE the body.stats-on rule below, deliberately.
   `body:not(.is-owner) .qck-badge` and `body.stats-on .qck-badge` are both 0-3-0, so which
   one wins is purely which comes later; appended at the end of the file instead, a visitor
   opening the stats view would have got the badge back on top of it. */
.qck-badge { position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 60; display: none; align-items: center; gap: 8px; height: 46px; padding: 0 20px 0 16px; white-space: nowrap; max-width: calc(100vw - 24px); border-radius: var(--ed-r-sheet); font-family: var(--lb-font, var(--font)); font-size: 13px; font-weight: 650; line-height: 1; text-decoration: none; color: var(--ink, #f4f3f1); background: var(--card-bg, rgba(20,20,22,0.92)); border: 1px solid var(--card-border, var(--ed-hair-2)); box-shadow: 0 10px 30px -12px rgba(0,0,0,0.55); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); transition: transform .15s ease; }
.qck-badge:hover { transform: translateX(-50%) translateY(-2px); }
/* the mark is the WORDMARK now — a word needs width, and a word in a circle is a word in a hole */
.qck-badge .qb-mark { display: inline-flex; align-items: center; flex: 0 0 auto; }
.qck-badge .qb-mark svg { height: 17px; width: auto; display: block; }
.qck-badge b { font-weight: 800; }
body:not(.is-owner) .qck-badge, body.preview .qck-badge { display: inline-flex; }
body.panel-open .qck-badge { display: none; } /* don't fight the settings panel while it's open */

body.stats-on #grid, body.stats-on #site-footer, body.stats-on .qck-badge, body.stats-on .sec-tools,
body.stats-on .dev-float, body.stats-on .undo-dock, body.stats-on #clay-page,
body.stats-on .clay-badge { display: none !important; }
/* No `.wrap` on a clay page, so the board and the filter bar hang off the body — which needs the
   top room the wrap's padding gave them. */
body.stats-on #stats { padding-top: 84px; transition: padding-top .45s cubic-bezier(.32,.72,0,1); }
body.stats-on.stats-tight #stats { padding-top: 64px; }
body.stats-on .wrap #stats { padding-top: 4px; }
/* the dock keeps ONE button while the dashboard is up — the way back is the way in */
body.stats-on #ed-bar .ed-collapse > button:not([data-act="stats"]),
body.stats-on #ed-bar .ed-collapse .sep,
body.stats-on #ed-bar .ed-devices,
body.stats-on #ed-bar [data-act="preview"] { display: none !important; }
#ed-bar [data-act="stats"].on { background: var(--qck, #C87F5E); color: #fff; }
/* With one button left in it, .ed-collapse's 8px right margin is the whole gap between the button
   and the bar's own padding — so Close sat 8px left of centre inside the capsule. */
body.stats-on #ed-bar .ed-collapse { margin-right: 0; }

.st { max-width: 1200px; margin: 0 auto; padding: 4px 4px 140px; font-family: var(--qck-font, Inter, system-ui, sans-serif); color: var(--st-ink); }
.st-fade { opacity: .35; transition: opacity .18s; }

/* ---- the filter bar (the site menu, re-tasked) ---- */
#site-nav.st-navbar .st-navinner { background: rgba(19,19,22,.86) !important; border-color: var(--st-line) !important; gap: 10px; }
/* the report's own title is a lowercase word and nothing else — see the block by `.st-lbl` */
.st-navttl { display: inline-flex; align-items: center; gap: 8px; flex: none; color: #fff; font-weight: 700; font-size: 14px; text-transform: lowercase; }
.st-navttl svg { display: none; }
.st-chips { display: flex; align-items: center; gap: 4px; flex: 1 1 auto; overflow-x: auto; scrollbar-width: none; padding-right: 6px; -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 14px), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 14px), transparent); }
.st-chips::-webkit-scrollbar { display: none; }
/* the range switch reads lowercase too, like the report's own 7d/30d/90d/1y */
.st-chip { text-transform: lowercase; flex: none; display: inline-flex; align-items: center; gap: 5px; border: 0; background: transparent; color: rgba(255,255,255,.62);
  font: inherit; font-weight: 650; font-size: 13px; padding: 7px 13px; border-radius: 999px; cursor: pointer; white-space: nowrap; transition: background .15s, color .15s; }
.st-chip:hover { color: #fff; background: rgba(255,255,255,.08); }
.st-chip.on { background: #fff; color: #111; }
.st-chip svg { width: 12px; height: 12px; opacity: .7; }
.st-chip.pro { color: rgba(255,255,255,.4); }
.st-navx { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border: 0; border-radius: 999px; background: rgba(255,255,255,.08); color: #fff; cursor: pointer; }
.st-navx:hover { background: rgba(255,255,255,.16); }
.st-navx svg { width: 15px; height: 15px; }

/* ---- the grid ---- */
/* The row height has to hold label row + value + footer, because .st-body clips rather than
   spilling over the heading. 128 never did — the numbers were riding up over their own labels long
   before anything clipped. Checked by measurement, not by eye — scripts/check-stats-fit.mjs. */
/* ── AND THE GRID BACKFILLS ITS OWN HOLES ─────────────────────────────────────────────────────
   "why leave empty slots here.." — the hero is `w2 h2` and the three numbers beside it are `w1`,
   so column four of the second row had nothing in it and the next card was `w4` and could not go
   there. `dense` lets a later small card take the slot instead of leaving a square of nothing in
   the middle of the page. It can move a card earlier than its source order, which is the price,
   and it is the right price on a board of independent figures where nothing reads as a sequence. */
/* AND A ROW IS A FLOOR, NOT A CEILING. `140px` flat is what `scripts/check-stats-fit.mjs` exists
   to police: `.st-body` clips rather than spilling over the card's own heading, so two rows that
   need 65px in a 63px slot lose two pixels off the top of a numeral, which reads as an odd font
   until somebody looks twice. `minmax(140px, auto)` keeps the rhythm — nothing ever gets smaller —
   and lets the one row that genuinely needs three more pixels have them. */
.st-grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(140px, auto);
  gap: 14px; grid-auto-flow: row dense; }
.st-card { position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 8px; overflow: hidden;
  grid-column: span 1; grid-row: span 1; padding: 16px 18px; border-radius: 20px; border: 1px solid var(--st-line);
  background: var(--st-card); color: var(--st-ink); font: inherit; text-align: left; cursor: default;
  transition: border-color .2s, background .2s, transform .2s; }
.st-card.w2 { grid-column: span 2; } .st-card.w3 { grid-column: span 3; } .st-card.w4 { grid-column: span 4; }
.st-card.h2 { grid-row: span 2; } .st-card.h3 { grid-row: span 3; }
.st-card.has-more { cursor: pointer; }
.st-card.has-more:hover { border-color: rgba(255,255,255,.2); background: var(--st-card2); transform: translateY(-2px); }
.st-card.is-locked { cursor: pointer; }
body.no-vt .st-card { animation: st-rise .5s cubic-bezier(.32,.72,0,1) backwards; animation-delay: calc(var(--i, 0) * 26ms); }
@keyframes st-rise { from { opacity: 0; transform: translateY(14px) scale(.985); } }

.st-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; flex: none; }
/**
 * ── THE OWNER'S REPORT LOOKS LIKE OUR OWN REPORT ───────────────────────────────────────────────
 *
 * "can we make the analytics for the customer more same design as we have for our admin dash?"
 *
 * The two were already the same COLOURS — `--st-card` and `--i-card` are the same hex, so are the
 * line, the ink and the accent. What made them read as two different products was the furniture:
 * every card here wore an accent icon tile in its corner, its heading was set bold and in Title
 * Case, and its delta was a coloured pill. The report at /dashboard/analytics has none of those —
 * a small dim label, a big number, a small dim line under it, and that is the whole card.
 *
 * So the furniture goes. Nothing about the board's LAYOUT changes: it is a bento of different-sized
 * cards because an owner's report has a hero and a wide chart in it, and that is the right shape
 * for it. What changes is what a card is made of.
 */
/* lowercased HERE rather than in the strings: the labels come from one place in site.js and a
   language pack could put a proper noun in one, which `text-transform` would then flatten — but
   these are eight English words about a website, and the alternative is eight literals that drift
   apart from the report they are matching. */
.st-lbl { font-size: 12.5px; font-weight: 400; color: var(--st-dim); letter-spacing: 0;
  text-transform: lowercase; }
/* NO ICON TILE. The report has no icons at all; twelve accent squares are twelve things competing
   with the numbers they sit beside. The LOCK stays — it is not decoration, it is the only thing on
   a locked card saying why it is empty. */
.st-ic { display: none; }
.st-ic.st-iclock { flex: none; width: 30px; height: 30px; display: grid; place-items: center;
  border-radius: 10px; background: rgba(255,255,255,.06); color: var(--st-faint); }
.st-ic svg { width: 16px; height: 16px; }
/* A big number HANGS at the bottom of its card. A list has to grow from the top instead — with
   flex-end an over-tall list spills UPWARD, straight over the card's own heading. */
.st-body { display: flex; flex-direction: column; justify-content: flex-end; gap: 6px; flex: 1 1 auto; min-height: 0; }
.st-listbody { justify-content: flex-start; padding-top: 4px; }
/* A safety net for the same fault the list hit: whatever is in here, it clips rather than painting
   over the card's own heading. */
.st-body { overflow: hidden; }
.st-foot { display: flex; align-items: center; gap: 8px; flex: none; flex-wrap: wrap; font-size: 12px; }
/* line-height 1.04 was clipping the ASCENDER, not the layout: the ink box of this face is taller
   than a 1.04 line box, so .st-body's overflow:hidden shaved the top off every number. */
/* the same face and the same size as `.ins-card b` — a number that IS the point is set in the
   display face, which is the rule the homepage and the workspace both follow */
.st-val { display: block; font-family: "Bricolage Grotesque", "Figtree", -apple-system, sans-serif;
  font-weight: 800; font-size: 34px; line-height: 1.16; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.st-hero .st-val { font-size: 66px; line-height: 1.08; }
.st-note { font-size: 12.5px; color: var(--st-faint); font-weight: 600; }
.st-name { font-weight: 800; font-size: 26px; line-height: 1; letter-spacing: -.02em; }
.st-vs { color: var(--st-faint); font-size: 11.5px; font-weight: 600; }
/* A CHANGE IS WORDS, NOT A BADGE. The report writes its deltas as coloured text on the line they
   belong to; a filled pill makes the smallest number on the card the loudest thing on it. */
.st-delta { display: inline-flex; align-items: center; gap: 3px; font-weight: 700; font-size: 12px; padding: 0; border-radius: 0; background: none; }
.st-delta svg { width: 12px; height: 12px; }
.st-delta.up { color: var(--st-up); }
.st-delta.down { color: var(--st-down); }
.st-delta.down svg { transform: rotate(180deg); }
.st-delta.flat { color: var(--st-faint); }
.st-delta.flat svg { display: none; }

/* live */
.st-livewrap { display: flex; align-items: center; gap: 10px; }
.st-pulse { width: 10px; height: 10px; border-radius: 50%; background: var(--st-up); box-shadow: 0 0 0 0 rgba(95,208,138,.6); animation: st-pulse 2s infinite; flex: none; }
@keyframes st-pulse { 70% { box-shadow: 0 0 0 12px rgba(95,208,138,0); } 100% { box-shadow: 0 0 0 0 rgba(95,208,138,0); } }
.st-liveCard .st-val { font-size: 34px; }

/* sparkline + area */
.st-spark { display: block; width: 100%; height: 40px; margin-top: 2px; }
/* the dash is set from the measured path in stDraw() — see the note there */
.st-sparkline { fill: none; stroke: var(--st-accent); stroke-width: 2; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
.st-sparkfill { opacity: 0; }
.st-spark.st-in .st-sparkfill { animation: st-fade .8s .35s forwards; }
@keyframes st-fade { to { opacity: 1; } }
.st-areawrap { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.st-area { display: block; width: 100%; flex: 1 1 auto; min-height: 90px; }
.st-arealine { fill: none; stroke: var(--st-accent); stroke-width: 2.5; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
.st-areafill { opacity: 0; }
.st-areawrap.st-in .st-areafill { animation: st-fade .9s .4s forwards; }
.st-areadot { position: absolute; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%; background: var(--st-accent);
  box-shadow: 0 0 0 4px rgba(200,127,94,.22); opacity: 0; }
.st-areawrap.st-in .st-areadot { animation: st-fade .4s 1.1s forwards; }
.st-axis { display: flex; justify-content: space-between; gap: 6px; margin-top: 6px; font-size: 10.5px; color: var(--st-faint); font-weight: 600; flex: none; }

/* bars */
.st-bars { display: flex; align-items: flex-end; gap: 6px; flex: 1 1 auto; min-height: 0; margin-top: 10px; }
.st-bar { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 5px; min-width: 0; height: 100%; }
.st-bar i { display: block; width: 100%; height: 0; min-height: 2px; border-radius: 7px 7px 4px 4px; background: rgba(255,255,255,.11);
  transition: height .7s cubic-bezier(.32,.72,0,1); transition-delay: calc(var(--i, 0) * 26ms); }
.st-bars.st-in .st-bar i { height: var(--h); }
.st-bar.hi i { background: var(--st-accent); }
.st-bar em { display: block; height: 13px; font-style: normal; font-size: 9.5px; line-height: 13px; color: var(--st-faint);
  font-weight: 650; white-space: nowrap; overflow: visible; }
.st-bar.hi em { color: var(--st-ink); }
/* 24 hour labels would collide, so only every sixth carries text — the rest just hold the row open */
.st-bars.st-hours { gap: 2px; }
.st-bars.st-hours .st-bar em { position: relative; left: 0; }

/* rows */
.st-rows { display: flex; flex-direction: column; gap: 9px; width: 100%; }
.st-row { display: flex; flex-direction: column; gap: 5px; }
.st-rowtop { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 12.5px; }
.st-rowtop b { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-rowtop em { font-style: normal; color: var(--st-faint); font-weight: 650; font-variant-numeric: tabular-nums; flex: none; }
.st-track { display: block; height: 6px; border-radius: 999px; background: rgba(255,255,255,.07); overflow: hidden; }
.st-track i { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--st-accent);
  transition: width .8s cubic-bezier(.32,.72,0,1); transition-delay: calc(var(--i, 0) * 55ms); }
.st-rows.st-in .st-track i { width: var(--w); }
.st-row:nth-child(2) .st-track i { background: color-mix(in srgb, var(--st-accent) 74%, #fff); }
.st-row:nth-child(3) .st-track i { background: color-mix(in srgb, var(--st-accent) 52%, #fff); }
.st-row:nth-child(n+4) .st-track i { background: rgba(255,255,255,.3); }
.st-none { color: var(--st-faint); font-size: 13px; line-height: 1.5; }

/* locked — a promise and a lock, never a fake number */
.st-card.is-locked { background: rgba(255,255,255,.022); border-style: dashed; border-color: rgba(255,255,255,.11); gap: 4px; }
.st-card.is-locked .st-body { justify-content: center; }
.st-card.is-locked:hover { border-color: rgba(200,127,94,.45); background: rgba(200,127,94,.045); transform: none; }
.st-iclock { background: rgba(255,255,255,.06) !important; color: var(--st-faint) !important; }
.st-card.is-locked:hover .st-iclock { background: rgba(200,127,94,.14) !important; color: var(--st-accent) !important; }
.st-promise { font-size: 13px; line-height: 1.45; color: var(--st-dim); }
.st-unlock { display: inline-flex; align-items: center; gap: 5px; color: var(--st-accent); font-weight: 700; }
.st-unlock svg { width: 12px; height: 12px; }
.st-sec { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 28px 2px 12px; }
.st-sec b { font-size: 15px; font-weight: 750; }
.st-sec span { font-size: 12.5px; color: var(--st-faint); }

/* the hero's number and its note sit on one baseline, so the sparkline gets the room */
.st-line1 { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.st-line1 .st-val { display: inline-block; }
.st-hero .st-spark { height: 64px; margin-top: 10px; }

/* loading — the real board, breathing, with its numbers still on the way */
.st.st-loading .st-card { pointer-events: none; }
.st.st-loading .st-val, .st.st-loading .st-name, .st.st-loading .st-delta,
.st.st-loading .st-rows, .st.st-loading .st-none, .st.st-loading .st-spark, .st.st-loading .st-areawrap { opacity: 0; }
.st.st-loading .st-bars { opacity: .25; }
.st.st-loading .st-card::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(100deg, transparent 34%, rgba(255,255,255,.045) 50%, transparent 66%);
  background-size: 300% 100%; animation: st-shine 1.5s infinite; }
@keyframes st-shine { to { background-position: -170% 0; } }
.st-first { display: flex; align-items: center; gap: 14px; padding: 16px 18px; margin: 0 0 14px; border-radius: 18px;
  border: 1px solid rgba(200,127,94,.28); background: rgba(200,127,94,.07); }
.st-first > svg { width: 22px; height: 22px; color: var(--st-accent); flex: none; }
.st-first b { display: block; font-size: 14.5px; margin-bottom: 2px; }
.st-first span { display: block; font-size: 13px; color: var(--st-dim); line-height: 1.5; }
.st-fine { margin: 18px 2px 0; font-size: 11.5px; line-height: 1.6; color: var(--st-faint); max-width: 620px; }

/* ---- the detail modal ---- */
.st-scrim { position: fixed; inset: 0; z-index: 2147483200; display: grid; place-items: center; padding: 20px;
  background: rgba(4,4,6,.7); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); opacity: 0; transition: opacity .2s; }
.st-scrim.on { opacity: 1; }
.st-modal { width: min(680px, 100%); max-height: min(84vh, 780px); display: flex; flex-direction: column; overflow: hidden;
  border-radius: 26px; border: 1px solid var(--st-line, rgba(255,255,255,.09)); background: #131316; color: #f4f2ee;
  font-family: var(--qck-font, Inter, system-ui, sans-serif); box-shadow: 0 40px 90px -30px rgba(0,0,0,.8); }
.st-mhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; padding: 22px 24px 14px; flex: none; }
.st-mhead h3 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -.02em; }
.st-mhead p { margin: 4px 0 0; font-size: 13px; color: rgba(244,242,238,.56); }
.st-mx { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border: 0; border-radius: 999px;
  background: rgba(255,255,255,.08); color: #fff; cursor: pointer; }
.st-mx:hover { background: rgba(255,255,255,.16); }
.st-mx svg { width: 15px; height: 15px; }
.st-mbody { padding: 4px 24px 8px; overflow-y: auto; flex: 1 1 auto; }
.st-mbody .st-areawrap { height: 190px; margin-bottom: 16px; }
.st-mbody .st-bars { height: 150px; }
.st-mnote { margin: 0; padding: 14px 24px 20px; font-size: 12.5px; line-height: 1.6; color: rgba(244,242,238,.5); flex: none; border-top: 1px solid rgba(255,255,255,.07); }
.st-dh { margin: 20px 0 10px; font-size: 12.5px; font-weight: 700; color: rgba(244,242,238,.56); }
.st-dtable { display: flex; flex-direction: column; }
.st-drow { display: flex; align-items: baseline; gap: 12px; padding: 11px 0; border-bottom: 1px solid rgba(255,255,255,.06); font-size: 13.5px; }
.st-drow:last-child { border-bottom: 0; }
.st-drow b { font-weight: 650; min-width: 74px; flex: none; }
.st-drow span { color: rgba(244,242,238,.44); font-size: 12.5px; flex: 1 1 auto; }
.st-drow em { font-style: normal; font-weight: 750; font-variant-numeric: tabular-nums; flex: none; }
.st-ringwrap { display: grid; place-items: center; padding: 10px 0 18px; }
.st-ring { position: relative; width: 150px; height: 150px; }
.st-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.st-ringbg { fill: none; stroke: rgba(255,255,255,.08); stroke-width: 13; }
.st-ringfg { fill: none; stroke: var(--st-accent, #C87F5E); stroke-width: 13; stroke-linecap: round;
  transition: stroke-dashoffset 1s cubic-bezier(.32,.72,0,1); }
.st-ringc { position: absolute; inset: 0; display: grid; place-items: center; }
.st-ringc .st-val { font-size: 30px; }

/* ---- narrower ---- */
@media (max-width: 1080px) {
  .st-grid { grid-template-columns: repeat(2, 1fr); }
  .st-card.w3, .st-card.w4 { grid-column: span 2; }
  .st-hero .st-val { font-size: 54px; }
}
@media (max-width: 620px) {
  /* One-column cards have room for a delta OR a note, not both — wrapped to two lines the footer
     pushed the value up over the label. The delta is the one that carries information. */
  .st-card:not(.w2):not(.w3):not(.w4) .st-foot { flex-wrap: nowrap; }
  .st-card:not(.w2):not(.w3):not(.w4) .st-foot .st-delta + .st-vs { display: none; }
  .st { padding-bottom: 120px; }
  /* The row height is not decoration: label row + value + footer has to FIT, because .st-body clips
     rather than spilling over the heading. Measured against the tallest of them (a 30px value with a
     delta chip under it), not guessed. */
  .st-grid { grid-auto-rows: minmax(128px, auto); gap: 10px; }
  .st-card { padding: 12px 14px; border-radius: 17px; gap: 5px; }
  .st-card.w2, .st-card.w3, .st-card.w4 { grid-column: span 2; }
  .st-ic { width: 26px; height: 26px; border-radius: 9px; }
  .st-ic svg { width: 14px; height: 14px; }
  .st-body { gap: 4px; }
  .st-val { font-size: 30px; }
  .st-hero .st-val { font-size: 46px; }
  .st-navttl b { display: none; }
  .st-chip { padding: 7px 11px; font-size: 12.5px; }
  .st-modal { border-radius: 22px; max-height: 88vh; }
  .st-mhead, .st-mbody { padding-left: 17px; padding-right: 17px; }
  .st-mnote { padding-left: 17px; padding-right: 17px; }
}
@media (prefers-reduced-motion: reduce) {
  .st-card, .st-bar i, .st-track i, .st-ringfg { transition: none !important; animation: none !important; }
  .st-sparkline, .st-arealine { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; transition: none !important; }
  .st-sparkfill, .st-areafill, .st-areadot { opacity: 1 !important; animation: none !important; }
  .st-bars.st-in .st-bar i { transition: none; }
}
