/* ═══════════════════════════════════════════════════════════════════════
   Intelletto.ai data-visualisation pattern: _dataviz.css

   David McCandless's way of drawing a number, in the house look (Elevated Light). The owner, 19 October 2026:
   "All dashboards and overview should be designed based on David McCandless. Any chart, graph, KPI card or
   dashboard must follow the intelletto-dataviz skill in .claude/skills/intelletto-dataviz/SKILL.md. Use tokens from
   apps/intelletto-theme.css; don't introduce new colours, fonts or charting libraries without asking. All tables
   should use alternate colors on tables to make viewing clearer."

   OPT-IN. Only the pages that were redesigned link this file; nothing here reaches any other page.

   THE OTHER dv- FAMILY. Ingenium Studio draws its own charts with .v32 .dv-* in _sve-shared.css, in a categorical palette.
   This sheet is the pattern for every dashboard and overview outside Ingenium Studio: neutral ink and one highlight. Do not
   mix the two on a page, and do not add a third: extend this sheet, under its rules.

   HOW A PAGE USES IT
     <link rel="stylesheet" href="intelletto-theme.css">   BEFORE _dataviz.css and before the page's family stylesheet
                                                           (_hire-shared.css, _talent-crm-shared.css, _sve-shared.css):
                                                           its tokens then exist, while every colour the page already
                                                           shows is still set by the page's own stylesheet, which comes
                                                           after it. A page that already links another sheet ahead of
                                                           the theme (an app-chrome sheet) keeps it there.
     <link rel="stylesheet" href="_hire-shared.css">       the page's own stylesheet(s), unchanged
     <style> ...the page's own rules... </style>
     <link rel="stylesheet" href="_dataviz.css">           LAST: after every other stylesheet and inline style.
   LINKING THE THEME has one COLOUR side effect on a hire page: the theme's .btn-ghost:hover paints a fill that
   _hire-shared.css's ghost buttons never had. A page with ghost buttons puts its own back in its own <style>:
   .btn-ghost:hover { background: transparent }. (It also brings one rule that is not a colour, [aria-busy="true"]
   { cursor: progress }, which is right for a chart that is loading.) Prove the rest in a browser, not by reading: the
   computed colours of the page's own elements must be the same before and after (the Requisitions overview did, and its
   test is the model). On paper the theme brings its own @media print block too (it hides .toolbar, .filter-row and .btn,
   whitens th and the pills, and strips link underlines). A family sheet with a print block of its own already does the
   same, and the hire sheet's matches the theme's; a page whose family sheet has none (talent CRM, SVE, a theme-only page)
   inherits the theme's, so check its print preview and run its colour probe once under print media.
   Tokens it reads, from the theme: --card --bg-dark --bg-panel --border --border-2 --text --text-muted --teal --red
   --gold --radius --viz-teal-ink --viz-green-ink. From the page's own shared stylesheet, where it defines them (each
   has a fallback): --th-bg --teal-dim --menu-active. A page whose sheets define no --teal-dim (a theme-only page) defines
   it in its own <style>: the fallback is the stripe itself, so without it a hovered or selected row differs from a striped
   one by its bar and hairlines alone.

   NO COLOUR OF ITS OWN. Every colour below is a var(--token), with two literals. The neutral data ink, defined once
   (--dv-ink): every mark that is not the highlight is drawn in it. And #EDF3F5, the project's table-header colour,
   which is only the fallback of the page's own --th-bg (the hire and talent sheets define it; a page whose sheets do
   not has the standard header anyway, and may define --th-bg in its own <style>). One mark per chart is highlighted, in var(--teal)
   (inside the navigation shell the shell re-points --teal at the person's accent, so the highlight follows it).
   WHERE THE HIGHLIGHT MARKS THE LEADER, A TIE FOR FIRST HIGHLIGHTS NONE: two bars level at the top would be one answer where
   the data has two, so neither is highlighted and a line under the chart names the tie ("A and B tie for first, with 9
   open requisitions each"). A highlight that marks what the question is about rather than a rank (the Open square of
   a status chart) stays where it is.
   Green, gold and red keep their meaning (success, attention, consequence) and are never a category. Violet is for
   values an AI produced and is not defined here. No pie, donut, dual axis, 3D, gradient or shadow on a mark, no
   mixed colour (the shared contrast audit cannot read one), and no charting library: bars and squares are plain HTML,
   sized by custom properties the page's script sets.

   THE COMPONENTS (every class carries the dv- prefix; none repeats a name in _sve-shared.css)

   1. KPI card, in a grid:
        <div class="dv-kpis">
          <div class="dv-kpi">
            <div class="dv-kpi-label">Open JDs</div>          Inter 500
            <div class="dv-kpi-value" id="kpi-open">34</div>   Ubuntu Condensed 400, never bold
            <div class="dv-kpi-note">Since 1 Oct</div>         JetBrains Mono; only where the data gives a delta or
          </div>                                               a period; an empty note takes no room
        </div>
      Each box takes one pastel TONE (the owner, 20 October 2026: "KPI boxes should use different pastel colors"),
      a class beside dv-kpi: dv-tone-teal, dv-tone-blue, dv-tone-amber, dv-tone-green or dv-tone-coral. The box is the
      theme's --viz-*-tint with its --viz-*-edge border, and the figure is in the matching --viz-*-ink. The tone says
      what the figure is about: coral a failure, amber something a person must look at, green finished or ready,
      blue under way, teal waiting its turn. A plain total (a count, a sum, a time) takes teal or blue, never a
      status colour, and no two boxes side by side share a tone. No violet: on these pages violet is what an AI made.
   2. Chart card, with its title, caption and note. .dv-row lays cards side by side and stacks them when narrow;
      the caption sits at the foot of the card, so the captions of two cards side by side line up:
        <div class="dv-row">
          <!-- CHART SPEC (four lines: Question, Decision, Data, Form) -->
          <section class="dv-card" data-chart="name" aria-labelledby="t1">
            <div class="dv-card-head"><h2 class="dv-title" id="t1">A question, or the plain finding</h2>
                                      ...a segmented control, if there is one...</div>
            <div class="dv-plot" role="img" aria-label="the whole chart, in words">...the marks...</div>
            <p class="dv-note">One sentence, when the reader needs one.</p>
            <p class="dv-source"><a href="#table">Source</a> · period · live</p>     JetBrains Mono caption
          </section>
        </div>
   3. Horizontal bars: labels left, the value printed ON the bar at its left end, sorted by the page's script, no
      axis. A bar's length is --dv-p (0 to 1) of the track. The value is dark ink, which reads on the neutral ink, on
      the accent (the shell's accents are all luminance-matched to teal: 5.7:1) and on the white beside a bar too short
      to hold it. One bar may be .dv-hbar-hi (none when the top is a tie, see above). A label too long for its column is cut short with an ellipsis (the row's
      title holds the full name); on a narrow screen the label sits above its bar:
        <div class="dv-hbars">
          <div class="dv-hbar dv-hbar-hi" title="the full label and detail">
            <span class="dv-hbar-label">Name</span>
            <span class="dv-hbar-track"><span class="dv-hbar-fill" style="--dv-p:.8"></span>
                                        <span class="dv-hbar-value">9</span></span>
          </div> ...
        </div>
      .dv-hbars-enter on .dv-hbars lets the bars grow in (300ms, none under prefers-reduced-motion): the page adds
      it when the reader presses a control, never at first paint.
   4. Area-scaled squares, for very different magnitudes or a part of a whole of at most five parts. The side is the
      square root of the value: --dv-a is sqrt(value / largest value), so the AREA is the value. They stand on a
      shared baseline with the value and label below. One may be .dv-sq-hi:
        <ul class="dv-squares">
          <li class="dv-sq dv-sq-hi" style="--dv-a:.74"><span class="dv-sq-box"></span>
            <span class="dv-sq-value">34</span><span class="dv-sq-label">Open</span></li> ...
        </ul>
   5. Segmented control: two or more options, aria-pressed on each button, the first option the absolute one:
        <div class="dv-seg" role="group" aria-label="..."><button type="button" class="dv-seg-btn" aria-pressed="true">
          Count</button><button type="button" class="dv-seg-btn" aria-pressed="false">Per role</button></div>
   6. Zebra table. EVERY table gets class dv-table; rows the page's script draws later alternate too, because the
      rule is on the rows, not on the markup. White and --dv-stripe (--bg-panel, 1.14:1 against white) alternate;
      --dv-stripe is the one line to change if the stripe should be fainter or stronger. THE STRIPE COUNTS EVERY ROW:
      a table that filters by hiding rows (hidden, display:none) breaks the alternation wherever a row is hidden, so
      redraw the rows the reader is to see, or use the grouped variant; never hide rows. A table whose rows expand puts
      each row and its detail rows in one <tbody> and adds dv-table-grouped, so the stripe alternates by record, not by
      line. The header is solid (the page's --th-bg, else #EDF3F5). It does not stick: a header can only stick to a
      box that itself scrolls, and the hire frame's tables sit in a box with no height, so the document scrolls and
      there is nothing to stick to. A page that wants one gives its table's box a height and an overflow, puts
      position: sticky; top: 0 on the thead in its own <style>, and proves it in a browser test: scroll the box, then
      measure. A row that does something (the whole row opens a record) opts in with class dv-table-rows-act on the
      table: a hovered row then takes the page's hover tint (--teal-dim) and a teal bar on its first cell, and keeps the
      pointer cursor. A row that does nothing does not react, because a reaction is a promise that a click does something:
      no tint, no bar and no pointer either (the family sheets give every row a pointer, and this sheet takes it back
      unless the table opts in; a link inside the row keeps its own). The Requisitions overview does not opt in: only its
      job code is a link. A placeholder row (loading, empty, failed) carries class
      dv-row-placeholder and never reacts. A selected row (aria-selected="true" or .is-selected) takes the same tint,
      the bar, and a hairline above and below, in any table. The stripe never has a bar, and --teal-dim alone is too
      near the stripe to tell rows apart, so the bar is what makes them visible. The tinted status pills of _hire-shared.css take the theme's text-safe inks on a striped row (see the rule),
      because a stripe darkens what a translucent pill sits on. .dv-tablewrap is a box that scrolls a wide table
      sideways inside itself:
        <div class="dv-tablewrap"><table class="dv-table">...</table></div>
   7. States: .dv-state with .dv-state-empty (say what will appear and what fills it), .dv-state-loading (a neutral
      skeleton of .dv-skel lines, no motion) or .dv-state-error (what failed and how to try again), each with an
      optional .dv-state-title and .dv-state-text. An error also offers the way to try again: a <button
      class="dv-state-action">Try again</button> after the text, whose handler is the page's (the Requisitions overview
      reloads the page: it reads nothing new). A failed read never shows a number: a figure that could not be read is a
      dash, and the error state beside it says what failed. A failed read says so ONCE with role="alert" (the one that
      matters most); any other error box it shows has role="status", so a screen reader announces one alert and not
      three. Every component answers the hidden attribute (el.hidden = true hides a card, a grid or a box), whatever
      display it sets for itself.
   8. Page frame for narrow screens: class dv-frame on the page's .layout. At 720px and under the sidebar becomes a
      strip above the content, so the content has the whole width and nothing but a table scrolls sideways. (Inside
      the navigation shell the sidebar is hidden altogether and this changes nothing.)

   Every chart carries its CHART SPEC comment, a title in Ubuntu Condensed 400 that is the recruiter's question or the
   plain finding, one caption line (Source · period · live) and a text equivalent (role="img" with an aria-label). Mark
   each chart card data-chart="name": a test finds every page that links this sheet, finds each chart by that mark, and
   asks for the CHART SPEC directly above it.

   THE SCRIPT HALF. This sheet is the CSS half of the pattern. The other half is a page's script, and the Requisitions
   overview's inline script is the model: copy these helpers from it verbatim, keep their tests, and do not re-derive them.
   dvList (a 200 that carries no list is a failed read), dvFigure (a dash until the figure's own read has answered, and a
   lower bound written with a plus only above zero: the helper itself refuses "0+"), dvState and dvTableError (one
   role="alert" per failed read, the others role="status"), the table renderer's two guards (renderJDTable: after a
   failure a pressed filter keeps saying the read failed, and before the list has loaded it keeps saying it is loading,
   never "none"), ofList (a caption names the list a
   picture used, and says nothing of a count when there is none, never "all 0"), put and say, and the bar and square
   builders (every value goes through textContent, never innerHTML). A shared script file would need the owner's word.

   FORCED COLOURS (Windows High Contrast) replace every fill, so a bar or a square would vanish: each gets an outline, the
   highlight a heavier one, a selected row a bar that is a border, not a shadow, a hovered row of a table that opts
   in an outline (it takes no room, so nothing moves when the pointer arrives), and the segmented control's pressed option
   a heavier underline while the other option has none (a transparent underline is forced visible, so both would read alike).

   PAPER. A browser prints no backgrounds unless the reader ticks "Background graphics" (Chrome, Safari and Firefox all
   ship with it off), and a bar and a square are backgrounds. On paper each is drawn as an outline, the highlight in a
   heavier one, so the chart prints whatever that setting is, and each asks for its fill too (print-color-adjust: exact)
   in a browser that honours it. The stripe, the hover and the controls are gone, and a sentence that only explains a
   control carries dv-note-control and goes with it. Test it with a real PDF printed without background graphics: print
   media emulated on screen still paints backgrounds, so it cannot see this.

   NOT IN THIS SHEET YET: a single stacked bar and a line for change over time, both in the skill's list of forms. The first
   page whose data calls for one adds it here, under the same rules (neutral ink, one highlight, caption, text equivalent).
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  --dv-ink: rgba(13,24,41,.16);
  --dv-stripe: var(--bg-panel);
  --dv-ui: 'Inter', system-ui, sans-serif;
  --dv-head: 'Ubuntu Condensed', sans-serif;
  --dv-mono: 'JetBrains Mono', ui-monospace, monospace;
  --dv-gap: 12px;
  --dv-sq-max: clamp(72px, 14vw, 128px);
}

/* The hidden attribute hides a component, whatever display the component sets for itself: an author's display beats the
   browser's [hidden], so every class that sets one is named here (a test derives the list from the sheet). */
