/* Winetobe — shared stylesheet.
 *
 * Refreshed 2026-08-25. The app was styled as a static dashboard; it is now a
 * role-aware tool people read numbers off all day, and the visual language follows
 * that: flat surfaces, hairline rules, typographic hierarchy instead of boxes,
 * and tabular numerals so figures line up down a column.
 *
 * DELIBERATELY ABSENT: drop shadows, glows, and pill/bubble shapes. Nothing is
 * rounded for its own sake — corners are 2-3px, enough to avoid looking sharp-edged
 * and not enough to read as a card. Emphasis comes from weight, size and space.
 *
 * THE LEFT RAIL IS FIXED. Its layout, proportions and behaviour were settled and
 * signed off; the block below is carried over unchanged apart from token names.
 * Do not restyle it without being asked.
 *
 * ASKED ONCE, 2026-09-02: the nav inside the rail — and only the nav — was rebuilt
 * as a vertical list (see "rail nav" below). The rail's width, padding, order of
 * sections and drawer behaviour are untouched and remain frozen.
 */

:root{
    --ink:#1a2332;
    --paper:#f7f5f0;
    --surface:#ffffff;
    --surface-2:#f4f1ea;
    --surface-3:#edeae1;      /* table header / inset ground */
    --line:#d8d3c8;
    --line-soft:#e7e3d9;
    --accent:#008466;         /* validated: chroma floor + CVD + contrast pass */
    --accent-hi:#d97757;      /* selected state, dE 15.9 vs accent under protanopia */
    --accent-soft:#e4ede9;
    --muted:#6b6558;
    --grid:#e9e5da;
    --neg:#a03e3e;
    --pos:#2f6f4f;
    --warn:#8a6a1c;
    --faint:#9a927f;
    --nopic:#b3ab98;
    --bar:#bdb6a6;            /* chart bars at rest — readable, not shouting */
    /* Year-on-year series. Validated as a categorical pair against the light chart
       surface: lightness band, chroma floor, CVD separation (ΔE 15.1 deutan / 9.0
       tritan), normal-vision separation and contrast all pass.
       NOT accent + accent-hi: that pair measures ΔE 5.1 under protanopia, i.e.
       indistinguishable, despite the comment on --accent-hi claiming otherwise. */
    --chart-y1:#41599c;       /* older year  */
    --chart-y2:#008466;       /* newer year  */
    --mono: "SF Mono","IBM Plex Mono",Consolas,monospace;
    --sans: "IBM Plex Sans","Helvetica Neue",Arial,sans-serif;
  }
  /* Dark theme — opt-in only (the app defaults to light regardless of OS, by
     product decision). A warm near-black that keeps the blue-ink lineage; the
     accent is brightened so it stays legible on the dark ground. Every rule
     below reads these tokens, so nothing needs a second dark-specific selector. */
  :root[data-theme="dark"]{
    --ink:#e9e5db;
    --paper:#0f1218;
    --surface:#161a21;
    --surface-2:#1b202a;
    --surface-3:#212734;
    --line:#2c313d;
    --line-soft:#232833;
    --accent:#25b591;
    --accent-hi:#e8896b;
    --accent-soft:#15342c;
    --muted:#9a9384;
    --grid:#242a34;
    --neg:#e06a6a;
    --pos:#5fb98a;
    --warn:#c9a24a;
    --faint:#7c7566;
    --nopic:#6b6456;
    --bar:#3a4150;
    /* Dark is SELECTED, not flipped: the dark lightness band (L 0.48–0.67) is
       narrower than light's, so both steps are re-chosen and re-validated against
       the dark chart surface. ΔE 16.7 deutan / 7.2 tritan. */
    --chart-y1:#5b7fd4;
    --chart-y2:#14a882;
  }
  html{ color-scheme:light; }
  :root[data-theme="dark"]{ color-scheme:dark; }
  *{box-sizing:border-box;}
  body{
    margin:0; background:var(--paper); color:var(--ink); font-family:var(--sans);
    line-height:1.45; -webkit-text-size-adjust:100%;
    -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  }
  /* Figures must line up down a column — proportional digits make a table of
     rupiah amounts impossible to scan. Applied broadly; prose is unaffected. */
  .kpi .num, .right, .hint, .aicap, table, .chanrow, .mono, code{
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
  }

  /* ================= app shell: fixed left rail + content column =================
     KEEP AS IS — see the header note. All controls live in the rail; the content
     column is pure data. On <=900px the rail becomes a slide-in drawer. */
  .app{ display:grid; grid-template-columns:256px 1fr; min-height:100vh; }

  .rail{
    background:var(--surface); border-right:1px solid var(--line);
    position:sticky; top:0; height:100vh; overflow-y:auto;
    display:flex; flex-direction:column; gap:20px; padding:20px 16px 22px;
  }
  .brand{ display:flex; align-items:center; gap:10px; }
  .brand .mark{
    width:30px; height:30px; border-radius:6px; background:var(--accent); color:#fff;
    display:grid; place-items:center; flex:0 0 auto; font-family:var(--mono); font-weight:700; font-size:12px;
  }
  .brand h1{ font-size:14px; font-weight:700; font-family:var(--mono); letter-spacing:0.03em;
    text-transform:uppercase; line-height:1.15; margin:0; }
  .brand .bs{ font-size:9.5px; color:var(--muted); font-family:var(--mono); letter-spacing:0.08em; text-transform:uppercase; }

  .rail-sec{ display:flex; flex-direction:column; gap:9px; }
  .rail-sec > .lbl{ font-family:var(--mono); font-size:9.5px; text-transform:uppercase; letter-spacing:0.11em; color:var(--muted); }
  .rail-sec .tabs{ margin-bottom:0; }
  .rail-div{ height:1px; background:var(--line); }

  .actions{ display:flex; flex-direction:column; gap:8px; }
  .btn{
    display:flex; align-items:center; justify-content:center; gap:8px; width:100%; min-height:42px;
    padding:0 14px; border-radius:3px; cursor:pointer; font-family:var(--mono); font-size:10.5px;
    letter-spacing:0.05em; text-transform:uppercase; border:1px solid var(--ink); background:var(--ink); color:var(--paper);
    text-decoration:none;
  }
  .btn:hover{ background:var(--accent); border-color:var(--accent); color:#fff; }
  .btn.ghost{ background:transparent; color:var(--ink); }
  .btn.ghost:hover{ background:var(--accent-soft); border-color:var(--accent); color:var(--ink); }
  .btn:disabled{ opacity:.55; cursor:default; }

  /* Data freshness — a stated fact, not a badge. Same restraint as .who: mono,
     small, no surface or border of its own. It goes amber only when the figure is
     genuinely old, so the colour still means something when it appears. */
  .fresh{ font-family:var(--mono); font-size:10.5px; color:var(--ink); line-height:1.55; }
  .fresh .ago{ display:block; color:var(--muted); font-size:9.5px; margin-top:2px; }
  .fresh.stale, .fresh.stale .ago{ color:var(--warn); }

  .rail-foot{ margin-top:auto; display:flex; align-items:center; justify-content:space-between; gap:10px; }
  .rail-foot .who{ font-family:var(--mono); font-size:10px; color:var(--muted); }
  /* Squared, not round. The rail's layout is fixed, but a 32px circle is the
     clearest example of the bubble shape the refresh was asked to drop, and it
     now matches the other controls beside it. */
  .iconbtn{
    display:inline-flex; align-items:center; justify-content:center; width:34px; height:34px;
    border:1px solid var(--line); border-radius:3px; background:var(--surface-2); color:var(--muted);
    cursor:pointer; font-size:14px; transition:border-color .15s, color .15s, background .15s;
  }
  .iconbtn:hover{ border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }
  .themebtn{
    display:inline-flex; align-items:center; gap:7px; min-height:34px; padding:0 11px;
    border:1px solid var(--line); border-radius:3px; background:var(--surface-2); color:var(--ink); cursor:pointer;
    font-family:var(--mono); font-size:10px; text-transform:uppercase; letter-spacing:0.05em;
    transition:border-color .15s, background .15s;
  }
  .themebtn:hover{ border-color:var(--accent); background:var(--accent-soft); color:var(--ink); }
  .linkbtn{
    background:none; border:none; color:var(--muted); font-family:var(--mono); font-size:10px;
    text-transform:uppercase; letter-spacing:0.05em; cursor:pointer; padding:0; flex:0 0 auto;
  }
  .linkbtn:hover{ background:none; color:var(--ink); text-decoration:underline; }
  /* ================= end of the fixed rail ================= */

  main{ min-width:0; display:flex; flex-direction:column; }
  .topbar{
    position:sticky; top:0; z-index:20; display:flex; align-items:center; gap:12px;
    padding:0 34px; height:52px; background:var(--paper); border-bottom:1px solid var(--line);
  }
  .hamb{ display:none; }
  .scope{ display:flex; align-items:center; gap:8px; min-width:0; }
  .scope .meta{ font-family:var(--mono); font-size:10.5px; color:var(--faint);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; letter-spacing:.01em; }
  .topbar .spacer{ flex:1; }
  .content{ padding:26px 34px 80px; max-width:1180px; }
  .overlay{ display:none; }

  /* ---------- panels ----------
     Flat: a hairline and a ground change, no shadow. Depth here would be
     decoration — the page is a stack of sections, not a pile of cards. */
  .panel{
    background:var(--surface); border:1px solid var(--line); border-radius:3px;
    padding:22px 24px; margin-bottom:16px;
  }
  .panel h2{
    font-size:10.5px; text-transform:uppercase; letter-spacing:0.1em;
    font-family:var(--mono); color:var(--muted); margin:0; font-weight:600;
  }
  .resulthead{
    display:flex; justify-content:space-between; align-items:baseline;
    gap:12px; flex-wrap:wrap; margin-bottom:16px;
  }
  .resulthead h2{ margin:0; }
  .resulthead h3{
    margin:0; font-size:19px; font-weight:600; letter-spacing:-0.012em; line-height:1.25;
  }
  /* An action sitting in a .resulthead beside a heading wants .themebtn, not .btn:
     .btn is a rail control and is width:100%, so it renders as a bar across the
     whole panel. .themebtn is already that compact shape — the "← Back" in a
     drill-down — and this only teaches it to be an <a> too, for a download link. */
  a.themebtn{ text-decoration:none; }

  button{
    background:var(--ink); color:var(--paper); border:none; padding:9px 15px; border-radius:3px;
    font-family:var(--mono); font-size:10.5px; letter-spacing:0.05em; text-transform:uppercase; cursor:pointer;
  }
  button:hover{ background:var(--accent); }
  button:disabled{ opacity:.55; cursor:default; }
  button.ghost{ background:none; color:var(--ink); border:1px solid var(--line); }
  button.ghost:hover{ background:var(--ink); color:var(--paper); border-color:var(--ink); }

  /* ---------- filters (rail) ---------- */
  .segmented{ display:flex; flex-wrap:wrap; border:1px solid var(--line); border-radius:3px; overflow:hidden; }
  .segmented button{
    flex:1 1 auto; min-width:40px; background:var(--surface); color:var(--muted); border:none; border-right:1px solid var(--line);
    padding:8px; font-size:10px; transition:background .15s, color .15s;
  }
  .segmented button:last-child{ border-right:none; }
  .segmented button.on{ background:var(--ink); color:var(--paper); }
  .segmented button:hover{ background:var(--accent-soft); color:var(--ink); }
  .segmented button.on:hover{ background:var(--ink); color:var(--paper); }
  .flabel{ font-family:var(--mono); font-size:9.5px; text-transform:uppercase; letter-spacing:0.08em; color:var(--muted); }
  .fgroup{ display:flex; flex-direction:column; align-items:stretch; gap:6px; }
  select, #picSel, #periodSel{
    font-family:var(--mono); font-size:11.5px; padding:9px 10px; border:1px solid var(--line);
    border-radius:3px; background:var(--surface-2); color:var(--ink); width:100%;
  }
  select:focus{ outline:2px solid var(--accent); outline-offset:-2px; }

  /* ---------- rail nav: a vertical list of places ----------
     app.js emits `class="tab on"`. The old sheet only styled `.tab.active`, so the
     current view was never marked — both classes are matched here.

     THE ONE SANS IN THE RAIL, and the reason is hierarchy. Everything else in the
     rail is a control, and this sheet sets controls in mono uppercase: the section
     labels, the buttons, the segmented filters, the freshness line. These three are
     not controls, they are the places the app goes. At 10.5px uppercase they read at
     exactly the weight of the word "View" labelling them, and a menu that does not
     outrank its own heading is most of why this was hard to scan.

     Vertical, one per row. The underline that marked the current view was a
     horizontal-tab idiom borrowed into a 256px column: three items do not fit on one
     line, so the row wrapped — raggedly, and at a point that moved with the signed-in
     role, since a manager sees one item and an admin three. The same rule now runs
     down the LEFT edge, full-bleed to the rail's own border. It is reserved as
     transparent on every row, so nothing shifts sideways as the current view changes.

     Rows are 40px against the 42px buttons below them — the rail is a touch drawer
     under 900px, and 33px was under any sensible target there. */
  .tabs{ display:flex; flex-direction:column; gap:2px; margin:0 -16px; }
  .tab{
    display:flex; align-items:center; width:100%; min-height:40px;
    margin:0; padding:8px 16px 8px 14px; text-align:left; cursor:pointer;
    background:none; border:none; border-left:2px solid transparent; border-radius:0;
    font-family:var(--sans); font-size:13.5px; font-weight:500; letter-spacing:0;
    text-transform:none; color:var(--muted);
    transition:color .15s, border-color .15s, background-color .15s;
  }
  /* Deliberately not set on .tab.on: an equal-specificity background there would
     beat :hover and leave the current row the one item in the rail that feels dead. */
  .tab:hover{ background:var(--surface-2); color:var(--ink); }
  .tab.on, .tab.active{ color:var(--ink); font-weight:600; border-left-color:var(--accent); }
  .tab:focus-visible, tr.clickrow:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

  /* ---------- KPI row ----------
     No border, no shadow: an inset ground and a strong numeral. The label sits
     ABOVE the figure so the eye lands on the number first when scanning down. */
  .kpis{ display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:1px;
    background:var(--line-soft); border:1px solid var(--line-soft); border-radius:3px;
    overflow:hidden; margin-bottom:18px; }
  .kpi{ background:var(--surface); padding:15px 17px 16px; }
  .kpi .lbl{
    font-size:9.5px; color:var(--muted); text-transform:uppercase; letter-spacing:0.09em;
    font-family:var(--mono); margin-bottom:7px;
  }
  .kpi .num{
    font-family:var(--mono); font-size:21px; font-weight:700; letter-spacing:-0.02em; line-height:1.15;
  }
  .kpi .sub{ font-size:10px; color:var(--faint); font-family:var(--mono); margin-top:6px; line-height:1.5; }
  .kpi.hi{ background:var(--accent-soft); }
  .kpi.hi .lbl{ color:var(--accent); }
  .kpi .picname{ font-family:var(--sans); font-size:15px; font-weight:600; line-height:1.3; letter-spacing:0; }

  /* ---------- channel split ----------
     Squared, hairline-ruled inline facts. Not chips — a chip is a control, and
     these are read-only. */
  .chanrow{ display:flex; flex-wrap:wrap; gap:0; margin:-4px 0 18px;
    border:1px solid var(--line-soft); border-radius:3px; overflow:hidden; }
  .chanrow .chip{
    font-family:var(--mono); font-size:11px; color:var(--muted); background:var(--surface);
    border:none; border-right:1px solid var(--line-soft); border-radius:0;
    padding:9px 14px; flex:1 1 auto;
  }
  .chanrow .chip:last-child{ border-right:none; }
  .chanrow .chip b{ color:var(--ink); font-weight:700; letter-spacing:.02em; }

  /* ---------- tables ----------
     Hairlines, not rules. The header is a quiet band rather than a heavy underline;
     the emphasis belongs to the figures. */
  .tblwrap{ overflow-x:auto; }
  table{ width:100%; border-collapse:collapse; font-size:13px; }
  th{
    text-align:left; font-family:var(--mono); font-size:9.5px; text-transform:uppercase;
    letter-spacing:0.08em; color:var(--muted); font-weight:600; white-space:nowrap;
    padding:9px 12px; background:var(--surface-2); border-bottom:1px solid var(--line);
  }
  th:first-child{ border-top-left-radius:3px; }
  th:last-child{ border-top-right-radius:3px; }
  td{ padding:10px 12px; border-bottom:1px solid var(--line-soft); }
  tbody tr:last-child td{ border-bottom:none; }
  tbody tr{ transition:background .12s; }
  tbody tr:hover td{ background:var(--surface-2); }
  tr.clickrow{ cursor:pointer; }
  tr.clickrow:hover td{ background:var(--accent-soft); }
  .right{ text-align:right; font-family:var(--mono); font-size:12px; white-space:nowrap; }
  .namecell{ font-weight:500; }
  .codecell{ font-family:var(--mono); font-size:10.5px; color:var(--faint); }
  .nopic{ color:var(--nopic); }
  .neg{ color:var(--neg); }
  .hit{ color:var(--pos); font-weight:700; }
  .miss{ color:var(--neg); font-weight:700; }

  /* ---------- chart ----------
     Inline SVG drawn in barChart(); this only sets the frame. */
  .chartbox{ position:relative; height:230px; margin-top:4px;
    border-top:1px solid var(--line-soft); padding-top:14px; }

  /* Legend sits above the plot and always ships for two or more series, so identity
     never rests on colour alone. Square swatches, in keeping with the rest. */
  .chartlegend{ display:flex; gap:16px; align-items:center; margin:14px 0 -6px;
    border-top:1px solid var(--line-soft); padding-top:12px; }
  .chartlegend .lg{ display:inline-flex; align-items:center; gap:7px;
    font-family:var(--mono); font-size:10px; color:var(--muted); letter-spacing:.04em; }
  .chartlegend .lg i{ width:10px; height:10px; border-radius:2px; display:inline-block; }
  /* The legend already carries the rule; two would box the plot in for no reason. */
  .chartlegend + .chartbox{ border-top:none; margin-top:0; padding-top:6px; }
  .chartbox svg .bar rect{ transition:fill .15s; }
  .chartbox svg .bar:hover rect{ fill:var(--accent); }

  /* ---------- ask panel ---------- */
  .searchrow{ display:flex; gap:10px; align-items:center; }
  .searchrow input[type=text]{
    flex:1; padding:12px 13px; border:1px solid var(--line); border-radius:3px; font-size:14px; font-family:var(--sans);
    background:var(--surface-2); color:var(--ink);
  }
  .searchrow input[type=text]::placeholder{ color:var(--faint); }
  .searchrow input[type=text]:focus{ outline:2px solid var(--accent); outline-offset:-2px; }
  .searchrow .btn{ width:auto; min-width:110px; }
  /* The answer is rendered Markdown now, not preformatted text, so pre-wrap is
     gone — it would preserve the model's own line wrapping and fight the layout. */
  .ainote{
    font-size:13.5px; line-height:1.7; color:var(--ink);
    border-left:2px solid var(--accent); padding:2px 0 2px 16px;
  }
  .ainote p{ margin:0 0 10px; }
  .ainote p:last-child{ margin-bottom:0; }
  .ainote ul{ margin:0 0 10px; padding-left:18px; }
  .ainote li{ margin:2px 0; }
  .ainote strong{ font-weight:600; color:var(--ink); }
  .ainote code{ font-family:var(--mono); font-size:12px; }
  /* Figures the analyst puts in a table are the point of the answer — give them
     the same alignment and weight the dashboard's own tables use. */
  .ainote .tblwrap{ margin:0 0 12px; }
  .ainote table{ width:auto; min-width:min(100%, 320px); border-collapse:collapse; }
  .ainote th, .ainote td{
    padding:5px 14px 5px 0; text-align:right; white-space:nowrap;
    border-bottom:1px solid var(--line); font-variant-numeric:tabular-nums;
  }
  .ainote th:first-child, .ainote td:first-child{ text-align:left; }
  .ainote th{
    font-family:var(--mono); font-size:9.5px; text-transform:uppercase;
    letter-spacing:.06em; color:var(--faint); font-weight:500;
  }
  .aicap{
    font-size:9.5px; color:var(--faint); font-family:var(--mono); text-transform:uppercase;
    letter-spacing:0.07em; margin-top:12px;
  }

  /* ---------- misc ---------- */
  /* ---------- the analyst is working ----------
     A 2px hairline, not a spinner: enough motion to read as alive, no glow and no
     circle. The text beside it carries the actual information. */
  .thinking{ margin-top:12px; }
  .thinking .bar{ height:2px; background:var(--line-soft); overflow:hidden; }
  .thinking .bar i{
    display:block; height:100%; width:34%; background:var(--accent);
    animation:askslide 1.15s ease-in-out infinite;
  }
  @keyframes askslide{ from{ transform:translateX(-110%); } to{ transform:translateX(320%); } }
  .thinking .msg{
    margin-top:9px; font-family:var(--mono); font-size:10.5px; color:var(--muted);
    font-variant-numeric:tabular-nums;   /* the seconds must not shift the line */
  }
  /* A moving bar is the one thing here someone may need switched off. */
  @media (prefers-reduced-motion:reduce){
    .thinking .bar i{ animation:none; width:100%; opacity:.45; }
  }

  /* A disabled input must look disabled, or a typed-into-but-ignored box reads as
     the app losing keystrokes. */
  .searchrow input[type=text]:disabled{ opacity:.55; cursor:default; }

  /* ---------- progressive lists ----------
     "Show more" is a full-width hairline bar rather than a button floating in
     space: it reads as the continuation of the table it extends. */
  .listfilter{ margin:0 0 12px; }
  .listmore{ margin-top:10px; }
  .listmore .btn{
    width:100%; min-height:38px; font-size:10px;
    border-color:var(--line); background:transparent; color:var(--muted);
  }
  .listmore .btn:hover{ border-color:var(--accent); color:var(--accent); background:transparent; }

  .empty{ text-align:center; padding:40px 20px; color:var(--faint); font-family:var(--mono); font-size:12.5px; }
  .hint{ font-size:10.5px; color:var(--faint); font-family:var(--mono); letter-spacing:.01em; }
  .status{ font-size:12px; font-family:var(--mono); color:var(--accent); white-space:pre-line; }
  .statuserr{ color:var(--neg); }
  .statuswarn{ color:var(--warn); }
  .note{
    font-size:11.5px; color:var(--muted); margin-top:14px; line-height:1.6;
    background:var(--surface-2); border-left:2px solid var(--line); padding:10px 14px; border-radius:0 3px 3px 0;
  }
  .twocol{ display:grid; grid-template-columns:1fr 1fr; gap:16px; }
  .twocol > .panel{ margin-bottom:0; }
  /* Stacks until 1400px, not 900px.
     Two panels on a 1200px screen leave the name column ~380px wide, sharing it
     with three nowrap numeric columns — and real product names here run to 125
     characters, so almost every row wrapped to five or seven lines. Stacking is
     the MORE compact layout despite using one column: at full width nearly every
     name fits on one line, so all 15 rows and the "show more" control fit in less
     vertical space than 8 rows did before. Verified at 1200px against the real
     catalogue. Above 1400px there is genuinely room for two. */
  @media(max-width:1400px){ .twocol{ grid-template-columns:1fr; } }

  /* ---- tablet / phones: the rail becomes a slide-in drawer ----
     A fixed rail on a 390px screen would be a regression, so below 900px it
     goes off-canvas behind a hamburger and slides in over a dimmed overlay. */
  @media (max-width:900px){
    .app{ grid-template-columns:1fr; }
    .rail{
      position:fixed; top:0; left:0; z-index:50; width:290px; max-width:86vw; height:100vh;
      transform:translateX(-100%); transition:transform .25s ease; border-right:1px solid var(--line);
    }
    .rail.open{ transform:none; }
    .overlay{ position:fixed; inset:0; background:rgba(10,12,16,.45); z-index:40; }
    .overlay.show{ display:block; }
    .hamb{
      display:inline-flex; align-items:center; justify-content:center; width:42px; height:42px;
      border:1px solid var(--line); border-radius:3px; background:var(--surface); color:var(--ink);
      cursor:pointer; font-size:16px; flex:0 0 auto;
    }
    .topbar{ padding:0 16px; }
    .content{ padding:18px 16px 60px; }
    .segmented button{ min-height:44px; }
    .tab{ min-height:46px; font-size:14px; }
    select{ padding:12px 10px; }
  }
  @media (prefers-reduced-motion:reduce){ .rail{ transition:none; } }

  @media(max-width:640px){
    .panel{ padding:16px; margin-bottom:14px; }
    .kpis{ grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); }
    .kpi{ padding:13px 14px; }
    .kpi .num{ font-size:18px; }
    .chartbox{ height:200px; }
    .searchrow{ flex-wrap:wrap; }
    .searchrow input[type=text]{ font-size:16px; }  /* 16px stops iOS zooming on focus */
    .searchrow .btn{ width:100%; }
    table{ font-size:12px; }
    th, td{ padding:8px 8px; }
    .right{ font-size:11px; }
    .resulthead h3{ font-size:17px; }
  }

  /* ---- phones: data tables restyle into stacked cards ----
     Sideways-scrolling tables are the weakest moment on a 390px screen, so each
     row becomes a labelled block. The renderers emit a data-label on every cell;
     the ::before pulls it in as the row-key. One source, no duplicate markup. */
  @media(max-width:560px){
    .tblwrap table, .tblwrap thead, .tblwrap tbody, .tblwrap th, .tblwrap td, .tblwrap tr{ display:block; }
    .tblwrap thead{ position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
    .tblwrap tbody tr{
      border:1px solid var(--line-soft); border-radius:3px; background:var(--surface);
      padding:4px 13px; margin-bottom:9px;
    }
    .tblwrap tbody tr:hover td, .tblwrap tr.clickrow:hover td{ background:transparent; }
    .tblwrap td{
      display:flex; justify-content:space-between; align-items:center; gap:14px;
      padding:9px 0; border-bottom:1px solid var(--line-soft); text-align:right;
    }
    .tblwrap tr td:last-child{ border-bottom:none; }
    .tblwrap td::before{
      content:attr(data-label); font-family:var(--mono); font-size:9px; text-transform:uppercase;
      letter-spacing:0.07em; color:var(--muted); text-align:left; flex:0 0 auto;
    }
    /* the name cell carries no label — let it span full width as a heading */
    .tblwrap td.namecell{ justify-content:flex-start; text-align:left; font-weight:600; padding-top:12px; }
    .tblwrap td.namecell::before{ content:none; }
  }
