/* Copied unchanged from VIP_Agent_Dashboard_v2.themed.html by scripts/refresh-reference.cjs.
   Do not edit this copy. Put project styles in a separate file loaded after it. */

  :root {
    /* Ground — four steps, all near-black, green-shifted */
    --bg:            #090b0f;
    --bg-panel:      #14171e;
    --bg-panel-alt:  #191d25;
    --bg-sunk:       #0c0f14;
    --border:        #2a2f3a;
    --text:          #d1ccc5;   /* Val's softer warm grey (was #e8eaed, too white) */
    --text-dim:      #8b92a3;
    --text-faint:    #5a6273;
    --accent:        #42c1ac;
    --accent-dim:    #276f63;
    --upsell:        #42c1ac;
    --push:          #6c9ef8;
    --decline:       #f2746c;
    --declineticket: #d98c4a;
    --amber:         #e8c84a;
    --purple:        #a78bfa;
    /* Neon — LIGHT ONLY: glows, 1px bars, text-shadows. Never fill/border/label. */
    --neon-teal:   #5ce0c6;
    --neon-blue:   #8ab4ff;
    --neon-red:    #ff8f86;
    --neon-amber:  #f5d95f;
    --neon-violet: #bda2ff;
    --neon-orange: #f5a95e;
    /* Elevation — bottom-weighted, as if lit from above a few mm off the page */
    --lift-1: 0 1px 1px rgba(0,0,0,0.5), 0 2px 6px -3px rgba(0,0,0,0.7);
    --lift-2: 0 1px 1px rgba(0,0,0,0.5), 0 4px 10px -4px rgba(0,0,0,0.6),
              0 18px 40px -22px rgba(0,0,0,1);
    --lift-3: 0 30px 80px -24px rgba(0,0,0,0.92);
    /* One easing for the entire UI — fast out, settled by the end */
    --ease: cubic-bezier(0.2, 0.9, 0.25, 1);
    --font-display: 'Geist', 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, sans-serif;
    --font-mono: 'Geist Mono', 'SFMono-Regular', Consolas, 'Courier New', monospace;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  /* `overflow-x: hidden` here would turn the page into a scroll container and
     silently break every `position: sticky` inside it. `clip` holds back
     horizontal overflow without doing that. */
  /* Page ground — layered on html (not body) so a short page never ends in a
     visible seam partway down the window. */
  html {
    max-width: 100%;
    background:
      /* two accent washes bleeding in from above the fold */
      radial-gradient(1100px 420px at 16% -16%, rgba(66,193,172,0.07), transparent 70%),
      radial-gradient(900px 380px at 88% -20%, rgba(108,158,248,0.034), transparent 70%),
      /* corners pulled down */
      radial-gradient(1400px 900px at 50% 108%, rgba(0,0,0,0.55), transparent 72%),
      /* 32px millimetre grid at 1.2% white — felt, not seen */
      repeating-linear-gradient(0deg,  rgba(255,255,255,0.012) 0 1px, transparent 1px 32px),
      repeating-linear-gradient(90deg, rgba(255,255,255,0.012) 0 1px, transparent 1px 32px),
      /* a broad green haze through the upper middle */
      radial-gradient(1200px 700px at 46% 18%, rgba(46,86,78,0.16), transparent 70%),
      var(--bg);
    background-repeat: no-repeat;
    background-attachment: fixed;
  }
  body { max-width: 100%; overflow-x: clip; }
  body {
    background: transparent;
    color: var(--text);
    font-family: var(--font-display);
    font-size: 14px;
    line-height: 1.5;
    padding: 0 0 40px;
    /* Numbers line up digit under digit everywhere; a hair of negative tracking
       tightens Geist without touching small type. */
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.003em;
  }
  .page-wrap { max-width: 1780px; margin: 0 auto; padding: 0 20px; }

  /* ---------- GRID SPARKS ----------
     The page ground is ruled with a 32px grid. A spark is a short head of light
     that runs along those rules, dragging a tail that thins and fades behind
     it: it enters off one end of its route and leaves off the other, so it is
     never switched on or off in place. Out in the margins only, since that is
     the only ground nothing is laid over.

     A canvas rather than SVG elements: the head moves every frame and the tail
     is seven tapering pieces behind it, which is a redraw per frame rather than
     a property to animate.

     z-index -1 is what keeps it background. The root's own background paints
     first and negative-z children paint straight after it, so the spark sits
     over the grid and the accent washes but under every piece of content — one
     that runs behind a panel is simply never seen. */
  #gridSparks {
    position: fixed; inset: 0; width: 100%; height: 100%;
    z-index: -1; pointer-events: none;
  }
  /* Motion in the corner of the eye is exactly what this setting is for. */
  @media (prefers-reduced-motion: reduce) { #gridSparks { display: none; } }
  /* Mono, wide-tracked. The small-caps glyphs are their own codepoints, so any
     font used here has to actually carry them — the stack falls back through
     faces known to have them before hitting the generic monospace. */
  /* .header h1 sets a negative tracking that beats a single class, so this
     has to outrank it — mono small caps need air, not tightening. */
  .header h1.brand-title {
    font-family: var(--font-mono), "DejaVu Sans Mono", "Segoe UI Symbol", monospace;
    letter-spacing: 0.12em; text-transform: none;
  }

  /* ---------- THE DOT AT THE END ----------
     The words never change. The final dot alone carries anything: a small
     spark that fires on it every few seconds. Both page titles end in a period
     and the face is monospace at one size, so a single offset serves both.

     The glow FLOWS INLINE after the text rather than being pinned to the h1's
     right edge. That matters: an absolutely-positioned version needs the box to
     hug its own text, which means inline-block plus max-width, and at a narrow
     viewport that combination caps the box at the container and clips the
     heading with the glow stranded to its right. Flowing inline it lands after
     the last glyph at any width, and the negative margin pulls it back over the
     dot.

     Geometry is measured, not written down. A period's ink is a speck in the
     bottom-left of an advance box that is mostly empty, so centring the burst
     on that box — or on a fixed offset tuned against one font — leaves it high
     and to the right of the dot it is supposed to be on. centreBrandDot asks
     the font where the ink really is and sets --dot-pull and --dot-drop from
     it; the fallbacks are what Geist Mono at 20px works out to.

     margin-right nets the box's own width back out, so however far it is
     pulled, nothing after it on the line shifts. */
  .header h1.brand-title::after {
    content: "";
    display: inline-block;
    width: 20px; height: 20px;
    margin-left: var(--dot-pull, -17px);
    margin-right: calc(-20px - var(--dot-pull, -17px));
    vertical-align: baseline;
    position: relative;
    bottom: var(--dot-drop, -9px);
    pointer-events: none;
    opacity: 0;
    /* Four tapered rays and a core bloom, all radial so nothing has an edge to
       alias — a hard-stop gradient looks pixelated once it is scaled and
       rotated. */
    background:
      radial-gradient(closest-side ellipse at 50% 50%, rgba(233,252,247,0.95), rgba(233,252,247,0.35) 45%, rgba(233,252,247,0) 100%) center center / 100% 2.6px no-repeat,
      radial-gradient(closest-side ellipse at 50% 50%, rgba(233,252,247,0.95), rgba(233,252,247,0.35) 45%, rgba(233,252,247,0) 100%) center center / 2.6px 100% no-repeat,
      radial-gradient(closest-side ellipse at 50% 50%, rgba(160,236,218,0.6), rgba(160,236,218,0) 100%) center center / 62% 1.8px no-repeat,
      radial-gradient(closest-side ellipse at 50% 50%, rgba(160,236,218,0.6), rgba(160,236,218,0) 100%) center center / 1.8px 62% no-repeat,
      radial-gradient(closest-side circle at 50% 50%, rgba(92,224,198,0.55), rgba(92,224,198,0) 72%) center center / 46% 46% no-repeat;
  }
  /* One pass: charge, burst, then a twist out as it fades. The charge is the
     beat that makes the flash read as a burst rather than something switching
     on. The GAP between passes is random, so this is a single run that JS
     starts — a CSS loop can only ever be metronomic. */
  .header h1.brand-title.dot-lit::after {
    animation: brandDotBurst 1.12s cubic-bezier(0.28,0.55,0.22,1) 1;
  }
  @keyframes brandDotBurst {
    0%   { opacity: 0;    transform: scale(0.10) rotate(0deg); }
    21%  { opacity: 0.8;  transform: scale(0.38) rotate(10deg); }
    39%  { opacity: 1;    transform: scale(1.5)  rotate(32deg); }
    64%  { opacity: 0.5;  transform: scale(1.15) rotate(84deg); }
    100% { opacity: 0;    transform: scale(0.85) rotate(146deg); }
  }

  /* ---------- TOP NAV ---------- */
  .topbar {
    position: sticky; top: 0; z-index: 200;
    /* Opaque, not 97%. At 3% translucency whatever scrolled under the bar
       tinted it, so on Feedback — where the panel-coloured filter bar sits
       directly beneath — the nav and everything on it read a shade brighter
       than on the other pages. Solid, it is the same on every page. */
    background: var(--bg);
    border-bottom: 1px solid rgba(255,255,255,0.06);
    box-shadow: 0 1px 0 rgba(0,0,0,0.7), 0 10px 26px -18px rgba(0,0,0,1);
    margin-bottom: 22px;
  }
  .topbar-inner {
    max-width: 1780px; margin: 0 auto; padding: 11px 20px;
    display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  }
  .brandmark { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; white-space: nowrap; }
  .brandmark span { color: var(--accent); text-shadow: 0 0 12px rgba(92,224,198,0.4); }
  .nav-tabs { display: flex; gap: 6px; }
  .nav-tab {
    background: transparent; border: 1px solid transparent; color: var(--text-dim);
    padding: 7px 15px; border-radius: 999px; font-size: 13px; font-weight: 600;
    cursor: pointer; font-family: inherit;
    transition: color .16s var(--ease), background .16s var(--ease),
                border-color .16s var(--ease), transform .16s var(--ease), box-shadow .16s var(--ease);
  }
  .nav-tab:hover { color: var(--text); background: rgba(255,255,255,0.04); transform: translateY(-1px); }
  .nav-tab.active {
    background: rgba(66,193,172,0.13); border-color: rgba(92,224,198,0.34); color: var(--neon-teal);
    box-shadow: inset 0 1px 0 rgba(92,224,198,0.16), 0 0 12px -8px var(--neon-teal);
  }
  .nav-tab .tab-badge {
    display: inline-block; margin-left: 7px; font-size: 10px; font-weight: 800;
    background: var(--amber); color: #14161c; border-radius: 999px; padding: 1px 6px;
  }
  /* Unread feedback is the one badge that should read as "act on me", so it
     takes the decline red rather than the amber the review queue uses. */
  /* Dark text rather than white: #ff8080 is light enough that white on it
     falls to about 2.3:1, which is unreadable at 10px. Dark gives ~9:1 and
     matches how the amber review badge beside it is already handled. */
  .nav-tab .tab-badge.badge-new { background: #ff8080; color: #14161c; letter-spacing: .3px; }
  .nav-tab .tab-badge.badge-seen { background: var(--accent); color: #10241f; }
  .topbar-meta {
    margin-left: auto; text-align: right; color: var(--text-faint);
    font-size: 11px; font-family: var(--font-mono); line-height: 1.6;
  }
  .storage-pill {
    display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 700;
    padding: 3px 9px; border-radius: 999px; font-family: var(--font-display);
    letter-spacing: 0.03em; text-transform: uppercase;
  }
  .storage-pill.ok { background: rgba(66,193,172,0.12); color: var(--accent); }
  .storage-pill.warn { background: rgba(232,200,74,0.12); color: var(--amber); }
  .storage-pill.bad { background: rgba(242,116,108,0.14); color: var(--decline); }
  .storage-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

  .view { display: none; }
  .view.active { display: block; }

  .header {
    display: flex; justify-content: space-between; align-items: flex-start;
    margin-bottom: 20px; padding-bottom: 18px; border-bottom: 1px solid var(--border); gap: 20px;
  }
  .header h1 { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; }
  .header .sub { color: var(--text-dim); font-size: 12.5px; margin-top: 4px; }
  .header .meta { text-align: right; color: var(--text-faint); font-size: 11.5px; font-family: var(--font-mono); white-space: nowrap; }

  /* ---------- BANNERS ---------- */
  .banner {
    border-radius: 9px; padding: 11px 15px; margin-bottom: 14px; font-size: 12.5px;
    display: flex; align-items: flex-start; gap: 10px; line-height: 1.55;
  }
  .banner.info { background: rgba(108,158,248,0.08); border: 1px solid #35507d; color: #b9cdf3; }
  .banner.warn { background: rgba(232,200,74,0.07); border: 1px solid #6e5f22; color: #e9dda6; }
  .banner.bad  { background: rgba(242,116,108,0.08); border: 1px solid #7d3b37; color: #f0bcb8; }
  .banner.good { background: rgba(66,193,172,0.07); border: 1px solid #276f63; color: #96cec4; }
  .banner b { color: #fff; }
  .banner-x {
    margin-left: auto; cursor: pointer; color: inherit; opacity: 0.55;
    background: none; border: none; font-size: 16px; line-height: 1; font-family: inherit; flex-shrink: 0;
  }
  .banner-x:hover { opacity: 1; }

  /* ---------- FILTER BAR ---------- */
  /* The filter bar rides under the top nav on every page, so only the
     content below it ever scrolls away. */
  .filters {
    display: flex; gap: 10px 9px; align-items: flex-end; flex-wrap: wrap;
    background: var(--bg-panel); border: 1px solid var(--border);
    border-radius: 10px; padding: 14px 16px; margin-bottom: 18px;
    position: sticky; top: var(--topbar-h, 50px); z-index: 90;
    box-shadow: 0 10px 22px -14px rgba(0,0,0,0.85);
  }
  .filters.collapsed { padding: 8px 16px; }
  .filters.collapsed .filter-group, .filters.collapsed .reset-btn { display: none; }
  .filters-toggle {
    position: absolute; top: 6px; right: 10px;
    background: none; border: 1px solid var(--border); color: var(--text-faint);
    font-size: 10px; padding: 2px 8px; border-radius: 5px; cursor: pointer;
    font-family: inherit; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 700;
  }
  .filters-toggle:hover { color: var(--text); border-color: var(--text-faint); }
  .filters.collapsed .filters-summary { display: block; }
  .filters-summary { display: none; font-size: 11.5px; color: var(--text-dim); padding-right: 90px; }

  /* Section collapse */
  .panel.collapsed > *:not(.big-header-row):not(.panel-head-row) { display: none; }
  /* The head row survives the fold so the section can be unfolded again — but
     only the control that unfolds it has any business being there. Month to
     Date's Export List button was still sitting beside the title with nothing
     on screen to export; it goes away with the table and comes back with it. */
  .panel.collapsed .panel-head-actions > *:not(.collapse-btn) { display: none; }
  .collapse-btn {
    background: none; border: 1px solid var(--border); color: var(--text-faint);
    font-size: 10.5px; padding: 4px 11px; border-radius: 6px; cursor: pointer;
    font-family: inherit; font-weight: 700; letter-spacing: 0.03em; white-space: nowrap;
  }
  .collapse-btn:hover { color: var(--text); border-color: var(--text-faint); }
  /* A panel header that carries controls (a collapse button, the review queue)
     off to the right of the title. Excluded from the collapse rule above so it
     stays visible when the section is folded. */
  .panel-head-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
  .panel-head-row .panel-title { margin-bottom: 0; }
  .panel-head-row .panel-head-left { min-width: 0; }
  .panel-head-row .panel-head-left .panel-sub { margin-bottom: 0; }
  .panel-head-row .panel-head-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
  /* Month to Date: the Chats and Missed cells act on a click but carry no
     data-drill, so they need the pointer the drill cells get for free. */
  .mtd-table td.mtd-act { cursor: pointer; }
  .mtd-table td.mtd-act:focus-visible { outline: 2px solid var(--accent-dim); outline-offset: -2px; }
  /* Each group is as wide as the widest thing inside it — its label or its
     control — rather than a fixed 172px every one had to fill. Short ones like
     Brand and VIP Level shrink to their words and hand the rest of the row
     back, which is what keeps the bar on fewer lines.

     max-content is driven by the LABEL in almost every case, and a label never
     changes, so the widths stay put instead of twitching as you tick options. */
  .filter-group {
    display: flex; flex-direction: column; gap: 6px; justify-content: flex-end;
    /* Hug the content, but give way when a row is close to full rather than
       pushing the last control onto a line of its own. */
    flex: 0 1 auto; width: max-content; min-width: 96px; max-width: 100%;
  }
  .filter-group.grow { flex: 1 1 240px; min-width: 240px; }
  /* These three only ever hold a short phrase — a month name, "All Brands",
     "All Levels" — so they are capped rather than left to take the width their
     widest dropdown option would ask for. The room goes back to the row. */
  .filter-group.narrow { flex: 0 1 116px; width: auto; min-width: 116px; }
  /* The label is the widest thing here and it can ellipsis, so the group does
     not have to be as wide as the sentence. */
  #view-heatmaps .filters .filter-group:has(#hmKpiAgents) { flex: 0 1 150px; min-width: 150px; }
  /* Every control in a filter bar is the same height and type size, whatever
     element it happens to be — a select, a date input and the custom dropdown
     div all sat at different heights, which is what made the row look ragged.
     The label sits on a fixed line so the controls line up even when a label
     wraps to two words. */
  .filters .filter-label {
    min-height: 15px; line-height: 15px; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
  }
  .filters select,
  .filters input[type="date"],
  .filters input[type="text"],
  .filters .dropdown-trigger {
    height: 38px; box-sizing: border-box; font-size: 14px; width: 100%;
  }
  .filters select, .filters input[type="date"], .filters input[type="text"] { padding: 0 11px; }
  /* The calendar button is drawn by the browser inside the field, hard against
     whatever precedes it. Its own margin is the only thing that keeps it off
     the year, and a little right padding stops it hugging the border. */
  .filters input[type="date"] { padding-right: 8px; }
  .filters input[type="date"]::-webkit-calendar-picker-indicator { margin-left: 4px; }
  /* A select narrower than its longest option clips the START of its text, so
     "Logged-in users only" renders as "ogged-in users only" — and text-overflow
     does not apply to a select in Chrome. So a group holding one simply never
     shrinks; the give comes from the capped groups instead. */
  .filters .filter-group:has(select) { flex-shrink: 0; }
  .filters .dropdown-trigger { padding: 0 11px; }
  /* The dropdown has a 146px floor for use outside a filter bar. In one, the
     group is the authority on width — without this the dropdown overflowed a
     narrower group and overlapped the filter beside it. */
  .filters .dropdown { min-width: 0; width: 100%; }
  /* And if the value is ever wider than the box, it ends in an ellipsis rather
     than spilling out. */
  .filters .dropdown-trigger { min-width: 0; overflow: hidden; }
  .filters .dropdown-trigger > span:first-child {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
  }
  /* The floor belongs on the menu, not the button: the trigger has to fit the
     column it sits in, but the list that opens out of it should stay readable
     whatever that column is. */
  .filters .dropdown-panel { min-width: 146px; }
  .filters .reset-btn { height: 38px; box-sizing: border-box; }
  /* Two date fields cannot share a one-control column — this group needs the
     width of two, and the inputs have to be allowed to shrink into it. */
  .filters .date-pair { display: flex; gap: 6px; width: 100%; }
  .filters .date-pair input[type="date"] { flex: 1 1 0; min-width: 0; }
  /* Wide enough for the two date fields and no wider. 248 was too tight:
     the native picker icon sat on top of the year. */
  .filters .filter-group:has(.date-pair) { flex: 0 1 262px; min-width: 262px; }
  .filter-label {
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-dim); font-weight: 700;
  }
  /* Indented by the control border beneath it. Flush with the group edge, the
     label starts level with the OUTSIDE of that border and reads a pixel or
     two left of the field it names; this lands it on the inside edge. */
  /* One line, always: a label that wrapped when its group gave way would
     make the whole bar taller. */
  .filters .filter-label { padding-left: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* The one exception: this label names a pair of inputs rather than a single
     control, so it is centred across both instead of sitting over the first. */
  .filters .filter-group:has(.date-pair) .filter-label { text-align: center; }

  /* Every control in the filter bar sits at the same near-black as the inside
     of a table, so the bar reads as a frame and the controls as wells cut into
     it. The 1px border is what separates them now, not a lighter fill. */
  .filters select,
  .filters input[type="date"],
  .filters input[type="text"],
  .filters input[type="number"],
  .filters .dropdown-trigger,
  .filters .reset-btn {
    background: #0D0F14;
  }
  /* The date field's picker button is drawn by the browser, and with no
     colour-scheme to go on it paints a black glyph that all but disappears on
     a dark field. Replacing its background image swaps the whole icon for one
     drawn in the accent green. */
  .filters input[type="date"]::-webkit-calendar-picker-indicator {
    width: 15px; height: 15px; padding: 0; opacity: 1; cursor: pointer;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2342c1ac' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4.5' width='18' height='17' rx='2'/%3E%3Cpath d='M16 2v5M8 2v5M3 10.5h18'/%3E%3C/svg%3E");
    background-size: contain; background-repeat: no-repeat; background-position: center;
    transition: filter 0.15s ease;
  }
  .filters input[type="date"]::-webkit-calendar-picker-indicator:hover {
    filter: brightness(1.35);
  }
  select, input[type="date"], input[type="text"], input[type="number"], textarea {
    background: var(--bg-panel-alt); border: 1px solid var(--border); color: var(--text);
    padding: 10px 12px; border-radius: 6px; font-size: 15px; font-family: inherit;
  }
  select:focus, input:focus, textarea:focus { outline: none; border-color: var(--accent-dim); }
  /* Browsers paint saved credentials on a hard white background, which is
     unreadable here. Force the panel colour back over the top of it. */
  input:-webkit-autofill,
  input:-webkit-autofill:hover,
  input:-webkit-autofill:focus,
  input:-webkit-autofill:active {
    -webkit-text-fill-color: var(--text);
    -webkit-box-shadow: 0 0 0 1000px var(--bg-panel-alt) inset;
    box-shadow: 0 0 0 1000px var(--bg-panel-alt) inset;
    caret-color: var(--text);
    transition: background-color 100000s ease-in-out 0s;
  }

  .dropdown { position: relative; min-width: 146px; }
  .dropdown-trigger {
    background: var(--bg-panel-alt); border: 1px solid var(--border); color: var(--text);
    padding: 8px 10px; border-radius: 6px; font-size: 13px; font-family: inherit;
    cursor: pointer; display: flex; justify-content: space-between; align-items: center;
    gap: 8px; white-space: nowrap; user-select: none;
  }
  .dropdown-trigger:hover { border-color: var(--text-faint); }
  .dropdown-trigger .arrow { font-size: 10px; color: var(--text-faint); transition: transform 0.12s ease; }
  .dropdown.open .dropdown-trigger .arrow { transform: rotate(180deg); }
  .dropdown-panel {
    display: none; position: absolute; top: calc(100% + 6px); left: 0;
    min-width: 100%; max-height: 280px; overflow-y: auto;
    background: #1d212a; border: 1px solid var(--border); border-radius: 8px;
    padding: 6px; z-index: 120; box-shadow: 0 8px 24px rgba(0,0,0,0.5);
  }
  .dropdown.open .dropdown-panel { display: block; }
  .dropdown-option {
    display: flex; align-items: center; gap: 8px; padding: 6px 8px;
    border-radius: 5px; font-size: 12.5px; cursor: pointer; white-space: nowrap;
  }
  .dropdown-option:hover { background: rgba(255,255,255,0.04); }
  .dropdown-option input[type="checkbox"] { accent-color: var(--accent); cursor: pointer; }
  .dropdown-divider { height: 1px; background: var(--border); margin: 6px 2px; }
  .dropdown-action { font-size: 11px; color: var(--accent); padding: 5px 8px; cursor: pointer; }
  .dropdown-action:hover { text-decoration: underline; }

  .reset-btn {
    background: transparent; border: 1px solid var(--border); color: var(--text-dim);
    padding: 8px 14px; border-radius: 6px; font-size: 12.5px; cursor: pointer;
    height: 36px; font-family: inherit; white-space: nowrap;
  }
  .reset-btn:hover { border-color: var(--text-faint); color: var(--text); }

  /* ---------- CARDS / PANELS ---------- */
  /* ---------- THE FIVE-AND-FIVE LADDER ----------
     Overview and KPI Breakdown are the same period at two grains, so they have
     to carry the same number of columns or the pairing stops reading. Both step
     5 -> 3 -> 2 -> 1 together, on the same breakpoints, and four is skipped
     entirely: with five items a four-column row strands the fifth on a line of
     its own, which looks like a bug rather than a wrap.

     The breakpoints are NOT the widths you would expect, because body carries
     zoom: 0.8 below 2200px (see the rule further down). Media queries measure
     the real viewport while layout happens in zoomed pixels, so the space a row
     actually has is min(1780, viewport / 0.8) - 80 for the two paddings. A KPI
     box wants 300px and five of them plus gaps want 1532, which lands at a
     viewport of ~1290 — not the 1500 the old rule assumed, which cut the row to
     two boxes a full 200px early and left them looking stretched.

     A summary card copes with less than a KPI box does, so the cards could hold
     five a good deal longer. They step down with the boxes anyway: the two rows
     agreeing is worth more than squeezing one extra breakpoint out of the top
     one. Heatmaps keeps auto-fit through .summary-row.four, which outranks all
     of this. */
  .summary-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin-bottom: 16px; }
  .card {
    background: var(--bg-panel); border: 1px solid var(--border); border-radius: 14px;
    padding: 14px 16px; border-top: 2px solid var(--border); min-width: 0;
    box-shadow: var(--lift-1);
  }
  /* The cap sets the KPI's colour; a matching inner bloom under it carries that
     colour down into the card, so each reads as its own hue rather than a flat
     slab with a coloured edge. 9px of the 10px blur is pulled back by the
     negative spread — a hint of light, not a halo. The edge stays hard. */
  .card.upsell  { --cap: var(--upsell); border-top-color: var(--upsell);
                  background: linear-gradient(180deg, rgba(66,193,172,0.06), transparent);
                  box-shadow: inset 0 2px 10px -9px var(--neon-teal),  var(--lift-1); }
  .card.push    { --cap: var(--push); border-top-color: var(--push);
                  background: linear-gradient(180deg, rgba(108,158,248,0.06), transparent);
                  box-shadow: inset 0 2px 10px -9px var(--neon-blue),  var(--lift-1); }
  .card.decline { --cap: var(--decline); border-top-color: var(--decline);
                  background: linear-gradient(180deg, rgba(242,116,108,0.06), transparent);
                  box-shadow: inset 0 2px 10px -9px var(--neon-red),   var(--lift-1); }
  /* The orange the Decline Ticket box wears. The KPI boxes had this colour from
     the start; the cards never did, because until now no card needed it. */
  .card.ticket  { --cap: var(--declineticket); border-top-color: var(--declineticket);
                  background: linear-gradient(180deg, rgba(217,140,74,0.06), transparent);
                  box-shadow: inset 0 2px 10px -9px var(--neon-orange), var(--lift-1); }
  /* Amber and Net — the two summary figures — also take a 6% wash fading out,
     so the row reads as one set in two colours rather than styled-and-plain. */
  .card.amber   { --cap: var(--amber); border-top-color: var(--amber);
                  background: linear-gradient(180deg, rgba(232,200,74,0.06), transparent);
                  box-shadow: inset 0 2px 10px -9px var(--neon-amber), var(--lift-1); }
  .card.net     { --cap: var(--accent); border-top-color: var(--accent);
                  background: linear-gradient(180deg, rgba(66,193,172,0.06), transparent);
                  box-shadow: inset 0 2px 10px -9px var(--neon-teal),  var(--lift-1); }
  /* These name the four figures the whole dashboard is built around, so
     they read as headings rather than faint captions. */
  .card-label {
    font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text); font-weight: 800; margin-bottom: 8px;
  }
  .card-value { font-size: 23px; font-weight: 600; font-family: var(--font-mono); letter-spacing: -0.022em; overflow-wrap: anywhere; }
  .card-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 4px; }
  /* The per-KPI split lives inside the box it belongs to: the parts only mean
     anything sitting under the total they add up to. */
  .card-kpis { margin-top: 9px; border-top: 1px solid var(--border); padding-top: 7px;
    display: flex; flex-direction: column; gap: 2px; }
  /* The wash needs room to read as a shape rather than a stripe: padding gives
     it that, and the matching negative margin hands the width back so the text
     still lines up with the card's content edge. */
  .card-kpi-row { display: flex; justify-content: space-between; align-items: baseline;
    font-size: 11px; color: var(--text-dim); padding: 3px 8px; margin: 0 -8px;
    cursor: pointer; border-radius: 8px; }
  .card-kpi-row:hover { color: var(--text); }   /* the wash is the drill panel now */
  .card-kpi-row b { font-family: var(--font-mono); color: var(--text); font-weight: 600; }
  /* The KPI names carry the same colour they do everywhere else in the report,
     so a colour means one thing wherever you meet it. */
  .kpi-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }
  /* 38px is what every control in a .filters bar is, and these three sit in a
     row together — the month picker is outside .filters, so it was taking the
     global select padding and standing a few pixels taller than the button
     beside it. */
  .awaiting-btn { height: 38px; border-radius: 7px; cursor: pointer; font-size: 12px; font-weight: 700;
    padding: 0 14px; border: 1px solid var(--border); background: var(--bg-panel-alt); color: var(--text-dim); }
  .big-header-row #monthSelect {
    height: 38px; box-sizing: border-box; padding: 0 11px; font-size: 14px; width: 100%;
  }
  .big-header-row #toggleSummaryBtn { height: 38px; box-sizing: border-box; }
  .awaiting-btn.hot { border-color: rgba(232,200,74,0.55); background: rgba(232,200,74,0.14); color: var(--amber); }
  /* Revenue rail sits to the left of the KPI boxes and carries the totals the
     removed "Where the money came from" table used to hold. */
  .kpi-with-rail { display: grid; grid-template-columns: minmax(210px, 0.9fr) 3fr; gap: 10px; align-items: stretch; }
  .rev-rail { border: 1px solid var(--border); border-radius: 9px; background: var(--bg-panel-alt); padding: 12px 13px; }
  .rev-rail-label { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint); }
  .rev-rail-value { font-size: 25px; font-weight: 600; font-family: var(--font-mono); color: var(--accent); margin-top: 3px; text-shadow: 0 0 14px rgba(92,224,198,0.3); }
  .rev-rail-list { margin-top: 11px; border-top: 1px solid var(--border); padding-top: 9px; display: flex; flex-direction: column; gap: 5px; }
  .rev-rail-row { display: flex; justify-content: space-between; align-items: baseline; font-size: 11.5px; color: var(--text-dim); cursor: pointer; border-radius: 3px; padding: 2px 0; }
  .rev-rail-row:hover { color: var(--text); }   /* the wash is the drill panel now */
  .rev-rail-row b { font-family: var(--font-mono); color: var(--text); font-weight: 600; }
  .agentcmp .kt-expand { cursor: pointer; user-select: none; color: var(--text-faint); margin-right: 6px; font-size: 10px; }
  .agentcmp tr.kt-detail td { background: rgba(255,255,255,0.02); font-size: 11px; }
  .agentcmp .kt-detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; padding: 6px 2px; }
  .agentcmp .kt-detail-box { border: 1px solid var(--border); border-radius: 7px; padding: 7px 9px; }
  .agentcmp .kt-detail-box .h { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); margin-bottom: 5px; }
  /* This table is read across, not scanned down, so it carries a size up from
     the compact tables elsewhere. */
  table.agentcmp { font-size: 17px; }
  table.agentcmp thead th { font-size: 12px; padding: 10px 8px; }
  table.agentcmp td { font-size: 17px; padding: 14px 8px; }
  table.agentcmp .kt-name { font-size: 21px; font-weight: 800; }
  /* The expansion carries the same sizes as the KPI boxes it mirrors. */
  .agentcmp tr.kt-detail td { font-size: 15px; }
  /* Fixed eight columns, not auto-fit: with a 210px minimum the eighth box
     tipped past the page width and dropped to its own row. minmax(0,1fr) lets
     them shrink together instead of wrapping. */
  .agentcmp .kt-detail-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 8px; }
  .agentcmp .kt-detail-box { padding: 10px; min-width: 0; }
  .agentcmp .kt-detail-box .inline-time-row span:last-child,
  .agentcmp .kt-detail-row b { white-space: nowrap; }
  .agentcmp .kt-detail-box .h { font-size: 13px; color: var(--text); font-weight: 800; margin-bottom: 8px; }
  .agentcmp .kt-detail-row { font-size: 15px; padding: 3px 0; }
  .agentcmp .kt-detail-row b { font-size: 15px; }
  .agentcmp .kt-detail-box .inline-time-row { font-size: 15px; }
  .agentcmp .kt-detail-row { display: flex; justify-content: space-between; padding: 1px 0; cursor: pointer; }
  .agentcmp .kt-detail-row:hover { color: var(--text); }
  .agentcmp .kt-detail-row b { font-family: var(--font-mono); font-weight: 600; }

  /* A 1px light bar across the top — brightest at the left, gone by 62% (a
     runway, not a centred flare) — plus a 120px 2.8%-alpha wash below it.
     Depth comes from the lift shadow + inset, not from a border. */
  .panel {
    background:
      linear-gradient(90deg, rgba(92,224,198,0.5) 0 70px,
                             rgba(92,224,198,0.16) 34%,
                             transparent 62%) top left / 100% 1px no-repeat,
      linear-gradient(180deg, rgba(92,224,198,0.028), rgba(255,255,255,0) 120px),
      var(--bg-panel);
    box-shadow: inset 0 1px 0 rgba(92,224,198,0.07), var(--lift-2);
    border-radius: 10px; padding: 18px 20px; margin-bottom: 16px;
  }
  /* Variants recolour the bar and the inset only — same alphas throughout. */
  .panel.pt-blue {
    background:
      linear-gradient(90deg, rgba(138,180,255,0.5) 0 70px,
                             rgba(138,180,255,0.16) 34%,
                             transparent 62%) top left / 100% 1px no-repeat,
      linear-gradient(180deg, rgba(138,180,255,0.028), rgba(255,255,255,0) 120px),
      var(--bg-panel);
    box-shadow: inset 0 1px 0 rgba(138,180,255,0.07), var(--lift-2);
  }
  .panel.pt-amber {
    background:
      linear-gradient(90deg, rgba(245,217,95,0.5) 0 70px,
                             rgba(245,217,95,0.16) 34%,
                             transparent 62%) top left / 100% 1px no-repeat,
      linear-gradient(180deg, rgba(245,217,95,0.028), rgba(255,255,255,0) 120px),
      var(--bg-panel);
    box-shadow: inset 0 1px 0 rgba(245,217,95,0.07), var(--lift-2);
  }
  .panel.pt-purple {
    background:
      linear-gradient(90deg, rgba(189,162,255,0.5) 0 70px,
                             rgba(189,162,255,0.16) 34%,
                             transparent 62%) top left / 100% 1px no-repeat,
      linear-gradient(180deg, rgba(189,162,255,0.028), rgba(255,255,255,0) 120px),
      var(--bg-panel);
    box-shadow: inset 0 1px 0 rgba(189,162,255,0.07), var(--lift-2);
  }
  .panel.pt-decline {
    background:
      linear-gradient(90deg, rgba(255,143,134,0.5) 0 70px,
                             rgba(255,143,134,0.16) 34%,
                             transparent 62%) top left / 100% 1px no-repeat,
      linear-gradient(180deg, rgba(255,143,134,0.028), rgba(255,255,255,0) 120px),
      var(--bg-panel);
    box-shadow: inset 0 1px 0 rgba(255,143,134,0.07), var(--lift-2);
  }
  .panel-title { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
  .panel-sub { font-size: 11.5px; color: var(--text-faint); margin-bottom: 16px; }
  .big-header { font-size: 22px; font-weight: 800; letter-spacing: -0.01em; color: var(--text); }
  /* flex-end used to bottom-align the title with the taller block of controls
     opposite it, so the heading floated down the panel instead of sitting in
     its top-left corner. The controls keep their own flex-end internally, so
     they still line up with each other. */
  .big-header-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
  .combined-totals-divider {
    font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: #fff;
    font-weight: 800; margin: 4px 0 12px 0; padding-top: 16px; border-top: 1px solid var(--border);
  }

  .agent-mini-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(410px, 1fr)); gap: 12px; margin-bottom: 14px; }
  .agent-mini-card { background: var(--bg-panel-alt); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; min-width: 0; }
  .agent-mini-name { font-size: 13px; font-weight: 700; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
  .amn-bonus { font-family: var(--font-mono); font-size: 12.5px; color: var(--accent); font-weight: 700; }
  .agent-mini-stats {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px 12px;
    margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--border);
  }
  .mini-stat-label { font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); font-weight: 600; }
  .mini-stat-value { font-size: 14px; font-family: var(--font-mono); font-weight: 600; margin-top: 2px; }

  .kpi-box-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
  /* At this type size five across would crush every box, so they wrap on
     their own and take as many rows as they need. */
  #combinedKpiBoxGrid.kpi-box-grid { grid-template-columns: repeat(5, 1fr); margin-top: 4px; }

  /* The ladder lives HERE, after both base rules, not up with .summary-row.
     #combinedKpiBoxGrid.kpi-box-grid and the media-query rules that override it
     have identical specificity, so the later one wins — declared earlier, the
     whole ladder lost to the base rule above and the boxes stayed five across
     at every width. */
  @media (max-width: 1289px) {
    .summary-row { grid-template-columns: repeat(3, 1fr); }
    #combinedKpiBoxGrid.kpi-box-grid { grid-template-columns: repeat(3, 1fr); }
  }
  @media (max-width: 799px) {
    .summary-row { grid-template-columns: repeat(2, 1fr); }
    #combinedKpiBoxGrid.kpi-box-grid { grid-template-columns: repeat(2, 1fr); }
  }
  @media (max-width: 549px) {
    .summary-row { grid-template-columns: 1fr; }
    #combinedKpiBoxGrid.kpi-box-grid { grid-template-columns: 1fr; }
  }
  .kpi-box {
    background: var(--bg-panel-alt); border: 1px solid var(--border); border-radius: 12px;
    padding: 8px 15px 14px; border-top: 3px solid var(--border); min-width: 0;
    box-shadow: var(--lift-1);
  }
  /* Each box carries its own colour the way the summary cards above do: a wash
     under the cap that fades out by the middle of the box. Layered OVER the
     panel fill rather than replacing it, so only the hue changes and the five
     boxes keep the one surface. */
  .kpi-box-upsell        { --cap: var(--upsell); border-top-color: var(--upsell);
                           background: linear-gradient(180deg, rgba(66,193,172,0.07), transparent 62%), var(--bg-panel-alt);
                           box-shadow: inset 0 2px 10px -9px var(--neon-teal),   var(--lift-1); }
  .kpi-box-push          { --cap: var(--push); border-top-color: var(--push);
                           background: linear-gradient(180deg, rgba(108,158,248,0.07), transparent 62%), var(--bg-panel-alt);
                           box-shadow: inset 0 2px 10px -9px var(--neon-blue),   var(--lift-1); }
  .kpi-box-decline       { --cap: var(--decline); border-top-color: var(--decline);
                           background: linear-gradient(180deg, rgba(242,116,108,0.07), transparent 62%), var(--bg-panel-alt);
                           box-shadow: inset 0 2px 10px -9px var(--neon-red),    var(--lift-1); }
  .kpi-box-declineticket { --cap: var(--declineticket); border-top-color: var(--declineticket);
                           background: linear-gradient(180deg, rgba(217,140,74,0.07), transparent 62%), var(--bg-panel-alt);
                           box-shadow: inset 0 2px 10px -9px var(--neon-orange), var(--lift-1); }
  .kpi-box-total         { --cap: var(--accent); border-top-color: var(--accent);
                           background: linear-gradient(180deg, rgba(66,193,172,0.07), transparent 62%), var(--bg-panel-alt);
                           box-shadow: inset 0 2px 10px -9px var(--neon-teal), var(--lift-1); }
  /* ---------- THE COLOURED EDGE ----------
     The cap named which KPI a card belongs to and then stopped dead at the top
     two pixels. This carries that same colour down both sides, thinning out
     before the halfway mark, so the hue belongs to the whole card rather than
     sitting on top of it.

     It has to be a ring rather than a border: a border takes one flat colour,
     and border-image, which would take a gradient, cannot follow a radius. So
     a box the size of the card's border box is filled with the fade and then
     masked by two copies of itself — one clipped to its content box, one not —
     which cancel each other everywhere except the outermost pixel. inset -1px
     lands that pixel exactly on the border it is replacing.

     --cap is what each variant fills it with. A card without one gets a
     transparent ring and looks exactly as it did. */
  .card, .kpi-box { position: relative; }
  /* The hover tint used to REPLACE the card's background, which meant the
     colour wash disappeared the moment the pointer arrived and came back when
     it left. Worse, a gradient cannot transition to a flat colour, so it
     snapped in both directions however long the transition said it was — that
     hard flip is what made the hover feel cheap.

     It fades in on a layer of its own instead: the wash stays where it is, and
     opacity is something the browser can actually animate. isolation makes the
     card a stacking context so z-index -1 lands the tint above the card's own
     background and below its figures, rather than washing over them. */
  .card { isolation: isolate; }
  .card::after {
    content: ''; position: absolute; inset: 0; z-index: -1;
    border-radius: inherit; pointer-events: none; opacity: 0;
    background: radial-gradient(130% 90% at 50% 0%,
                rgba(66,193,172,0.13), rgba(66,193,172,0.04) 55%, transparent 100%);
    transition: opacity .34s var(--ease);
  }
  .card.clickable:hover::after { opacity: 1; }
  .card::before, .kpi-box::before {
    content: ''; position: absolute; inset: -1px; pointer-events: none;
    border-radius: inherit; padding: 1px;
    /* Full strength under the cap, then down to a trace that still reaches the
       bottom corner — coloured the whole way rather than a stub that stops. The
       weaker stops are mixed from --cap rather than declared, so a variant only
       ever names its colour once. */
    background: linear-gradient(180deg,
      var(--cap, transparent) 0%,
      color-mix(in srgb, var(--cap, transparent) 34%, transparent) 38%,
      color-mix(in srgb, var(--cap, transparent) 13%, transparent) 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
  }

  .kpi-box-title { font-size: 23px; font-weight: 800; margin: 0 0 8px; letter-spacing: -0.018em; text-align: center; line-height: 1.2; text-shadow: none; }
  /* These rows were mostly gap: a faint label on the left, a number far away on
     the right. Bigger type, a tighter column, and the value in full strength. */
  .kpi-box-row {
    display: flex; justify-content: space-between; gap: 12px; font-size: 16px;
    color: var(--text); font-family: var(--font-mono);
    /* Same trade as .card-kpi-row: padded for the wash, pulled back out so the
       column of figures still ends on the box's own edge. */
    padding: 4px 9px; margin: 0 -9px; border-radius: 9px;
  }
  .kpi-box-row span:first-child { font-family: var(--font-display); color: var(--text-dim); font-weight: 600; }
  .kpi-box-row span:last-child { font-weight: 700; }
  /* The heading and the three big stats centre; the label/value rows below
     keep their two edges, which is what lets the numbers line up down the
     column and reads better than a centred pair. */
  .kpi-box-stats {
    text-align: center;
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
    margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid #262b35;
  }
  .kpi-box-stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); font-weight: 700; }
  .kpi-box-stat-value { font-size: 22px; font-family: var(--font-mono); font-weight: 700; margin-top: 3px; letter-spacing: -0.022em; }
  /* Totals is the only box whose figures glow, and only because the glow hue
     equals the ink. */
  .kpi-box-total .kpi-box-stat-value { text-shadow: 0 0 14px rgba(92,224,198,0.3); }
  .kpi-box-inline-time { margin-top: 8px; padding-top: 8px; border-top: 1px dashed #2a2f3a; }
  .inline-time-row {
    display: flex; justify-content: space-between; align-items: center; gap: 12px;
    font-size: 15px; padding: 4px 0; font-family: var(--font-mono); color: var(--text);
  }
  .inline-time-row span:first-child { font-family: var(--font-display); color: var(--text-dim); font-weight: 600; display: flex; align-items: center; }
  .inline-time-row span:last-child { font-weight: 700; white-space: nowrap; }
  .tier-swatch { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 7px; flex-shrink: 0; }
  .tier-teal { background: #70cebe; }
  .tier-green { background: #42c1ac; }
  .tier-yellow { background: #e8c84a; }
  .tier-orange { background: #f2a14c; }
  .tier-red { background: #f2746c; }
  .net-positive { color: var(--accent); }
  .net-negative { color: var(--decline); }
  .net-zero { color: var(--text-faint); }

  /* ---------- TABLE ---------- */
  .table-wrap { width: 100%; }
  table.main { width: 100%; border-collapse: collapse; font-size: 14px; table-layout: fixed; }
  table.main th {
    text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em;
    color: var(--text-dim); font-weight: 700; padding: 9px 6px;
    border-bottom: 1px solid var(--border); vertical-align: bottom;
  }
  table.main th.sortable { cursor: pointer; user-select: none; }
  table.main th.sortable:hover { color: var(--text-dim); }
  table.main th .sort-ind { color: var(--accent); font-size: 9px; }
  /* Everything centres. The old mix of left, right and centre meant a value
     rarely sat under the coloured header it belonged to, which is what made
     the table look crooked. .num keeps its mono face, loses its right edge. */
  table.main th, table.main td { text-align: center; vertical-align: middle; }
  table.main th.num, table.main td.num { text-align: center; }
  table.main td.num { font-family: var(--font-mono); }
  /* One face, one weight, one size for every column label, whatever classes
     the cell happens to carry. */
  table.main thead th { font-family: var(--font-display); font-weight: 700; }
  table.main .link-btns { justify-content: center; }
  table.main .cell-sub { text-align: center; }
  /* Middle, not top. Cells in one row hold very different amounts — a bare
     figure next to a stacked name-and-real-name next to a block of buttons —
     and hanging them all from the top edge left the short ones stranded high
     while the tall ones set the row height. Centring is what table.dupes has
     always done, which is why the missed-opportunities table looked right and
     this one did not. */
  table.main td { padding: 10px 6px; border-bottom: 1px solid #20242B; vertical-align: middle; overflow-wrap: anywhere; }
  table.main tr.agent-row:hover td { background: rgba(255,255,255,0.022); }
  table.main tr.row-review td { background: rgba(232,200,74,0.035); }
  table.main tr.row-review:hover td { background: rgba(232,200,74,0.06); }
  /* A name is one thing, so it gets one line.

     table.main td sets overflow-wrap:anywhere so that a long player handle
     cannot blow the layout out. The catch is that "anywhere" also drops the
     cell's min-content width to a single character, which leaves auto table
     layout free to squeeze the Agent column down to nothing and then break the
     name wherever it likes — at the space in "Georg (E)", or straight through
     the middle of a word. On the Feedback page, which carries sixteen columns
     rather than the breakdown's fourteen, that column was being squeezed to
     43px and names were losing their tail to the line below; how narrow it got
     depended on the window, which is why it came and went with the resolution.

     Opting these two spans out puts the real width of the name back into the
     min-content calculation, so the column is sized to hold it. Measured
     across 1280-2300px of table width: no name wraps, and the table still fits
     without a horizontal scrollbar. If a stage name ever does get long enough
     to need one, .fb-scroll is already there to take it — a scrollbar is the
     right answer, half a name on the next line is not. */
  .agent-name, .agent-name + .agent-real { white-space: nowrap; }
  /* Same rule for the list in Agents Involved: it may wrap between names, at
     the comma, but never inside one. */
  .agent-inline { white-space: nowrap; }
  .agent-name { font-weight: 800; font-size: 14px; }
  /* An agent keeps the same colour in every table, so a row can be placed
     without reading the name. Assigned from the name itself rather than a
     fixed list, so a new agent gets a colour without anyone editing this. */
  /* Val's palette. Two things were wrong with the old one: ag-c0 was the
     accent teal, so whoever sorted first wore the same colour as every header
     and button on the page; and moving that agent only handed the teal to the
     next one, because the colour comes from position in the sorted list.

     Ordered by separation rather than by name, so the agents who exist get the
     eight most distinct colours and only a very large team reaches the ones
     that start to resemble each other. Hexes are Val's own; the ones marked
     lifted keep his hue at a lightness that can actually be read on this
     background — the originals are 600/700 weights meant for a light page and
     ran as low as 2.5:1 here. */
  .ag-c0  { color: #0EA5E9; }   /* Sky Blue                      */
  .ag-c1  { color: #c86a45; }   /* Burnt Sienna  (lifted, muted) */
  .ag-c2  { color: #9865f1; }   /* Violet        (lifted)        */
  .ag-c3  { color: #EAB308; }   /* Golden Yellow                 */
  .ag-c4  { color: #74849b; }   /* Slate         (lifted)        */
  .ag-c5  { color: #497dee; }   /* Cobalt Blue   (lifted)        */
  .ag-c6  { color: #e34f4f; }   /* Crimson       (lifted)        */
  .ag-c7  { color: #D97706; }   /* Amber                         */
  .ag-c8  { color: #F97316; }   /* Orange                        */
  .ag-c9  { color: #7b74ec; }   /* Indigo        (lifted)        */
  .ag-c10 { color: #06B6D4; }   /* Cyan                          */
  .ag-c11 { color: #e96616; }   /* Chocolate     (lifted)        */
  .ag-c12 { color: #EF4444; }   /* Vermilion                     */
  .ag-c13 { color: #597ee9; }   /* Royal Blue    (lifted)        */
  .ag-c14 { color: #9867e4; }   /* Deep Purple   (lifted)        */
  /* The person, not the handle: deliberately quiet so the handle still reads
     as the name and the colour stays the thing that identifies the agent. */
  .agent-name + .agent-real { color: var(--text-faint); font-weight: 600; }
  /* Not one of the rotating agent colours — it is the absence of an agent. */
  .ag-none { color: var(--amber); font-style: italic; }
  .cell-sub { display: block; font-size: 11.5px; color: var(--text-faint); margin-top: 3px; font-family: var(--font-display); }
  .cell-native { display: block; font-size: 11.5px; color: var(--text-dim); margin-top: 3px; font-family: var(--font-mono); }

  .kpi-tag {
    font-size: 10px; font-weight: 600; padding: 2px 6px; border-radius: 5px;
    display: inline-block; white-space: nowrap;
    background: #1f232c; border: 1px solid var(--border);
  }
  .kpi-tag.upsell { border-color: #276f63; color: var(--upsell); }
  .kpi-tag.push { border-color: #42588c; color: var(--push); }
  .kpi-tag.decline { border-color: #99413c; color: var(--decline); }
  .kpi-tag.ticket { border-color: #8a5a2c; color: var(--declineticket); }
  .kpi-tag.unknown { border-color: #3a3f52; color: var(--text-dim); }
  /* One claim, two offers. Outlined like every other tag, in the violet the
     report already uses for a manual intervention. */
  .split-tag {
    font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 4px;
    display: inline-block; white-space: nowrap; letter-spacing: 0.04em;
    background: #1f232c; border: 1px solid #6d5fa6; color: #c4b5fd;
  }

  .status-tag { font-size: 12px; font-weight: 700; padding: 3px 9px; border-radius: 4px; display: inline-block; white-space: nowrap; }
  .status-tag.onchat { background: rgba(112,206,190,0.14); color: #70cebe; }
  .status-tag.cross { background: rgba(108,158,248,0.14); color: var(--push); }
  .status-tag.pending { background: rgba(139,146,163,0.1); color: var(--text-dim); }
  .status-tag.review { background: rgba(232,200,74,0.16); color: var(--amber); }

  /* Successful control */
  /* Centred like every other column. flex-start was pinning the pill and the
     tick/cross to the left edge no matter what the cell's text-align said. */
  .succ-cell { display: flex; align-items: center; justify-content: center; min-height: 26px; }
  /* Successful and Feedback used to be pulled back to the middle by a rule of
     their own, because every other cell top-aligned. Every cell is centred
     now, so that rule is gone — but it spanned two selector lines, and taking
     only the second one left this one dangling on a trailing comma, which
     silently glued the two <td>s onto .succ-pill below and made the cells
     themselves inline-block. Hence the wrecked column. */
  .succ-pill {
    font-size: 12px; font-weight: 700; padding: 3px 8px; border-radius: 4px;
    display: inline-block; white-space: nowrap; letter-spacing: 0;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  }
  .succ-pill.yes { background: #1f232c; border: 1px solid #276f63; color: var(--accent); padding: 2px 7px; }
  .succ-pill.no { background: #1f232c; border: 1px solid #99413c; color: var(--decline); padding: 2px 7px; }
  /* Outlined like the Share Case button rather than a block of colour: dark
     base, amber border, amber label. The padding gives back the pixel the
     border takes, so the pill is exactly the size it was. */
  .succ-pill.review, .succ-pill.undecided {
    background: #1f232c; border: 1px solid #7a6a24; color: var(--amber); padding: 2px 7px;
  }
  /* Grey, not amber: nothing is being asked of anyone yet. */
  .succ-pill.notclaimed { background: #1f232c; border: 1px solid #3a3f52; color: var(--text-dim); padding: 2px 7px; }
  .cross-note { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 4px;
    background: rgba(139,146,163,0.10); color: var(--text-dim); white-space: nowrap; }
  /* Red: this one cannot be decided until the link is fixed. */
  .succ-pill.unclassified { background: #1f232c; border: 1px solid #8a3a34; color: #ff8a80; padding: 2px 7px; }

  .deposit-time-tag {
    font-size: 10px; font-weight: 600; background: #1f232c; border: 1px solid var(--border);
    padding: 2px 7px; border-radius: 5px; display: inline-block; white-space: nowrap;
  }
  .deposit-time-tag.dt-teal { color: #70cebe; border-color: #2a6f65; }
  .deposit-time-tag.dt-green { color: #42c1ac; border-color: #276f63; }
  .deposit-time-tag.dt-yellow { color: #e8c84a; border-color: #8a762c; }
  .deposit-time-tag.dt-orange { color: #f2a14c; border-color: #95622a; }
  .deposit-time-tag.dt-red { color: #f2746c; border-color: #99413c; }
  .deposit-time-tag.na { color: var(--text-faint); border-style: dashed; font-weight: 400; }

  .dorm-tag { font-size: 12px; font-weight: 700; padding: 3px 9px; border-radius: 5px; display: inline-block; white-space: nowrap; }
  .dorm-d0 { color: #f2746c; background: #1f232c; border: 1px solid #99413c; }
  .dorm-d1 { color: #8b92a3; background: #1f232c; border: 1px solid #3a3f52; }
  .dorm-d2 { color: #c4b5fd; background: #1f232c; border: 1px solid #6d5fa6; }
  .dorm-d3 { color: #a78bfa; background: #1f232c; border: 1px solid #7c5cbf; }
  .dorm-d4 { color: #b464f5; background: #1f232c; border: 1px solid #5b21b6; }
  .dorm-na { color: var(--text-faint); font-style: italic; font-size: 10px; }

  .rate-main { font-weight: 700; }
  .rate-breakdown { font-size: 9px; color: var(--purple); display: block; margin-top: 1px; }
  .provisional { opacity: 0.42; }
  .provisional-mark { font-size: 9px; color: var(--amber); display: block; margin-top: 1px; }

  .fb-dot[data-act] { cursor: pointer; }
  .fb-dot[data-act]:hover { filter: brightness(1.25); }
  .fb-dot {
    display: inline-flex; align-items: center; gap: 4px; font-size: 9.5px;
    font-weight: 700; padding: 2px 7px; border-radius: 4px; white-space: nowrap;
  }
  .fb-dot.has { background: rgba(66,193,172,0.13); color: var(--accent); }
  .fb-dot.await { background: rgba(232,200,74,0.16); color: var(--amber); }
  .fb-dot.none { background: rgba(139,146,163,0.08); color: var(--text-faint); }

  .link-btns { display: flex; gap: 4px; flex-wrap: wrap; min-width: 0; max-width: 100%; }
  table.main td .link-btns > * { max-width: 100%; }
  table.main .act-groups { min-width: 0; max-width: 100%; }
  .link-btn {
    font-size: 11.5px;
    font-size: 9.5px; font-weight: 600; padding: 4px 7px; border-radius: 5px;
    border: 1px solid var(--border); background: #1f232c; color: var(--text-dim);
    cursor: pointer; white-space: nowrap; text-decoration: none; display: inline-block;
    font-family: inherit; line-height: 1.3;
  }
  .link-btn:hover { border-color: var(--text-faint); color: var(--text); }
  .link-btn.player:hover { border-color: var(--accent-dim); color: var(--accent); }
  .link-btn.offer:hover { border-color: #3b5a99; color: var(--push); }
  .link-btn.claimed:hover { border-color: #2a6f65; color: #70cebe; }
  .link-btn.details:hover { border-color: #6d5fa6; color: var(--purple); }
  /* Plain until there is something to see. Amber means "waiting on the agent",
     green means "dealt with" — a button that is amber by default makes both of
     those meaningless. */
  .link-btn.fb { border-color: var(--border); color: var(--text-dim); }
  .link-btn.fb:hover { background: rgba(255,255,255,0.06); color: var(--text); }
  .link-btn.fb.has { border-color: var(--accent-dim); color: var(--accent); }
  .link-btn.fb.has:hover { background: rgba(66,193,172,0.12); }
  .link-btn.disabled { opacity: 0.3; cursor: not-allowed; }

  .date-time-cell { font-family: var(--font-mono); font-size: 13px; color: var(--text); }

  /* ---------- DATE & TIME: THE TWO STAMPS ----------
     One case has two moments — the chat the offer went out on and the chat it
     was claimed on — and reading which was which off their position never
     worked. Each is labelled, in the same outlined style as Share Case and
     Feedback: amber is the offer, green is the claim. The two stamps say On
     Chat, Cross Chat and Pending on their own, so nothing repeats a tag that
     Deposited (Time) or Successful already carries. */
  /* Stacked, not side by side. The column is the narrowest in the table and
     the stamp already wrapped to two lines in it; a label sitting beside that
     pushed the cell past its own width and over the Agent column. The tag goes
     above its stamp instead, which fits the column it has always had. */
  .tl { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
  .tl-item { min-width: 0; }
  .tl-tag {
    font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 4px;
    border: 1px solid var(--border); background: #1f232c; color: var(--text-dim);
    font-family: var(--font-display); line-height: 1.3; display: inline-block;
    white-space: nowrap; letter-spacing: 0.05em; margin-bottom: 3px;
  }
  .tl-tag.offer { border-color: #7a6a24; color: var(--amber); }
  .tl-tag.claim { border-color: var(--accent-dim); color: var(--accent); }
  .tl-tag.claim.none { border-color: #3a3f52; color: var(--text-faint); }
  .tl-stamp { font-family: var(--font-mono); font-size: 13px; color: var(--text); line-height: 1.35; }
  .tl-stamp.faint { color: var(--text-faint); font-style: italic; font-size: 12px; }
  /* Wraps between words, never inside one — "same conversat/ion" was the cell
     being narrower than a single word could sit in. */
  .tl-note { font-size: 11px; color: var(--text-faint); margin-top: 2px; line-height: 1.35; overflow-wrap: normal; word-break: normal; }
  .tl-gap { font-size: 10.5px; color: var(--purple); line-height: 1.2; }
  .search-input { width: 100%; }
  .search-input::placeholder { color: var(--text-faint); }

  /* ---------- IMPORT VIEW ---------- */
  .import-grid { display: grid; grid-template-columns: 1.25fr 1fr; gap: 16px; align-items: start; }
  .dropzone {
    border: 2px dashed var(--border); border-radius: 12px; padding: 40px 24px;
    text-align: center; cursor: pointer; background: var(--bg-panel-alt); transition: all 0.15s ease;
  }
  .dropzone:hover, .dropzone.drag { border-color: var(--accent-dim); background: rgba(66,193,172,0.05); }
  .dropzone-icon { font-size: 34px; margin-bottom: 10px; opacity: 0.65; }
  .dropzone-text { font-size: 15px; font-weight: 600; margin-bottom: 6px; }
  .dropzone-text-sub { font-size: 12px; color: var(--text-faint); line-height: 1.6; max-width: 520px; margin: 0 auto; }
  .import-stat-row {
    display: flex; justify-content: space-between; gap: 10px; padding: 8px 0;
    border-bottom: 1px solid #1f232c; font-size: 12.5px; color: var(--text-dim);
  }
  .import-stat-row:last-child { border-bottom: none; }
  .import-stat-row b { font-family: var(--font-mono); color: var(--text); }
  .btn {
    background: #1f232c; border: 1px solid var(--border); color: var(--text);
    padding: 8px 14px; border-radius: 6px; font-size: 12.5px; cursor: pointer;
    font-family: inherit; font-weight: 600; white-space: nowrap;
  }
  .btn:hover { border-color: var(--text-faint); }
  .btn.primary { background: rgba(66,193,172,0.14); border-color: var(--accent-dim); color: var(--accent); }
  .btn.primary:hover { background: rgba(66,193,172,0.22); }
  .btn.warn { border-color: #7a6a24; color: var(--amber); }
  .btn.warn:hover { background: rgba(232,200,74,0.12); }
  .btn.danger { border-color: #7d3b37; color: var(--decline); }
  .btn.danger:hover { background: rgba(242,116,108,0.12); }
  .btn-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }

  table.fx { width: 100%; border-collapse: collapse; font-size: 12px; }
  table.fx th {
    text-align: left; font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-faint); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--border);
  }
  table.fx td { padding: 4px 8px; border-bottom: 1px solid #1f232c; }
  table.fx input {
    width: 100%; padding: 5px 8px; font-size: 12px; font-family: var(--font-mono); text-align: right;
  }
  .fx-code { font-family: var(--font-mono); font-weight: 700; font-size: 12px; }
  .fx-seen { font-size: 9.5px; color: var(--accent); font-weight: 700; }
  .fx-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0 22px; }

  .log-box {
    background: #10131a; border: 1px solid var(--border); border-radius: 8px;
    padding: 12px 14px; font-family: var(--font-mono); font-size: 11.5px;
    max-height: 320px; overflow-y: auto; line-height: 1.7; color: var(--text-dim);
  }
  .log-line.ok { color: var(--accent); }
  .log-line.warn { color: var(--amber); }
  .log-line.err { color: var(--decline); }

  /* ---------- MODAL ---------- */
  .modal-backdrop {
    display: none; position: fixed; inset: 0; background: rgba(6,8,12,0.78);
    z-index: 500; padding: 24px 20px; overflow-y: auto;
    overscroll-behavior: contain;   /* the page behind a dialog stays put */
  }
  /* flex + margin:auto centres a short dialog and still lets a tall one
     scroll from its top edge, which align-items:center would clip. */
  .modal-backdrop.open { display: flex; }
  .modal-backdrop.open > .modal { margin: auto; }
  .modal {
    max-width: 940px; margin: 0 auto; background: var(--bg-panel);
    border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--lift-3);
  }
  .modal.narrow { max-width: 620px; }
  .modal-head {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
    padding: 18px 22px; border-bottom: 1px solid var(--border);
  }
  .modal-title { font-size: 16px; font-weight: 700; }
  .modal-subtitle { font-size: 12px; color: var(--text-dim); margin-top: 3px; }
  /* Red, like every other way out of a window. Centred as a flex box rather
     than left to line-height: the glyph is text, and a bare line-height only
     ever gets it roughly onto the middle. */
  .modal-close {
    background: none; border: 1px solid #7d3b37; color: var(--decline);
    width: 30px; height: 30px; border-radius: 6px; cursor: pointer; font-size: 15px;
    font-family: inherit; flex-shrink: 0; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    /* The glyph hangs off a baseline, so its ink sits 1.5px below the middle of
       the line box even when the box itself is centred. Bottom padding lifts the
       centred content by half of it, which puts the ink on the true centre. */
    padding: 0 0 3px;
  }
  .modal-close:hover { background: rgba(242,116,108,0.12); border-color: var(--decline); color: var(--decline); }
  .modal-body { padding: 18px 22px 22px; }
  .modal-foot {
    padding: 14px 22px; border-top: 1px solid var(--border);
    display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap;
  }

  /* ---------- THE FEEDBACK WINDOW, TIGHTENED ----------

     It is the tallest dialog in the report — status, who it is for, the
     verdict, impact, case details, case links and two note boxes — and on a
     laptop it ran past the bottom of the screen and had to be scrolled.

     Nothing is taken out here and no wording changes; the padding, the label
     margins and the note boxes are simply pulled in. Scoped to this one
     dialog so Details, Relink and the drill popups keep their own spacing. */
  #feedbackBackdrop .modal-head { padding: 13px 20px; }
  #feedbackBackdrop .modal-body { padding: 12px 20px 14px; }
  #feedbackBackdrop .modal-foot { padding: 11px 20px; }
  #feedbackBackdrop .field-label { margin: 10px 0 5px; }
  #feedbackBackdrop .fb-textarea { min-height: 84px; }
  #feedbackBackdrop .fb-opt { padding: 8px 12px; }
  #feedbackBackdrop .fb-choice { margin-bottom: 4px; }
  #feedbackBackdrop .impact-box { padding: 9px 12px; gap: 7px 14px; }
  #feedbackBackdrop .fb-case-box { padding: 9px 11px; }
  #feedbackBackdrop .note-block { margin-top: 4px; }
  #feedbackBackdrop .fb-agent-picks label { padding: 5px 10px; }

  /* The pill, the waiting time and the "waiting for ..." line are three items
     in one flex row here, and .fb-age carries a margin-top for the stacked
     layout it has in the Status column. It has no business nudging this one
     off the baseline of the two beside it. */
  #fbStatusRow .fb-age { margin-top: 0; }

  .det-section { margin-bottom: 18px; }
  .det-section:last-child { margin-bottom: 0; }
  .det-head {
    font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.07em; font-weight: 800;
    color: var(--accent); margin-bottom: 9px; padding-bottom: 6px; border-bottom: 1px solid var(--border);
  }
  .det-head.blue { color: var(--push); }
  .det-head.purple { color: var(--purple); }
  .det-head.amber { color: var(--amber); }
  .det-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 9px 18px; }
  .det-item { min-width: 0; }
  .det-label { font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); font-weight: 600; }
  .det-value { font-size: 12.5px; margin-top: 2px; overflow-wrap: anywhere; }
  .det-value.mono { font-family: var(--font-mono); }
  .det-value a { color: var(--push); text-decoration: none; }
  .det-value a:hover { text-decoration: underline; }
  .det-value.dim { color: var(--text-faint); }
  .det-note {
    background: #10131a; border: 1px solid var(--border); border-radius: 7px;
    padding: 10px 12px; font-size: 12.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere;
  }
  .tag-cloud { display: flex; flex-wrap: wrap; gap: 5px; }
  .tag-chip {
    font-size: 10px; padding: 2px 7px; border-radius: 4px;
    background: #1f232c; border: 1px solid var(--border); color: var(--text-dim);
  }

  .fb-choice { display: flex; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
  .fb-opt {
    flex: 1; min-width: 120px; padding: 11px 12px; border-radius: 8px; cursor: pointer;
    border: 1px solid var(--border); background: var(--bg-panel-alt); text-align: center;
    font-size: 12.5px; font-weight: 700; font-family: inherit; color: var(--text-dim);
  }
  .fb-opt:hover { border-color: var(--text-faint); color: var(--text); }
  .fb-opt.sel-yes { background: rgba(66,193,172,0.16); border-color: var(--accent-dim); color: var(--accent); }
  .fb-opt.sel-no { background: rgba(242,116,108,0.16); border-color: #99413c; color: var(--decline); }
  .fb-opt.sel-un { background: rgba(232,200,74,0.14); border-color: #6e5f22; color: var(--amber); }
  .fb-textarea { width: 100%; min-height: 130px; resize: vertical; line-height: 1.6; background: var(--bg-sunk); }
  .field-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); font-weight: 700; margin: 14px 0 7px; }

  /* Five figures across one row. minmax(0,1fr) lets them shrink together
     rather than one long value forcing a wrap. */
  .fb-agent-picks { display: flex; flex-wrap: wrap; gap: 8px; }
  .fb-agent-picks label {
    display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
    border: 1px solid var(--border); background: var(--bg-panel-alt);
    padding: 6px 11px; border-radius: 999px; font-size: 12.5px; user-select: none;
  }
  .fb-agent-picks label:hover { border-color: var(--accent-dim); }
  /* Same reason as .agent-name — a chip may be pushed to the next row, but the
     name inside it does not break. */
  .fb-agent-picks label { white-space: nowrap; }
  .fb-agent-picks input { width: auto; height: auto; margin: 0; }
  .fb-agent-picks label.on { border-color: var(--accent-dim); color: var(--accent); background: rgba(66,193,172,0.1); }
  .fb-agent-picks .on-day { font-size: 10.5px; color: var(--text-faint); }
  .fb-case-box {
    background: var(--bg-panel-alt); border: 1px solid var(--border);
    border-radius: 8px; padding: 11px 12px;
  }
  .fb-case-box.sunk { background: var(--bg-sunk); }
  /* Three figures, centred over their labels, the way the Case Details facts
     below them are. Scoped to this box so the relink preview, which shares the
     class, keeps its own layout. */
  #feedbackImpact > div { text-align: center; }
  .fb-amber { color: var(--amber); }
  .fb-facts {
    display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px;
  }
  .fb-fact { min-width: 0; text-align: center; display: flex; flex-direction: column; }
  /* Labels and values each get a fixed band, so the row stays symmetrical
     whatever a cell contains — a long native-currency figure, or a value that
     wraps. The labels were shortened to fit on one line, but the bands are
     what stop a future rename from breaking the alignment again. */
  .fb-fact .l {
    font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-faint); font-weight: 700; line-height: 1.25;
    /* Top-aligned, not bottom: a label that wraps then begins on the same
       line as the ones that do not, which is what reads as aligned. The band
       keeps the values level underneath regardless. */
    min-height: 26px; display: flex; align-items: flex-start; justify-content: center;
  }
  .fb-fact .v {
    font-size: 13px; font-family: var(--font-mono); font-weight: 700;
    /* break-word, not anywhere: "anywhere" split Unclassified as "Unclassifie"
       + "d", dropping one letter onto its own line. This only breaks a word
       when it genuinely cannot fit, and text values get a smaller size so
       they usually do not have to. */
    margin-top: 6px; overflow-wrap: break-word; line-height: 1.3;
    min-height: 32px; display: flex; flex-direction: column;
    align-items: center; justify-content: flex-start;
  }
  .fb-fact .n { display: block; font-size: 10px; color: var(--text-faint); font-family: var(--font-mono); font-weight: 600; }
  .fb-fact.txt .v { font-size: 11.5px; font-family: var(--font-display); }
  /* The case this all belongs to, named before the numbers describing it. */
  .fb-case-head {
    display: flex; align-items: baseline; justify-content: center; gap: 10px;
    flex-wrap: wrap; margin-bottom: 15px; text-align: center;
  }
  .fb-case-head .who { font-size: 15px; font-weight: 800; color: var(--text); }
  .fb-case-head .when { font-size: 12.5px; font-family: var(--font-mono); color: var(--text-dim); }
  .fb-links { justify-content: center; margin-top: 2px; }
  .fb-links .act-groups { width: 100%; }
  .fb-links .link-btns { justify-content: center; }
  /* Green: these open the actual conversations, which is the useful thing to
     reach for in this window. */
  /* No colour override here: these are the same buttons as the Case Links
     column, so they should look the same. Painting every one green made the
     popup disagree with the table and drowned out Open All, which is the one
     that is meant to stand out. */
  @media (max-width: 620px) { .fb-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  .impact-box {
    background: var(--bg-panel-alt); border: 1px solid var(--border); border-radius: 8px;
    padding: 12px 14px; display: grid; grid-template-columns: repeat(auto-fit, minmax(120px,1fr)); gap: 10px 16px;
  }

  /* ---------- FOOTNOTE ---------- */
  .footnote {
    margin-top: 24px; padding-top: 18px; border-top: 1px solid var(--border);
    font-size: 11.5px; color: var(--text-faint); line-height: 1.8;
  }
  .footnote b { color: var(--text-dim); }
  .legend-title {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-dim); font-weight: 700; margin-bottom: 12px;
  }
  .legend-row { padding: 7px 0; border-bottom: 1px solid #1a1e26; }
  .legend-row:last-child { border-bottom: none; }
  .legend-swatch { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin: 0 3px 0 8px; vertical-align: middle; }
  .legend-swatch.dt-teal { background: #70cebe; }
  .legend-swatch.dt-green { background: #42c1ac; }
  .legend-swatch.dt-yellow { background: #e8c84a; }
  .legend-swatch.dt-orange { background: #f2a14c; }
  .legend-swatch.dt-red { background: #f2746c; }

  .empty-state { text-align: center; padding: 60px 20px; color: var(--text-faint); }
  .empty-state h2 { font-size: 17px; color: var(--text-dim); font-weight: 600; margin-bottom: 8px; }
  .empty-state p { font-size: 13px; max-width: 480px; margin: 0 auto 18px; line-height: 1.7; }

  /* ---------- AUTH ---------- */
  #authGate {
    position: fixed; inset: 0; z-index: 900; display: flex;
    align-items: center; justify-content: center; padding: 24px;
    background:
      radial-gradient(circle at 20% 0%, #1b2530 0%, transparent 55%),
      radial-gradient(circle at 80% 100%, #1a2028 0%, transparent 55%),
      var(--bg);
  }
  #authGate.hidden { display: none; }
  .auth-card {
    width: 100%; max-width: 400px; background: var(--bg-panel);
    border: 1px solid var(--border); border-radius: 14px; padding: 30px 28px;
    box-shadow: 0 24px 70px rgba(0,0,0,0.55);
  }
  .auth-brand { font-size: 19px; font-weight: 800; letter-spacing: -0.01em; margin-bottom: 5px; }
  .auth-brand span { color: var(--accent); }
  .auth-sub { font-size: 12.5px; color: var(--text-dim); margin-bottom: 22px; line-height: 1.6; }
  .auth-field { margin-bottom: 13px; }
  .auth-field label {
    display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-faint); font-weight: 700; margin-bottom: 6px;
  }
  .auth-field input { width: 100%; padding: 10px 12px; font-size: 13.5px; }
  .auth-btn {
    width: 100%; margin-top: 8px; padding: 11px; border-radius: 7px; cursor: pointer;
    background: rgba(66,193,172,0.16); border: 1px solid var(--accent-dim); color: var(--accent);
    font-size: 13.5px; font-weight: 700; font-family: inherit;
  }
  .auth-btn:hover { background: rgba(66,193,172,0.26); }
  /* Only until boot has wired the form — see the note on #authForm. */
  .auth-btn:disabled {
    background: rgba(139,146,163,0.08); border-color: var(--border);
    color: var(--text-faint); cursor: default;
  }
  .auth-btn:disabled:hover { background: rgba(139,146,163,0.08); }
  .auth-error {
    margin-top: 13px; font-size: 12px; color: var(--decline);
    background: rgba(242,116,108,0.09); border: 1px solid #7d3b37;
    border-radius: 7px; padding: 9px 11px; display: none;
  }
  .auth-error.show { display: block; }
  .auth-note { margin-top: 18px; font-size: 11px; color: var(--text-faint); line-height: 1.65; }
  #appRoot.locked { display: none; }

  .user-chip {
    display: inline-flex; align-items: center; gap: 7px; font-size: 11.5px; font-weight: 700;
    padding: 5px 11px; border-radius: 999px; background: var(--bg-panel-alt);
    border: 1px solid var(--border); color: var(--text-dim); white-space: nowrap;
  }
  .user-chip .role-dot { width: 7px; height: 7px; border-radius: 50%; }
  .role-admin { background: var(--accent); }
  .role-agent { background: var(--push); }
  .role-viewer { background: var(--text-faint); }
  /* Red: this is the one control up here that throws work away. */
  .signout-btn {
    background: none; border: 1px solid #7d3b37; color: var(--decline);
    font-size: 11px; padding: 5px 10px; border-radius: 6px; cursor: pointer; font-family: inherit;
  }
  .signout-btn:hover { background: rgba(242,116,108,0.12); border-color: var(--decline); }

  /* ---------- FROZEN HEADERS + INNER SCROLL ---------- */
  /* overflow-x used to be hidden, which is what turned a narrow window into a
     wreck rather than a scroll. table.main is table-layout:fixed, so its
     fourteen columns divide whatever width they are given — at 768px that is
     50px a column. The cells that can shrink did; the ones that cannot, above
     all the agent name, ran straight out of their cell and over the column
     next door, and hidden meant you could neither see nor reach what was
     under it. Measured spill on the agent name: 4px at 1366, 9px at 1280,
     24px at 1024, 40px at 768. */
  /* overscroll-behavior: contain is what stops the wheel handing off to the
     page the moment a table reaches its last row. Without it the browser
     chains the leftover scroll to whatever is behind, the page jumps, and
     because the wheel then stays latched to that scroller the table ignores
     you until the pointer crosses onto a different row. Contain keeps the
     gesture where the pointer is. */
  .table-scroll {
    max-height: calc(100vh - 200px); min-height: 320px; overflow-y: auto; overflow-x: auto;
    overscroll-behavior: contain;
    border: 1px solid var(--border); border-radius: 8px; background: #0D0F14;
  }
  /* A table that grows with its content is not a scroller, so it hands the
     wheel on rather than swallowing it. */
  .table-scroll[style*="max-height:none"],
  #view-users .table-scroll { overscroll-behavior: auto; }
  /* The floor the columns stop shrinking at — the same 1696px the table
     settles to inside a full-width page-wrap, which is the width it was laid
     out for. Below this the box scrolls sideways instead of crushing the
     content. Zero spill at every size measured from 2560 down. .fb-wide is
     excluded: it sizes to content and has always had its own scroller. */
  /* 1680, not 1690, and the ten pixels matter.

     A full day of rows makes the box scroll vertically, and that scrollbar
     takes about twelve pixels out of the container — so at full width the
     container is ~1684 while the table was pinned at 1690. Six pixels of
     overflow: enough for a horizontal scrollbar to appear, not enough for it
     to reveal anything. A bar that says there is more to see when there is not.

     1680 clears the gutter. The ten pixels come back out of the numeric
     columns by giving Agent a slightly larger share below, because it is the
     only column holding something that cannot wrap. */
  table.main:not(.fb-wide) { min-width: 1680px; }
  table.main:not(.fb-wide) th:nth-child(2) { width: 9%; }
  /* Same idea, much smaller table. Users and Sessions are six fixed columns
     sized in percentages, so on a narrow window the Actions column drops to
     around 55px while still holding Remove, Test sign-in and Sign out — and a
     .link-btn does not wrap. Measured spill there: 4px at a 500px container,
     17px at 400, 28px at 320. Clean at 560; 600 leaves room for a longer
     username or stage name. */
  table.users-table { min-width: 600px; }
  /* The same floor for the other two wide tables, so all three behave alike.

     Without one, an auto-layout table has no reason to stop shrinking: on the
     Feedback page the Date & Time column fell to 32px and broke the timestamp
     over six lines, and Case Links stacked one button per row. The breakdown
     never did that only because table.main has a floor. These two numbers are
     chosen so the shared columns land where the breakdown's do — Date & Time
     92px against its 97px, Player 98px against 97px — and below that the box
     scrolls instead of squeezing.

     Feedback needs the larger number: sixteen columns rather than eleven. */
  table.fb-wide { min-width: 2100px; }
  table.dupes { min-width: 1600px; }
  /* The Feedback page carries the breakdown's 14 columns plus its own two, so
     it is the one page that needs the whole window rather than the 1780px
     column the rest of the report reads better in. */
  #view-feedback .page-wrap { max-width: none; padding: 0 14px; }
  .fb-scroll { overflow-x: auto; }          /* only bites on a narrow laptop */
  /* Both panels on this page carry a table, and they used to be max-height:none
     — so each grew to whatever its rows needed, the page itself did the
     scrolling, the frozen headers scrolled away with it, and you could never
     have both tables on screen at once. Half the viewport each, less the
     chrome around them, so the two are the same size, both visible, and each
     scrolls inside itself under its own frozen header. */
  /* height, not max-height, and that is the whole point of it: with a maximum
     the shorter table just collapses to its rows — 375px against 250px when
     Missed Opportunities has two entries and Attempts has thirty — and the two
     panels stop matching. A fixed height holds both boxes the same whatever
     is in them. */
  #view-sessions > .page-wrap {
    display: flex; flex-direction: column;
    height: calc(100vh / var(--z, 1) - var(--topbar-h, 57px) - 62px);
    padding-bottom: 10px;
  }
  #view-sessions #sessionsBody {
    display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto;
  }
  #view-sessions #sessionsBody > .panel {
    display: flex; flex-direction: column; min-height: 0;
  }
  #view-sessions #sessionsBody > .panel:first-child { flex: 1 1 0; }
  #view-sessions #sessionsBody > .panel:last-child  { flex: 2 1 0; }
  /* min-height:0 so the flex share decides the height rather than the 320px
     floor every other table keeps. */
  #view-sessions .table-scroll {
    flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto;
  }
  #view-sessions .footnote { flex: 0 0 auto; margin-top: 8px; padding-top: 8px; }
  #view-sessions .header { margin-bottom: 10px; padding-bottom: 10px; }
  #view-sessions #sessionsBody > .panel { padding: 10px 14px; margin-bottom: 10px; }
  #view-sessions .panel-sub { margin-bottom: 7px; }

  #view-feedback > .page-wrap {
    display: flex; flex-direction: column;
    /* The nav also carries a 22px bottom margin, which is page height spent
       before this element starts. */
    height: calc(100vh / var(--z, 1) - var(--topbar-h, 57px) - 62px); padding-bottom: 10px;
  }
  /* min-height:0 on both, or a flex item refuses to shrink below its content
     and the overflow comes straight back. */
  #view-feedback > .page-wrap > .panel {
    display: flex; flex-direction: column; min-height: 0; flex: 1 1 0;
  }
  /* 280px is a header plus three rows. On a normal screen the flex share is
     larger and this never binds; on a short one it holds three rows visible
     rather than letting both tables shrink to a sliver. */
  #view-feedback .fb-scroll { flex: 1 1 auto; height: auto; min-height: 280px; max-height: none; }
  /* The chrome above the tables spends less of the page on padding so the rows
     get it instead. Only on this page — every other view keeps its spacing. */
  #view-feedback .header { margin-bottom: 10px; padding-bottom: 10px; }
  #view-feedback .header .sub { margin-top: 2px; }
  #view-feedback .filters { margin-bottom: 10px; padding: 9px 16px; }
  #view-feedback #fbExportBtn { margin-left: auto; }
  /* 38px is what every control in a filter bar is, .reset-btn included, so the
     two buttons line up with the selects beside them rather than sitting a
     couple of pixels short. */
  #view-feedback #fbExportBtn, #view-feedback #fbShareViewBtn { height: 38px; box-sizing: border-box; }
  /* A leading emoji plus a space sat off-centre: the glyph carries its own
     side bearing and the space is whatever the font says. Laid out as flex
     with a fixed gap, the pair is centred on the button's real middle. */
  .share-view-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    height: 38px; box-sizing: border-box;
    /* The emoji glyph does not fill its advance box: it carries 2.6px of empty
       space before its ink, so with equal padding it sat further from the left
       border than the last letter sits from the right. Taking that much off the
       left padding evens what the eye actually sees. */
    padding-left: 11.4px;
  }
  .share-view-btn .btn-ico { line-height: 1; }
  #view-feedback > .page-wrap > .panel { padding: 10px 14px; margin-bottom: 10px; }
  #view-feedback .panel-title { margin-bottom: 2px; }
  #view-feedback .panel-sub { margin-bottom: 7px; }
  #view-feedback .footnote { margin-top: 8px; padding-top: 8px; flex: 0 0 auto; }
  .fb-scroll::-webkit-scrollbar { height: 11px; }
  /* Inherit the breakdown's own size rather than shrinking: these are the
     same rows, so the numbers should be the same size in both places.

     Auto, deliberately. Pinning these columns to the breakdown's fixed 121px
     did make the two pages agree on width, but 121px is far too narrow for the
     Case Links cell here and every link stacked into its own line. Content
     sizing keeps that column wide enough for the buttons to sit in a row. */
  table.fb-wide { table-layout: auto; width: 100%; }
  table.fb-wide td, table.fb-wide th { padding: 9px 6px; white-space: normal; }
  table.fb-wide .link-btns { display: flex; flex-wrap: wrap; gap: 3px; }
  table.fb-wide .link-btn { padding: 3px 7px; font-size: 11px; }
  /* Successful sizes to its pill here rather than to a fourteenth of the table,
     which left it hard up against Feedback next door. A floor under the column
     puts the breathing room back without touching anything else. */
  table.fb-wide th:nth-child(12), table.fb-wide td:nth-child(12) { min-width: 110px; }
  /* The two added columns are the point of the page — give them room and stop
     the note text from squeezing the row layout around it. */
  table.fb-wide th:nth-last-child(2), table.fb-wide td:nth-last-child(2) { width: 17%; min-width: 190px; }
  table.fb-wide th:last-child, table.fb-wide td:last-child { width: 9%; min-width: 128px; }
  .table-scroll::-webkit-scrollbar { width: 11px; }
  .table-scroll::-webkit-scrollbar-track { background: #0D0F14; }
  .table-scroll::-webkit-scrollbar-thumb { background: #333947; border-radius: 6px; border: 2px solid #0D0F14; }
  .table-scroll::-webkit-scrollbar-thumb:hover { background: #414958; }
  table.main thead th {
    position: sticky; top: 0; z-index: 6;
    background: #181B20;
    box-shadow: inset 0 -1px 0 var(--border);
    border-bottom: none;
  }
  /* The attempts table scrolls with the PAGE rather than inside its own box.
     That is the whole trick: a sticky header is bound by its own table, so
     once the page is the scroller the header pins under the filters and then
     lets go by itself on the last row — no scroll listener, no measuring how
     far down the table ends. An inner scrollport would trap it at the top of
     that box instead, which is why max-height and overflow go away here. */
  .table-scroll.page-flow { max-height: none; overflow: visible; }
  .page-flow table.main thead th { top: var(--stick-top, 50px); }
  /* Below this width the table cannot fit and horizontal scrolling matters
     more than a pinned header, so the inner scroller comes back. */
  @media (max-width: 1400px) {
    .table-scroll.page-flow {
      max-height: calc(100vh - 200px); overflow-x: auto; overflow-y: auto;
    }
    .page-flow table.main thead th { top: 0; }
  }

  /* ---------- COLOURED HEADERS ----------
     Every column header gets its own colour so the table reads as groups:
     context (slate) · who/what (teal, blue) · verdict (amber) ·
     money (purple, blue, green) · timing (yellow, violet) · payout (orange, green) */
  table.main th { font-size: 12.5px; letter-spacing: 0.02em; padding: 11px 6px; }
  table.main thead th { box-shadow: inset 0 -1px 0 var(--border), inset 0 3px 0 var(--th-accent, transparent); }
  /* ONE accent for structure; the existing semantic colours stay in the body.
     A different hue per column made the header compete with the badges and
     figures underneath, which are the things that actually carry meaning.

     So: a single continuous teal rule across the whole header, muted grey
     labels, and teal reserved for the money columns — Last Deposit through
     Bonus. Grouping is done with separators instead of colour. */
  table.main thead th { --th-accent: #349a93; color: #9CA5B3; }
  table.main thead th.th-dorm,
  table.main thead th.th-avg,
  table.main thead th.th-tag,
  table.main thead th.th-dep,
  table.main thead th.th-net,
  table.main thead th.th-bonus { color: #55b0a9; }

  /* Four groups — context | money | outcome | actions. The rule falls before
     Last Deposit (4), Successful (12) and Case Links (14); the Feedback page
     adds two columns after these, so the same positions still hold. */
  table.main th:nth-child(4),  table.main td:nth-child(4),
  table.main th:nth-child(12), table.main td:nth-child(12),
  table.main th:nth-child(14), table.main td:nth-child(14) {
    border-left: 1px solid #303641;
  }
  /* Successful and Feedback are one thought — the verdict, and the
     conversation about it — so they sit closer than the money columns they
     follow. Only the inner edges are pulled in; column 12 keeps its left
     padding because the group separator rule draws a border there. */
  table.main th:nth-child(12), table.main td:nth-child(12) { padding-right: 2px; }
  table.main th:nth-child(13), table.main td:nth-child(13) { padding-left: 2px; }

  /* The action button wears the same amber/green as the Status column beside
     it, so the thing you click and the thing you read agree. */
  /* Matched to .link-btn.fb.has above, which is the same weight and would
     otherwise keep its green border and text while only the background went
     amber — the two-tone button. Source order settles the tie. */
  .link-btn.copied, .btn.copied {
    background: rgba(66,193,172,0.24); border-color: var(--accent); color: var(--accent);
    animation: copiedPop .3s ease-out;
  }
  @keyframes copiedPop {
    0%   { transform: scale(0.92); }
    55%  { transform: scale(1.07); }
    100% { transform: scale(1); }
  }
  .fb-cell { display: flex; flex-direction: column; align-items: center; gap: 5px; }
  /* Two visually separate bands: opening conversations vs acting on the row.
     The rule between them is what stops Open All reading as "open Details". */
  .act-groups { display: flex; flex-direction: column; gap: 6px; }
  .act-groups .act-row { padding-top: 6px; border-top: 1px dashed #2a2f3a; }
  /* No bolder than its neighbours — the teal already sets it apart, and the
     extra weight made it measurably wider than the buttons beside it. */
  .link-btn.open-all { border-color: var(--accent-dim); color: var(--accent); }
  .link-btn.open-all:hover { background: rgba(66,193,172,0.16); border-color: var(--accent); }
  /* Outlined, the same way Share Case and Share View are: the dark base with a
     coloured border and label, rather than a solid block of colour. These sit
     in the same cell as those buttons and a filled one read as a different
     kind of control. The colour still says which state it is in. */
  .link-btn.fb.fb-await { border-color: #7a6a24; color: var(--amber); font-weight: 700; }
  .link-btn.fb.fb-await:hover { background: rgba(232,200,74,0.12); border-color: var(--amber); }
  .link-btn.fb.fb-done { border-color: var(--accent-dim); color: var(--accent); font-weight: 700; }
  .link-btn.fb.fb-done:hover { background: rgba(66,193,172,0.12); }

  /* ---------- RATE AS A COLOURED TAG ---------- */
  .rate-tag {
    font-size: 12px; font-weight: 700; padding: 3px 9px; border-radius: 5px;
    display: inline-block; white-space: nowrap;
  }
  .rate-tag.r-top  { color: #70cebe; background: #1f232c; border: 1px solid #2a6f65; }
  .rate-tag.r-high { color: #42c1ac; background: #1f232c; border: 1px solid #276f63; }
  .rate-tag.r-mid  { color: #e8c84a; background: #1f232c; border: 1px solid #8a762c; }
  .rate-tag.r-low  { color: #f2a14c; background: #1f232c; border: 1px solid #95622a; }
  .rate-tag.r-zero { color: var(--text-faint); background: #1f232c; border: 1px solid #3a3f52; }

  /* ---------- BIGGER SUMMARY TYPE ---------- */
  .card-label { font-size: 12.5px; }
  .card-value { font-size: 30px; }
  .card-sub { font-size: 12.5px; }
  .mini-stat-label { font-size: 10.5px; }
  .mini-stat-value { font-size: 17px; }
  .agent-mini-name { font-size: 15px; }
  .amn-bonus { font-size: 14.5px; }
  /* These win over the base rules above — this block is later in the sheet,
     so it is the one that decides. Change sizes HERE. */
  .kpi-box-title { font-size: 23px; }
  .kpi-box-stat-label { font-size: 11px; }
  .kpi-box-stat-value { font-size: 22px; }
  .kpi-box-row { font-size: 16px; }
  .inline-time-row { font-size: 15px; }
  .panel-title { font-size: 17px; }
  /* Same weight as the KPI box headings it sits under. */
  #agentsPanel .panel-title { font-size: 23px; }
  #agentsPanel .panel-sub { font-size: 14px; }
  .panel-sub { font-size: 13.5px; }
  .big-header { font-size: 25px; }
  .combined-totals-divider { font-size: 14px; }

  /* ---------- KPI COMPARISON TABLE ---------- */
  .kpi-table-wrap { margin: 4px 0 14px; }
  table.kpitab { width: 100%; border-collapse: collapse; font-size: 12.5px; table-layout: fixed; }
  /* Frozen like the other two tables. It was the only one you could scroll a
     column heading off the top of. */
  table.kpitab th {
    text-align: right; font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-faint); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--border);
    white-space: nowrap;
    position: sticky; top: 0; z-index: 3; background: #181B20;
  }
  table.kpitab th:first-child { text-align: left; }
  /* Same accent treatment as the Agent Breakdown header, so a column means the
     same thing whichever table you are reading. */
  table.agentcmp thead th { border-bottom: 2px solid var(--th-accent, var(--border)); }
  table.agentcmp thead th.h-agent { --th-accent: #64748b; color: #a3aec0; }
  table.agentcmp thead th.h-att   { --th-accent: #3b5a99; color: #8fb6fb; }
  table.agentcmp thead th.h-suc   { --th-accent: #8a762c; color: #f0d478; }
  table.agentcmp thead th.h-rate  { --th-accent: #95622a; color: #f2b06b; }
  table.agentcmp thead th.h-dep   { --th-accent: #7c5cbf; color: #c4b5fd; }
  table.agentcmp thead th.h-net   { --th-accent: #276f63; color: #63cab7; }
  table.agentcmp thead th.h-bonus { --th-accent: #42c1ac; color: #70cebe; }
  /* A proper target rather than a 10px arrow you have to hunt for — missing it
     used to open the drill popup instead. */
  .agentcmp .kt-expand {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; margin-right: 10px; border-radius: 6px;
    border: 1px solid var(--accent-dim); background: rgba(66,193,172,0.14);
    color: var(--accent); font-size: 11px; cursor: pointer; user-select: none;
    vertical-align: middle;
  }
  .agentcmp .kt-expand:hover { background: rgba(66,193,172,0.3); border-color: var(--accent); }
  .agentcmp .kt-name-toggle { cursor: pointer; }
  .agentcmp .kt-name-toggle:hover { text-decoration: underline; text-underline-offset: 3px; }
  .agentcmp .kt-expand.open { background: var(--accent); color: #0f1115; border-color: var(--accent); }
  table.kpitab td {
    padding: 8px; border-bottom: 1px solid #1f232c; text-align: right;
    font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  }
  table.kpitab td:first-child { text-align: left; font-family: var(--font-display); }
  table.kpitab tr:last-child td { border-bottom: none; }
  table.kpitab tr.kt-total td {
    background: rgba(66,193,172,0.07); font-weight: 800; border-top: 1px solid var(--accent-dim);
    border-bottom: none; color: var(--text);
  }
  table.kpitab tr:hover td { background: rgba(255,255,255,0.025); }
  table.kpitab tr.kt-total:hover td { background: rgba(66,193,172,0.11); }
  .kt-name { display: inline-flex; align-items: center; gap: 7px; font-weight: 700; font-size: 12.5px; }
  .kt-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
  .kt-rev { color: #63cab7; font-weight: 700; }
  /* Colour and weight carry these two, not light. A glow on a figure inside a
     table row competes with the row beside it — these columns are read down,
     compared against each other, and a halo on every cell makes that harder
     rather than easier. The one figure that does glow is the Net Revenue card
     on the dashboard, where it is alone and is the headline. */
  .kt-bonus { color: var(--accent); font-weight: 800; }
  .kt-dim { color: var(--text-faint); }
  .kt-await { color: var(--amber); }
  .kt-bar {
    height: 4px; border-radius: 2px; margin-top: 4px; background: #1f232c; overflow: hidden;
  }
  .kt-bar span { display: block; height: 100%; border-radius: 2px; }

  /* ---------- MONTH TO DATE ----------
     Reads like the Agent KPI Attempts table: every cell centred on both axes,
     and each heading wears the same teal accent bar. Written with table.mtd-table
     so it outweighs the kpitab defaults it sits after. */
  table.mtd-table th, table.mtd-table td,
  table.mtd-table th:first-child, table.mtd-table td:first-child { text-align: center; vertical-align: middle; }
  table.mtd-table thead th {
    color: #9CA5B3; font-weight: 700; font-size: 11px;
    box-shadow: inset 0 -1px 0 var(--border), inset 0 3px 0 var(--th-accent, #349a93);
  }
  table.mtd-table thead th.mtd-h-net, table.mtd-table thead th.mtd-h-bonus { color: #55b0a9; }

  /* ---------- RELINK ---------- */
  /* Yellow with the link mark, the same as Share View and Share List, so every
     control that hands someone a URL reads the same wherever it turns up. */
  .link-btn.share {
    border-color: #7a6a24; color: var(--amber);
    display: inline-flex; align-items: center; justify-content: center; gap: 4px;
    /* Same trick as the larger buttons: the emoji ink starts inside its own
       box, so the left padding gives back what the glyph already carries. The
       label has no descender, so its ink also rides high in the line box.

       Each pair still sums to what .link-btn sets — 12.5px across, 8px down —
       so the button is exactly the size it was; only the glyphs inside move. */
    padding: 3.5px 7.3px 4.5px 5.2px;
  }
  .link-btn.share:hover { background: rgba(232,200,74,0.12); border-color: var(--amber); color: var(--amber); }
  .link-btn.relink { border-color: #8a3a34; color: #ff8a80; }
  .link-btn.relink:hover { background: rgba(242,116,108,0.22); color: #ffb0a8; }
  .relink-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px 16px; }
  .relink-field label {
    display: block; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-faint); font-weight: 700; margin-bottom: 5px;
  }
  .relink-field input, .relink-field select { width: 100%; font-size: 12.5px; padding: 7px 9px; }
  .relink-field .hint { font-size: 10px; color: var(--text-faint); margin-top: 4px; line-height: 1.5; }
  .relink-field .hint.live { color: var(--accent); }
  .override-flag {
    font-size: 9px; font-weight: 800; padding: 1px 5px; border-radius: 3px;
    background: rgba(242,116,108,0.16); color: #ff8a80; margin-left: 4px; vertical-align: middle;
  }

  /* ---------- FEEDBACK NOTES ---------- */
  .note-block { margin-top: 6px; }
  .note-owner {
    font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
    font-weight: 800; margin-bottom: 5px;
  }
  .note-owner.val { color: var(--accent); }
  .note-owner.agent { color: var(--push); }
  .fb-textarea.agent-note { min-height: 100px; }
  .readonly-note {
    background: var(--bg-sunk); border: 1px solid var(--border); border-radius: 7px;
    padding: 10px 12px; font-size: 12.5px; line-height: 1.6; white-space: pre-wrap;
    overflow-wrap: anywhere; color: var(--text-dim); min-height: 46px;
  }
  .fb-dot.agent { background: rgba(108,158,248,0.14); color: var(--push); }
  .fb-dot.both { background: rgba(66,193,172,0.16); color: var(--accent); }

  /* ---------- HEATMAPS ---------- */
  .hm-wrap { overflow-x: auto; padding-bottom: 4px; }
/* The 24h monitor strip reuses table.hm for its cell styling, but wants thin
   rows and 96 columns rather than a chunky grid. */
table.hm.ic-tl { border-spacing: 1px; table-layout: fixed; }
table.hm.ic-tl td.ic-cell { height: 26px; padding: 0; border-radius: 3px; }
table.hm.ic-tl th.row-head { width: 84px; min-width: 84px; font-size: 10.5px; font-weight: 700; color: var(--text-dim); text-align: right; padding-right: 10px; white-space: nowrap; letter-spacing: 0.03em; }
table.hm.ic-tl td[data-icslot] { cursor: pointer; }
table.hm.ic-tl td[data-icslot]:hover { outline: 2px solid var(--accent-dim); outline-offset: -1px; }
table.hm.ic-tl th { font-size: 9px; padding: 0 0 3px; color: var(--text-faint); font-weight: 600; }
.ic-two-col { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: 12px; align-items: start; }
@media (max-width: 1400px) { .ic-two-col { grid-template-columns: minmax(0, 1fr); } }
  table.hm { border-collapse: separate; border-spacing: 2px; font-size: 10.5px; width: 100%; }
  table.hm th {
    font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-faint); font-weight: 700; padding: 3px 2px; text-align: center; white-space: nowrap;
  }
  table.hm th.row-head { text-align: right; padding-right: 8px; color: var(--text-dim); font-size: 10.5px; min-width: 74px; }
  table.hm td {
    text-align: center; border-radius: 4px; padding: 0; height: 27px;
    font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; cursor: default;
    border: 1px solid transparent;
  }
  table.hm td.zero { color: #333947; background: rgba(255,255,255,0.014); }
  table.hm td.peak { border-color: rgba(255,255,255,0.35); }
  table.hm tfoot td, table.hm td.total-cell {
    background: rgba(255,255,255,0.05) !important; color: var(--text-dim); font-weight: 800;
  }
  /* ---------- KPI ATTEMPTS: THE PANEL'S OWN FILTER ----------
     Two segmented switches in the corner of one panel, not another row on the
     page filter bar: they narrow this grid alone and mean nothing to Volume
     or Coverage. Built to sit quietly until one is moved off its default. */
  .hm-panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
  .hm-controls { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; flex: 0 0 auto; }
  .hm-controls-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
  .hm-seg { display: inline-flex; background: var(--bg-sunk); border: 1px solid var(--border); border-radius: 8px; padding: 2px; }
  .hm-seg button {
    appearance: none; border: 0; background: transparent; color: var(--text-dim);
    font-family: inherit; font-size: 11.5px; font-weight: 600; padding: 5px 12px;
    border-radius: 6px; cursor: pointer; white-space: nowrap;
  }
  .hm-seg button:hover { color: var(--text); background: rgba(255,255,255,0.04); }
  .hm-seg button.on { color: #0d1014; background: var(--text-dim); }
  .hm-seg.outcome button.on[data-v="succ"] { background: var(--accent); }
  .hm-seg.outcome button.on[data-v="unsucc"] { background: var(--decline); }
  .hm-seg.side button.on[data-v="offers"] { background: var(--push); }
  .hm-seg.side button.on[data-v="claims"] { background: var(--purple); }
  .hm-seg-label { font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-faint); font-weight: 700; margin-right: 2px; }
  .hm-slice-note { font-size: 11.5px; color: var(--text-faint); text-align: right; max-width: 560px; line-height: 1.5; }
  .hm-slice-note b { color: var(--text-dim); }
  table.hm td.hot { cursor: pointer; }
  table.hm td.hot:hover { outline: 2px solid var(--accent-dim); outline-offset: -1px; }

  .hm-legend { display: flex; align-items: center; gap: 7px; margin-top: 12px; font-size: 11px; color: var(--text-faint); }
  .hm-swatch { width: 26px; height: 12px; border-radius: 3px; }
  .hm-note { font-size: 11.5px; color: var(--text-faint); margin-top: 10px; line-height: 1.6; }

  .mix-row { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
  .mix-label { width: 92px; font-size: 12.5px; color: var(--text-dim); flex-shrink: 0; font-weight: 600; }
  .mix-track { flex: 1; display: flex; height: 22px; border-radius: 5px; overflow: hidden; background: #1f232c; }
  .mix-seg { height: 100%; }
  .mix-total { width: 128px; text-align: right; font-family: var(--font-mono); font-size: 11.5px; color: var(--text-dim); flex-shrink: 0; }

  table.dupes { width: 100%; border-collapse: collapse; font-size: 14px; }
  table.dupes th {
    text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
    color: #9CA5B3; font-weight: 700; padding: 9px 8px;
    border-bottom: none; box-shadow: inset 0 -1px 0 var(--border), inset 0 3px 0 #349a93;
    position: sticky; top: 0; background: #181B20; z-index: 3;
  }
  table.dupes th, table.dupes td { text-align: center; vertical-align: middle; }
  /* Teal on the figures, grey on everything else — the same split the Agent
     KPI Attempts header uses, so the two tables read as one system. */
  table.dupes thead th.num { color: #55b0a9; }
  /* Three groups: who and where | what happened | what to do about it.
     Before Chats (4), Flag (8) and Case Links (11). */
  table.dupes th:nth-child(4),  table.dupes td:nth-child(4),
  table.dupes th:nth-child(8),  table.dupes td:nth-child(8),
  table.dupes th:nth-child(10), table.dupes td:nth-child(10),
  table.dupes th:nth-child(11), table.dupes td:nth-child(11) {
    border-left: 1px solid #303641;
  }
  table.dupes th.num, table.dupes td.num { text-align: center; font-family: var(--font-mono); }
  table.dupes .link-btns { justify-content: center; }
  table.dupes .cell-sub { text-align: center; }
  table.dupes td { padding: 10px 8px; border-bottom: 1px solid #20242B; }
  table.dupes tr.missed td { background: rgba(232,200,74,0.055); }
  table.dupes tr:hover td { background: rgba(255,255,255,0.03); }
  /* ---------- DRILL-DOWN ----------
     Sized so the full breakdown table fits on FHD and 2K without ever
     scrolling sideways: it tracks the viewport rather than a fixed width. */
  #drillBackdrop, #hmDrillBackdrop { z-index: 490; }
  #detailsBackdrop, #feedbackBackdrop, #relinkBackdrop { z-index: 620; }
  .modal.drill { max-width: min(1720px, calc(100vw - 48px)); width: 100%; }
  .drill-body { padding: 14px 18px 18px; }
  .drill-scroll { max-height: calc(100vh - 250px); min-height: 200px; }
  .modal-backdrop { padding: 24px 20px; }

  /* ================== THE DRILL AFFORDANCE ==================
     Every figure on this dashboard opens the rows behind it, and until now
     every single one of them said so the same way:

         [data-drill]:hover { background: rgba(66,193,172,0.09) }

     A flat teal fill of whatever box the figure happened to land in — the
     full width of the table cell, hard square corners, the same slab
     whether it wrapped a six-figure sum or the word "Upsell". Square
     because it had no choice: a <td> inside a border-collapse table ignores
     border-radius outright, so that property could never have rounded there.

     WHAT REPLACES IT is a layer the element owns rather than a property of
     the element. An inset ::before, held 2px in from the cell edges, with a
     7px radius, a soft vertical wash, a hairline accent ring and a little
     teal bloom beneath it, fading in on opacity alone. Three things follow
     from that shape:

       - It rounds where the cell cannot. The radius belongs to a box the
         pseudo-element draws, and nothing about the table can veto it.
       - It leaves the cell background alone, so the row hover underneath
         still reads and the two stack: the ROW says where you are, the
         panel says which figure you are on.
       - It sits at z-index -1 under an isolate, so the number stays on top
         of the light instead of behind it — the same trick .card::after
         already uses for its tint.

     WHAT IS EXCLUDED, AND WHY. The slabs. A .card, a reason box on the
     heatmaps page and the review-queue button are whole surfaces with a
     hover of their own (a lift, a tint, and now a sheen), and .card spends
     BOTH its pseudo-elements already — ::before on the gradient ring,
     ::after on the tint — so a blanket [data-drill]::before would have
     silently deleted every card’s ring. The chips are excluded for the
     plainer reason that they are already small rounded boxes that light up;
     a panel inside one is a box in a box. */
  [data-drill] { cursor: var(--cur-open, pointer); }
  [data-drill]:not(.card):not(.excuse-stat):not(.excuse-total):not(.awaiting-btn):not(.speed-chip):not(.tag-chip),
  .mtd-table td.mtd-act, .card-head {
    position: relative; isolation: isolate;
  }
  [data-drill]:not(.card):not(.excuse-stat):not(.excuse-total):not(.awaiting-btn):not(.speed-chip):not(.tag-chip)::before,
  .mtd-table td.mtd-act::before, .card-head::before {
    content: ''; position: absolute; inset: var(--drill-inset, 2px); z-index: -1;
    border-radius: 7px; pointer-events: none; opacity: 0;
    background: linear-gradient(180deg, rgba(66,193,172,0.17), rgba(66,193,172,0.05));
    box-shadow: inset 0 0 0 1px rgba(92,224,198,0.30),
                0 6px 18px -12px var(--neon-teal);
    transition: opacity .18s var(--ease);
  }
  [data-drill]:not(.card):not(.excuse-stat):not(.excuse-total):not(.awaiting-btn):not(.speed-chip):not(.tag-chip):hover::before,
  .mtd-table td.mtd-act:hover::before, .card-head:hover::before { opacity: 1; }
  [data-drill]:focus-visible { outline: 2px solid var(--accent-dim); outline-offset: -2px; }

  /* ---- --drill-inset: THE PANEL IS SIZED FROM THE TEXT, NOT FROM THE BOX ----
     A flat inset: 2px assumed every target had padding to spare. A table cell
     does — 8px on all four sides, so the panel sits 6px clear of the figure.
     Almost nothing else on the dashboard does. Measured, at the base type
     scale:

         .card-kpi-row          padding 3px 8px    → 1px of clearance
         .kpi-box-row           padding 4px 9px    → 2px
         .inline-time-row       padding 4px 0      → 2px, and MINUS 2px across:
                                                     the tier dot starts at the
                                                     row’s own left edge, so the
                                                     ring was drawn through it
         .ministat              no padding at all  → 0px
         .kpi-box-title         no padding, and a 27.6px line box in a 22px
                                element — the panel came out SMALLER than the
                                word it was meant to hold

     So the inset is a variable, and a target that has no padding of its own
     borrows the room from outside its box instead. Negative values here are
     the panel growing past the element, which is safe: it is painted at
     z-index -1 with pointer-events:none, only ever one is visible at a time,
     and the slack it grows into is the gap or padding of the container, not
     a neighbour’s text. Each number below is (the clearance wanted) minus
     (the padding the element already has), aiming at about 5px all round and
     a little more across than down — a panel wider than it is tall reads as
     holding a line of text rather than boxing a word. */
  .card-kpi-row                { --drill-inset: -2px -1px; }
  .kpi-box-row.drillrow        { --drill-inset: -1px -1px; }
  .inline-time-row.drillrow    { --drill-inset: -1px -9px; }
  .ministat                    { --drill-inset: -4px -4px; }
  .kpi-box-title[data-drill]   { --drill-inset: -6px -8px; }
  .agentcmp .kt-detail-row     { --drill-inset: -4px -8px; }
  .rev-rail-row                { --drill-inset: -3px -8px; }
  .agent-mini-name[data-drill] { --drill-inset: -3px -6px; }
  /* The head block of a summary card holds a 30px figure and its caption, so
     it wants the most room of any of them. */
  .card-head                   { --drill-inset: -4px -7px; }
  /* Two places where the panel now reaches into a gap that was tighter than
     the panel: the KPI rows under a card sat 2px apart, the three little
     stats at the top of a KPI box 4px. Opened up so the light has somewhere
     to go and two adjacent panels can never share an edge. */
  .card-kpis { gap: 6px; }
  .kpi-box-stats { gap: 10px; }

  /* ---- THE HEADLINE FIGURE IS A TARGET TOO ----
     "net revenue, deposits in, attempts, successful, agent bonus — there is no
     hovering indicator on the main numbers, just on the ones below."

     Correct, and the reason is that the card IS the drill for its headline
     figure: the click is on the slab, so the slab is what lit up, while the
     per-KPI rows inside it are drills in their own right and lit up
     individually. Next to a row wearing a crisp panel, a whole card merely
     lifting did not read as the same kind of thing.

     cardKpi() now wraps the label, the figure and the caption in a .card-head
     so that block can take the same panel. It is deliberately NOT given
     data-drill — the click still belongs to the card, and a second target
     inside the first would only be a second thing to miss. Keying the panel
     off .card-head:hover rather off the card’s is what keeps the two honest:
     the pointer is over the number or it is over a row, never both, so
     exactly one panel is ever lit. */
  .ministat { position: relative; }
  .ministat:hover { border-radius: 6px; }   /* the wash is the drill panel now */
  .kpi-box-title[data-drill]:hover { color: var(--accent); }
  .speed-chip[data-drill]:hover { border-color: var(--accent-dim); color: var(--text); }
  .agent-mini-name[data-drill]:hover { color: var(--accent); }

  /* ================== TWO POINTERS, TWO DESTINATIONS ==================
     Both kinds of target used to be cursor:pointer, so nothing on screen
     said whether a click would open a panel here or throw the page away to
     Intercom. They are drawn apart now, as two cursors that share one arrow
     and differ in the badge hanging off it:

       --cur-open   a small teal window, bottom right. Opens something
                    INSIDE the tool: a drill-down, a details card, the
                    feedback reader, a heatmap cell's list.
       --cur-away   a blue arrow leaving the box. Goes somewhere else, in a
                    new tab: the back-office profile, a chat in Intercom, a
                    Jira ticket.

     They are SVG data URIs so the badge can carry the theme’s own two hues
     rather than whatever the platform draws, and each falls back to a stock
     keyword (zoom-in / alias) that still tells the two apart if a browser
     declines the image. 30px square with the hotspot on the arrow point at
     4 2 — inside the 32px ceiling every platform honours. */
  :root {
    --cur-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M4 2 L4 21 L9 16.4 L12 22.6 L15 21.2 L12.1 15.2 L18.4 15 Z' fill='%23eaf2ff' stroke='%230d0f14' stroke-width='1.4' stroke-linejoin='round'/%3E%3Crect x='16.8' y='16.8' width='12.4' height='12.4' rx='3.4' fill='%230d0f14' stroke='%235ce0c6' stroke-width='1.4'/%3E%3Crect x='19.4' y='19.8' width='7.2' height='6.5' rx='1.3' fill='none' stroke='%235ce0c6' stroke-width='1.3'/%3E%3Cpath d='M19.4 22.1 H26.6' stroke='%235ce0c6' stroke-width='1.3'/%3E%3C/svg%3E") 4 2, zoom-in;
    --cur-away: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M4 2 L4 21 L9 16.4 L12 22.6 L15 21.2 L12.1 15.2 L18.4 15 Z' fill='%23eaf2ff' stroke='%230d0f14' stroke-width='1.4' stroke-linejoin='round'/%3E%3Crect x='16.8' y='16.8' width='12.4' height='12.4' rx='3.4' fill='%230d0f14' stroke='%238ab4ff' stroke-width='1.4'/%3E%3Cpath d='M20.2 25.8 L26 20 M21.6 20 L26 20 L26 24.4' fill='none' stroke='%238ab4ff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 4 2, alias;
  }
  /* Opens here */
  .card.clickable,
  .mtd-table td.mtd-act,
  table.hm td.hot, table.hm td.total-cell.hot,
  [data-act="details"], [data-act="feedback"], [data-act="fbopen"], [data-act="mofb"],
  [data-act="relink"], [data-act="liftcap"] { cursor: var(--cur-open); }
  /* Leaves for somewhere else, in a new tab */
  a[target="_blank"], [data-caselink],
  .link-btn.player, .link-btn.offer, .link-btn.claimed, .link-btn.open-all,
  [data-act="openall"], [data-act="moopenall"], [data-act="moopenchats"] {
    cursor: var(--cur-away);
  }
  /* A link with nothing behind it promises neither. */
  .link-btn.disabled, .link-btn[disabled], .link-btn:disabled { cursor: default; }

  /* ---------- USERS ---------- */
  table.users-table { width: 100%; border-collapse: collapse; font-size: 12.5px; table-layout: fixed; }
  table.users-table th {
    text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-faint); font-weight: 700; padding: 8px; border-bottom: 1px solid var(--border);
    position: sticky; top: 0; z-index: 3; background: #181B20;
  }
  table.users-table td { padding: 8px; border-bottom: 1px solid #1f232c; vertical-align: top; }
  table.users-table tr:last-child td { border-bottom: none; }
  table.users-table input, table.users-table select {
    width: 100%; font-size: 12.5px; padding: 6px 8px;
  }
  table.users-table input.pw { font-family: var(--font-mono); letter-spacing: 0.04em; }
  table.users-table tr.me-row td { background: rgba(66,193,172,0.045); }
  .you-chip {
    display: inline-block; margin-top: 5px; font-size: 9px; font-weight: 800; text-transform: uppercase;
    letter-spacing: 0.05em; padding: 1px 7px; border-radius: 999px;
    background: rgba(66,193,172,0.16); color: var(--accent);
  }
  .stage-warn { font-size: 10px; color: var(--amber); margin-top: 5px; line-height: 1.45; }
  .link-btn.danger-btn { border-color: #7d3b37; color: var(--decline); }
  .link-btn.danger-btn:hover { background: rgba(242,116,108,0.14); }
  table.users-table.perms td { font-size: 12.5px; }
  table.users-table.perms tr:hover td { background: rgba(255,255,255,0.025); }

  /* Agents side by side */
  table.agentcmp { table-layout: auto; }
  table.agentcmp td:first-child { width: 22%; }
  table.agentcmp .kt-name { font-size: 13.5px; }

  .speed-strip { display: flex; flex-wrap: wrap; gap: 5px; }
  .speed-chip {
    display: inline-flex; align-items: center; font-size: 10.5px; font-weight: 600;
    padding: 3px 9px; border-radius: 5px; background: #1a1e26;
    border: 1px solid var(--border); color: var(--text-dim); white-space: nowrap;
  }
  .speed-chip b { margin-left: 5px; color: var(--text); font-family: var(--font-mono); }
  .speed-chip.zero { opacity: 0.38; }
  .speed-chip.zero b { color: var(--text-faint); }

  /* Watch-tag match feedback */
  .match-row {
    display: grid; grid-template-columns: minmax(120px, 1.1fr) auto; gap: 2px 14px;
    padding: 8px 11px; border-radius: 7px; background: #10131a;
    border-left: 3px solid var(--border); margin-bottom: 6px; align-items: baseline;
  }
  .match-row.ex-tag  { border-left-color: #6d5fa6; }
  .match-row.ex-any  { border-left-color: #8a762c; }
  .match-row.ex-none { border-left-color: var(--decline); }
  .match-phrase { font-size: 12.5px; font-weight: 700; overflow-wrap: anywhere; }
  .match-count { font-size: 11.5px; color: var(--text-dim); font-family: var(--font-mono); text-align: right; }
  .match-eg {
    grid-column: 1 / -1; font-size: 10.5px; color: var(--text-faint);
    overflow-wrap: anywhere; margin-top: 2px;
  }

  .section-label {
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.09em;
    font-weight: 800; margin: 4px 0 10px; display: flex; align-items: center; gap: 9px;
    color: var(--accent);
  }
  .section-label::before {
    content: ""; width: 4px; height: 15px; border-radius: 2px; background: currentColor; flex: 0 0 4px;
  }
  .section-label::after { content: ""; flex: 1; height: 1px; background: currentColor; opacity: 0.22; }
  .section-label.sl-teal { color: #42c1ac; }
  /* The second label follows a grid of cards rather than a heading, so it needs
     room above it or it reads as a caption on the row it is actually ending. */
  #summaryPanel .summary-row + .section-label { margin-top: 20px; }
  .section-label.sl-blue { color: #6c9ef8; }
  .section-label.sl-amber { color: #e8c84a; }
  .section-label.sl-purple { color: #a78bfa; }

  /* Panel headings get the same treatment so sections separate at a glance */
  .panel-title {
    display: flex; align-items: center; gap: 9px; color: var(--text);
  }
  .panel-title::before {
    content: ""; width: 4px; height: 14px; border-radius: 2px; background: var(--accent); flex: 0 0 4px;
  }
  .panel.pt-blue .panel-title::before { background: var(--push); }
  .panel.pt-amber .panel-title::before { background: var(--amber); }
  .panel.pt-purple .panel-title::before { background: var(--purple); }
  .panel.pt-decline .panel-title::before { background: var(--decline); }

  .combined-totals-divider {
    display: flex; align-items: center; gap: 10px; color: var(--accent);
  }
  .combined-totals-divider::before {
    content: ""; width: 4px; height: 15px; border-radius: 2px; background: currentColor; flex: 0 0 4px;
  }
  .combined-totals-divider::after { content: ""; flex: 1; height: 1px; background: currentColor; opacity: 0.22; }

  /* The reason stats, wrapped with their total */
  .excuse-wrap { border: 1px solid var(--border); border-radius: 11px; padding: 12px; background: rgba(255,255,255,0.014); }
  .excuse-total {
    margin-top: 10px; padding: 12px 15px; border-radius: 9px;
    background: linear-gradient(180deg, rgba(232,200,74,0.09), rgba(232,200,74,0.03));
    border: 1px solid #6e5f22; display: flex; align-items: center; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
  }
  .excuse-total[data-drill]:hover { border-color: var(--amber); background: rgba(232,200,74,0.14); }
  .et-label { font-size: 12.5px; font-weight: 800; color: var(--amber); text-transform: uppercase; letter-spacing: 0.05em; }
  .et-figs { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
  .et-n { font-size: 24px; font-weight: 800; font-family: var(--font-mono); color: var(--amber); text-shadow: 0 0 14px rgba(245,217,95,0.28); }
  .et-pct { font-size: 14px; font-weight: 700; font-family: var(--font-mono); color: var(--text-dim); }
  .et-sub { font-size: 11.5px; color: var(--text-faint); }

  table.dupes th[data-hmsort] { cursor: pointer; user-select: none; }
  table.dupes th[data-hmsort]:hover { color: var(--text-dim); }
  /* The Feedback page tables sort on their own state, so their headers are
     marked with their own attribute and need the same affordance. */
  table.dupes th[data-fbsort], table.main th[data-fbsort] { cursor: pointer; user-select: none; }
  table.dupes th[data-fbsort]:hover, table.main th[data-fbsort]:hover { color: var(--text-dim); }
  table.dupes th.sorted { color: var(--accent); }
  table.dupes th .sort-ind { color: var(--accent); font-size: 9px; }
  .summary-row.four { grid-template-columns: repeat(auto-fit, minmax(186px, 1fr)); }

  .excuse-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
  .excuse-stat {
    background: #1a1e26; border: 1px solid var(--border); border-radius: 9px;
    padding: 13px 15px; border-left: 3px solid var(--border); min-width: 0;
  }
  .excuse-stat.ex-bal  { border-left-color: #276f63; }
  .excuse-stat.ex-pend { border-left-color: #8a3a34; }
  .excuse-stat.ex-limit { border-left-color: #6d5fa6; }
  .excuse-stat.ex-tag  { border-left-color: #6d5fa6; }
  .excuse-stat.ex-any  { border-left-color: #8a762c; background: linear-gradient(180deg, rgba(232,200,74,0.05), #1a1e26); }
  .excuse-stat.ex-none { border-left-color: var(--decline); background: linear-gradient(180deg, rgba(242,116,108,0.07), #1a1e26); }
  .excuse-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
  .excuse-n { font-size: 26px; font-weight: 700; font-family: var(--font-mono); line-height: 1.1; }
  .excuse-pct { font-size: 13px; font-weight: 700; font-family: var(--font-mono); color: var(--text-faint); }
  .excuse-stat[data-drill] { cursor: pointer; }
  .excuse-stat[data-drill]:hover { border-color: var(--accent-dim); background: rgba(66,193,172,0.06); }
  .excuse-l { font-size: 12.5px; font-weight: 700; margin-top: 5px; }
  .excuse-s { font-size: 11px; color: var(--text-faint); margin-top: 2px; line-height: 1.45; }

  /* A flex container ignores the cell's text-align, so without this the chips
     packed against the left edge while every other column sat centred. */
  .reason-list { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; align-items: center; }
  table.dupes td:nth-child(9) { min-width: 210px; }
  .reason-chip {
    font-size: 9.5px; font-weight: 700; padding: 2px 7px; border-radius: 4px;
    white-space: nowrap; line-height: 1.5;
  }
  .reason-chip.reason-bal { background: #1f232c; color: #63cab7; border: 1px solid #276f63; }
  .reason-chip.reason-pend { background: #1f232c; color: #ff8a80; border: 1px solid #8a3a34; }
  .reason-chip.reason-limit { background: #1f232c; color: #c4b5fd; border: 1px solid #6d5fa6; }
  .reason-chip.reason-tag { background: #1f232c; color: #c4b5fd; border: 1px solid #6d5fa6; }
  .reason-chip.reason-more { background: #1f232c; color: var(--text-faint); border: 1px solid #3a3f52; }

  .rules-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px 16px; margin-bottom: 12px; }
  .rules-field label {
    display: block; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-faint); font-weight: 700; margin-bottom: 5px;
  }
  .rules-field input { width: 100%; font-size: 12.5px; padding: 7px 9px; }
  .rules-field .hint { font-size: 10px; color: var(--text-faint); margin-top: 4px; line-height: 1.5; }
  #watchTags { width: 100%; min-height: 96px; font-size: 12.5px; line-height: 1.7; resize: vertical; }
  .tag-suggest { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
  .tag-suggest .tag-chip { cursor: pointer; }
  .tag-suggest .tag-chip:hover { border-color: var(--accent-dim); color: var(--accent); }

  .miss-flag {
    font-size: 12px; font-weight: 700; padding: 2px 8px; border-radius: 4px;
    background: #1f232c; border: 1px solid #8a762c; color: var(--amber);
    white-space: nowrap; display: inline-block;
  }
  /* The heavier flag: a day nothing explains. Red rather than amber, and its
     own class now — it used to be an inline override on top of .miss-flag. */
  .miss-flag.no-reason { border-color: #8a3a34; color: #ff8a80; }
  .ok-flag {
    font-size: 12px; font-weight: 700; padding: 2px 8px; border-radius: 4px;
    background: #1f232c; border: 1px solid #276f63; color: var(--accent);
    white-space: nowrap; display: inline-block;
  }
  /* Same outline as Share Case and the Pending Review pill. Its own class, not
     a .miss-flag with an inline override, so the heatmap flags that share that
     class keep their own look. */
  .fb-awaiting {
    display: inline-block; font-size: 12px; font-weight: 700; padding: 2px 8px;
    border-radius: 4px; background: #1f232c; border: 1px solid #7a6a24;
    color: var(--amber); white-space: nowrap;
  }

  /* ---------- HOW LONG FEEDBACK HAS BEEN WAITING ----------

     A heat indicator, not a second status. The row stays "Feedback Awaiting
     Attention" whether it has been sitting for two hours or two weeks — all
     this does is say how long, and colour how much that ought to bother you.

     So: the text is always the real elapsed hours, never a band, and the
     colour is the only thing that changes at a threshold. Nothing else on the
     row is tinted, so a long list can be scanned down this one column.

     display:flex rather than inline-flex so it drops onto its own line
     directly under the pill; the dot is a pseudo-element on currentColor so
     it can never drift out of step with the text beside it. */
  .fb-age {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    margin-top: 5px; font-size: 11.5px; font-weight: 700;
    font-family: var(--font-display); white-space: nowrap;
  }
  .fb-age::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: currentColor; flex: none;
  }
  /* Named for the threshold each one starts at, so the CSS cannot drift away
     from fbAgeTier() and nobody is tempted to read them as statuses. */
  .fb-age.age-0  { color: #53ac6c; }   /* under 12h  — fresh          */
  .fb-age.age-12 { color: #c9cf4d; }   /* 12 to 24h  — starting to age */
  .fb-age.age-24 { color: #f2a14c; }   /* 24 to 48h  — needs attention */
  .fb-age.age-48 { color: #f2766c; }   /* 48h and up — overdue         */
  /* Read already: how long ago, in the same green the Received pill uses. */
  .fb-age.fb-age-ok { color: var(--accent); }

  /* The report is laid out in fixed pixels, so it fills far more of a
     1920-wide screen than a 2560-wide one — the same design reads as zoomed
     in on 1080p. Scaling the page below 2K makes both look alike.

     zoom, not transform: a transform would create a containing block and
     break every fixed-position modal. The backdrops use inset:0, which zoom
     scales cleanly. Media queries still measure the real viewport, so the
     narrow-screen rules below do not compound with this. */
  @media (max-width: 2199px) {
    body { zoom: 0.8; }
    /* Readable back as a number: vh resolves in layout pixels and the zoom is
       applied afterwards, so anything sizing itself against the viewport has
       to divide it back out or it comes up short by exactly this factor. */
    :root { --z: 0.8; }
  }

  @media (max-width: 1500px) {
    table.main td { padding: 7px 4px; }
    table.main th { padding: 6px 4px; }
  }
  @media (max-width: 1000px) {
    .import-grid { grid-template-columns: 1fr; }
    .agent-mini-grid { grid-template-columns: 1fr; }
    .kpi-box-grid { grid-template-columns: 1fr; }
  }

  /* ---------- ONE HOVER, EVERYWHERE ----------

     The same teal already carrying Last Deposit, Agent Pushed For and Bonus.
     Rows take a 7% wash plus a 3px edge down the left, which reads as a cursor
     and is what makes a fourteen-column row easy to follow across to Case
     Links. Everything you can click takes the wash with a teal border and teal
     text.

     Last in the sheet on purpose: every rule below restates a selector that
     already exists further up, so it has to come after to win the cascade
     rather than each of forty rules being rewritten in place.

     The exception is anything destructive — Remove, RELINK, Clear feedback,
     Sign everyone out. Those stay red under the pointer. A delete that turns
     friendly when you reach for it is how people delete things they meant to
     keep. The amber Feedback marker keeps its amber for the same reason: the
     colour is the message. */

  :root { --hover-wash: rgba(66,193,172,0.07); --hover-wash-strong: rgba(66,193,172,0.16); }

  table.main tr td, table.kpitab tr td, table.dupes tr td, table.users-table tr td,
  .nav-tab, .btn, .link-btn, .fb-opt, .reset-btn, .collapse-btn, .awaiting-btn,
  .signout-btn, .modal-close, .dropdown-trigger, .dropdown-option, .filters-toggle,
  .card-kpi-row, .rev-rail-row, .speed-chip, .tag-chip, .fb-agent-picks label,
  select, input, textarea {
    transition: background-color .16s ease, border-color .16s ease,
                color .16s ease, box-shadow .16s ease;
  }

  /* ---- rows: wash, and an edge on the leading cell ---- */
  table.main tr.agent-row:hover td, table.main tr.row-review:hover td,
  table.kpitab tr:hover td, table.dupes tr:hover td,
  table.users-table tr:hover td { background: var(--hover-wash); }
  table.main tr.agent-row:hover td:first-child, table.main tr.row-review:hover td:first-child,
  table.kpitab tr:hover td:first-child, table.dupes tr:hover td:first-child,
  table.users-table tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

  /* The two KPI rows are [data-drill] targets and take the rounded panel
     instead; a dropdown option is not, and keeps the wash. */
  .card-kpi-row:hover, .rev-rail-row:hover { color: var(--text); }
  .dropdown-option:hover { background: var(--hover-wash); color: var(--text); }

  /* ---- anything you can click ---- */
  .nav-tab:hover, .filters-toggle:hover, .collapse-btn:hover, .reset-btn:hover,
  .dropdown-trigger:hover,
  .awaiting-btn:hover, .btn:hover, .fb-opt:hover, .link-btn:hover,
  .link-btn.player:hover, .link-btn.offer:hover, .link-btn.claimed:hover,
  .link-btn.details:hover, .link-btn.fb:hover, .link-btn.fb.has:hover,
  .link-btn.fb.fb-done:hover, .link-btn.open-all:hover,
  .speed-chip[data-drill]:hover, .tag-suggest .tag-chip:hover,
  .fb-agent-picks label:hover {
    background: var(--hover-wash); border-color: var(--accent-dim); color: var(--accent);
  }
  .btn.primary:hover, .auth-btn:hover, .agentcmp .kt-expand:hover {
    background: var(--hover-wash-strong); border-color: var(--accent); color: var(--accent);
  }

  /* ---- fields ---- */
  select:hover, textarea:hover,
  input[type="date"]:hover, input[type="text"]:hover, input[type="number"]:hover,
  input[type="password"]:hover { border-color: var(--accent-dim); }

  /* ---- the exceptions ---- */
  .btn.danger:hover {
    background: rgba(242,116,108,0.14); border-color: #7d3b37; color: #ffb0a8;
  }
  .link-btn.danger-btn:hover, .link-btn.relink:hover {
    background: rgba(242,116,108,0.20); border-color: #7d3b37; color: #ffb0a8;
  }
  .link-btn.fb.fb-await:hover {
    background: rgba(232,200,74,0.12); border-color: var(--amber); color: var(--amber);
  }
  .excuse-total[data-drill]:hover {
    background: rgba(232,200,74,0.14); border-color: var(--amber);
  }
  /* Still dead until boot finishes wiring the form. */
  .auth-btn:disabled:hover {
    background: rgba(139,146,163,0.08); border-color: var(--border); color: var(--text-faint);
  }

  /* ---------- FILLING THE ROW ----------

     The Date & Time cell stacks the offer over the claim, which made every row
     about 100px tall. That space is real but it is not evenly available: two
     things in the row decide whether the frame moves, and neither is a value.

     Frozen on purpose, each for a measured reason:
       .tl (Date & Time)   sets the row height. Raise it and every row grows.
       .link-btn           fourteen buttons across the widest column. One notch
                           and Case Links wraps to another line, which grows the
                           row too.
       .dorm-tag           "43d x1.7" runs 3px past Last Deposit at 12.5px.
       .deposit-time-tag   "Following Day" already fills its column at 10px.
       .cell-sub           carries the player name under the KPI tag; at 12.5px
                           a two-word name breaks onto a second line.
       .succ-pill          the drill window gives Successful a narrower column
                           than the page does. "Pending Review" is already
                           clipped there at 12px; at 13px "Not Claimed" starts
                           clipping too, so the pill stays where it is rather
                           than being one size on the page and another in the
                           window.

     What is left is the figures, and those are what the row now has room for.
     Scoped to table.main so it lands on all four surfaces that share this row
     — the Agent KPI Attempts page, the drill window, the heatmap drill and the
     Feedback page — and on nothing else. Costs 4px of row height. */
  table.main td, table.main .agent-name { font-size: 16px; }
  table.main .cell-native { font-size: 12.5px; }
  table.main .kpi-tag { font-size: 11px; }
  table.main .rate-tag { font-size: 13px; }

  /* ---------- FILLING THE DASHBOARD ----------

     The opposite problem to the table above. Here the headline figures are
     already the loudest things on the page — card values at 30px, box titles
     at 23px, mini-stat values at 22px — and raising them would only widen a
     gap that is already wide. What is undersized is everything underneath
     them, measured against the room it sits in:

       .card-kpi-row     11px, with ~300px of the card's width unused and the
                         card itself 41px taller than its content.
       .kpi-box-row      16px, with ~200px unused.
       .inline-time-row  15px, with ~175px unused. Three of the five KPI boxes
                         are stretched to match the two that carry a Last
                         Deposit section, so they run 164px short of their own
                         height.

     Left alone, and each for a reason: the card values, box titles and
     mini-stat values, which lead already; and .kpi-box-stat-label, where
     "Success Rate" fills its 98px column exactly and has nowhere to go.

     Costs 15px on the boxes and 5px on the cards. The grid keeps its columns
     and nothing wraps. */
  .card-kpi-row { font-size: 13px; }
  .kpi-box-row { font-size: 17px; }
  .inline-time-row { font-size: 16px; }

  /* ---------- INSTRUMENT-PANEL EFFECTS ----------
     Light is only ever added where the glow is the same hue as the ink it
     surrounds, and never under body text. Glow is for the headline number. */
  .card.net .card-value { text-shadow: 0 0 14px rgba(92,224,198,0.3); }
  /* Net revenue is the number the report exists to produce, so it breathes
     rather than sits. text-shadow only: nothing moves, no layout is touched,
     and the figure stays perfectly legible at both ends of the swell.

     On a marker of its own rather than .card.net, which Successful also wears
     — pulsing both would say they carry the same weight. */
  /* Each headline figure reads in its own card's colour, so the row is five
     distinct things at a glance rather than five white numbers under five
     coloured caps.

     Scoped to .hero, not to the variant alone: .decline, .push and .net are
     also worn by cards on Heatmaps, where the figure should stay plain. */
  .card.hero.net     .card-value { color: var(--accent);        text-shadow: 0 0 14px rgba(92,224,198,0.30); }
  .card.hero.push    .card-value { color: var(--push);          text-shadow: 0 0 14px rgba(138,180,255,0.30); }
  .card.hero.decline .card-value { color: var(--decline);       text-shadow: 0 0 14px rgba(255,143,134,0.30); }
  .card.hero.ticket  .card-value { color: var(--declineticket); text-shadow: 0 0 14px rgba(245,169,94,0.30); }
  /* Net revenue alone breathes; the animated shadow supersedes the static one
     above while the colour keeps coming from it. */
  .card.rev-lead .card-value { animation: revPulse 3.4s ease-in-out infinite; }
  @keyframes revPulse {
    0%, 100% { text-shadow: 0 0 4px rgba(92,224,198,0.32), 0 0 13px rgba(66,193,172,0.16); }
    50%      { text-shadow: 0 0 7px rgba(120,232,208,0.60), 0 0 21px rgba(66,193,172,0.32); }
  }
  /* Headings and net +/- figures must read as text, not glow. */
  .panel-title, .big-header, .header h1, .modal-title, .combined-totals-divider { text-shadow: none; }

  /* =====================================================================
     INTERACTION SPEC — one curve, two speeds. The teal colour convergence
     and the §7 exceptions (danger red, amber markers, disabled) already live
     in the consolidated hover block higher up; this layer adds the MOTION:
     lifts, press, the signature sheen, the nav rule, card parallax, the
     inner-row nudge and the glyph moves. Nothing transforms more than 3px or
     14%. Killed wholesale by prefers-reduced-motion below. */

  /* §1 — transform on the house curve for controls */
  .btn, .reset-btn, .collapse-btn, .awaiting-btn, .link-btn, .fb-opt,
  .auth-btn, .signout-btn, .nav-tab, .filters-toggle, .modal-close,
  .hm-seg button, .kt-expand, .fb-agent-picks label, .tag-chip {
    transition: transform .13s var(--ease), background-color .16s var(--ease),
                border-color .16s var(--ease), color .16s var(--ease),
                box-shadow .16s var(--ease);
  }
  /* tags, pills and fields sit slower — they are status, not controls */
  .kpi-tag, .succ-pill, .status-tag, .rate-tag, .dorm-tag, .deposit-time-tag,
  .fb-dot, .miss-flag, .ok-flag, .tl-tag, select, input, textarea {
    transition: transform .22s var(--ease), background-color .22s var(--ease),
                border-color .22s var(--ease), color .22s var(--ease),
                box-shadow .22s var(--ease), opacity .22s var(--ease);
  }
  /* rows a touch slower than controls */
  table.main tr td, table.kpitab tr td, table.dupes tr td, table.users-table tr td {
    transition: background-color .2s var(--ease), box-shadow .2s var(--ease), color .2s var(--ease);
  }

  /* §2 — buttons lift 1px on hover; every press feels immediate */
  .btn:hover, .link-btn:hover, .fb-opt:hover, .kt-expand:hover { transform: translateY(-1px); }
  .btn:active, .link-btn:active, .nav-tab:active, .fb-opt:active,
  .modal-close:active, .hm-seg button:active, .kt-expand:active,
  .fb-agent-picks label:active { transform: translateY(1px) scale(0.978); transition-duration: .09s; }

  /* §2 — the signature sheen: a raked band of light crossing once on arrival.
     ::after at z-index -1 sits over the slab but under the label; the -30%
     overhang and -14deg skew keep it a rake of light, never a sliding
     rectangle. Only full-size buttons and nav tabs carry it. */
  .btn, .auth-btn, .awaiting-btn, .reset-btn, .collapse-btn, .fb-opt,
  .nav-tab, .share-view-btn, .link-btn.open-all {
    position: relative; overflow: hidden; isolation: isolate;
  }
  .btn::after, .auth-btn::after, .awaiting-btn::after, .reset-btn::after,
  .collapse-btn::after, .fb-opt::after, .nav-tab::after,
  .share-view-btn::after, .link-btn.open-all::after {
    content: ""; position: absolute; top: -30%; bottom: -30%; left: 0; width: 62%;
    z-index: -1; opacity: 0; pointer-events: none;
    background: linear-gradient(90deg, transparent,
                rgba(255,255,255,0.055) 30%, rgba(255,255,255,0.115) 50%,
                rgba(255,255,255,0.055) 70%, transparent);
    transform: translateX(-140%) skewX(-14deg);
    transition: opacity .3s var(--ease);
  }
  .btn:hover::after, .auth-btn:hover::after, .awaiting-btn:hover::after,
  .reset-btn:hover::after, .collapse-btn:hover::after, .fb-opt:hover::after,
  .nav-tab:hover::after, .share-view-btn:hover::after, .link-btn.open-all:hover::after {
    opacity: 1; animation: sigSheen .82s cubic-bezier(0.32,0.5,0.26,1);
  }
  @keyframes sigSheen {
    from { transform: translateX(-140%) skewX(-14deg); }
    to   { transform: translateX(250%)  skewX(-14deg); }
  }
  /* teal band on the primary / confirm buttons instead of white */
  .btn.primary:hover::after, .auth-btn:hover::after, .link-btn.open-all:hover::after {
    background: linear-gradient(90deg, transparent,
                rgba(92,224,198,0.09) 30%, rgba(92,224,198,0.2) 50%,
                rgba(92,224,198,0.09) 70%, transparent);
  }

  /* ===== §2b — THE SAME SHEEN ON THE SLABS THAT CARRY FIGURES =====
     "hovering over the agent dashboard boards (net revenue, deposits in,
     upsell etc) the animation should be like the button animation with the
     little shine of light" — and the same for the heatmaps page: its four
     summary cards and the reason boxes beneath them.

     WHY THESE CANNOT USE THE BUTTONS’ OWN MECHANISM. A button draws its
     sheen with an extra ::after that is skewed and slid across on a
     transform, clipped by overflow:hidden. Every slab here fails both
     halves of that. .card and .kpi-box spend ::before on the gradient ring,
     which is inset -1px — OUTSIDE the box — so an overflow:hidden would eat
     the ring they were given for; and .card spends ::after on the hover
     tint as well, so there is no pseudo-element left to borrow.

     SO THE BAND IS A BACKGROUND LAYER INSTEAD. A 100deg gradient sized to
     55% of the slab and 240% of its height (the overhang is what keeps it a
     rake of light rather than a sliding rectangle, doing the job the
     buttons’ -14deg skew does), swept left to right by background-position
     over the same 1.25s on the same curve. It needs no extra element and no
     overflow rule, because a background is already clipped by the border-
     radius of the box it is painted in.

     --sheen is the band’s colour as an r,g,b triple, so each slab rakes in
     its own hue and the row reads as one set in five colours rather than
     five identical white flashes. */
  .card.clickable, .kpi-box, .excuse-stat[data-drill], .excuse-total[data-drill] { --sheen: 255,255,255; }
  .card.clickable.upsell, .card.clickable.net, .kpi-box-upsell, .kpi-box-total,
  .excuse-stat[data-drill]            { --sheen: 92,224,198; }
  .card.clickable.push,    .kpi-box-push          { --sheen: 138,180,255; }
  .card.clickable.decline, .kpi-box-decline       { --sheen: 255,143,134; }
  .card.clickable.ticket,  .kpi-box-declineticket { --sheen: 245,169,94; }
  .card.clickable.amber,   .excuse-total[data-drill] { --sheen: 245,217,95; }

  /* THE TRAVEL, AND WHY IT IS −140% → 250%. A background-position percentage
     is not a fraction of the box, it is a fraction of the box MINUS the
     image: at 55% wide, 100% only moves the band by the 45% of the box it
     does not already fill. The first pass swept −80% → 180%, which is
     −0.36 → 0.81 of the box — so the band STARTED with its right third
     already inside the frame and ENDED with its left third still in it,
     leaving a dim smear parked against the edge instead of a light that
     crosses and goes. Clearing the left edge needs p ≤ −(0.55/0.45) =
     −122%, clearing the right needs p ≥ 1/0.45 = 222%; −140% and 250% are
     those two with a little margin. The resting position is the start of
     the sweep, so nothing shows until the pointer arrives.

     The cards already own ::after for the radial hover tint, so the band
     joins it as a second background layer on the same pseudo-element. The
     radial is restated here because background-image replaces the whole
     layer list — it is the same one .card::after declares above. */
  .card.clickable::after {
    background-image:
      linear-gradient(100deg, transparent 0%, rgba(var(--sheen),0.04) 24%,
                      rgba(var(--sheen),0.17) 50%, rgba(var(--sheen),0.04) 76%,
                      transparent 100%),
      radial-gradient(130% 90% at 50% 0%,
                      rgba(66,193,172,0.13), rgba(66,193,172,0.04) 55%, transparent 100%);
    background-size: 55% 240%, auto;
    background-position: -140% 50%, 0 0;
    background-repeat: no-repeat, no-repeat;
    transition: opacity .18s var(--ease);
  }
  .card.clickable:hover::after { animation: slabSheenCard .82s cubic-bezier(0.32,0.5,0.26,1); }
  @keyframes slabSheenCard {
    from { background-position: -140% 50%, 0 0; }
    to   { background-position: 250% 50%, 0 0; }
  }

  /* The KPI Breakdown boxes and the heatmaps page’s reason boxes have their
     ::after free, so theirs is the band on its own. isolation:isolate is
     what puts z-index -1 above the slab’s own background and below its
     figures; position:relative is already on .card and .kpi-box and is
     added here for the two reason boxes. */
  .kpi-box, .excuse-stat[data-drill], .excuse-total[data-drill] { isolation: isolate; }
  .excuse-stat[data-drill], .excuse-total[data-drill] { position: relative; }
  .kpi-box::after, .excuse-stat[data-drill]::after, .excuse-total[data-drill]::after {
    content: ''; position: absolute; inset: 0; z-index: -1;
    border-radius: inherit; pointer-events: none; opacity: 0;
    background-image: linear-gradient(100deg, transparent 0%,
                      rgba(var(--sheen),0.04) 24%, rgba(var(--sheen),0.17) 50%,
                      rgba(var(--sheen),0.04) 76%, transparent 100%);
    background-size: 55% 240%;
    background-position: -140% 50%;
    background-repeat: no-repeat;
    transition: opacity .18s var(--ease);
  }
  .kpi-box:hover::after, .excuse-stat[data-drill]:hover::after,
  .excuse-total[data-drill]:hover::after {
    opacity: 1; animation: slabSheen .82s cubic-bezier(0.32,0.5,0.26,1);
  }
  @keyframes slabSheen {
    from { background-position: -140% 50%; }
    to   { background-position: 250% 50%; }
  }

  /* The reason boxes lift with the light, the way every other hoverable
     slab on the page does. The stat boxes already had their 2px; the total
     below them never did, and sat still while the four above it moved. */
  .excuse-total[data-drill] {
    transition: transform .34s var(--ease), border-color .34s var(--ease),
                background-color .34s var(--ease), box-shadow .34s var(--ease);
  }
  .excuse-total[data-drill]:hover { transform: translateY(-2px); }


  /* §3 — nav tabs: a 2px rule grown from the centre. Hover is a weaker
     version of active — half opacity, 26% inset vs 22% plus a bloom. */
  .nav-tab::before {
    content: ""; position: absolute; left: 50%; right: 50%; bottom: 4px; height: 2px;
    border-radius: 2px; background: var(--accent); opacity: 0;
    transition: left .24s var(--ease), right .24s var(--ease), opacity .24s var(--ease);
  }
  .nav-tab:hover::before  { left: 26%; right: 26%; opacity: 0.5; }
  .nav-tab.active::before { left: 22%; right: 22%; opacity: 1; box-shadow: 0 0 8px -2px var(--accent); }
  .nav-tab.active { box-shadow: inset 0 1px 0 rgba(92,224,198,0.16), 0 0 12px -8px var(--neon-teal), var(--lift-1); }

  /* §4 — cards lift 3px and the figure inside leads by one more (a small
     parallax); the shadow deepens lift-1 → lift-2. */
  .card.clickable {
    transition: transform .34s var(--ease), border-color .34s var(--ease),
                box-shadow .34s var(--ease);
  }
  /* Rises and swells a little, with a soft accent-tinted shadow under it doing
     the work a hard border change used to. A fraction over 1 on the scale is
     the whole trick — enough to feel like it came forward, too little to be
     caught moving. */
  .card.clickable:hover {
    transform: translateY(-3px) scale(1.012);
    border-color: var(--accent-dim);
    box-shadow: inset 0 2px 10px -9px var(--neon-teal), var(--lift-2),
                0 14px 34px -22px rgba(66,193,172,0.75);
  }
  .card.clickable:active { transform: translateY(-1px) scale(1.004); transition-duration: .12s; }
  .card-value, .card-label { transition: transform .24s var(--ease), color .22s var(--ease); }
  .card.clickable:hover .card-value { transform: translateY(-1px); }
  .card.clickable:hover .card-label { color: var(--text); }
  .excuse-stat[data-drill]:hover { transform: translateY(-2px); }

  /* §5 — sortable headers go neon-teal, the indicator nudges up */
  table.main th.sortable, table.main th[data-hmsort], table.main th[data-fbsort],
  table.dupes th[data-hmsort], table.dupes th[data-fbsort] {
    transition: color .2s var(--ease), background-color .2s var(--ease);
  }
  table.main th.sortable:hover, table.main th[data-hmsort]:hover, table.main th[data-fbsort]:hover,
  table.dupes th[data-hmsort]:hover, table.dupes th[data-fbsort]:hover { color: var(--neon-teal); }
  .sort-ind { display: inline-block; transition: transform .2s var(--ease), opacity .2s var(--ease); }
  th:hover .sort-ind { transform: translateY(-1px); }

  /* §5 — inner rows slide 2px right instead of lifting (their container is
     already lifting, so a vertical move would fight it) */
  .card-kpi-row, .rev-rail-row, .kpi-box-row.drillrow, .inline-time-row.drillrow {
    transition: transform .28s var(--ease), background-color .28s var(--ease), color .28s var(--ease);
  }
  .card-kpi-row:hover, .rev-rail-row:hover,
  .kpi-box-row.drillrow:hover, .inline-time-row.drillrow:hover {
    /* No background here any more: these rows are [data-drill] targets, so
       the rounded panel above is already painting under them, and a second
       wash on top of it only muddied the ring. The nudge and the text
       colour are the part that was theirs. */
    transform: translateX(2px); color: var(--text);
  }

  /* §6 — glyphs become the action they stand for */
  .modal-close { transition: transform .26s var(--ease), background-color .22s var(--ease),
                             border-color .22s var(--ease), color .22s var(--ease); }
  .modal-close:hover  { transform: rotate(90deg); }
  .modal-close:active { transform: rotate(90deg) scale(0.9); transition-duration: .09s; }
  .banner-x { transition: transform .26s var(--ease), opacity .22s var(--ease); }
  .banner-x:hover { transform: rotate(90deg); }
  .share-view-btn .btn-ico { transition: transform .28s var(--ease); }
  .share-view-btn:hover .btn-ico { transform: rotate(-14deg) scale(1.12); }
  .dropzone-icon { transition: transform .34s var(--ease), opacity .34s var(--ease); }
  .dropzone:hover .dropzone-icon { transform: translateY(-5px) scale(1.07); opacity: 1; }
  .dropdown-option input[type="checkbox"] { transition: transform .16s var(--ease); }
  .dropdown-option:hover input[type="checkbox"] { transform: scale(1.18); }
  .kt-expand.open { transform: scale(1.06); }

  /* §6 — heatmap cells: the one place scale is meaningful */
  table.hm td.hot {
    position: relative;
    transition: transform .16s var(--ease), outline-color .16s var(--ease), filter .16s var(--ease);
    outline: 2px solid transparent; outline-offset: -1px;
  }
  table.hm td.hot:hover { transform: scale(1.14); z-index: 4; filter: brightness(1.12); outline-color: var(--accent); }

  /* Motion kill switch — mechanical, not floaty; off entirely when asked. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation: none !important; transition-duration: .01ms !important; }
  }

/* ---- next block ---- */

  /* ---------- FEEDBACK INBOX: the page and the window ----------
     The list, the reader and their states are styled by the stylesheets the
     inbox injects (fb-format / fb-rail / fb-reader); this is only the frame:
     the page column, the filter bar, the two panes and the window. */

  /* The page column: the report's own 1780px, not the full width the old
     two-table page needed, so the reader's lines stay readable. The page-wrap
     keeps its fixed height (a flex column), and the mailbox takes the rest. */
  #view-feedback > .page-wrap.fbx-page { max-width: 1780px; padding: 0 20px 10px; }

  /* The filter bar is always open, and not sticky here: the panes scroll
     inside themselves, and a sticky bar's z-index would cover the list's menu. */
  #fbxFilters { position: relative; top: auto; z-index: auto; margin-bottom: 12px; flex: 0 0 auto; }
  .fbf-search {
    display: flex; flex-direction: column; gap: 6px; justify-content: flex-end;
    flex: 1 1 240px; min-width: 200px; max-width: 420px;
  }
  .fbf-search input[type="text"] { width: 100%; }
  .fbf-tail { display: flex; align-items: flex-end; gap: 9px; margin-left: auto; flex: 0 0 auto; }
  .filters .fbf-tail .reset-btn, .filters .fbf-tail .btn { height: 38px; box-sizing: border-box; }
  .filters .fbf-tail .btn { padding: 0 14px; font-size: 13px; }
  .filters .fbf-tail .btn.share-view-btn { padding-left: 11.4px; }
  .fbf-count {
    font-family: var(--font-mono); font-size: 12px; line-height: 38px;
    color: var(--text-faint); white-space: nowrap; letter-spacing: 0.03em;
  }
  .fbf-count b { color: var(--text-dim); font-weight: 700; }
  .fbf-count.is-on b { color: var(--accent); }
  .fbf-tail .reset-btn.is-idle { opacity: 0.45; }
  .fbr-empty .fbf-empty-act { margin-top: 12px; }

  /* The two panes. The list takes the smaller of what its columns need and
     52% of the row; the reader takes the rest. Both stretch to the height
     they are given and scroll inside themselves. */
  .fbx-mailbox {
    display: grid;
    /* 56% and not 52%: the list carries two more columns than it did (KPI and
       Net), and at 52% it came out 43px short of what its own columns ask for
       — so it scrolled sideways inside its track and clipped REPLIED against
       the reader. The cap still binds before --rail-w does; the reader keeps
       44%, which is more than a column of prose needs. */
    /* EQUAL, not “whatever the list asks for”. The list used to take as much
       as its columns wanted and the reader got the rest, which left one pane
       visibly larger than the other whichever way the numbers fell. Two 1fr
       tracks split the row down the middle and stay there; what changed to
       make the columns fit that half is the type scale below, not the share. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    gap: 16px; align-items: stretch;
    flex: 1 1 auto; min-height: 0;
  }
  .fbx-rail, .fbx-reader {
    background:
      linear-gradient(90deg, rgba(92,224,198,0.5) 0 70px,
                             rgba(92,224,198,0.16) 34%,
                             transparent 62%) top left / 100% 1px no-repeat,
      linear-gradient(180deg, rgba(92,224,198,0.028), rgba(255,255,255,0) 120px),
      var(--bg-panel);
    box-shadow: inset 0 1px 0 rgba(92,224,198,0.07), var(--lift-2);
    border-radius: 10px;
    min-width: 0; min-height: 0;
  }
  .fbx-rail { display: flex; flex-direction: column; }
  .fbx-reader { overflow: auto; overscroll-behavior: contain; }
  /* The report's own scrollbar (the Sessions page's Sign-in history). */
  .fbx-reader::-webkit-scrollbar { width: 11px; }
  .fbx-reader::-webkit-scrollbar-track { background: #0D0F14; border-radius: 0 10px 10px 0; }
  .fbx-reader::-webkit-scrollbar-thumb { background: #333947; border-radius: 6px; border: 2px solid #0D0F14; }
  .fbx-reader::-webkit-scrollbar-thumb:hover { background: #414958; }

  /* A narrow window: one column, the page scrolls. */
  @media (max-width: 1080px) {
    #view-feedback > .page-wrap.fbx-page { height: auto; }
    .fbx-mailbox { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; }
    .fbx-rail { max-height: 62vh; }
    .fbx-reader { overflow: visible; }
    .fbwin-body { overflow: auto; }
  }

  /* THE FEEDBACK WINDOW — the app's modal, as wide as the Agent KPI Attempts
     table (the 1780px page column less its 20px padding a side, less the
     panel's 20px a side), so the backdrop shows on both sides and a click
     there closes it. Under Details / Relink (620), so either can open on top. */
  #fbxWinBackdrop { z-index: 600; padding: 18px 20px; }
  #fbxWinBackdrop .modal.fbwin {
    max-width: none; width: min(1700px, calc(100% - 40px)); height: 100%;
    display: flex; flex-direction: column; min-height: 0;
  }
  .fbwin .modal-head { padding: 13px 20px; flex: 0 0 auto; align-items: center; }
  .fbwin .modal-title { font-size: 19px; }
  .fbwin .modal-subtitle { font-size: 14px; margin-top: 4px; }
  .fbwin-body {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    padding: 14px 16px 16px;
  }
  .fbwin-body #fbxFilters { box-shadow: none; }

/* ---- next block ---- */

  /* Built from the app's own .modal-backdrop / .modal / .modal-head, so it opens,
     looks and closes like every other popup in the report. It sits above all of
     them (Details and Feedback are 620), because a case link can be clicked from
     inside those windows too. */
  #linkBackdrop { z-index: 700; }
  /* Closed but warm: laid out at full size and see-through, under the page,
     so Player 360 can load in the background and is ready the moment a case
     is opened. Transparent rather than hidden: browsers slow a hidden frame
     right down, and the report would crawl. (It is inert while warm.) */
  #linkBackdrop.warm:not(.open) { display: flex; opacity: 0; z-index: -1; pointer-events: none; }
  :root { --p360: #f29b54; }
  .modal.link-win {
    /* Wide enough for Player 360 and the chat side by side, with 80px of
       backdrop left on each side to click away on. vw/vh resolve before body's
       0.8 zoom, so they are divided back out. */
    width: 100%; max-width: calc((100vw - 160px) / var(--z, 1));
    height: calc(100vh / var(--z, 1) - 48px);
    display: flex; flex-direction: column; overflow: hidden;
  }
  /* The header: the case on the left, its figures in the middle, the links
     and the stepping on the right. The pane switch is in the bar under it. */
  .link-win .modal-head.lw-head {
    display: grid; grid-template-columns: minmax(0, max-content) minmax(0, 1fr) max-content; align-items: center; gap: 18px;
    padding: 8px 14px 8px 22px; flex: none;
    background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0) 70%), var(--bg-panel);
  }
  .lw-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
  .lw-title .agent-name { font-size: 16px; }
  .lw-title .kpi-tag { font-size: 10.5px; }
  .lw-player { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .lw-head-mid { display: flex; align-items: center; gap: 8px; }
  .lw-head-end { grid-column: 3; display: flex; align-items: center; justify-content: flex-end; gap: 6px; min-width: 0; }
  .lw-gap { width: 6px; }
  .lw-sep { width: 1px; height: 22px; background: rgba(255,255,255,0.08); margin: 0 6px; }

  /* Small round and pill buttons share one quiet glass look. */
  .lw-step, .lw-swap {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0; cursor: pointer;
    width: 30px; height: 30px; border-radius: 999px; color: var(--text-dim);
    background: rgba(255,255,255,0.035); border: 1px solid rgba(255,255,255,0.08);
    transition: color .15s, background .15s, border-color .15s, box-shadow .15s, transform .15s;
  }
  .lw-step svg, .lw-swap svg, .lw-panes svg, .lw-links svg {
    width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  }
  .lw-step:hover:not(:disabled), .lw-swap:hover:not(:disabled) {
    color: var(--accent); border-color: rgba(66,193,172,0.45); background: rgba(66,193,172,0.1);
    box-shadow: 0 6px 16px -8px rgba(66,193,172,0.8);
  }
  .lw-step:disabled, .lw-swap:disabled { opacity: 0.3; cursor: default; }
  .link-win .modal-head .lw-step { width: 34px; height: 34px; }

  /* Backend / Player 360 / Chat: a pill, each button lit in its pane's own
     colour while that pane is on show; the lit ones sit side by side. */
  .lw-panes {
    display: inline-flex; gap: 4px; padding: 4px; border-radius: 999px;
    background: rgba(6,8,12,0.55); border: 1px solid rgba(255,255,255,0.07);
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.45);
  }
  .lw-panes button {
    --c: 150,160,180;
    display: inline-flex; align-items: center; gap: 7px; padding: 7px 16px 7px 13px; border-radius: 999px;
    font: 600 13px var(--font-display); color: var(--text-dim); cursor: pointer;
    background: transparent; border: 1px solid transparent;
    transition: color .15s, background .15s, border-color .15s, box-shadow .15s;
  }
  .lw-panes button.bo { --c: 66,193,172; }
  .lw-panes button.p360 { --c: 242,155,84; }
  .lw-panes button.chat { --c: 108,158,248; }
  .lw-panes button.tr { --c: 160,130,250; }
  .lw-tr.busy button[data-mode="en"] { opacity: 0.6; }
  .lw-panes button:hover:not(:disabled) { color: var(--text); background: rgba(255,255,255,0.05); }
  .lw-panes button.on {
    color: rgb(var(--c));
    background: linear-gradient(180deg, rgba(var(--c),0.28), rgba(var(--c),0.1));
    border-color: rgba(var(--c),0.55);
    box-shadow: 0 6px 18px -6px rgba(var(--c),0.65), inset 0 1px 0 rgba(255,255,255,0.08);
  }
  .lw-panes button.on svg { filter: drop-shadow(0 0 4px rgba(var(--c),0.7)); }
  .lw-panes button:disabled { opacity: 0.3; cursor: default; }
  .lw-swap { width: auto; height: 36px; padding: 0 15px 0 12px; font: 600 12.5px var(--font-display); }
  .lw-swap svg { transition: transform .25s; }
  .lw-swap:hover:not(:disabled) svg { transform: rotate(180deg); }

  /* The real links, and one click to copy them all: tinted pills that lift. */
  .lw-links { display: flex; align-items: center; gap: 6px; min-width: 0; }
  .link-win .lw-links .link-btn {
    --c: 66,193,172;
    display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px 7px 13px; border-radius: 999px;
    font: 600 12.5px var(--font-display); color: rgb(var(--c)); text-decoration: none;
    background: rgba(var(--c),0.09); border: 1px solid rgba(var(--c),0.32);
    transition: transform .15s, background .15s, box-shadow .15s;
  }
  .link-win .lw-links .link-btn svg { width: 13px; height: 13px; opacity: 0.85; }
  .link-win .lw-links .link-btn.offer { --c: 232,200,74; }
  .link-win .lw-links .link-btn.claimed { --c: 92,224,198; }
  .link-win .lw-links .link-btn.lw-copy { --c: 170,180,200; cursor: pointer; }
  .link-win .lw-links .link-btn:hover {
    transform: translateY(-1px); background: rgba(var(--c),0.16);
    box-shadow: 0 8px 18px -10px rgba(var(--c),0.9);
  }
  .link-win .lw-copy.done, .link-win .lw-share.done { color: #0d1014; background: var(--accent); border-color: var(--accent); }
  /* Share Case: the report's own Share Case colour, split from the links by a line. */
  .link-win .lw-links .link-btn.lw-share { --c: 232,200,74; cursor: pointer; }
  .lw-links .lw-sep { margin: 0 4px; }

  /* Backend, Player 360 and the chat, left to right, each shown one always
     the same width as the others; the 1px gap on the border colour is the
     line between them. Swap turns the order round. The 0.8 page zoom is
     undone here, so everything inside draws at the size Intercom, Tableau
     and the backoffice draw themselves, and sizes below are real px. */
  .lw-body {
    flex: 1; min-height: 0; display: grid; gap: 1px;
    grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr);
    zoom: calc(1 / var(--z, 1)); color-scheme: dark; background: var(--border);
  }
  .lw-body > [hidden] { display: none; }
  /* Player 360 out of view stays drawn, see-through and under the other
     panes: a hidden frame is slowed right down by the browser, and a player
     going in at that moment would stall. */
  .lw-body { position: relative; }
  .lw-body > .lw-p360[hidden] { display: flex; position: absolute; inset: 0; opacity: 0; pointer-events: none; z-index: -1; }

  /* Over the report whenever it isn't showing the player asked for: loading,
     not found, no email, or Tableau not answering. Never someone else's data
     under a small label. */
  .lw-p360 { position: relative; }
  .lw-cover {
    position: absolute; left: 0; right: 0; top: 42px; bottom: 0; z-index: 2;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
    padding: 24px; text-align: center; background: rgba(13,16,21,0.97);
  }
  .lw-cover[hidden] { display: none; }
  .lw-cover.wait { background: rgba(13,16,21,0.7); }
  .lw-cover-title { font: 700 19px var(--font-display); color: var(--text); }
  .lw-cover.notfound .lw-cover-title, .lw-cover.none .lw-cover-title, .lw-cover.nobox .lw-cover-title { color: var(--p360); }
  .lw-cover.error .lw-cover-title { color: #ff8a80; }
  .lw-cover-sub { font: 13.5px var(--font-mono); color: var(--text-dim); max-width: 520px; overflow-wrap: anywhere; }
  .lw-cover-btns { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
  .lw-cover .btn { border-radius: 999px; }
  .lw-cover.wait .lw-cover-title::before {
    content: ''; display: inline-block; width: 15px; height: 15px; margin-right: 11px; vertical-align: -2px;
    border-radius: 50%; border: 2px solid rgba(242,155,84,0.3); border-top-color: var(--p360);
    animation: lw-spin 0.8s linear infinite;
  }
  @keyframes lw-spin { to { transform: rotate(360deg); } }
  .lw-bo { order: 1; }
  .lw-p360 { order: 2; }
  .lw-chat { order: 3; }
  .lw-body.swap .lw-bo { order: 3; }
  .lw-body.swap .lw-chat { order: 1; }
  .lw-chat { position: relative; min-width: 0; min-height: 0; background: var(--bg-sunk); container-type: inline-size; }
  /* The chat drawn 15% closer, messages and facts alike (Val, 28 Sep). */
  .lw-chat { zoom: 1.15; }
  .lw-state { height: 100%; display: flex; align-items: center; justify-content: center; color: var(--text-faint); font-size: 14px; }

  .lw-bo, .lw-p360 { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--bg-sunk); }
  .lw-bar {
    flex: none; height: 42px; display: flex; align-items: center; gap: 8px; padding: 0 10px 0 16px;
    background: #10131a; border-bottom: 1px solid var(--border);
  }
  .lw-bar-name { font-size: 13.5px; font-weight: 700; color: var(--p360); white-space: nowrap; }
  .lw-bar-name.bo { color: var(--accent); }
  .lw-bar-host { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: auto; }
  /* The report's boxes, for when none of them is plainly the email box. */
  .lw-boxpick {
    max-width: 240px; padding: 4px 6px; border-radius: 6px; cursor: pointer;
    font: 600 12px var(--font-display); color: var(--amber); background: #161a22; border: 1px solid #7a6a24;
  }
  .lw-mail[data-lw] { cursor: pointer; }
  /* The email going into the report's email box: grey while the report
     loads, green once it is in, amber when the report took no email. */
  .lw-mail {
    min-width: 0; margin-right: auto; padding: 3px 10px; border-radius: 999px;
    font: 12px var(--font-mono); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    color: var(--text-dim); background: #161a22; border: 1px solid var(--border);
  }
  .lw-mail[data-state="ok"] { color: var(--accent); border-color: var(--accent-dim); }
  .lw-mail[data-state="miss"] { color: var(--amber); border-color: #7a6a24; }
  .lw-mail[data-state="none"] { font-family: inherit; color: var(--text-faint); }
  .lw-bar .link-btn { font-size: 11.5px; padding: 6px 13px; border-radius: 999px; background: rgba(255,255,255,0.035); border-color: rgba(255,255,255,0.1); }
  .lw-bar .link-btn:hover { color: var(--accent); border-color: rgba(66,193,172,0.45); background: rgba(66,193,172,0.1); }
  .lw-bar .link-btn.done { color: #0d1014; background: var(--accent); border-color: var(--accent); }
  .lw-frame { position: relative; flex: 1; min-height: 0; background: var(--bg-sunk); }
  /* The report sits in a box cut to its tiles (fit/trim in the script): drawn
     larger by the width of its white border on each side (--l --t --r --b) and
     shifted, with the box hiding the edge. */
  .lw-frame .lw-crop { position: relative; width: 100%; height: 100%; margin: 0 auto; overflow: hidden; }
  .lw-frame .lw-crop tableau-viz {
    position: absolute; display: block;
    left: calc(-1px * var(--l, 0)); top: calc(-1px * var(--t, 0));
    width: calc(100% + 1px * (var(--l, 0) + var(--r, 0))); height: calc(100% + 1px * (var(--t, 0) + var(--b, 0)));
  }
  .lw-frame tableau-viz iframe { width: 100% !important; height: 100% !important; border: 0; }
  .lw-frame > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: #fff; }
  /* A framed page on a light colour scheme inside this dark window gets an
     opaque WHITE backing from the browser wherever the page itself is
     transparent: that was the white round Player 360. Telling the browser
     the frame is light too drops the backing, and the pane shows through. */
  .lw-frame > iframe, .lw-frame tableau-viz, .lw-frame tableau-viz iframe { color-scheme: light; }
  /* Backoffice dark mode, drawn by the report from outside the page (no
     add-on): grey first, which takes the coloured ClearType edges out of the
     text so it doesn't fray; then turned round and dimmed, so the page goes
     near-black and the brightest thing left is the grey of the text. Nothing
     comes out white. */
  .lw-frame.dark > iframe { filter: grayscale(1) invert(0.915) brightness(0.86) sepia(0.1); }
  .lw-step.lw-dark.on { color: #c4b5fd; border-color: rgba(167,139,250,0.5); background: rgba(167,139,250,0.14); box-shadow: 0 6px 16px -8px rgba(167,139,250,0.8); }
  /* A report made at a set size is drawn at that size, centred on the app's
     own background, instead of stretched over the pane with Tableau's white
     round it; a pane smaller than the report scrolls. */
  .lw-frame.sized { overflow: auto; }

  /* Three side by side leave the chat narrow: a slimmer side column. */
  @container (max-width: 760px) {
    .lw-chat .cw { grid-template-columns: minmax(0, 1fr) 200px; }
    .lw-chat .cw-main { padding: 14px 14px 30px; }
    .lw-chat .cw-msg { max-width: 92%; }
    .lw-chat .cw-side { padding: 14px 12px 20px; }
  }
  .lw-blocked { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; padding: 24px; text-align: center; }
  .lw-blocked-title { font-size: 15px; font-weight: 700; color: var(--text); }
  .lw-blocked .btn { text-decoration: none; }

  /* ---- chat ---- */
  .cw { display: grid; grid-template-columns: minmax(0, 1fr) 270px; height: 100%; }
  .cw-main {
    overflow-y: auto; padding: 18px 26px 36px;
    /* A soft teal glow top left, blue bottom right, and a faint dot grid:
       enough life that it reads as a chat, quiet enough not to fight it. */
    background:
      radial-gradient(rgba(255,255,255,0.055) 1px, transparent 1.4px) 0 0 / 22px 22px,
      radial-gradient(1000px 560px at 0% 0%, rgba(66,193,172,0.16), transparent 62%),
      radial-gradient(900px 600px at 100% 100%, rgba(108,158,248,0.14), transparent 60%),
      linear-gradient(180deg, #111720 0%, #0c1016 100%);
  }
  .cw-side { overflow-y: auto; padding: 16px 18px 24px; border-left: 1px solid var(--border); background: #11141a; }
  .cw-stream { display: flex; flex-direction: column; gap: 4px; max-width: 980px; margin: 0 auto; }

  .cw-day {
    align-self: center; margin: 10px 0 6px; padding: 4px 13px; border-radius: 999px;
    font-size: 12px; font-weight: 700; letter-spacing: 0.03em; color: var(--text-dim);
    background: rgba(20,24,31,0.9); border: 1px solid #2a2f3a;
  }
  /* Offer amber and claim green, the colours of the OFFER and CLAIM tags in
     the table's Date & Time column. */
  .cw-chathead {
    align-self: stretch; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin: 4px 0 8px;
    padding: 9px 14px; border-radius: 10px; font-size: 13px; color: #e3d49a;
    background: rgba(20,24,31,0.88); border: 1px solid #7a6a24;
  }
  .cw-chathead b { font-size: 14px; color: var(--amber); letter-spacing: 0.04em; }

  /* The yellow things: the folded bot menu, and where the claim chat begins. */
  .cw-pre {
    align-self: stretch; margin: 4px 0 8px; padding: 9px 14px; border-radius: 9px; cursor: pointer;
    font: 700 13px var(--font-display); color: #f5d964; text-align: center;
    background: rgba(232,200,74,0.13); border: 1px solid rgba(232,200,74,0.5);
  }
  .cw-pre:hover { background: rgba(232,200,74,0.2); }
  .cw-pre-body { display: none; flex-direction: column; gap: 4px; padding-left: 12px; border-left: 2px solid rgba(232,200,74,0.4); margin-bottom: 6px; }
  .cw-pre-body.open { display: flex; }
  /* Where the claim chat begins: red, so it cannot be scrolled past. */
  .cw-claim {
    align-self: stretch; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; margin: 26px 0 10px;
    padding: 11px 16px; border-radius: 10px; font-size: 13.5px; color: #ffc2bd;
    background: linear-gradient(90deg, rgba(242,116,108,0.32), rgba(242,116,108,0.1));
    border: 1px solid rgba(242,116,108,0.8); box-shadow: 0 0 0 3px rgba(242,116,108,0.1);
  }
  .cw-claim b { font-size: 14.5px; letter-spacing: 0.04em; color: #ffd9d6; }
  .cw-claim .cw-later { font-weight: 700; padding: 2px 9px; border-radius: 999px; color: #fff; background: rgba(242,116,108,0.4); }
  .cw-claim a, .cw-chathead a { color: inherit; margin-left: auto; font-weight: 600; }

  /* Time between one message and the next. */
  .cw-gap { display: flex; align-items: center; gap: 10px; margin: 2px 0; font: 600 11.5px var(--font-mono); color: #6f7890; }
  .cw-gap::before, .cw-gap::after { content: ''; flex: 1; border-top: 1px dashed rgba(255,255,255,0.13); }

  .cw-msg { display: flex; flex-direction: column; max-width: 74%; }
  .cw-msg.in { align-self: flex-start; }
  .cw-msg.out { align-self: flex-end; align-items: flex-end; }
  .cw-who { display: flex; align-items: baseline; gap: 6px; margin: 8px 4px 5px; font-size: 14px; color: var(--text-dim); }
  .cw-who b { font-weight: 700; color: var(--text); }
  .cw-msg.out .cw-who b, .cw-msg.out .cw-role { color: var(--accent); }
  .cw-msg.bot .cw-who b { color: var(--text-dim); letter-spacing: 0.04em; }
  .cw-role { font-weight: 600; }
  .cw-bubble {
    padding: 10px 14px; border-radius: 14px; font-size: 14.5px; line-height: 1.5; color: var(--text);
    background: #252b36; border: 1px solid #343c4b; box-shadow: 0 2px 8px rgba(0,0,0,0.28); overflow-wrap: anywhere;
  }
  .cw-msg.in .cw-bubble { border-top-left-radius: 4px; }
  .cw-msg.out .cw-bubble {
    background: linear-gradient(135deg, rgba(66,193,172,0.3), rgba(38,120,107,0.2));
    border-color: rgba(92,224,198,0.42); border-top-right-radius: 4px;
  }
  .cw-msg.bot .cw-bubble { background: rgba(18,21,28,0.88); color: #aab2c2; border: 1px dashed #414858; box-shadow: none; }
  .cw-bubble p { margin: 0; }
  .cw-bubble p:empty { height: 0.5em; }
  .cw-bubble a { color: #8ab4ff; }
  .cw-bubble b, .cw-bubble strong { color: #fff; }
  .cw-bubble img { display: block; max-width: 280px; max-height: 340px; border-radius: 8px; margin: 4px 0; }
  .cw-bubble .cw-h { display: block; font-weight: 700; margin: 4px 0; }
  .cw-file {
    display: inline-flex; align-items: center; gap: 6px; margin: 4px 0; padding: 6px 10px; border-radius: 7px;
    font-size: 12.5px; color: var(--text-dim); background: #14171e; border: 1px solid #2f3542; text-decoration: none;
  }
  .cw-note {
    align-self: stretch; margin: 6px 0; padding: 8px 12px; border-radius: 8px; font-size: 13px;
    background: rgba(232,200,74,0.07); border: 1px solid rgba(232,200,74,0.3); color: #efd67a;
  }
  .cw-note-h { font-size: 12px; font-weight: 700; margin-bottom: 2px; opacity: 0.9; }
  .cw-event { align-self: center; max-width: 80%; margin: 3px 0; font-size: 12.5px; color: #7c869a; text-align: center; overflow-wrap: anywhere; }
  .cw-event.kpi { color: var(--accent); }
  .cw-quick { align-self: flex-start; display: flex; flex-wrap: wrap; gap: 5px; max-width: 74%; margin: 3px 0 2px; }
  .cw-quick span { font-size: 12px; padding: 3px 10px; border: 1px solid #353c4a; border-radius: 999px; color: #8a93a6; background: rgba(18,21,28,0.7); }
  .cw-time { font-size: 11.5px; color: var(--text-faint); font-weight: 400; margin-left: 4px; }
  /* A message in English (.cw-t) beside the words as written (.cw-o); the
     chat's Translated / Original switch shows one or the other. */
  .cw-t { white-space: pre-wrap; }
  .cw-o.has-t, .lw-chat.orig .cw-t { display: none; }
  .lw-chat.orig .cw-o.has-t { display: block; }
  .cw-lang {
    display: block; width: fit-content; margin: 5px 0 -2px auto; padding: 0 7px; border-radius: 999px;
    font: 600 10.5px/17px var(--font-mono); letter-spacing: 0.03em; color: #b8a6f5; background: rgba(160,130,250,0.13);
  }

  /* Side column: the app's Details styles, one column wide. */
  .cw-side .det-section { margin-bottom: 16px; }
  .cw-side .det-grid { grid-template-columns: 1fr; gap: 9px; }
  .cw-side .det-head { font-size: 12px; }
  .cw-side .det-head.offer { color: var(--amber); border-bottom-color: #7a6a24; }
  .cw-side .det-head.claim { color: var(--accent); border-bottom-color: var(--accent-dim); }
  .cw-side .det-head .c-claim { color: var(--accent); }
  .cw-side .det-label { font-size: 11px; }
  .cw-side .det-value { font-size: 14px; }
  .cw-ext { color: #8ab4ff; font-family: var(--font-mono); font-size: 13px; text-decoration: none; }
  .cw-ext:hover { text-decoration: underline; }
  .cw-summary { font-size: 13.5px; line-height: 1.55; color: var(--text-dim); }
  .cw-summary + .cw-summary { margin-top: 12px; }
  .cw-summary p { margin: 0; }
  .cw-summary b { color: var(--text); }
  .cw-sumhead { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 4px; }
  .cw-stars { color: var(--amber); letter-spacing: 1px; }
  .cw-idlink { font-family: var(--font-mono); font-size: 13px; color: #8ab4ff; background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
  .cw-idlink:hover { text-decoration: underline; }

  /* The case on show, visible through the backdrop and gone when it closes. */
  tr.agent-row.lw-viewing > td { background: rgba(66,193,172,0.05); }
  tr.agent-row.lw-viewing > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

  /* The case last on show in a popup, lit for a moment once the popup closes
     (flashLastCase): the Feedback page's new-message light, fbrdNewShow, in
     the same amber and the same 2.3s: up fast, a hold, a long fade. Drawn
     behind each cell's content, outlined round the row as a whole. */
  tr.agent-row.case-flash > td { position: relative; isolation: isolate; }
  tr.agent-row.case-flash > td::after {
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0;
    background: linear-gradient(180deg, rgba(245,217,95,0.19), rgba(245,217,95,0.06));
    box-shadow: inset 0 1px 0 rgba(245,217,95,0.52), inset 0 -1px 0 rgba(245,217,95,0.52);
    animation: caseFlashShow 2.3s cubic-bezier(.4,0,.4,1) forwards;
  }
  tr.agent-row.case-flash > td:first-child::after { box-shadow: inset 1px 0 0 rgba(245,217,95,0.52), inset 0 1px 0 rgba(245,217,95,0.52), inset 0 -1px 0 rgba(245,217,95,0.52); }
  tr.agent-row.case-flash > td:last-child::after { box-shadow: inset -1px 0 0 rgba(245,217,95,0.52), inset 0 1px 0 rgba(245,217,95,0.52), inset 0 -1px 0 rgba(245,217,95,0.52); }
  @keyframes caseFlashShow { 0% { opacity: 0; } 7% { opacity: 1; } 48% { opacity: 1; } 100% { opacity: 0; } }
  @media (prefers-reduced-motion: reduce) { tr.agent-row.case-flash > td::after { animation: none; } }

  /* The case links: always Player Account, Player 360, then the chat(s), in
     that order, wrapping only among themselves; Open All on a line of its
     own under them; then the dashed line and Share Case / EDIT. */
  .act-groups .act-open.act-stack { flex-direction: column; align-items: center; flex-wrap: nowrap; }
  .act-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; max-width: 100%; }

  /* The case links open here now, so they take the app's "opens here" cursor.
     Player Account too: the backoffice always loads in the Backend pane. */
  a.link-btn[data-case]:not(.player), .link-btn.open-all[data-act="openall"],
  html[data-kpi-frames="on"] a.link-btn.player[data-case] { cursor: var(--cur-open); }
  .fbrd .fbrd-links .link-btn.p360::after { --sheen: 242,155,84; }
  .link-btn.p360:hover { border-color: #7a5230; color: var(--p360); }

  /* Feedback, beside the player's name: opens the case's Feedback over this
     window. The dot is the row's Feedback colour: grey none, amber waiting on
     the agent, green dealt with. */
  .lw-title .lw-fb {
    flex: none; margin-left: 8px; display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 15px 6px 12px; border-radius: 999px; cursor: var(--cur-open, pointer);
    font: 700 12.5px var(--font-display); color: #e9edf3; letter-spacing: 0.01em;
    border: 1px solid transparent;
    background:
      linear-gradient(180deg, rgba(66,193,172,0.16), rgba(108,158,248,0.08)) padding-box,
      linear-gradient(#151a22, #121620) padding-box,
      linear-gradient(120deg, rgba(92,224,198,0.95), rgba(108,158,248,0.85) 55%, rgba(92,224,198,0.35)) border-box;
    transition: filter .15s, transform .15s;
  }
  .lw-title .lw-fb:hover { filter: brightness(1.18); transform: translateY(-1px); }
  .lw-title .lw-fb svg { width: 15px; height: 15px; flex: none; fill: none; stroke: #7fe6d3; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
  .lw-fb-dot { width: 7px; height: 7px; border-radius: 50%; margin-left: 2px; background: #6a7284; box-shadow: 0 0 0 3px rgba(106,114,132,0.2); }
  .lw-fb-dot.new { background: var(--amber); box-shadow: 0 0 0 3px rgba(232,200,74,0.2); }
  .lw-fb-dot.seen { background: var(--accent); box-shadow: 0 0 0 3px rgba(66,193,172,0.2); }
  /* Feedback opened from here sits over this window (Feedback is 600, this
     700), and closing it brings this window back on top. */
  #linkBackdrop.open.lw-under { z-index: 590; }

  /* The case's figures, under the header whatever panes are on show: what
     the offer asked (amber) to what came in (green) on one line, then the
     time and rate, then the bonus. Every box the same height, so their
     edges line up. */
  .lw-case {
    flex: none; position: relative; display: flex; align-items: center; justify-content: center;
    height: 60px; padding: 0 22px; overflow: hidden; white-space: nowrap; font-family: var(--font-display);
    background:
      radial-gradient(460px 110px at 36% -10%, rgba(232,200,74,0.05), rgba(0,0,0,0) 70%),
      radial-gradient(560px 120px at 58% 120%, rgba(66,193,172,0.075), rgba(0,0,0,0) 70%),
      radial-gradient(360px 90px at 76% 50%, rgba(108,158,248,0.03), rgba(0,0,0,0) 70%),
      linear-gradient(180deg, #121720, #0d1016);
    border-bottom: 1px solid rgba(255,255,255,0.06);
  }
  .lw-case[hidden] { display: none; }
  .lw-case::before {
    content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px;
    background: linear-gradient(90deg, rgba(0,0,0,0) 8%, rgba(232,200,74,0.25) 30%, rgba(92,224,198,0.28) 58%, rgba(108,158,248,0.15) 76%, rgba(0,0,0,0) 92%);
  }
  .lw-case::after {
    content: ''; position: absolute; inset: 0; pointer-events: none; opacity: 0.35;
    background-image: radial-gradient(rgba(255,255,255,0.09) 1px, rgba(0,0,0,0) 1.2px);
    background-size: 14px 14px;
    -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 10%, #000 40%, #000 60%, rgba(0,0,0,0) 90%);
            mask-image: linear-gradient(90deg, rgba(0,0,0,0) 10%, #000 40%, #000 60%, rgba(0,0,0,0) 90%);
  }
  .lwc-in { flex: none; position: relative; z-index: 1; display: flex; align-items: center; gap: 22px; }
  .lwc-jr, .lwc-po { position: relative; z-index: 1; display: flex; align-items: center; }
  .lwc-jr { gap: 10px; }
  .lwc-po { gap: 8px; }
  .lwc-jr::before {
    content: ''; position: absolute; left: 40px; right: 40px; top: 50%; height: 2px; margin-top: -1px; border-radius: 2px; z-index: -1;
    background: linear-gradient(90deg, rgba(232,200,74,0.5), rgba(232,200,74,0.35) 32%, rgba(92,224,198,0.4) 60%, rgba(66,193,172,0.7));
  }
  .lwc-box {
    --c: 232,200,74;
    box-sizing: border-box; height: 46px; position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-width: 112px; padding: 0 15px; border-radius: 11px;
    background: linear-gradient(180deg, rgba(var(--c),0.06), rgba(var(--c),0.015) 70%), #12161d;
    border: 1px solid rgba(var(--c),0.38);
  }
  .lwc-box.claim { --c: 92,224,198; }
  .lwc-k { font-size: 10px; font-weight: 700; letter-spacing: 0.13em; text-transform: uppercase; color: rgba(var(--c),0.85); }
  .lwc-v { font-size: 16px; font-weight: 700; color: #f1f4f8; line-height: 1; display: inline-flex; align-items: baseline; gap: 6px; }
  .lwc-n { font: 500 11.5px var(--font-mono); color: #6b7486; }
  .lwc-none { color: var(--text-faint); font-weight: 600; }
  .lwc-box.hero {
    --c: 66,193,172; min-width: 128px; padding: 0 18px;
    background: linear-gradient(180deg, rgba(66,193,172,0.12), rgba(66,193,172,0.03)), #111719;
    border-color: rgba(92,224,198,0.55);
  }
  .lwc-box.hero .lwc-k { color: #7fe6d3; }
  .lwc-box.hero .lwc-v { font-size: 18px; color: #7fe9d4; }
  .lwc-box.hero .lwc-v.neg { color: var(--decline); }
  .lwc-node {
    width: 20px; height: 20px; flex: none; display: grid; place-items: center; border-radius: 50%;
    background: #0e1117; border: 1px solid rgba(255,255,255,0.14);
  }
  .lwc-node svg { width: 10px; height: 10px; fill: none; stroke: #9aa3b4; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .lwc-node.a { border-color: rgba(232,200,74,0.4); }
  .lwc-node.a svg { stroke: #e8c84a; }
  .lwc-node.g { border-color: rgba(92,224,198,0.45); }
  .lwc-node.g svg { stroke: #7fe6d3; }
  .lwc-vs { width: 1px; height: 38px; z-index: 1; background: linear-gradient(180deg, rgba(0,0,0,0), rgba(255,255,255,0.16), rgba(0,0,0,0)); }
  .lwc-box.pay {
    --c: 190,198,212; flex-direction: row; gap: 0; padding: 0 4px; min-width: 0;
    background: linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.01) 70%), #12161d;
    border-color: rgba(255,255,255,0.13);
  }
  .lwc-pi { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 0 14px; }
  .lwc-box.pay .lwc-k { color: #d6dbe3; }
  .lwc-pd { width: 1px; height: 28px; background: rgba(255,255,255,0.1); }
  .lwc-box.pay .deposit-time-tag, .lwc-box.pay .rate-tag {
    display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; padding: 1px 10px; border-radius: 999px;
    background: rgba(66,193,172,0.08);
  }
  .lwc-box.pay .deposit-time-tag.na, .lwc-box.pay .rate-tag.r-zero { background: none; }
  .lwc-box.pay svg { width: 12px; height: 12px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .lwc-box.bonus { --c: 232,200,74; flex-direction: row; gap: 10px; padding: 0 16px 0 9px; }
  .lwc-coin {
    width: 25px; height: 25px; flex: none; border-radius: 50%; display: grid; place-items: center;
    font: 800 12.5px var(--font-display); color: #3a2c05;
    background: radial-gradient(circle at 35% 30%, #fff3b8, #f1cf55 45%, #b8901f 100%);
    box-shadow: inset 0 -2px 3px rgba(0,0,0,0.25);
  }
  .lwc-col { display: flex; flex-direction: column; gap: 4px; }
  .lwc-box.bonus .lwc-v { font-size: 17px; font-weight: 800; color: #f5e39a; }
  .lwc-if { font-size: 10px; font-weight: 600; color: var(--amber); opacity: 0.85; margin-left: 7px; letter-spacing: 0; text-transform: none; }

  /* The same figures, squeezed into the header's middle. */
  .lw-head > .lw-case { grid-column: 2; min-width: 0; height: auto; padding: 0; background: none; border: 0; }
  .lw-head > .lw-case::before, .lw-head > .lw-case::after { display: none; }
  .lw-head .lw-case .lwc-in { gap: 12px; }
  .lw-head .lw-case .lwc-jr { gap: 5px; }
  .lw-head .lw-case .lwc-jr::before { left: 30px; right: 30px; }
  .lw-head .lw-case .lwc-po { gap: 6px; }
  .lw-head .lw-case .lwc-box { height: 42px; min-width: 0; padding: 0 11px; gap: 3px; border-radius: 10px; }
  .lw-head .lw-case .lwc-k { font-size: 9.5px; letter-spacing: 0.1em; }
  .lw-head .lw-case .lwc-v { font-size: 15px; gap: 5px; }
  .lw-head .lw-case .lwc-n { font-size: 10.5px; }
  .lw-head .lw-case .lwc-box.hero { min-width: 0; padding: 0 14px; }
  .lw-head .lw-case .lwc-box.hero .lwc-v { font-size: 16.5px; }
  .lw-head .lw-case .lwc-node { width: 16px; height: 16px; }
  .lw-head .lw-case .lwc-node svg { width: 8px; height: 8px; }
  .lw-head .lw-case .lwc-vs { height: 30px; }
  .lw-head .lw-case .lwc-box.pay { padding: 0 2px; }
  .lw-head .lw-case .lwc-pi { padding: 0 10px; gap: 4px; }
  .lw-head .lw-case .lwc-pd { height: 24px; }
  .lw-head .lw-case .lwc-box.pay .deposit-time-tag, .lw-head .lw-case .lwc-box.pay .rate-tag { font-size: 11px; padding: 0 8px; gap: 5px; }
  .lw-head .lw-case .lwc-box.pay svg { width: 11px; height: 11px; }
  .lw-head .lw-case .lwc-box.bonus { gap: 8px; padding: 0 12px 0 7px; }
  .lw-head .lw-case .lwc-coin { width: 22px; height: 22px; font-size: 11px; }
  .lw-head .lw-case .lwc-box.bonus .lwc-v { font-size: 15px; }
  .lw-head .lw-case .lwc-if { font-size: 9px; margin-left: 5px; }

  /* Backend / Player 360 / Chat and Swap, on the top edge of the panes they
     switch. Made 25% bigger than the first squeezed version, for easier clicking. */
  .lw-panebar {
    flex: none; position: relative; display: flex; align-items: center; justify-content: center;
    height: 50px; padding: 0 22px;
    background:
      radial-gradient(420px 60px at 50% 120%, rgba(108,158,248,0.06), rgba(0,0,0,0) 70%),
      linear-gradient(180deg, #11151c, #0d1016);
    border-bottom: 1px solid var(--border);
  }
  .lw-panebar .lw-head-mid { gap: 8px; }
  .lw-panebar .lw-panes { padding: 4px; gap: 3px; }
  .lw-panebar .lw-panes button { padding: 5px 15px 5px 12.5px; gap: 7.5px; font-size: 15px; }
  .lw-panebar .lw-panes svg { width: 16px; height: 16px; }
  .lw-panebar .lw-swap { height: 36px; padding: 0 15px 0 12.5px; gap: 7.5px; font-size: 14.5px; }
  .lw-panebar .lw-swap svg { width: 16px; height: 16px; }