.dv-kpis[hidden], .dv-kpi[hidden], .dv-row[hidden], .dv-card[hidden], .dv-card-head[hidden],
.dv-hbars[hidden], .dv-hbar[hidden], .dv-hbar-track[hidden], .dv-hbar-fill[hidden], .dv-hbar-value[hidden],
.dv-squares[hidden], .dv-sq[hidden], .dv-sq-box[hidden], .dv-seg[hidden], .dv-state[hidden], .dv-skel[hidden] { display: none; }

/* ── 1. KPI card ── */
.dv-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--dv-gap); }
.dv-kpi {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
  padding: 14px 16px 12px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
}
.dv-kpi-label { font: 500 12px/1.3 var(--dv-ui); color: var(--text-muted); }
.dv-kpi-value { font: 400 40px/1.1 var(--dv-head); color: var(--text); }
.dv-kpi-note { font: 400 11px/1.4 var(--dv-mono); color: var(--text-muted); }
.dv-kpi-note:empty { display: none; }

/* KPI tones (see THE COMPONENTS, 1): a pastel box, its edge and its figure's ink, all three from the theme. */
.dv-kpi.dv-tone-teal { background: var(--viz-teal-tint); border-color: var(--viz-teal-edge); }
.dv-kpi.dv-tone-teal .dv-kpi-value { color: var(--viz-teal-ink); }
.dv-kpi.dv-tone-blue { background: var(--viz-blue-tint); border-color: var(--viz-blue-edge); }
.dv-kpi.dv-tone-blue .dv-kpi-value { color: var(--viz-blue-ink); }
.dv-kpi.dv-tone-amber { background: var(--viz-amber-tint); border-color: var(--viz-amber-edge); }
.dv-kpi.dv-tone-amber .dv-kpi-value { color: var(--viz-amber-ink); }
.dv-kpi.dv-tone-green { background: var(--viz-green-tint); border-color: var(--viz-green-edge); }
.dv-kpi.dv-tone-green .dv-kpi-value { color: var(--viz-green-ink); }
.dv-kpi.dv-tone-coral { background: var(--viz-coral-tint); border-color: var(--viz-coral-edge); }
.dv-kpi.dv-tone-coral .dv-kpi-value { color: var(--viz-coral-ink); }

