:root{
  /* RS Dashboards Design System — token layer.
     Monochrome editorial frame: pure white canvas, pure black ink, warm hairlines.
     There is deliberately NO mid-gray text role in the system; hierarchy comes from
     Inter's weight axis. --ink2/--ink3 survive as *weight-and-tint* steps because a
     dense analytics grid needs a quieter label than a poster does, but they are tints
     of the system ink rather than the old blue-grays. */
  --canvas:#ffffff; --bg:#f7f4ef; --panel:#ffffff;
  --line:#e5e0d8; --line2:#f1ede6;          /* hairline · hairline-soft */
  --ink:#000000; --ink2:#3d3a35; --ink3:#6b655c;
  --accent:#000000; --accent-ink:#ffffff;   /* primary = black; selected = primary surface */
  --sel-bg:var(--accent); --sel-ink:var(--accent-ink);   /* count-badge fill */
  --ember:#9c4f2b;                          /* single-shot urgent action, never a section colour */
  --good:#2f7d4f; --good-bg:#e6f0e9;        /* semantic-success */
  --warn:#8a6410; --warn-bg:#eddfb6;        /* block-ochre ground */
  --bad:#9c3226;  --bad-bg:#ecd9d5;         /* block-rose ground */
  --off:#6b655c;  --off-bg:#f1ede6;
  --block-sage:#d7dfcc; --block-mist:#d8e0e6; --block-alabaster:#f1eadd;
  --block-clay:#e7d3c4; --block-ink:#1f2329;
  /* Shadow-light by design: elevation 1 is a hairline, not a shadow. This is the
     documented level-2 "soft elevation", reserved for things that genuinely float. */
  --shadow:0 4px 16px rgba(0,0,0,.06);
  --r-xs:2px; --r-sm:6px; --r-md:8px; --r-lg:24px; --r-xl:32px; --r-pill:50px; --r-full:9999px;
  --radius:var(--r-lg);
  /* The OS draws the select popup and the date picker. Without this it draws them
     light on a dark page, which is most of why these controls looked foreign. */
  color-scheme:light;
}
/* Dark mode is NOT part of the design system ("Dark mode is not documented"). The
   system's own note names the ink colour-block as the closest analog, so the dark
   theme is derived from block-ink rather than invented — an extension, flagged as
   such, so nobody mistakes it for a documented surface. */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --canvas:#1f2329; --bg:#171a1f; --panel:#1f2329;
    --line:#343941; --line2:#282d34;
    --ink:#ffffff; --ink2:#c9c4bb; --ink3:#9a948b;
    --accent:#ffffff; --accent-ink:#000000;
    --sel-bg:rgba(255,255,255,.16); --sel-ink:var(--ink);   /* on-inverse-soft */
    color-scheme:dark;
    --ember:#c4703f;
    /* Block grounds re-derived for the dark theme: same hue families, dropped to
       sit on the ink canvas. A light mist panel on a dark page reads as a hole. */
    --block-sage:#232b24; --block-mist:#232a31; --block-alabaster:#272420;
    --block-clay:#2c2420; --block-ink:#12151a;
    --good:#7fc79b; --good-bg:#1d3327; --warn:#e0c98a; --warn-bg:#3a3320;
    --bad:#e0a79c; --bad-bg:#3a2a27; --off:#9a948b; --off-bg:#282d34;
    --shadow:0 4px 16px rgba(0,0,0,.45);
  }
}
:root[data-theme="dark"]{
    --canvas:#1f2329; --bg:#171a1f; --panel:#1f2329;
    --line:#343941; --line2:#282d34;
    --ink:#ffffff; --ink2:#c9c4bb; --ink3:#9a948b;
    --accent:#ffffff; --accent-ink:#000000;
    --sel-bg:rgba(255,255,255,.16); --sel-ink:var(--ink);   /* on-inverse-soft */
    color-scheme:dark;
    --ember:#c4703f;
    /* Block grounds re-derived for the dark theme: same hue families, dropped to
       sit on the ink canvas. A light mist panel on a dark page reads as a hole. */
    --block-sage:#232b24; --block-mist:#232a31; --block-alabaster:#272420;
    --block-clay:#2c2420; --block-ink:#12151a;
    --good:#7fc79b; --good-bg:#1d3327; --warn:#e0c98a; --warn-bg:#3a3320;
    --bad:#e0a79c; --bad-bg:#3a2a27; --off:#9a948b; --off-bg:#282d34;
    --shadow:0 4px 16px rgba(0,0,0,.45);
  }
/* Mono is taxonomy, never body: eyebrows, captions, metric labels, column heads.
   Always uppercase with positive tracking. */
.eyebrow,.stat span,.cardStats span,.kpi span,.gcell span,.insightBox h4,.fgroup h4,th{
  font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  text-transform:uppercase;letter-spacing:.6px;font-weight:400}
/* Mono runs wider than Inter, so the documented 12px caption wraps a three-part
   label inside these dense tiles. The role is kept; the size is tightened per
   surface in the component rules below. */
*{box-sizing:border-box}
/* An author display rule beats the UA stylesheet's [hidden]{display:none}, so
   .modal{display:flex} left the dialog — and its 60% backdrop — permanently over
   the page. Force it for every element that toggles via the hidden attribute. */
[hidden]{display:none !important}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font:14px/1.5 Inter,system-ui,-apple-system,"Segoe UI",sans-serif;-webkit-font-smoothing:antialiased}
h1,h4{margin:0}
button,input,select{font:inherit;color:inherit}

.topbar{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  padding:14px 20px;background:var(--panel);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:30}
.brand{display:flex;align-items:center;gap:12px}
/* RS monogram. The path is relative so the same declaration resolves both when
   app.css is served from /app.css and when it is inlined into a design artboard
   sitting next to its own copy of the file. */
.brand .dot{width:30px;height:30px;border-radius:var(--r-full);flex:none;
  background:url(rs-mark.png) center/contain no-repeat}
.topbar h1{font-size:16px;font-weight:650;letter-spacing:-.01em}
.sub{margin:1px 0 0;font-size:12px;color:var(--ink3)}
.topActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
/* Source / Range / From / To / Sort. The pill wrapper always matched the system;
   the control inside did not — it kept the browser's own arrow, font metrics and
   focus ring, which is what made these look foreign next to the facet pills. */
.dateField,.sortField{position:relative}
.dateField{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--ink2);
  background:var(--bg);border:1px solid var(--line);border-radius:var(--r-pill);padding:5px 11px;
  transition:border-color .14s}
.dateField:hover,.sortField:hover{border-color:var(--ink3)}
/* Focus lives on the pill, not on the control, so the whole component lights up. */
.dateField:focus-within,.sortField:focus-within{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.dateField input{border:0;background:transparent;font-size:12.5px}
.dateField input:focus,.dateField select:focus,.sortField select:focus{outline:none}

/* Strip the native control and draw our own chevron — the same Material Symbol
   the facet dropdowns use, so every "opens a list" affordance is one shape. */
/* Superseded by the custom menu: the element stays in the DOM as the source of
   truth for every existing reader, but is no longer what you see or click. */
.nativeSelect{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden}
.selDd .ddBtn{border:0;background:transparent;padding:0;height:auto;font-size:12.5px;font-weight:520;color:var(--ink)}
.selDd .ddBtn:hover{background:transparent}
.selDd{display:inline-flex}
.dateField select,.sortField select{appearance:none;-webkit-appearance:none;
  border:0;background:transparent;cursor:pointer;font-size:12.5px;font-weight:520;
  color:var(--ink);padding:0 17px 0 0;margin:0}
.msym-ok .dateField:has(select:not(.nativeSelect))::after,.msym-ok .sortField:has(select:not(.nativeSelect))::after{
  content:"keyboard_arrow_down";font-family:"Material Symbols Outlined";
  font-feature-settings:"liga";font-size:16px;line-height:1;color:var(--ink3);
  position:absolute;right:10px;top:50%;transform:translateY(-50%);pointer-events:none}
.dateField:has(select):hover::after,.sortField:hover::after{color:var(--ink)}
/* The date fields keep the calendar button; only its default tint is wrong. */
.dateField input[type=date]::-webkit-calendar-picker-indicator{
  cursor:pointer;opacity:.5;transition:opacity .14s}
.dateField input[type=date]:hover::-webkit-calendar-picker-indicator{opacity:1}
.btn{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-pill);padding:7px 15px;cursor:pointer;
  font-size:13px;font-weight:520;display:inline-flex;align-items:center;gap:7px;transition:.14s}
.btn:hover{border-color:var(--ink3)}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.btn.primary:hover{filter:brightness(1.06)}
.btn.ghost{background:transparent}
.btn[disabled]{opacity:.55;cursor:progress}
.pill{background:var(--sel-bg);color:var(--sel-ink);border-radius:var(--r-full);font-size:11px;padding:1px 7px;font-weight:600}
/* Theme toggle. Shows the theme it will switch TO, so the moon means "go dark".
   Only one icon is ever in the flow. */
.iconBtn{gap:6px}
.iconBtn svg{flex:none}
/* Keyed off aria-pressed, not the root attribute: while the page is simply
   following a dark OS there is no data-theme set, and keying off it left a moon
   icon showing next to a "Light" label. */
.iconBtn .sun{display:none}
.iconBtn[aria-pressed="true"] .sun{display:inline-block}
.iconBtn[aria-pressed="true"] .moon{display:none}