/* ── 2. Chart card ── */
.dv-row { display: grid; gap: var(--dv-gap); grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.dv-card {
  display: flex; flex-direction: column; gap: 10px; min-width: 0;
  padding: 16px 18px 14px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
}
.dv-card-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; }
.dv-title { margin: 0; font: 400 20px/1.2 var(--dv-head); color: var(--text); }
.dv-note { margin: 0; font: 400 12px/1.45 var(--dv-ui); color: var(--text-muted); }
.dv-source { margin: auto 0 0; font: 400 11px/1.5 var(--dv-mono); color: var(--text-muted); }
.dv-source a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.dv-note:empty, .dv-source:empty { display: none; }
.dv-plot { min-width: 0; }

/* ── 3. Horizontal bars ── */
.dv-hbars { display: flex; flex-direction: column; gap: 7px; }
.dv-hbar { display: grid; grid-template-columns: minmax(5rem, 40%) minmax(0, 1fr); align-items: center; gap: 2px 12px; }
.dv-hbar-label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 500 13px/1.3 var(--dv-ui); color: var(--text);
}
.dv-hbar-track { position: relative; display: block; min-width: 0; height: 22px; }
.dv-hbar-fill {
  display: block; height: 100%;
  width: calc(100% * var(--dv-p, 0));
  background: var(--dv-ink); border-radius: 2px;
}
.dv-hbar-value {
  position: absolute; top: 0; bottom: 0; left: 6px; display: flex; align-items: center;
  font: 500 12px/1 var(--dv-mono); color: var(--text); white-space: nowrap; pointer-events: none;
}
.dv-hbar-hi .dv-hbar-fill { background: var(--teal); }
@media (max-width: 30rem) {
  .dv-hbar { grid-template-columns: minmax(0, 1fr); }
}
@keyframes dv-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.dv-hbars-enter .dv-hbar-fill { transform-origin: left center; animation: dv-grow .3s ease-out; }