.stats{display:flex;flex-direction:column;gap:12px;padding:14px 20px 0}
/* Library counts and performance figures are two sections with a rule between.
   Stacked by default, with the rule running across; side by side only where a
   screen is wide enough for both rows to sit on one line. */
.statGroup{display:flex;flex-direction:column;gap:7px;min-width:0}
.statLabel{font-size:10px;color:var(--ink3);padding-left:2px}
.statGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
.statGroup:first-child .statGrid{grid-template-columns:repeat(auto-fit,minmax(180px,260px))}
.statRule{height:1px;background:var(--line);flex:none;margin:2px 0}
@media (min-width:1600px){
  .stats{flex-direction:row;align-items:stretch;gap:16px}
  .statGroup:first-child{flex:0 0 auto}
  .statGroup:first-child .statGrid{grid-template-columns:repeat(3,minmax(150px,1fr))}
  .statGroup:last-child{flex:1 1 0}
  .statRule{width:1px;height:auto;align-self:stretch;margin:20px 0 0}
}
.stat{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:14px 18px}
.stat b{display:block;font-size:19px;font-weight:650;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
/* A period delta rides under its figure rather than beside it — at 19px there is
   no room on the line for both. */
.stat b .delta{display:block;font-size:11px;margin-top:2px}
.stat span{font-size:10px;color:var(--ink3);letter-spacing:.4px}

.toolbar{display:flex;gap:9px;align-items:center;flex-wrap:wrap;padding:16px 20px 14px;margin-top:16px;position:relative}
/* Same hairline as between Library and Performance, inset to the page gutter, so
   the metrics read as one block and the search + filters as the next. */
.toolbar::before{content:"";position:absolute;left:20px;right:20px;top:0;height:1px;background:var(--line)}
.search{flex:1;min-width:230px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-pill);padding:9px 13px;font-size:13.5px}
.search:focus{outline:2px solid var(--accent);outline-offset:-1px}
.seg{display:inline-flex;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-pill);padding:2px}
.seg button{border:0;background:transparent;padding:6px 15px;border-radius:var(--r-pill);cursor:pointer;font-size:13px;font-weight:530;color:var(--ink2)}
.seg button.on{background:var(--ink);color:var(--panel)}
.sortField{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--ink3);
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-pill);padding:5px 12px;
  transition:border-color .14s}
.sortField select{max-width:210px}

/* Facet dropdowns. Chips were honest but the row ran to five wrapping lines
   once funnel arrived; a dropdown per facet keeps multi-select and one line. */
.facets{display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:0 20px 14px}
.dd{position:relative}
.ddBtn{display:inline-flex;align-items:center;gap:7px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-pill);padding:8px 14px;cursor:pointer;font-size:13px;font-weight:480;white-space:nowrap}
.ddBtn:hover{border-color:var(--ink3)}
.ddBtn.active{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.ddBtn .cnt{background:var(--sel-bg);color:var(--sel-ink);border-radius:var(--r-full);font-size:10.5px;padding:1px 6px;font-weight:600}
.ddBtn .msym{flex:none}
.ddMenu{position:absolute;z-index:40;top:calc(100% + 5px);left:0;min-width:210px;max-height:320px;overflow:auto;
  max-width:min(340px,calc(100vw - 20px));
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow);padding:6px}
/* Near the right edge the menu opens leftwards instead of hanging off screen. */
.ddMenu.alignRight{left:auto;right:0}
/* Too narrow for either side: pin it to the window edge. */
.ddMenu.alignEdge{left:var(--menuShift,0);right:auto}
/* Long campaign and ad-set names wrap rather than being clipped. */
.ddMenu label{display:flex;align-items:flex-start;gap:9px;padding:7px 9px;border-radius:var(--r-sm);cursor:pointer;
  font-size:13px;line-height:1.35;word-break:break-word}
.ddMenu input{margin-top:2px}
.ddMenu label:hover{background:var(--bg)}
.ddMenu input{accent-color:var(--accent);margin:0}
.ddMenu .ddNone{padding:7px 9px;font-size:12.5px;color:var(--ink3)}

.resultsBar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 20px 10px}
.viewIcons{display:inline-flex;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-full);padding:2px}
.viewIcons button{border:0;background:transparent;padding:6px 9px;border-radius:var(--r-full);cursor:pointer;
  color:var(--ink3);display:inline-flex;align-items:center}
.viewIcons button.on{background:var(--ink);color:var(--panel)}
.viewIcons button:hover:not(.on){color:var(--ink)}

.filters{margin:0 20px 14px;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:15px}
.fgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:15px}
.fgroup h4{font-size:10px;letter-spacing:.5px;color:var(--ink3);margin-bottom:7px}
.fgroup.wide{grid-column:span 2}
.fgroup select,.fgroup input[type=date]{width:100%;background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-md);padding:7px 9px;font-size:13px}
.range{display:flex;gap:6px}
.range input{width:100%;background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-md);padding:7px 9px;font-size:13px}
.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{border:1px solid var(--line);background:var(--bg);border-radius:var(--r-pill);padding:5px 12px;font-size:12.5px;cursor:pointer;font-weight:520}
.chip.on{background:var(--ink);color:var(--panel);border-color:var(--ink)}
.frow{display:flex;align-items:center;gap:12px;margin-top:14px;padding-top:12px;border-top:1px solid var(--line2)}
.muted{color:var(--ink3);font-size:12.5px}

main{padding:0 20px 60px}
/* Masonry: equal-width columns, unequal rows, every creative at its own aspect
   ratio. CSS columns would be simpler but flow items top-to-bottom per column,
   which scrambles a ranked list — these are real columns filled shortest-first
   in rank order, so the best ads still sit near the top of the screen. */
.grid{display:flex;align-items:flex-start;gap:16px}
.gridCol{display:flex;flex-direction:column;gap:16px;flex:1 1 0;min-width:0}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;cursor:pointer;
  display:flex;flex-direction:column;transition:.16s}
/* The tile IS the ad mockup now, so the preview loses its own frame and lets the
   card supply it — otherwise every tile carries two nested rounded borders. */
.card .adPreview{border:0;border-radius:0}
/* Tighter than the detail view: a tile is ~260px wide, not ~340. */
.apCompact{font-size:13px}
.apCompact .apHead{padding:10px 10px 6px;gap:7px}
.apCompact .apAvatar{width:32px;height:32px}
.apCompact .apPage{font-size:13px}
.apCompact .apSponsored{font-size:11px}
.apCompact .apRun{padding:0 10px 8px;font-size:11px}
.apCompact .apCopy{padding:0 10px 8px}
.apCompact .apText{font-size:13px;-webkit-line-clamp:3}
.apCompact .apMore{font-size:12px}
.apCompact .apCard{padding:8px 10px;gap:8px}
.apCompact .apDomain{font-size:10.5px}
.apCompact .apHeadline{font-size:13px;line-height:1.25}
.apCompact .apCta{padding:6px 10px;font-size:12px}
/* The play control is the tile's, not the mockup's — keep it centred over the
   creative the way it was on the old thumbnail. */
.apCompact .apMedia{overflow:hidden}
.apCompact .apMedia img{width:100%;height:100%;object-fit:cover}
.apCompact .badges{top:8px;left:8px;right:8px}
.card:hover{transform:translateY(-2px);border-color:var(--ink)}
/* aspect-ratio is set per card from the creative's real pixel size; 4/5 is only
   the fallback for ads whose creative we could not capture. */