/* ── 4. Area-scaled squares ── */
.dv-squares { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px 24px; }
.dv-sq { display: grid; grid-template-rows: var(--dv-sq-max) auto auto; justify-items: start; row-gap: 6px; min-width: 0; }
.dv-sq-box { align-self: end; display: block; width: calc(var(--dv-sq-max) * var(--dv-a, 0)); aspect-ratio: 1; background: var(--dv-ink); }
.dv-sq-value { font: 500 12px/1 var(--dv-mono); color: var(--text); }
.dv-sq-label { font: 500 13px/1.3 var(--dv-ui); color: var(--text); }
.dv-sq-hi .dv-sq-box { background: var(--teal); }

/* ── 5. Segmented control ── */
.dv-seg { display: inline-flex; border: 1px solid var(--border-2); border-radius: var(--radius); overflow: hidden; }
.dv-seg-btn {
  appearance: none; margin: 0; padding: 6px 12px 4px;
  border: 0; border-bottom: 2px solid transparent;
  font: 600 12px/1.2 var(--dv-ui); color: var(--text-muted); background: var(--card); cursor: pointer;
}
.dv-seg-btn + .dv-seg-btn { border-left: 1px solid var(--border-2); }
.dv-seg-btn:hover { background: var(--bg-dark); color: var(--text); }
.dv-seg-btn[aria-pressed="true"] { color: var(--text); border-bottom-color: var(--teal); background: var(--bg-panel); }
.dv-seg-btn:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }

/* ── 6. Zebra table ── */
.dv-tablewrap { max-width: 100%; overflow-x: auto; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); }
.dv-table { width: 100%; border-collapse: collapse; }
.dv-table thead { background: var(--th-bg, #EDF3F5); }
.dv-table th { background: var(--th-bg, #EDF3F5); }
.dv-table tbody tr:nth-child(odd) { background: var(--card); }
.dv-table tbody tr:nth-child(even) { background: var(--dv-stripe); }
.dv-table.dv-table-grouped tbody tr:nth-child(n) { background: transparent; }
.dv-table.dv-table-grouped tbody:nth-of-type(odd) { background: var(--card); }
.dv-table.dv-table-grouped tbody:nth-of-type(even) { background: var(--dv-stripe); }
.dv-table tbody tr[aria-selected="true"],
.dv-table tbody tr.is-selected { background: var(--teal-dim, var(--bg-panel)); }
.dv-table:where(.dv-table-rows-act) tbody tr:where(:not(.dv-row-placeholder)):hover { background: var(--teal-dim, var(--bg-panel)); }
.dv-table.dv-table-grouped tbody tr[aria-selected="true"],
.dv-table.dv-table-grouped tbody tr.is-selected { background: var(--teal-dim, var(--bg-panel)); }
.dv-table.dv-table-grouped:where(.dv-table-rows-act) tbody tr:where(:not(.dv-row-placeholder)):hover { background: var(--teal-dim, var(--bg-panel)); }
/* The stripe is nearly the page's own hover tint, so a fill alone could not tell a hovered or selected row from a striped
   one. They take a teal bar on the first cell, which the stripe never has; a selected row also takes a hairline above
   and below, so it is told apart from a hovered one. Bars are inset shadows, which paint over the cell's own fill.
   A row reacts to the pointer only in a table that opts in (the rows act) and never when it is a placeholder; the
   :where() keeps those selectors as light as the stripe's, so the source order below decides. */
.dv-table:where(.dv-table-rows-act) tbody tr:where(:not(.dv-row-placeholder)):hover td:first-child,
.dv-table tbody tr[aria-selected="true"] td:first-child,
.dv-table tbody tr.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--teal); }
.dv-table tbody tr[aria-selected="true"] td,
.dv-table tbody tr.is-selected td { box-shadow: inset 0 1px 0 var(--teal), inset 0 -1px 0 var(--teal); }
.dv-table tbody tr[aria-selected="true"] td:first-child,
.dv-table tbody tr.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--teal), inset 0 1px 0 var(--teal), inset 0 -1px 0 var(--teal); }
/* A row that does nothing does not look as if it does. The family sheets give every row a pointer (_hire-shared.css and
   _talent-crm-shared.css: tbody tr { cursor: pointer }); only a table that opts in keeps it, and never on a placeholder
   row. A link inside a row keeps its own. These are heavier than the family sheets' rule, so the order of the sheets does
   not matter. */