.creative{position:relative;background:var(--off-bg);aspect-ratio:4/5;overflow:hidden;display:flex;align-items:center;justify-content:center}
.creative img{width:100%;height:100%;object-fit:cover;display:block}
.noimg{display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--ink3);font-size:11.5px;text-align:center;padding:14px}
.noimg svg{opacity:.4}
.badges{position:absolute;top:9px;left:9px;right:9px;display:flex;gap:6px;align-items:center}
.badge{background:rgba(0,0,0,.78);color:#fff;border-radius:var(--r-pill);padding:3px 9px;font-size:11px;font-weight:560;backdrop-filter:blur(6px)}
.badge.live{background:var(--pGreen);color:var(--pGreen-on)}
.badge.off{background:var(--pRed);color:var(--pRed-on)}
.play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none}
.play span{width:46px;height:46px;border-radius:50%;background:rgba(9,12,17,.6);backdrop-filter:blur(4px);
  display:flex;align-items:center;justify-content:center;color:#fff;font-size:16px;padding-left:3px}
.cardBody{padding:10px 12px 12px;display:flex;flex-direction:column;gap:8px;flex:1;
  border-top:1px solid var(--line2)}
.cardName{font-size:12.5px;font-weight:560;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.cardId{font-family:"JetBrains Mono",ui-monospace,monospace;font-size:10.5px;color:var(--ink3)}
.cardPills{display:flex;gap:5px;flex-wrap:wrap;align-items:center;justify-content:flex-start}
.cardPills .tag{font-size:11px;padding:2px 8px}
.cardPills .rec{font-size:11px;padding:2px 8px}
.cardPills .grade{min-width:24px;height:20px;font-size:11.5px}
/* Four stats now (CPA joined), so the gap tightens to keep $-figures unwrapped
   at the 230px minimum column width. */
.cardStats{display:flex;gap:7px;margin-top:auto;padding-top:8px;border-top:1px solid var(--line2);font-variant-numeric:tabular-nums}
.cardStats div{flex:1}
.cardStats b{display:block;font-size:13px;font-weight:620}
.cardStats span{font-size:9px;color:var(--ink3);letter-spacing:.3px}
.roasGood{color:var(--good)} .roasMid{color:var(--warn)} .roasBad{color:var(--bad)}

/* ONE pill palette, four hues, used by every pill in the app — status,
   funnel stage and format. A colour means the same thing wherever it
   appears: green = good / on / owned, blue = active-neutral, yellow = early /
   caution, red = off. Nothing defines its own pill colour any more; if a hue
   needs to change it changes here and moves everywhere at once.

   Each hue carries its own ON-colour because a pill inverts between themes: a
   saturated fill with white text in light mode, a light fill with dark text in
   dark mode. Reusing one foreground put near-white on light blue. All sixteen
   fg/bg pairs clear 4.5:1 in both themes. */
:root{
  --pGreen:#0b7a41;  --pGreen-bg:#e4f6ec;  --pGreen-on:#ffffff;
  --pBlue:#1a56a8;   --pBlue-bg:#e8f0fd;   --pBlue-on:#ffffff;
  --pYellow:#8a5e07; --pYellow-bg:#fdf4e2; --pYellow-on:#ffffff;
  --pRed:#b3261e;    --pRed-bg:#fdeceb;    --pRed-on:#ffffff;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --pGreen:#4ade80;  --pGreen-bg:#12301f;  --pGreen-on:#04240f;
    --pBlue:#7cb0f5;   --pBlue-bg:#12233d;   --pBlue-on:#0a1a2e;
    --pYellow:#fbbf24; --pYellow-bg:#33270a; --pYellow-on:#2a1c00;
    --pRed:#f87171;    --pRed-bg:#331617;    --pRed-on:#2a0c0c;
  }
}
:root[data-theme="dark"]{
    --pGreen:#4ade80;  --pGreen-bg:#12301f;  --pGreen-on:#04240f;
    --pBlue:#7cb0f5;   --pBlue-bg:#12233d;   --pBlue-on:#0a1a2e;
    --pYellow:#fbbf24; --pYellow-bg:#33270a; --pYellow-on:#2a1c00;
    --pRed:#f87171;    --pRed-bg:#331617;    --pRed-on:#2a0c0c;
  }
/* The wrap owns BOTH scroll axes. Without a height it grew to fit every row and
   the page scrolled instead, so `position:sticky` on the header froze it to a
   scrollport that was itself scrolling away.
   --tblMax is set from the real topbar height in JS: sized so that even at the
   page's maximum scroll the wrap's top still clears the sticky topbar — otherwise
   the frozen header parks *behind* it. */
.tableWrap{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  overflow:auto;max-height:var(--tblMax, calc(100vh - 260px))}
/* Fixed layout: the <colgroup> widths are authoritative, so a resize is exact and
   a frozen column's offset is knowable. */
table{border-collapse:separate;border-spacing:0;font-size:13px;table-layout:fixed}
th{position:sticky;top:0;z-index:4;background:var(--panel);text-align:left;padding:10px 12px;font-size:10px;
  letter-spacing:.4px;color:var(--ink3);border-bottom:1px solid var(--line);cursor:pointer;white-space:nowrap}
th:hover{color:var(--ink)}
/* Headers are drag handles as well as sort buttons. The grab cursor is the only
   hint that reordering exists, so it stays on every header. */
th{cursor:grab}
th:active{cursor:grabbing}
th.sorted{color:var(--ink)}
th.dragging{opacity:.4}
/* While a header is in flight, nothing on the page should select. */
body.colDragging{user-select:none;cursor:grabbing}
body.colResizing{user-select:none;cursor:col-resize}
.tblNote{margin:0 0 8px}

/* Frozen leading columns. They need an opaque fill, since the rest of the row
   slides underneath them, and they have to follow the row's hover state or the
   pinned part of a hovered row would read as a different row. */
td.frozen{position:sticky;z-index:3;background:var(--panel)}
tbody tr:hover td.frozen{background:var(--bg)}
th.frozen{z-index:6}
.frozenEdge{box-shadow:inset -1px 0 0 0 var(--line)}
/* The header's own bottom rule is drawn by its border; a frozen header cell needs
   both, so the shadow carries the vertical divider only. */

/* Resize grip on the right edge of every header. */
.colGrip{position:absolute;top:0;right:0;width:9px;height:100%;cursor:col-resize;
  border-right:2px solid transparent;transform:translateX(1px)}
.colGrip:hover{border-right-color:var(--accent)}
th{position:sticky}   /* sticky already; restated so the grip's containing block is the th */
/* The drop marker sits on the side the column will actually land on: dragging
   right drops after the target, dragging left drops before it. */
th.dropTarget{box-shadow:inset 2px 0 0 0 var(--accent);color:var(--ink)}
th.dropTarget.dropAfter{box-shadow:inset -2px 0 0 0 var(--accent)}

/* Column controls: table view only, aligned to the same 20px page gutter as
   everything else on the page. */
/* Typed filters sit in the facet row and must read as siblings of the dropdown
   buttons, not as stray form fields. */
.tfilter{display:inline-flex;align-items:center;gap:7px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-pill);padding:0 12px 0 14px;height:34px;cursor:text}
.tfilter:hover{border-color:var(--ink3)}
.tfilter.on{border-color:var(--accent)}
.tfLabel{font-size:12.5px;font-weight:560;color:var(--ink2);white-space:nowrap}
.tfilter.on .tfLabel{color:var(--accent)}
.tfilter input{border:0;background:none;font:inherit;font-size:12.5px;color:var(--ink);padding:0;outline:0;min-width:0}
.tfilter input::placeholder{color:var(--ink3)}

.tableTools{display:flex;align-items:center;gap:12px;margin:0 20px 10px}
.colHint{font-size:11.5px}
.colMenu{max-height:340px;overflow:auto;min-width:210px}
.colMenuFoot{border-top:1px solid var(--line2);margin-top:6px;padding-top:8px}
.colMenuFoot .btn{width:100%;justify-content:center}
/* Fixed layout means overflow has to be handled per cell, or a long campaign name
   spills across its neighbours. */