.dv-table:where(:not(.dv-table-rows-act)) tbody tr,
.dv-table tbody tr.dv-row-placeholder { cursor: auto; }
.dv-table:where(.dv-table-rows-act) tbody tr:where(:not(.dv-row-placeholder)) { cursor: pointer; }
/* A tinted pill is translucent, so a stripe darkens what it sits on. The shared pill inks stand at 4.4 to 4.6:1 on a white
   row (the teal ones at 4.42, the green ones at 4.59) and fall to 3.89 and 4.05 on the stripe, and to 4.09 and 4.25 on a
   hovered or selected row. These are the theme's own text-safe inks for the same two hues: they hold 4.7:1 or more on all
   three. */
.dv-table .state-pill.pill-offer-approved,
.dv-table .state-pill.pill-onb-in-progress { color: var(--viz-teal-ink); }
.dv-table .state-pill.pill-offer-accepted,
.dv-table .state-pill.pill-onb-ready-day-one,
.dv-table .state-pill.pill-appr-approved { color: var(--viz-green-ink); }

/* ── 7. States: empty, loading, error ── */
.dv-state {
  display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; text-align: left;
  border: 1px dashed var(--border-2); border-radius: var(--radius);
  font: 400 13px/1.45 var(--dv-ui); color: var(--text-muted);
}
.dv-state-title { margin: 0; font: 600 13px/1.3 var(--dv-ui); color: var(--text); }
.dv-state-text { margin: 0; }
.dv-state-loading { gap: 10px; padding: 4px 0; border-color: transparent; }
.dv-skel { display: block; height: 12px; border-radius: 6px; background: var(--bg-panel); }
.dv-state-error { border-style: solid; border-left: 3px solid var(--red); }
.dv-state-action {
  appearance: none; align-self: flex-start; margin: 4px 0 0; padding: 6px 12px;
  border: 1px solid var(--border-2); border-radius: var(--radius); background: var(--card);
  font: 600 12px/1.2 var(--dv-ui); color: var(--text); cursor: pointer;
}
.dv-state-action:hover { background: var(--bg-dark); }
.dv-state-action:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* ── 8. Page frame: the sidebar above the content on a narrow screen ── */
@media (max-width: 720px) {
  .dv-frame { grid-template-columns: minmax(0, 1fr); }
  .dv-frame > .side { display: flex; flex-wrap: wrap; align-items: center; gap: 0 2px; padding: 8px 10px; }
  .dv-frame > .side h3 { flex: 0 0 100%; margin: 0; padding: 2px 8px 6px; }
  .dv-frame > .side a { padding: 8px 10px; border-left: 0; border-bottom: 3px solid transparent; }
  .dv-frame > .side a.active { border-bottom-color: var(--menu-active, var(--gold)); }
  .dv-frame > .main { padding: 14px 16px; }
}