td{padding:9px 12px;border-bottom:1px solid var(--line2);vertical-align:middle;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
tbody tr{cursor:pointer}
tbody tr:hover{background:var(--bg)}
.num{text-align:right;font-variant-numeric:tabular-nums}
.thumb{width:40px;height:50px;object-fit:cover;border-radius:var(--r-sm);background:var(--off-bg);display:block}
.thumbPh{width:40px;height:50px;border-radius:var(--r-sm);background:var(--off-bg)}
.tName{font-weight:530;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tId{font-family:"JetBrains Mono",ui-monospace,monospace;font-size:10.5px;color:var(--ink3)}
.tag{display:inline-flex;align-items:center;gap:5px;border-radius:var(--r-pill);padding:2px 9px;font-size:11.5px;font-weight:560}
/* Status, format and funnel stage all draw from the shared palette above, so
   the same green means Active, Retention and Image alike. */
.tag.live{background:var(--pGreen-bg);color:var(--pGreen)}
.tag.off{background:var(--pRed-bg);color:var(--pRed)}
.tag.vid{background:var(--pBlue-bg);color:var(--pBlue)}
.tag.img{background:var(--pGreen-bg);color:var(--pGreen)}

/* Period-over-period movement. Spend stays neutral — spending more is neither
   good nor bad on its own; only the return metrics are coloured. */
.delta{font-size:10.5px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.delta.up{color:var(--good)} .delta.down{color:var(--bad)}
.delta.flat{color:var(--ink3)} .delta.newly{color:var(--warn)}
.periodNote{font-size:12px;color:var(--ink3);margin:0 0 12px}
/* Funnel stage: Prospecting yellow (earliest, least owned audience),
   Retargeting blue, Retention green. Leads and Engagement were not assigned a
   hue, so they stay neutral rather than borrowing a meaning they do not have. */
.tag.fun{background:var(--off-bg);color:var(--ink2);border:1px solid var(--line)}
.tag.fun.fProspecting{background:var(--pYellow-bg);color:var(--pYellow);border-color:transparent}
.tag.fun.fRetargeting{background:var(--pBlue-bg);color:var(--pBlue);border-color:transparent}
.tag.fun.fRetention{background:var(--pGreen-bg);color:var(--pGreen);border-color:transparent}
.badge.fun{background:rgba(9,12,17,.62);color:#e8ecf1;font-weight:500}
.cardStats b .delta{display:block;font-size:10px;margin-top:1px}

.empty{padding:70px 20px;text-align:center;color:var(--ink3)}
/* The dialog fits the viewport rather than running off it. The card is capped at
   the screen height and its body scrolls inside, so the card is always wholly in
   view and centred — a 9:16 story detail no longer pushes its own header and
   close button off the top. `safe center` is belt-and-braces: were the card ever
   to exceed the container it would fall back to start instead of scrolling to an
   unreachable top. */
.modal{position:fixed;inset:0;background:rgba(10,13,18,.6);backdrop-filter:blur(3px);z-index:60;
  display:flex;align-items:safe center;justify-content:center;padding:28px 20px;overflow:auto}
.modalCard{background:var(--panel);border-radius:var(--r-lg);max-width:1000px;width:100%;position:relative;box-shadow:var(--shadow);
  max-height:calc(100vh - 56px);max-height:calc(100dvh - 56px);display:flex;flex-direction:column;overflow:hidden}
.close{position:absolute;top:13px;right:13px;z-index:2;background:var(--panel);border:1px solid var(--line);
  width:34px;height:34px;border-radius:var(--r-full);cursor:pointer;font-size:13px;color:var(--ink);
  display:flex;align-items:center;justify-content:center;padding:0}
.close .msym{font-size:20px}
/* On the viewer the button sits over the scrim, not a panel. */
#lightboxClose{background:rgba(0,0,0,.55);border-color:rgba(255,255,255,.22);color:#fff}
#lightboxClose:hover{background:rgba(0,0,0,.78)}
/* min-height:0 lets this shrink below its content so the card's max-height wins
   and the overflow lands here rather than blowing the card out. */
.modalBody{display:grid;grid-template-columns:minmax(0,340px) minmax(0,1fr);gap:0;overflow:auto;min-height:0}
@media (max-width:760px){.modalBody{grid-template-columns:1fr}.fgroup.wide{grid-column:span 1}}
.mCreative{padding:22px;border-right:1px solid var(--line2);background:var(--bg);border-radius:var(--r-lg) 0 0 var(--r-lg)}
@media (max-width:760px){.mCreative{border-right:0;border-bottom:1px solid var(--line2);border-radius:var(--r-lg) var(--r-lg) 0 0}}
.mCreative .noimg{border-radius:0;background:#e4e6eb;justify-content:center;color:#65676b;height:100%}

/* --- "as the shopper saw it" preview -----------------------------------------
   A facsimile of the served Meta ad. It keeps Meta's own light chrome in both
   dashboard themes on purpose: a preview that restyled itself for dark mode
   would no longer show what the ad actually looked like in the feed. Colours
   here are Meta's, so they are literals rather than dashboard tokens. */
.adPreview{background:#fff;border:1px solid var(--line);border-radius:10px;overflow:hidden;
  font:14px/1.34 "Helvetica Neue",Helvetica,Arial,sans-serif;color:#050505}
.apHead{display:flex;align-items:center;gap:8px;padding:12px 12px 8px}
.apAvatar{width:40px;height:40px;border-radius:50%;flex:none;object-fit:cover;background:#e4e6eb;display:block}
.apAvatar.fallback{display:flex;align-items:center;justify-content:center;background:#1b1915;color:#fff8ea;
  font-size:14px;font-weight:700;letter-spacing:.02em}
.apWho{min-width:0}
.apPage{font-size:14px;font-weight:600;color:#050505;line-height:1.2}
.apSponsored{font-size:12px;color:#65676b;line-height:1.35}
.apRun{display:flex;align-items:center;gap:6px;padding:0 12px 10px;font-size:12px;color:#65676b}
.apDot{width:8px;height:8px;border-radius:50%;flex:none;background:#bcc0c4}
.apDot.on{background:#31a24c}
.apCopy{padding:0 12px 10px}
.apText{margin:0;font-size:14px;color:#050505;white-space:pre-wrap;word-break:break-word;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}
.apText.open{-webkit-line-clamp:unset;overflow:visible}
.apMore{margin-top:2px;padding:0;border:0;background:none;cursor:pointer;font:inherit;font-size:14px;
  font-weight:600;color:#65676b}
.apMore:hover{text-decoration:underline}
/* The creative sets its own height. Only ~14% of ads have cached pixel
   dimensions, so forcing a 4:5 box here cropped everything else to the wrong
   shape; letting the media size itself means a 9:16 reel is 9:16 and a 4:5
   image is 4:5, whether or not its dimensions were known up front. */
.apMedia{position:relative;background:#e4e6eb;display:flex}
.apMedia img,.apMedia video{width:100%;height:auto;display:block}
.apMedia.gone{aspect-ratio:4/5}
.apMedia.gone .noimg{width:100%}
.apCard{display:flex;align-items:center;gap:10px;padding:10px 12px;background:#f0f2f5;border-top:1px solid #dfe1e6}
.apCardText{min-width:0;flex:1 1 auto}
.apDomain{font-size:12px;color:#65676b;letter-spacing:.01em;line-height:1.3}
.apHeadline{font-size:15px;font-weight:700;color:#050505;line-height:1.25;margin-top:1px;word-break:break-word}
.apHeadline.missing{font-weight:500;font-style:italic;color:#8a8d91}
.apCta{flex:none;border:1px solid #ccd0d5;background:#e4e6eb;color:#050505;border-radius:6px;
  padding:8px 12px;font:inherit;font-size:13px;font-weight:600;cursor:default;
  text-decoration:none;display:inline-flex;align-items:center;white-space:nowrap}
/* When it carries a real destination it behaves like the button it imitates. */
a.apCta{cursor:pointer}
a.apCta:hover{background:#d8dade;border-color:#b9bec6;color:#050505}
/* Only a minority of rows carry creative_cta_type; a fallback button must not
   pass itself off as recorded data. */
.apCta.guessed{color:#65676b;border-style:dashed}

/* "View at actual size" — a circular icon button on the creative, in the system's
   icon-button shape. Sits over the image, so it carries its own solid fill. */
.lbOpen{position:absolute;top:10px;right:10px;width:34px;height:34px;border:0;border-radius:var(--r-full);
  background:rgba(0,0,0,.62);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;
  backdrop-filter:blur(4px);opacity:0;transition:opacity .14s ease;z-index:2}
.apMedia:hover .lbOpen,.lbOpen:focus-visible{opacity:1}
.lbOpen:hover{background:rgba(0,0,0,.82)}
.lbOpen .msym{font-size:18px}
/* Touch has no hover, so the control must not be invisible there. */
@media (hover:none){.lbOpen{opacity:.9}}

/* The viewer itself: elevation 3 — scrim plus the creative at its own size. */
.lightbox{z-index:80;padding:40px 24px;align-items:center;justify-content:center}
.lightbox:not([hidden]){display:flex}
.lbBody{display:flex;flex-direction:column;align-items:center;gap:12px;max-width:100%;max-height:100%}
.lbBody img,.lbBody video{max-width:min(100%,1400px);max-height:calc(100vh - 140px);
  width:auto;height:auto;display:block;border-radius:var(--r-md);background:var(--off-bg)}
.lbCap{font-family:"JetBrains Mono",ui-monospace,monospace;font-size:11px;letter-spacing:.5px;
  text-transform:uppercase;color:#fff;opacity:.85;text-align:center}
#lightboxClose{position:absolute;top:18px;right:22px;z-index:2}

/* The expand control sits beside close, so close keeps its corner. */
.close.cardBtn{right:55px}
/* Full screen: the card takes the window, and the creative column widens with it
   rather than leaving the extra space to the text. */
.modalCard.full{max-width:none;width:calc(100vw - 32px);height:calc(100vh - 32px);max-height:none}
.modalCard.full .modalBody{grid-template-columns:minmax(0,44%) minmax(0,1fr);height:100%}
.modalCard.full .mCreative{display:flex;flex-direction:column;justify-content:center}
@media (max-width:760px){.modalCard.full .modalBody{grid-template-columns:1fr}}

.mActions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}
/* These two sit on the sunk panel under the preview. As ghost buttons they all
   but vanished on the dark ground, so they get a filled surface and a border
   strong enough to read against it in either theme. */
:root{ --btnFill:#eceff3; --btnEdge:#c3cad3; }
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){ --btnFill:#242a33; --btnEdge:#3d4653; }
}
:root[data-theme="dark"]{ --btnFill:#242a33; --btnEdge:#3d4653; }
/* NB these are <a> elements: without an explicit colour they fall through to the
   UA link blue, which is what made them read as faint blue text on the dark
   ground rather than as buttons at all. */
.mActions .btn{text-decoration:none;display:inline-flex;align-items:center;padding:8px 14px;
  background:var(--btnFill);border:1px solid var(--btnEdge);color:var(--ink);font-weight:600}
.mActions .btn:hover{border-color:var(--accent);color:var(--accent)}
/* The primary of the two, in the same accent the app already uses for Refresh
   and Save insight. */
.mActions .btn.key{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.mActions .btn.key:hover{filter:brightness(1.08);color:var(--accent-ink)}
.mFacts{margin:12px 0 0;font-family:"JetBrains Mono",ui-monospace,monospace;font-size:11px;
  color:var(--ink3);line-height:1.65}
.mMeta{padding:22px 24px 24px}
.mName{font-size:17px;font-weight:640;letter-spacing:-.01em;line-height:1.35;padding-right:34px;word-break:break-word}
.mId{font-family:"JetBrains Mono",ui-monospace,monospace;font-size:11.5px;color:var(--ink3);margin-top:4px}
.mTags{display:flex;gap:7px;margin:13px 0 18px;flex-wrap:wrap}
.kpis{display:grid;grid-template-columns:repeat(5,1fr);gap:9px;margin-bottom:11px}
@media (max-width:900px){.kpis{grid-template-columns:repeat(3,1fr)}}
@media (max-width:520px){.kpis{grid-template-columns:repeat(2,1fr)}}
.kpi{background:var(--bg);border:1px solid var(--line2);border-radius:var(--r-md);padding:9px 11px}
.kpi b .delta{display:block;font-size:10.5px;margin-top:3px}
.kpi b{display:block;font-size:15.5px;font-weight:650;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.kpi span{font-size:9.5px;color:var(--ink3);letter-spacing:.4px}
/* The Radar row, directly under the money row and built to the same tile shape
   so the two read as one block: the call, then the axes it was blended from. */
.grades{display:grid;grid-template-columns:1.5fr repeat(4,1fr);gap:9px;margin-bottom:19px}
@media (max-width:900px){.grades{grid-template-columns:repeat(3,1fr)}}
@media (max-width:520px){.grades{grid-template-columns:repeat(2,1fr)}}
.gcell{background:var(--bg);border:1px solid var(--line2);border-radius:var(--r-md);padding:9px 11px;
  display:flex;flex-direction:column;align-items:flex-start;gap:6px}
.gcell b{display:block}
.gcell span{font-size:9.5px;color:var(--ink3);letter-spacing:.4px}
.gradeNote{margin:0 0 19px;font-size:12.5px;color:var(--ink3)}
/* Collapsible properties table. The summary is the only always-visible part, so
   it carries the top rule the table used to own. */
.propsBox{border-top:1px solid var(--line2)}
.propsBox>summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;
  padding:11px 0;font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink3);font-weight:600}
.propsBox>summary::-webkit-details-marker{display:none}
.propsBox>summary:hover{color:var(--ink2)}
.propsCaret{transition:transform .15s ease;flex:none}
.propsBox[open]>summary .propsCaret{transform:rotate(180deg)}
.props{display:grid;grid-template-columns:132px 1fr;gap:0}
.props dt{padding:9px 0;font-size:12.5px;color:var(--ink3);border-bottom:1px solid var(--line2)}
.props dd{padding:9px 0;margin:0;font-size:13px;border-bottom:1px solid var(--line2);word-break:break-word}
/* Two tabs over one panel. Built as the system's pill toggle — the same shape as
   the period switcher — so the selected one takes the primary surface. A bare
   underline was too quiet to read as a control at all.
   Inter, not mono: the system reserves mono for captions. */
.mTabs{display:inline-flex;gap:2px;align-items:center;margin-top:20px;
  background:var(--bg);border:1px solid var(--line);border-radius:var(--r-pill);padding:3px}
.mTab{border:0;background:transparent;cursor:pointer;padding:8px 16px;border-radius:var(--r-pill);
  font-size:13.5px;font-weight:500;letter-spacing:-.01em;color:var(--ink2);white-space:nowrap;
  transition:background-color .14s,color .14s}
.mTab:hover{color:var(--ink)}
.mTab.on{background:var(--accent);color:var(--accent-ink);font-weight:540}
.mPanel{padding-top:14px}

/* Creative analysis block — replaces the old free-text insight box. */
.analyzeBox{margin-top:0;padding-top:8px}
.analyzeOut{margin-bottom:11px}
/* The skill writes this text, but a human is expected to extend it, so it stays
   editable rather than being read-only output. */
.analyzeText{width:100%;min-height:96px;background:var(--bg);border:1px solid var(--line2);
  border-radius:var(--r-md);padding:14px 16px;font:inherit;font-size:13px;line-height:1.55;
  color:var(--ink);resize:vertical}
.analyzeText:focus{outline:2px solid var(--accent);outline-offset:-1px}
.analyzeHint{margin:8px 0 0;font-size:12px;line-height:1.5;color:var(--ink3)}
.analyzeHint.on{color:var(--ink2)}
.analyzeHint.err{color:var(--bad)}

/* "Analyzing…": a light sweep across the button and a slow turn on the wand, so
   the wait reads as work in progress. The system is shadow-light, so this is
   motion in the surface itself, not a spinner bolted on. */
.analyzeRow .btn.working{position:relative;overflow:hidden;cursor:progress;opacity:1}
.analyzeRow .btn.working::after{content:"";position:absolute;inset:0;
  background:linear-gradient(110deg,transparent 0%,rgba(255,255,255,.22) 45%,rgba(255,255,255,.34) 50%,rgba(255,255,255,.22) 55%,transparent 100%);
  transform:translateX(-100%);animation:sweep 1.4s ease-in-out infinite}
.analyzeRow .btn.working:not(.primary)::after{
  background:linear-gradient(110deg,transparent 0%,rgba(0,0,0,.05) 45%,rgba(0,0,0,.09) 50%,rgba(0,0,0,.05) 55%,transparent 100%)}
@keyframes sweep{to{transform:translateX(100%)}}
.msym.spin{animation:turn 2.2s linear infinite}
@keyframes turn{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .analyzeRow .btn.working::after{animation:none;opacity:.5}
  .msym.spin{animation:none}
}
.analyzeRow{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.analyzeRow .btn .msym{font-size:17px}
/* A disabled action still has to explain itself, so it stays legible rather than
   fading to nothing — the tooltip says why it is unavailable. */
.analyzeRow .btn:disabled{opacity:.45;cursor:not-allowed}
.analyzeRow .btn:disabled:hover{border-color:var(--line)}

/* Every button in the ad-detail panel lifts slightly on hover. The design system
   is shadow-light, so this is a scale, not a shadow — and it presses back in on
   click so the control feels physical rather than merely animated. */
#modalBody .btn,#modalBody .apCta,#modalBody .close,#modalBody .lbOpen{
  transition:transform .13s ease,background-color .14s,border-color .14s,color .14s}
#modalBody .btn:hover:not(:disabled),#modalBody .apCta:hover:not(.dead),
#modalBody .lbOpen:hover{transform:scale(1.04)}
#modalBody .btn:active:not(:disabled),#modalBody .apCta:active:not(.dead){transform:scale(.98)}
/* The close and expand controls are round, so they scale from their own centre. */
.close{transition:transform .13s ease,background-color .14s,border-color .14s}
.close:hover{transform:scale(1.08)}
.close:active{transform:scale(.96)}
/* A disabled button must not appear to respond. */
#modalBody .btn:disabled:hover{transform:none}
/* Respect a reduced-motion preference. */
@media (prefers-reduced-motion:reduce){
  #modalBody .btn,#modalBody .apCta,#modalBody .close,#modalBody .lbOpen,.close{transition:none}
  #modalBody .btn:hover,#modalBody .apCta:hover,#modalBody .lbOpen:hover,.close:hover{transform:none}
}

.insightBox{margin-top:19px}
.insightBox h4{font-size:10px;letter-spacing:.5px;color:var(--ink3);margin-bottom:7px}
.insightBox textarea{width:100%;min-height:78px;background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-md);
  padding:10px 12px;font:inherit;font-size:13px;resize:vertical}
.insightBox textarea:focus{outline:2px solid var(--accent);outline-offset:-1px}
.saveRow{display:flex;align-items:center;gap:10px;margin-top:9px}
.loading{padding:70px;text-align:center;color:var(--ink3)}
.spinner{width:26px;height:26px;border:2.5px solid var(--line);border-top-color:var(--accent);border-radius:50%;
  animation:spin .8s linear infinite;margin:0 auto 13px}
@keyframes spin{to{transform:rotate(360deg)}}

/* A CTA with no recorded destination: visibly inert, so it never looks like a
   link that failed. Roughly a fifth of ads have no landing_page_url. */
.apCta.dead{opacity:.5;cursor:not-allowed}
.apCta.dead:hover{filter:none}

.apMissing{font-size:12px;color:var(--ink3);font-style:italic}

/* ---- Dynamic-creative rotators -------------------------------------------
   The live ad cycles its copy, so the preview does too. Both the caption and the
   headline cross-fade on a 2s beat and expose a clickable pagination control:
   dots for the caption, a segmented slide bar for the headline. Hovering either
   pauses, because 2s is not long enough to read a full caption. */
.apText.fading,
.apHeadline.fading{opacity:0}
.apText,
.apHeadline{transition:opacity .16s ease}

/* Elongated segments, one per variant, sized to fill whatever they sit in.
   Headline bar lives inside .apCardText, which is a flex sibling of the CTA, so it
   stops where the button begins and never runs underneath it. */
.apSlide{display:flex;gap:3px;align-items:center;margin-top:7px}
.apSlideSeg{flex:1 1 0;height:3px;min-width:6px;padding:0;border:0;border-radius:99px;
  cursor:pointer;background:#d6d9de;transition:background .18s,height .18s}
.apSlideSeg:hover{background:#a9aeb6}
.apSlideSeg.on{background:var(--accent)}

/* Caption bar: full bleed, flush above the creative. */
.apSlideFull{margin:0 0 0;padding:0 0 6px;gap:4px}
.apSlideFull .apSlideSeg{height:4px;border-radius:0}
.apSlideFull .apSlideSeg:first-child{border-radius:99px 0 0 99px}
.apSlideFull .apSlideSeg:last-child{border-radius:0 99px 99px 0}

@media (prefers-reduced-motion:reduce){
  /* No auto-advance; the controls still work for stepping through by hand. */
  .apText,.apHeadline{transition:none}
  .apText.fading,.apHeadline.fading{opacity:1}
}

/* Inline playback in the grid. The video fills the tile the poster occupied, so
   the card does not resize when playback starts. */
.tileVideo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  background:#000;display:block;z-index:2}
.playBtn{width:46px;height:46px;border-radius:50%;border:0;cursor:pointer;
  background:rgba(9,12,17,.62);backdrop-filter:blur(4px);color:#fff;font-size:16px;
  padding-left:3px;display:flex;align-items:center;justify-content:center;
  transition:background .16s,transform .16s}
.playBtn:hover{background:rgba(9,12,17,.82);transform:scale(1.07)}
.play{pointer-events:none}          /* the overlay must not swallow card clicks */
.play .playBtn{pointer-events:auto} /* only the button itself is clickable */

/* ---------- Radar grading ---------- */
/* Letter grades are percentile ranks within the current view, so the palette is
   deliberately gentle: a C is "middle of this cohort", not a failure. */
.grade{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:22px;
  padding:0 6px;border-radius:6px;font-size:12px;font-weight:650;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.grade.gA1{background:#d7f5e3;color:#0b7a42}
.grade.gA {background:#e2f7ea;color:#12854c}
.grade.gB {background:#fdf3d7;color:#8a6410}
.grade.gC {background:#fde8d5;color:#a5560f}
.grade.gD {background:#fddcda;color:#b03028}
.grade.gF {background:#fbd5d2;color:#a1231c}
.grade.gNone{background:var(--off-bg);color:var(--ink3);font-weight:520}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .grade.gA1{background:#123322;color:#5fd897}
 :root:not([data-theme="light"]) .grade.gA{background:#12301f;color:#4ade80}
 :root:not([data-theme="light"]) .grade.gB{background:#33270a;color:#fbbf24}
 :root:not([data-theme="light"]) .grade.gC{background:#3a2410;color:#f59e56}
 :root:not([data-theme="light"]) .grade.gD{background:#3a1a18;color:#f87171}
 :root:not([data-theme="light"]) .grade.gF{background:#3d1714;color:#f87171}}

.rec{display:inline-flex;align-items:center;border-radius:6px;padding:3px 10px;font-size:12px;font-weight:600}
.rec.scale  {background:#d7f5e3;color:#0b7a42}
.rec.iterate{background:#fdf3d7;color:#8a6410}
.rec.pause  {background:#fbd5d2;color:#a1231c}
.rec.review {background:var(--off-bg);color:var(--ink2)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .rec.scale{background:#123322;color:#5fd897}
 :root:not([data-theme="light"]) .rec.iterate{background:#33270a;color:#fbbf24}
 :root:not([data-theme="light"]) .rec.pause{background:#3d1714;color:#f87171}}

/* Google Material Symbols. Hidden until the font is confirmed, because the glyph
   is a ligature: with the font missing the element would show the word "trophy".
   Blank is the better failure. */
.msym{font-family:"Material Symbols Outlined";font-weight:400;font-style:normal;line-height:1;
  letter-spacing:normal;text-transform:none;white-space:nowrap;direction:ltr;display:inline-block;
  font-size:19px;flex:none;visibility:hidden;
  -webkit-font-feature-settings:"liga";font-feature-settings:"liga";
  -webkit-font-smoothing:antialiased;font-variation-settings:"FILL" 0,"wght" 400,"GRAD" 0,"opsz" 24}
.msym-ok .msym{visibility:visible}
.btn .msym{font-size:18px}
.iconBtn .msym,.viewIcons .msym{font-size:19px}
.ddCaret{font-size:16px;opacity:.75}
.noimgIcon{font-size:26px;opacity:.45}
.mActions .btn .msym{font-size:18px;margin-right:2px}

.radarTabs{display:flex;gap:2px;align-items:center;flex-wrap:wrap;margin:0 0 14px;
  border-bottom:1px solid var(--line)}
.radarTabs button{border:0;background:transparent;padding:9px 14px;cursor:pointer;font-size:13.5px;
  font-weight:540;color:var(--ink2);border-bottom:2px solid transparent;margin-bottom:-1px;display:flex;gap:7px;align-items:center}
.radarTabs button:hover{color:var(--ink)}
.radarTabs button.on{color:var(--ink);border-bottom-color:var(--accent)}
.radarTabs .msym{font-size:20px;color:var(--ink3)}
.radarTabs button.on .msym{color:var(--accent)}
.radarTabs .tcount{background:var(--off-bg);border-radius:20px;padding:1px 8px;font-size:11.5px;font-weight:620;color:var(--ink2)}
.radarTabs button.on .tcount{background:var(--sel-bg);color:var(--sel-ink)}

/* Every sibling of this note is a full-width body child that supplies the 20px
   page gutter from its OWN padding. This one has a border, so its gutter has to
   come from the margin instead — padding would only push the text inward and
   leave the box itself stuck to both edges of the window. */
/* promo-banner-mist: the system's inline status banner. Colour is the depth device
   here, so it drops the accent rule and the border along with it. */
.radarNote{background:var(--block-mist);border:0;border-radius:var(--r-md);
  padding:16px 24px;margin:8px 20px 18px;font-size:12.5px;color:var(--ink);line-height:1.6}
.radarNote b{color:var(--ink);font-weight:540}


/* =============================================================================
 * Side panel, boards, tags, comments, account & settings.
 * ========================================================================== */
:root{--sbw:236px}
body{padding-left:var(--sbw);transition:padding-left .2s ease}
body.sbCollapsed{--sbw:64px}
.sidebar{position:fixed;left:0;top:0;bottom:0;width:var(--sbw);z-index:35;display:flex;flex-direction:column;
  background:var(--panel);border-right:1px solid var(--line);transition:width .2s ease;overflow:hidden}
.sbTop{display:flex;align-items:center;justify-content:space-between;padding:12px 10px 8px 12px;gap:6px;min-height:56px}
.sbBrand{display:flex;align-items:center;gap:10px;border:0;background:transparent;cursor:pointer;padding:4px;
  border-radius:var(--r-sm);font-size:14px;font-weight:640;letter-spacing:-.01em;color:var(--ink);min-width:0}
.sbBrand .dot{width:30px;height:30px;border-radius:var(--r-full);flex:none;background:url(rs-mark.png) center/contain no-repeat}
.sbCollapse,.sbAdd{border:0;background:transparent;cursor:pointer;color:var(--ink3);border-radius:var(--r-sm);
  width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;flex:none;transition:.14s}
.sbCollapse:hover,.sbAdd:hover{background:var(--bg);color:var(--ink)}
.sbCollapse .closed{display:none}
body.sbCollapsed .sbCollapse .closed{display:inline-block}
body.sbCollapsed .sbCollapse .open{display:none}
.sbNav,.sbBottom{display:flex;flex-direction:column;gap:2px;padding:4px 8px}
.sbBottom{margin-top:auto;padding-bottom:12px;border-top:1px solid var(--line2);padding-top:10px}
.sbItem{display:flex;align-items:center;gap:10px;border:0;background:transparent;cursor:pointer;text-align:left;
  padding:8px 10px;border-radius:var(--r-md);font-size:13.5px;font-weight:520;color:var(--ink2);min-width:0;transition:.14s;width:100%}
.sbItem .msym{font-size:20px;flex:none;color:var(--ink3)}
.sbItem:hover{background:var(--bg);color:var(--ink)}
.sbItem.on{background:var(--accent);color:var(--accent-ink)}
.sbItem.on .msym{color:var(--accent-ink)}
.sbText{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.sbSoon{margin-left:auto;font-family:"JetBrains Mono",ui-monospace,monospace;font-size:9.5px;letter-spacing:.5px;
  text-transform:uppercase;background:var(--sel-bg);color:var(--sel-ink);border-radius:var(--r-full);padding:1px 6px;flex:none}
.sbItem.on .sbSoon{background:rgba(127,127,127,.3);color:inherit}
.sbSection{padding:10px 8px 4px;flex:1;min-height:0;display:flex;flex-direction:column}
.sbHead{display:flex;align-items:center;justify-content:space-between;padding:4px 10px 4px;
  font-family:"JetBrains Mono",ui-monospace,monospace;font-size:10.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--ink3)}
.sbBoards{overflow:auto;flex:1;min-height:0;display:flex;flex-direction:column;gap:1px;padding-bottom:6px}
.sbRow{display:flex;align-items:center;gap:6px;padding:6px 8px 6px 10px;border-radius:var(--r-md);cursor:pointer;
  font-size:13px;color:var(--ink2);min-width:0;position:relative;transition:.14s}
.sbRow:hover{background:var(--bg);color:var(--ink)}
.sbRow.on{background:var(--accent);color:var(--accent-ink)}
.sbRow.on .msym,.sbRow.on .sbCount{color:inherit}
.sbRow .msym{font-size:18px;flex:none;color:var(--ink3)}
.sbRow.child{padding-left:30px}
.sbRow .sbCount{margin-left:auto;font-size:11px;color:var(--ink3);font-variant-numeric:tabular-nums;flex:none}
.sbRow .sbMore{opacity:0;border:0;background:transparent;cursor:pointer;color:inherit;border-radius:var(--r-sm);
  width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;flex:none;margin-right:-4px}
.sbRow .sbMore .msym{font-size:18px;color:inherit}
.sbRow:hover .sbMore,.sbRow.on .sbMore{opacity:.9}
.sbRow .chev{transition:transform .15s}
.sbRow.openGroup .chev{transform:rotate(90deg)}
.sbEmpty{padding:6px 10px;font-size:12.5px;color:var(--ink3);line-height:1.5}
.sbEmpty button{border:0;background:transparent;padding:0;color:var(--ink);text-decoration:underline;cursor:pointer;font-size:inherit}
body.sbCollapsed .sbText,body.sbCollapsed .sbSection,body.sbCollapsed .sbSoon{display:none}
/* Collapsed rail: the name shows on hover, since the icon is all that is left. */
body.sbCollapsed .sidebar{overflow:visible}
body.sbCollapsed [data-tip]{position:relative}
body.sbCollapsed [data-tip]::after{content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%) translateX(-4px);
  background:var(--ink);color:var(--canvas);font-size:12px;font-weight:520;padding:5px 9px;border-radius:var(--r-sm);white-space:nowrap;
  pointer-events:none;opacity:0;transition:opacity .12s,transform .12s;z-index:60;box-shadow:var(--shadow)}
body.sbCollapsed [data-tip]:hover::after{opacity:1;transform:translateY(-50%) translateX(0);transition-delay:.25s}
body.sbCollapsed .sbPill{position:absolute;top:4px;right:8px;margin:0}
.sbPill{margin-left:auto;flex:none;font-size:10.5px;padding:1px 6px}
.sbItem.on .sbPill{background:rgba(127,127,127,.35);color:inherit}
body.sbCollapsed .sbItem{justify-content:center;padding:8px 0}
body.sbCollapsed .sbTop{flex-direction:column;padding:10px 0 4px;gap:4px}
body.sbCollapsed .sbBrand{padding:4px}
.sbAvatar{width:24px;height:24px;border-radius:var(--r-full);background:var(--block-clay);color:var(--ink);flex:none;
  display:inline-flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:640;text-transform:uppercase}
.sbItem.on .sbAvatar{background:var(--accent-ink);color:var(--accent)}

/* Sub-pages replace the ads page wholesale; the sidebar stays. */
.subPage{padding:32px 28px 60px;max-width:820px}
.pageCard{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:26px 28px}
.pageCard h2{margin:0 0 4px;font-size:20px;font-weight:650;letter-spacing:-.01em}
.pageCard .lead{margin:0 0 22px;color:var(--ink3);font-size:13px}
.setRow{display:grid;grid-template-columns:180px 1fr;gap:16px;align-items:start;padding:16px 0;border-top:1px solid var(--line2)}
.setRow h4{font-size:13.5px;font-weight:600;margin:0 0 2px}
.setRow p{margin:0;font-size:12.5px;color:var(--ink3);line-height:1.5}
.setRow .seg{margin:0}
.setRow input[type=text],.setRow input[type=email]{background:var(--bg);border:1px solid var(--line);border-radius:var(--r-pill);
  padding:8px 13px;font-size:13.5px;min-width:260px}
.setRow input:focus{outline:2px solid var(--accent);outline-offset:-1px}
.setActions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:8px}
.setNote{font-size:12px;color:var(--good);margin-left:4px}
.ucBox{background:var(--block-mist);border-radius:var(--r-lg);padding:56px 32px;text-align:center;color:var(--ink2)}
.ucBox .msym{font-size:44px;color:var(--ink3)}
.ucBox h2{margin:10px 0 6px;font-size:22px;font-weight:650}
.ucBox p{margin:0 auto;max-width:420px;font-size:13.5px;line-height:1.55;color:var(--ink3)}

/* Board banner over the results. */
.boardBar{margin:0 20px 12px;padding:14px 18px;background:var(--block-alabaster);border-radius:var(--r-md);
  display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.boardBar .msym{font-size:22px;color:var(--ink2)}
.boardBar h3{margin:0;font-size:16px;font-weight:640;letter-spacing:-.01em}
.boardBar .muted{font-size:12.5px}
.boardBar .bbActs{margin-left:auto;display:flex;gap:6px}
.boardBar .btn{padding:5px 12px;font-size:12.5px;background:transparent}

/* Save-to-board bookmark: top-right of every card and of the detail preview. */
.card{position:relative}
.saveBtn{position:absolute;top:8px;right:8px;z-index:3;width:32px;height:32px;border-radius:var(--r-full);
  border:1px solid var(--line);background:var(--panel);color:var(--ink2);cursor:pointer;display:inline-flex;
  align-items:center;justify-content:center;padding:0;transition:transform .13s ease,background-color .14s,border-color .14s,color .14s;
  box-shadow:0 1px 3px rgba(0,0,0,.08)}
.saveBtn .msym{font-size:19px}
.saveBtn:hover{transform:scale(1.1);border-color:var(--ink)}
.saveBtn.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.saveBtn.on .msym{font-variation-settings:"FILL" 1,"wght" 400,"GRAD" 0,"opsz" 24}
.mCreative{position:relative}
.saveBtn.big{top:30px;right:30px;width:36px;height:36px}
.saveBtn.big .msym{font-size:21px}

/* Floating menus (board picker, board actions). Fixed-positioned so they escape
   overflow:hidden cards and the scrolling dialog alike. */
.float{position:fixed;z-index:80;min-width:220px;max-width:300px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-md);box-shadow:var(--shadow);padding:6px;max-height:min(360px,60vh);overflow:auto}
.float .fHead{padding:6px 9px 7px;font-family:"JetBrains Mono",ui-monospace,monospace;font-size:10.5px;letter-spacing:.6px;
  text-transform:uppercase;color:var(--ink3)}
.float .fGroup{padding:7px 9px 2px;font-size:11.5px;color:var(--ink3);font-weight:560}
.float label{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:var(--r-sm);cursor:pointer;font-size:13px}
.float label:hover{background:var(--bg)}
.float label.child{padding-left:22px}
.float input[type=checkbox]{accent-color:var(--accent);margin:0}
.float .fItem{display:flex;align-items:center;gap:9px;width:100%;border:0;background:transparent;text-align:left;
  padding:7px 9px;border-radius:var(--r-sm);cursor:pointer;font-size:13px;color:var(--ink)}
.float .fItem:hover{background:var(--bg)}
.float .fItem.danger{color:var(--bad)}
.float .fItem .msym{font-size:18px;color:var(--ink3)}
.float .fItem.danger .msym{color:var(--bad)}
.float .fNew{display:flex;gap:6px;padding:6px 6px 4px;border-top:1px solid var(--line2);margin-top:4px}
.float .fNew input{flex:1;min-width:0;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-pill);
  padding:6px 11px;font-size:12.5px}
.float .fNew input:focus{outline:2px solid var(--accent);outline-offset:-1px}
.float .fNew .btn{padding:5px 11px;font-size:12.5px}
.float .fNone{padding:7px 9px;font-size:12.5px;color:var(--ink3);line-height:1.5}

/* Tags — Shopify-style chips with an add field and suggestions. */
.tagBox{margin:22px 0 4px;padding-top:18px;border-top:1px solid var(--line2)}
/* The heading matches the tab labels above it in size and weight, so "Tags"
   reads as a section of the details, not as a caption. */
.tagHead{display:flex;align-items:center;gap:7px;font-size:13.5px;font-weight:540;color:var(--ink);margin-bottom:10px}
.tagHead .msym{font-size:19px;color:var(--ink2)}
.tagField{display:flex;flex-wrap:wrap;gap:6px;align-items:center;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-md);padding:7px 9px;position:relative;transition:border-color .14s}
.tagField:focus-within{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.chip{display:inline-flex;align-items:center;gap:3px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-pill);padding:2px 6px 2px 10px;font-size:12.5px;font-weight:520;max-width:100%}
.chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chip button{border:0;background:transparent;cursor:pointer;color:var(--ink3);padding:0;width:18px;height:18px;
  border-radius:var(--r-full);display:inline-flex;align-items:center;justify-content:center}
.chip button .msym{font-size:16px}
.chip button:hover{background:var(--line2);color:var(--ink)}
.tagInput{flex:1;min-width:120px;border:0;background:transparent;font-size:13px;padding:3px 4px;outline:0}
.tagInput::placeholder{color:var(--ink3)}
.tagSugg{position:absolute;left:-1px;right:-1px;top:calc(100% + 4px);z-index:5;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-md);box-shadow:var(--shadow);padding:4px;max-height:200px;overflow:auto}
.tagSugg button{display:block;width:100%;text-align:left;border:0;background:transparent;cursor:pointer;
  padding:6px 9px;border-radius:var(--r-sm);font-size:13px;color:var(--ink)}
.tagSugg button:hover,.tagSugg button.hi{background:var(--bg)}
.tagSugg button b{font-weight:640}
.tagHint{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:8px;font-size:12px;color:var(--ink3)}
.tagHint .chip{cursor:pointer;padding:1px 10px;border-style:dashed;color:var(--ink2);font-weight:500}
.tagHint .chip:hover{border-color:var(--ink);color:var(--ink);border-style:solid}
.tagHint .chip .msym{font-size:14px}
.tag.userTag{background:var(--block-alabaster);color:var(--ink2);border:1px solid var(--line)}
.tag.userTag .msym{font-size:13px;color:var(--ink3)}

/* Comments */
.cBox{padding-top:4px}
.cList{display:flex;flex-direction:column;gap:14px;margin-bottom:16px}
.cEmpty{margin:4px 0 0;font-size:13px;color:var(--ink3)}
.cItem{display:grid;grid-template-columns:30px 1fr;gap:10px}
.cAv{width:30px;height:30px;border-radius:var(--r-full);background:var(--block-clay);color:var(--ink);display:inline-flex;
  align-items:center;justify-content:center;font-size:12px;font-weight:640;text-transform:uppercase}
.cHead{display:flex;align-items:baseline;gap:8px;font-size:12.5px}
.cHead b{font-weight:620}
.cHead .cWhen{color:var(--ink3);font-size:11.5px}
.cHead .cDel{margin-left:auto;border:0;background:transparent;cursor:pointer;color:var(--ink3);padding:0;
  border-radius:var(--r-sm);opacity:0;transition:.14s}
.cItem:hover .cDel{opacity:1}
.cHead .cDel:hover{color:var(--bad)}
.cHead .cDel .msym{font-size:16px}
.cText{margin:3px 0 0;font-size:13.5px;line-height:1.5;white-space:pre-wrap;word-break:break-word}
.cForm{display:grid;grid-template-columns:30px 1fr;gap:10px}
.cForm textarea{width:100%;min-height:64px;resize:vertical;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-md);padding:9px 12px;font:inherit;font-size:13.5px;line-height:1.5}
.cForm textarea:focus{outline:2px solid var(--accent);outline-offset:-1px}
.cFoot{display:flex;align-items:center;gap:10px;margin-top:8px}
.cFoot .cAs{font-size:12px;color:var(--ink3)}
.cFoot .cAs button{border:0;background:transparent;padding:0;color:var(--ink);text-decoration:underline;cursor:pointer;font-size:inherit}
.cFoot .btn{margin-left:auto}
.cFoot .cErr{font-size:12px;color:var(--bad)}
.mTab .pill{margin-left:6px;font-size:10px;padding:0 6px}
.mTab.on .pill{background:rgba(127,127,127,.35);color:inherit}

/* Small dialog for naming and confirming. Above the ad card, so a board can be
   made from inside a picker without closing anything. */
.dlg{z-index:90}
.dlgCard{background:var(--panel);border-radius:var(--r-lg);width:min(440px,100%);padding:24px 26px;box-shadow:var(--shadow)}
.dlgCard h3{margin:0 0 4px;font-size:17px;font-weight:640;letter-spacing:-.01em}
.dlgCard .dlgMsg{margin:0 0 14px;font-size:13.5px;color:var(--ink2);line-height:1.5}
.dlgField{display:flex;flex-direction:column;gap:5px;margin:12px 0}
.dlgField label{font-size:12px;color:var(--ink3)}
.dlgField input,.dlgField select{background:var(--bg);border:1px solid var(--line);border-radius:var(--r-md);padding:9px 12px;font-size:14px}
.dlgField input:focus,.dlgField select:focus{outline:2px solid var(--accent);outline-offset:-1px}
.dlgActs{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}
.btn.danger{background:var(--bad);border-color:var(--bad);color:#fff}
.btn.danger:hover{filter:brightness(1.06)}
@media (max-width:760px){body{--sbw:64px}body .sbText,body .sbSection,body .sbSoon{display:none}body .sbItem{justify-content:center;padding:8px 0}
  body .sbTop{flex-direction:column;padding:10px 0 4px;gap:4px}body .sbCollapse{display:none}}
.toast{position:fixed;left:50%;bottom:28px;transform:translate(-50%,12px);z-index:100;background:var(--ink);color:var(--canvas);
  padding:10px 16px;border-radius:var(--r-pill);font-size:13px;opacity:0;pointer-events:none;transition:.18s}
.toast.on{opacity:1;transform:translate(-50%,0)}
.setRow .seg{justify-self:start}

/* @mentions */
.mention{color:var(--ink);font-weight:620;background:var(--block-mist);border-radius:var(--r-xs);padding:0 3px}
.cTa{position:relative}
.mentionPop{position:absolute;left:0;bottom:calc(100% + 6px);z-index:6;min-width:240px;max-width:100%;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow);padding:4px}
.mentionPop .mItem{display:flex;align-items:center;gap:9px;width:100%;border:0;background:transparent;text-align:left;
  padding:6px 8px;border-radius:var(--r-sm);cursor:pointer;color:var(--ink)}
.mentionPop .mItem.hi,.mentionPop .mItem:hover{background:var(--bg)}
.mentionPop .mItem .cAv{width:24px;height:24px;font-size:10.5px;flex:none}
.mentionPop .mItem b{display:block;font-size:13px;font-weight:600}
.mentionPop .mItem span:not(.cAv){display:block;font-size:11.5px;color:var(--ink3)}
/* Inbox */
.inboxHead{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:4px}
.inboxHead h2{margin:0}
.inboxEmpty{text-align:center;padding:40px 20px;color:var(--ink3)}
.inboxEmpty .msym{font-size:40px}
.inboxEmpty p{margin:8px auto 0;max-width:360px;font-size:13.5px;line-height:1.5}
.inboxList{display:flex;flex-direction:column;border-top:1px solid var(--line2)}
.inboxItem{display:grid;grid-template-columns:30px 1fr 12px;gap:12px;align-items:start;width:100%;text-align:left;
  border:0;border-bottom:1px solid var(--line2);background:transparent;padding:14px 6px;cursor:pointer;color:var(--ink);transition:.14s}
.inboxItem:hover{background:var(--bg)}
.inboxItem.unread .inboxWho{font-weight:560}
.inboxWho{font-size:13.5px}
.inboxAd{font-weight:620}
.inboxText{margin-top:3px;font-size:13px;color:var(--ink2);line-height:1.5;white-space:pre-wrap;word-break:break-word;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.inboxWhen{margin-top:4px;font-size:11.5px;color:var(--ink3)}
.inboxDot{width:9px;height:9px;border-radius:var(--r-full);background:var(--ember);margin-top:6px}

/* ---- roles & people ------------------------------------------------------ */
.subPage.wide{max-width:880px;display:flex;flex-direction:column;gap:16px}
.roleBadge{display:inline-flex;align-items:center;border-radius:var(--r-pill);padding:3px 11px;font-size:11.5px;
  font-weight:600;letter-spacing:.01em;white-space:nowrap;flex:none}
.roleBadge.r-admin{background:var(--ink);color:var(--canvas)}
.roleBadge.r-member{background:var(--block-mist);color:var(--ink)}
.peopleTitle{display:flex;align-items:center;gap:8px}
.infoDot{width:17px;height:17px;border-radius:var(--r-full);border:1px solid var(--line);background:transparent;
  color:var(--ink3);font-size:11px;font-weight:700;font-family:Inter,sans-serif;line-height:1;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;padding:0;transition:.14s}
.infoDot:hover{border-color:var(--ink);color:var(--ink)}
.rolesHelp{max-width:330px;padding:10px 12px 12px}
.rolesHelpRow{font-size:12.5px;line-height:1.5;color:var(--ink2);margin-top:7px}
.rolesHelpRow b{color:var(--ink);font-weight:620}
.roleWarn{display:flex;gap:12px;background:var(--warn-bg);color:var(--ink);border-radius:var(--r-md);padding:14px 16px;margin-bottom:18px}
.roleWarn .msym{font-size:22px;flex:none}
.roleWarn b{font-size:13.5px}
.roleWarn p{margin:3px 0 0;font-size:12.5px;line-height:1.5;color:var(--ink2)}
.peopleTable{display:flex;flex-direction:column}
.personRow{display:grid;grid-template-columns:30px minmax(0,1fr) 130px 130px;gap:14px;align-items:center;
  padding:12px 4px;border-bottom:1px solid var(--line2)}
/* The column heads use the same mono caption as the rest of the dashboard. */
.personRow.head{padding:0 4px 8px;border-bottom:1px solid var(--line);
  font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;text-transform:uppercase;
  letter-spacing:.6px;font-size:10px;color:var(--ink3)}
.personWho{min-width:0}
.personName{display:flex;align-items:center;gap:7px;font-size:13.5px;flex-wrap:wrap}
.personName b{font-weight:600}
.personYou{font-size:11px;color:var(--ink3);background:var(--bg);border-radius:var(--r-full);padding:1px 7px}
.personPending{display:inline-flex;align-items:center;gap:4px;font-size:11px;color:var(--warn);
  background:var(--warn-bg);border-radius:var(--r-full);padding:1px 8px}
.personPending .msym{font-size:13px}
.personMail{font-size:12px;color:var(--ink3);overflow:hidden;text-overflow:ellipsis}
.roleSelect{appearance:none;-webkit-appearance:none;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-pill);padding:5px 26px 5px 13px;font-size:12.5px;font-weight:520;cursor:pointer;width:100%;
  background-image:linear-gradient(45deg,transparent 50%,var(--ink3) 50%),linear-gradient(135deg,var(--ink3) 50%,transparent 50%);
  background-position:calc(100% - 14px) 52%,calc(100% - 9px) 52%;background-size:5px 5px,5px 5px;background-repeat:no-repeat}
.roleSelect:hover{border-color:var(--ink3)}
.roleSelect:focus{outline:2px solid var(--accent);outline-offset:-1px}
.personActs{display:flex;justify-content:flex-start}
.linkAct{border:0;background:transparent;padding:0;cursor:pointer;font-size:12.5px;font-weight:520;color:var(--ink2);
  text-align:left;transition:color .14s}
.linkAct:hover{color:var(--ink);text-decoration:underline}
.linkAct.danger{color:var(--ember)}
.linkAct.danger:hover{color:var(--bad)}
.linkAct:disabled{color:var(--ink3);cursor:not-allowed;text-decoration:none}
.roleNote{margin:14px 0 0;font-size:12px;color:var(--ink3)}
.tagRead{display:flex;flex-wrap:wrap;gap:6px}
.dlgField input[readonly]{font-family:"JetBrains Mono",ui-monospace,monospace;font-size:12px}
.setRow>.roleBadge,.setRow>div>.roleBadge{justify-self:start}
@media (max-width:700px){.personRow{grid-template-columns:30px minmax(0,1fr) auto;gap:10px}
  .personRow>.personActs{grid-column:2/4;justify-content:flex-end}.personRow.head{display:none}}