@media (forced-colors: active) {
  .dv-hbar-fill, .dv-sq-box { box-sizing: border-box; border: 1px solid; }
  .dv-hbar-hi .dv-hbar-fill, .dv-sq-hi .dv-sq-box { border-width: 4px; }
  .dv-table tbody tr[aria-selected="true"] td:first-child,
  .dv-table tbody tr.is-selected td:first-child { border-left: 4px solid; }
  /* A hovered row of a table that opts in: an outline, which takes no room, so the text does not move when the pointer
     arrives. (A border would move it, and a transparent border is forced to a visible colour here.) */
  .dv-table:where(.dv-table-rows-act) tbody tr:where(:not(.dv-row-placeholder)):hover { outline: 3px solid; outline-offset: -3px; }
  /* The segmented control: its underline is all that tells the pressed option from the other, and a transparent underline
     is forced visible, so both would read alike. The other option has none and the pressed one a heavier one; the padding
     keeps both the same height. */
  .dv-seg-btn[aria-pressed="false"] { border-bottom-style: none; padding-bottom: 6px; }
  .dv-seg-btn[aria-pressed="true"] { border-bottom-width: 4px; padding-bottom: 2px; }
}

@media (prefers-reduced-motion: reduce) {
  .dv-hbars-enter .dv-hbar-fill { animation: none; }
}

/* Paper: no tone-shifted surfaces, no controls (the shared stylesheets do the same for their own). */
@media print {
  .dv-seg, .dv-state-action, .dv-note-control { display: none; }
  .dv-card, .dv-kpi { background: var(--card); }
  .dv-table tbody tr:nth-child(odd),
  .dv-table tbody tr:nth-child(even) { background: var(--card); }
  .dv-table.dv-table-grouped tbody:nth-of-type(odd),
  .dv-table.dv-table-grouped tbody:nth-of-type(even) { background: var(--card); }
  /* A bar and a square are backgrounds, and a browser prints no backgrounds unless the reader ticks "Background
     graphics" (Chrome, Safari and Firefox ship with it off): drawn only as fills, a chart prints blank. On paper each mark
     is an outline, the highlight a heavier one, so it prints whatever that setting is. The border is inside the box, so a
     mark's length and area are still its value. print-color-adjust: exact keeps the fill as well in a browser that honours it. */
  .dv-hbar-fill, .dv-sq-box { box-sizing: border-box; border: 1px solid var(--text); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .dv-hbar-hi .dv-hbar-fill, .dv-sq-hi .dv-sq-box { border-width: 3px; }
}
