/* WrkTrack panel. Tokens are WrkUpGo's, verbatim — the WrkORCS panels are one family. */
* { box-sizing: border-box; margin: 0; }
:focus, :focus-visible { outline: none !important; }   /* standing owner rule — never a focus ring */
/* HIDDEN MEANS HIDDEN. The UA sheet's `[hidden] { display: none }` carries the specificity of a
   single attribute, so ANY author rule setting `display` on a class beats it — and a stylesheet
   this size sets `display` on hundreds of classes. `el.hidden = true` then does nothing at all,
   silently, and the element it was meant to remove goes on rendering and taking clicks.
   It had been patched one class at a time — `.lightbox`, then `.prodadd-accb` — each after the same
   bug was found again: a closed lightbox staying a full-screen overlay that ate every click on the
   page; a control shown to a reader who was never meant to have it. A per-class patch for a cascade
   problem means the next class is already broken and nobody has looked. This is the rule those were
   standing in for, and it is why nothing below needs its own. */
[hidden] { display: none !important; }
:root {
  --bg: #0f1115; --panel: #171a1f; --panel2: #1b1f27; --line: #262b32;
  /* The greys between a hairline and the ink. These existed only as literals scattered through the
     newer surfaces — eight uses of one, five of another — which is a missing token, not a style
     choice. Named for what they separate, never for how dark they are. */
  --line2: #333b45;   /* a border meant to be seen, not merely felt */
  --edge: #4a525e;    /* the edge of something active, and the bound lines on a chart */
  --bar: #5a6470;     /* a neutral data fill: placements, the third audience column */
  --ink: #e8eaed; --ink2: #c3cad2; --dim: #8a9199;
  --blue: #7cc0ff; --green: #22c55e; --amber: #f0c674; --red: #f87171;
  /* BOUGHT DELIVERY. Not --blue: that one is an accent, on persona chips and info pills, and it
     had drifted into the chart's own paid window while every legend dot, split bar, ring and
     from-ads figure used this darker one — the same role in two hues, half a shade apart, which is
     the one difference a reader is guaranteed not to notice and guaranteed to be misled by. It sat
     as a literal in eight rules, which is a missing token rather than a style choice. */
  --ads: #5b9bff;
  /* THE TARGET FAMILY. One colour for everything about where the account is going: the goal
     figure, the bar under it, the gap left, the account's own line and the projection off the end
     of it. A second for the retarget, the date the owner has dragged to and what it costs.

     Named for the ROLE, not the hue. Blue is already ads and green is already organic, and amber,
     which used to carry the retarget, is the one colour on this page the owner cannot tell from
     organic.

     Red and hot pink, chosen by the owner. They sit close in hue and 1.5x apart in lightness, so
     the pink is the brighter of the two rather than the different one, which is how it reads with
     the hue channel gone. Both clear 4.5:1 on the panel.

     Which is why NOTHING here leans on the pair being distinguishable. Wherever the two appear
     together they are already told apart by shape: the projection is a filled dot on a dashed
     line, the retarget a ring on a dotted one.

     Colour is never the only carrier. The account's line is solid and the projection dashed; the
     projection marker is a filled dot and the retarget a ring; the legend names every colour. */
  --target: #ff3b5c; --retarget: #ff69b4;
  /* The adapter: the arithmetic BETWEEN two tiles, not a category of its own.
     A deep orange rather than a bright one, and the reason is the owner's eyes: orange and green
     at the same lightness are the pair a red-green deficiency loses, and every bright orange on a
     dark ground lands within a percent of --green's luminance. This one sits at 0.79 of it, so
     there is a gap left when the hue is gone. It also has the arrow and the position between two
     tiles carrying it, which no colour can take away. */
  --adapter: #f97316;
  /* OUT OF RANGE, both directions. Above the high range and below the low one are two states of
     one fact, so they take one hue at two weights rather than two hues — and amber is the hue left
     once blue is ads, green is organic, red is the target and pink the retarget. Dark is above,
     light is below; the arrow beside them says which without the colour. */
  --over: #b49457; --under: #f0c674;
  /* OUT OF RANGE, both directions. Above the high range and below the low one are two states of
     one fact, so they take one hue at two weights rather than two hues — and amber is the hue left
     once blue is ads, green is organic, red is the target and pink the retarget. Dark is above,
     light is below; the arrow beside them says which without the colour. */
  --over: #b49457; --under: #f0c674;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  /* 4pt spacing scale. 8pt is too coarse for a panel this dense — 12 between 8 and 16 is the
     step this UI reaches for most. Tight values group related rows; the large ones separate
     whole groups inside a section. */
  /* Copy left, the page it came off right. Shared, so a reading's frozen figures line up
     with the note underneath them instead of each block picking its own column. */
  --notecols: minmax(0, 1.35fr) minmax(300px, 1fr);
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;

  /* Semantic layers, named by what sits at each one. Anything that floats picks a token here
     rather than inventing a number, so two overlays can never fight over an arbitrary 999. */
  --z-selbar: 30; --z-sticky: 40;
  /* A SHEET SITS UNDER THE MENUS IT OPENS. The Action Builder's own pickers are `.pickmenu`
     at dropdown level, appended to <body> — put the sheet above them and every dropdown
     inside it renders behind the backdrop, which is exactly what happened. */
  --z-modal-back: 44; --z-modal: 45; --z-dropdown: 50; --z-toast: 60;
  --z-lightbox: 80; --z-calendar: 90; --z-tooltip: 100;
}
body { background: var(--bg); color: var(--ink); font: 14px/1.5 -apple-system, "Segoe UI", Roboto, sans-serif; }
/* THIS IS AN INSTRUMENT, NOT A DOCUMENT. You hover it and study it; you never drag a highlight
   across it. Reaching for a row's toggle and catching the text instead turns half the table blue
   and leaves it that way until you click somewhere else — a state the page never asked for and
   cannot use. Typing surfaces are the exception, because a field whose text cannot be selected is
   a field that cannot be corrected. */
body { -webkit-user-select: none; user-select: none; }
input, textarea, select, [contenteditable="true"] { -webkit-user-select: text; user-select: text; }
* { scrollbar-width: thin; scrollbar-color: #2a323d transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #2a323d; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #39424f; }
b, strong { font-weight: 700; }
.num { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); }

/* ── chrome ─────────────────────────────────────────────────────────────── */
.topbar { display: flex; align-items: center; gap: 14px; padding: 12px 20px; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--bg); z-index: var(--z-sticky); }
.brandgroup { display: flex; align-items: center; gap: 12px; min-width: 0; }
.brand { font-weight: 800; font-size: 18px; letter-spacing: -.4px; line-height: 1; color: var(--ink); text-decoration: none; }
.brand span { color: var(--blue); }
/* BOOT STATE (owner 2026-09-08): while the board loads, the top bar keeps its own default chrome,
   background and logo exactly where they are — only the ITEMS (brand picker, page nav, asset links,
   pull chip) are held back, so a full menu never flashes over an empty page. One loader sits in the
   middle of the app. Nothing here touches the background; the ground stays the app's own dark. */
.booting .brandgroup > :not(.brand) { display: none; }
.booting .topbar > :not(.brandgroup):not(.spacer) { display: none; }
.booting #app > * { display: none; }
.booting #app { display: flex; align-items: center; justify-content: center; min-height: 70vh; }
.booting #app::after { content: ""; width: 30px; height: 30px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--ink2); animation: bootspin .8s linear infinite; }
@keyframes bootspin { to { transform: rotate(360deg); } }
.sep { color: var(--dim); opacity: .5; font: 500 14px -apple-system, sans-serif; line-height: 1; user-select: none; }
/* The nav scrolls inside itself once the seven items stop fitting. Without min-width:0 a flex item
   refuses to shrink below its content, so the row pushed the whole page sideways: the body scrolled
   horizontally at tablet width and below, dragging every section with it. */
.nav { display: flex; gap: 14px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.nav::-webkit-scrollbar { display: none; }
.nav-item { flex: none; }
.nav-item { font: 600 13px -apple-system, sans-serif; line-height: 1; color: var(--dim); text-decoration: none; transition: color .15s ease; }
.nav-item:hover { color: var(--ink); }
.nav-item[aria-current="page"] { color: var(--ink); }
.spacer { flex: 1; min-width: var(--s3); }
/* THE BAR WRAPS BEFORE IT OVERFLOWS, and the width it wraps at was measured rather than picked.
   The right-hand group is 394px that never shrinks (asset links 85, pull chip 88, view picker 179,
   plus the gaps between them), and the left group cannot go below about 200px without dropping the
   brand or the client it is naming. One row therefore stops fitting at roughly 660px, and below
   that the whole page scrolled sideways — every section dragged along by a bar nobody was looking
   at. 720px leaves the join a little clearance rather than landing on it.

   NOTHING IS HIDDEN TO MAKE IT FIT. The pull chip is how an operator knows the numbers are current
   and the view picker is how a client's arrangement is chosen; both are load-bearing, and a control
   that disappears at a width is a control somebody cannot find and has no way to know exists. Two
   rows costs vertical space on a phone, which is the cheaper thing to spend. */
@media (max-width: 720px) {
  .topbar { flex-wrap: wrap; row-gap: 10px; padding: 10px 14px; }
  /* AND IT STOPS BEING PINNED. Wrapped, the bar is 141px tall on a 375px screen — seventeen per
     cent of an iPhone's viewport, held permanently, on the one device with none to spare. Sticky
     earns its keep when the bar is a thin strip beside the content; at three rows it is a lid.
     It stays exactly where it was, at the top of the page, and scrolls away like everything else. */
  .topbar { position: static; }
  /* The spacer's whole job is pushing the two groups to opposite ends of ONE row. Once they are on
     separate rows it has nothing to push, and a flex:1 item on a wrapped line is just a hole. */
  .spacer { display: none; }
  .brandgroup { flex: 1 0 100%; }
  /* "B115 GRV" is one label, not two words to break between. Wrapped, it made the picker two lines
     tall and took the whole first row with it — a brand's code and its reference are read together
     or they are read as two different things. */
  .topbar .pick-label { white-space: nowrap; }
}
/* Touch: a nav item is 13px of text with no box of its own, which is about a 15px target on a
   phone. Padding gives it a row to be hit in and changes nothing a mouse ever sees. */
@media (pointer: coarse) {
  .nav { gap: 18px; }
  .nav-item { padding: 7px 0; }
}
.mockchip { font-size: 10px; font-weight: 800; letter-spacing: .8px; padding: 3px 8px; border-radius: 5px; background: #3a2e12; color: var(--amber); border: 1px solid #7a5f2a; cursor: help; }
main { max-width: 1180px; margin: 0 auto; padding: 24px 20px 100px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }

h1 { font-size: 22px; font-weight: 800; letter-spacing: -.3px; margin-bottom: 3px; }
.page-sub { color: var(--dim); font-size: 13px; margin-bottom: 20px; max-width: 76ch; }

.btn { border: 1px solid var(--line); background: var(--panel); color: var(--ink); padding: 7px 12px; border-radius: 8px; font-weight: 600; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: border-color .15s ease; }
.btn:hover { border-color: var(--line2); }
.btn.ghost { background: transparent; }
.btn.primary { background: var(--green); color: #062012; border-color: transparent; }
.btn.primary:hover { background: #34d06a; }
.btn.danger { background: transparent; color: var(--red); border-color: #5a2a2c; }
.btn.small { padding: 5px 9px; font-size: 12px; }
.btn:disabled { opacity: .45; cursor: default; }

/* ── section grammar (numbered, from the WrkResearch output surfaces) ────── */
.sec { border: 1px solid var(--line); border-radius: 12px; background: var(--panel); padding: 16px 18px; margin-bottom: 14px; }
.sec-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
/* THE HEAD OWNS THE GAP UNDER IT, whatever is put there. A block carries a top margin for the
   thing it follows INSIDE a tile, and that spacing is wrong against the tile's own title: a
   heading and the thing it titles are one group, and the chart opened 32px under its own name
   while every other tile opened at 14px. Adjacent margins collapse to the larger of the two, so
   without this every block moved to the top of a section brings its old separation with it.
   Written through `.sec >` for the specificity: a bare `.sec-head + *` ties with `.chartwrap`
   and loses to it on source order. */
.sec > .sec-head + * { margin-top: 0; }
.sec-n { width: 20px; height: 20px; flex: none; border-radius: 5px; background: rgba(124,192,255,.16); color: var(--blue); font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
.sec-title { font-size: 14px; font-weight: 700; }
.sec-note { font-size: 11px; color: var(--dim); text-transform: uppercase; letter-spacing: .04em; margin-left: auto; text-align: right; }
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--dim); }
.hint { font-size: 12px; color: var(--dim); }
.hint-dim { font-size: 11px; color: #868d95; }   /* 4.99:1 on the panel2 ground; #6b7178 was 3.39:1 */

/* "why" expander — shows the owner's own framework diagram beside the rule it enforces */
.why { margin-left: auto; }
.why-btn { border: none; background: transparent; color: var(--dim); font-size: 11px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; padding: 2px 4px; border-radius: 5px; }
.why-btn:hover { color: var(--blue); }
.why-img { margin-top: 12px; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: #fff; }
.why-img img { display: block; width: 100%; }

/* ── KPI strip ───────────────────────────────────────────────────────────── */
.kpis { display: flex; flex-wrap: wrap; gap: 6px 26px; align-items: baseline; padding: 12px 14px; border-radius: 9px; background: rgba(124,192,255,.06); }
.kpi { display: flex; flex-direction: column; gap: 1px; }
.kpi b { font-size: 19px; font-weight: 700; letter-spacing: -.3px; font-variant-numeric: tabular-nums; }
.kpi span { font-size: 10.5px; color: var(--dim); text-transform: uppercase; letter-spacing: .05em; }
.kpi.weak b { color: var(--dim); }
.kpi-rule { width: 1px; align-self: stretch; background: var(--line); margin: 0 2px; }

/* ── badges ──────────────────────────────────────────────────────────────── */
.pill { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; padding: 3px 9px; border-radius: 20px; white-space: nowrap; display: inline-flex; align-items: center; gap: 4px; }
.pill.tofu { background: rgba(124,192,255,.14); color: var(--blue); border: 1px solid rgba(124,192,255,.3); }
.pill.mofu { background: rgba(240,198,116,.14); color: var(--amber); border: 1px solid rgba(240,198,116,.3); }
.pill.bofu { background: rgba(34,197,94,.14); color: #4ade80; border: 1px solid rgba(34,197,94,.32); }
.pill.ok { background: rgba(34,197,94,.13); color: #4ade80; }
.pill.warn { background: rgba(240,198,116,.14); color: var(--amber); }
.pill.bad { background: rgba(248,113,113,.14); color: var(--red); }
.pill.neutral { background: #23272f; color: var(--dim); }
.pill.info { background: rgba(124,192,255,.13); color: var(--blue); }

/* ── the tag system ──────────────────────────────────────────────────────────
   ONE HEIGHT, ONE SHAPE, ONE SIZE. A content row carries seven of these side by side and they had
   drifted into four font sizes, three radii, three paddings and a stadium among the rounded rects,
   so the line read as a pile of unrelated stickers rather than as one row of facts about one post.
   What separates them now is what they SAY: family for an identifier against a word, weight and
   ink for how loud, border for a reading against a control. Never their size.

   The height is explicit rather than left to padding plus a line box, because half of these hold a
   glyph and half hold text, and the two do not measure the same. Sized down to 9px in a 16px box:
   these are micro-labels beside figures, never read as prose, and every pixel one of them gives
   back comes off a table that was scrolling sideways. */
.idpill, .fmtpill, .setpill, .reflink, .chip, .anbtn, .ansrc {
  display: inline-flex; align-items: center; gap: 3px;
  height: 16px; padding: 0 6px; margin: 0;
  border: 1px solid var(--line); border-radius: 5px; background: var(--panel2);
  font: 600 9px/1 -apple-system, "Segoe UI", Roboto, sans-serif; letter-spacing: .04em;
  color: var(--ink2); white-space: nowrap;
}
/* The ones that stack down the identity column keep a line each and shrink to their content. */
.idpill, .fmtpill, .setpill, .reflink { display: flex; width: fit-content; font-family: var(--mono); }
.fmtpill, .setpill, .reflink { margin-top: 3px; color: var(--dim); }
.idpill { font-weight: 700; color: var(--ink); border-color: var(--line2); }
.reflink { cursor: pointer; }
.reflink:hover { color: var(--ink); border-color: var(--line2); }
.reflink svg, .anbtn svg, .chip svg { flex: none; }
.idpill:focus, .idpill:focus-visible, .reflink:focus, .reflink:focus-visible { outline: none; }

.chip.plat { color: var(--dim); text-transform: uppercase; letter-spacing: .06em; }
.chip.persona { background: rgba(124,192,255,.10); color: var(--blue); border-color: rgba(124,192,255,.25); }
.chip.angle   { background: rgba(183,156,255,.10); color: #b79cff; border-color: rgba(183,156,255,.25); }
.chip.offer   { background: rgba(34,197,94,.10); color: #4ade80; border-color: rgba(34,197,94,.25); }
.chip.format  { background: rgba(240,198,116,.10); color: var(--amber); border-color: rgba(240,198,116,.25); }
.chip.ref { background: transparent; color: #6b7178; border: 1px dashed var(--line2); font-weight: 500; }
.chip.link { font-family: var(--mono); cursor: pointer; }
.chip.link:hover { color: var(--ink); border-color: var(--line2); }

.plat { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 999px; white-space: nowrap; }
.plat.meta { background: rgba(24,119,242,.16); color: #5b9bff; }
.plat.facebook { background: rgba(24,119,242,.16); color: #5b9bff; }
.plat.instagram { background: rgba(214,41,118,.16); color: #e26aa0; }

/* ── bars & pips ─────────────────────────────────────────────────────────── */
.bar { position: relative; display: block; height: 4px; margin-top: 4px; border-radius: 2px; background: rgba(255,255,255,.07); overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 2px; background: var(--blue); }
.bar i.win { background: var(--green); }
.bar i.low { background: var(--red); }
/* the quality bar's own threshold, drawn on every card so the bar is never invisible */
.bar.q { overflow: visible; }
.bar.q u { position: absolute; top: -2px; bottom: -2px; width: 1px; background: var(--ink2); opacity: .55; }

.pips { display: inline-flex; gap: 3px; }
.pips i { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.14); }
.pips i.on { background: var(--blue); }
.pips.g i.on { background: var(--green); }

/* ── funnel allocation ───────────────────────────────────────────────────── */
.funnel { display: flex; height: 30px; border-radius: 7px; overflow: hidden; border: 1px solid var(--line); }
.funnel div { display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; letter-spacing: .4px; color: #0f1115; }
.funnel .f-TOFU { background: #7cc0ff; }
.funnel .f-MOFU { background: #f0c674; }
.funnel .f-BOFU { background: #4ade80; }
.funnel-legend { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 10px; }
.fl { font-size: 12px; display: flex; align-items: center; gap: 6px; }
.fl u { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

/* ── cadence rail ────────────────────────────────────────────────────────── */
.rail { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.rail-cell { border: 1px solid var(--line); border-radius: 9px; padding: 11px 12px; background: var(--panel2); }
.rail-cell.hot { border-color: rgba(240,198,116,.55); background: rgba(240,198,116,.06); }
.rail-n { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.rail-l { font-size: 11.5px; font-weight: 600; margin-top: 2px; }
.rail-h { font-size: 10.5px; color: var(--dim); margin-top: 4px; line-height: 1.35; }

/* ── tables ──────────────────────────────────────────────────────────────── */
table.t { width: 100%; border-collapse: collapse; font-size: 12.5px; }
table.t th { text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--dim); font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
table.t th.r, table.t td.r { text-align: right; font-variant-numeric: tabular-nums; }
table.t td { padding: 9px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
table.t tbody tr:hover { background: var(--panel2); }
/* Rows that are still moving. A lift in LUMINANCE, not a tint: every hue on this panel already
   names a role, so a coloured row would be claiming one it does not have. Mixed from --ink so the
   lift keeps the surface's own neutral cast rather than going grey against it.
     row-live   an ad is running against it now
     row-fresh  published inside four days, so the numbers are a reading taken mid-flight
   Hover stays brighter than whichever base the row has, or hovering a live row would dim it. */
table.t tbody tr.row-fresh { background: rgba(232, 234, 237, .030); }
table.t tbody tr.row-live  { background: rgba(232, 234, 237, .065); }
table.t tbody tr.row-fresh:hover { background: rgba(232, 234, 237, .058); }
table.t tbody tr.row-live:hover  { background: rgba(232, 234, 237, .095); }
.t-code { font-family: var(--mono); font-size: 11px; color: var(--ink2); }
.t-sub { font-size: 11.5px; color: var(--dim); margin-top: 3px; line-height: 1.4; }
.axes { display: flex; gap: 4px; flex-wrap: wrap; }

/* ── asset grid ──────────────────────────────────────────────────────────── */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }
.card { border: 1px solid var(--line); border-radius: 11px; background: var(--panel); overflow: hidden; display: flex; flex-direction: column; transition: border-color .15s ease; }
.card:hover { border-color: var(--line2); }
.card.sel { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue) inset; }
.card.below { opacity: .5; }
.card-shot { position: relative; background: #fff; cursor: zoom-in; border: none; padding: 0; display: block; width: 100%; }
.card-shot img { display: block; width: 100%; height: 168px; object-fit: cover; object-position: top; }
.card-pick { position: absolute; top: 8px; left: 8px; width: 22px; height: 22px; border-radius: 6px; border: 1px solid rgba(0,0,0,.35); background: rgba(255,255,255,.92); display: grid; place-items: center; cursor: pointer; color: #0f1115; }
.card-pick.on { background: var(--blue); border-color: var(--blue); }
.card-pick:disabled { cursor: not-allowed; opacity: .55; }
.card-asp { position: absolute; bottom: 8px; right: 8px; font: 700 10px var(--mono); background: rgba(0,0,0,.62); color: #fff; padding: 2px 6px; border-radius: 4px; }
.card-body { padding: 11px 12px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.card-hook { font-size: 13px; font-weight: 600; line-height: 1.35; }
.card-tags { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
.card-q { display: flex; align-items: baseline; gap: 6px; }
.card-q b { font-size: 14px; font-variant-numeric: tabular-nums; }
.card-q b.low { color: var(--red); }
.card-q b.win { color: var(--green); }
.card-code { font-family: var(--mono); font-size: 10px; color: #6b7178; overflow-wrap: anywhere; margin-top: auto; padding-top: 6px; border-top: 1px solid var(--line); }

.hookrows { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; background: var(--panel2); border-radius: 7px; }
.hookrow { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.hookrow span { color: var(--dim); width: 76px; flex: none; }

/* ── filters + selection bar ─────────────────────────────────────────────── */
.filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
select, input[type=search] { font: 13px -apple-system, sans-serif; color: var(--ink); background: var(--panel2); border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; -webkit-appearance: none; }
input[type=search] { flex: 1; min-width: 180px; }
.selbar { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: var(--z-selbar); display: flex; align-items: center; gap: 12px; padding: 11px 16px; border-radius: 12px; background: var(--panel2); border: 1px solid var(--line2); box-shadow: 0 14px 40px rgba(0,0,0,.55); }
.selbar b { font-variant-numeric: tabular-nums; }

/* ── deploy tray ─────────────────────────────────────────────────────────── */
.job { border: 1px solid var(--line); border-radius: 11px; background: var(--panel); padding: 14px 16px; margin-bottom: 12px; }
.job.gated { border-color: rgba(240,198,116,.5); }
.job-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.job-strip { display: flex; gap: 4px; }
.job-strip img { width: 34px; height: 34px; object-fit: cover; border-radius: 5px; border: 1px solid var(--line); background: #fff; }
.job-stage { font-size: 13px; font-weight: 600; }
.job-el { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.steps { display: flex; gap: 3px; margin-top: 12px; }
.steps u { flex: 1; height: 3px; border-radius: 2px; background: rgba(255,255,255,.08); position: relative; overflow: hidden; }
.steps u.done { background: var(--blue); }
.steps u.now i { position: absolute; inset: 0 auto 0 0; background: var(--blue); }
.steps u.gate { background: var(--amber); }
.steps-lbl { display: flex; justify-content: space-between; font-size: 10px; color: var(--dim); margin-top: 5px; }

.approve { margin-top: 14px; border: 1px solid rgba(240,198,116,.4); background: rgba(240,198,116,.05); border-radius: 9px; padding: 13px 14px; }
.approve h4 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--amber); margin-bottom: 9px; display: flex; align-items: center; gap: 6px; }
.callrow { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; padding: 5px 0; }
.callrow .mono { font-size: 11.5px; }
.callrow.read { color: var(--dim); }
.callrow.write { color: var(--ink); }
.pf { display: flex; align-items: flex-start; gap: 7px; font-size: 12px; padding: 4px 0; }
.pf.warn { color: var(--amber); }
.approve-acts { display: flex; gap: 8px; margin-top: 12px; }
.writecard { margin: 10px 0; padding: 10px 12px; border-radius: 8px; background: #0f1116; border: 1px solid var(--line); font-family: var(--mono); font-size: 11.5px; line-height: 1.7; color: var(--ink2); }
.writecard b { color: var(--ink); }

/* ── entity grouping ─────────────────────────────────────────────────────── */
.egroup { border: 1px solid var(--line); border-radius: 9px; padding: 11px 13px; background: var(--panel2); margin-bottom: 8px; }
.egroup.grouped { border-color: rgba(248,113,113,.4); background: rgba(248,113,113,.05); }
.egroup-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.egroup-body { margin-top: 8px; font-size: 12px; color: var(--dim); }
.egroup-body code { font-family: var(--mono); font-size: 11px; color: var(--ink2); }

/* ── accounts ────────────────────────────────────────────────────────────── */
.acct { border: 1px solid var(--line); border-radius: 11px; background: var(--panel); padding: 15px 17px; margin-bottom: 12px; }
.acct.off { opacity: .55; border-style: dashed; }
.acct-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.acct-id { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.tools { margin-top: 12px; border-top: 1px solid var(--line); }
.tool { display: flex; align-items: center; gap: 9px; padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 12.5px; }
.tool:last-child { border-bottom: none; }
.tool .mono { flex: none; width: 158px; font-size: 11.5px; }
.tool .d { color: var(--dim); flex: 1; font-size: 11.5px; }

/* ── misc ────────────────────────────────────────────────────────────────── */
.absent { border: 1px dashed var(--line2); border-radius: 7px; padding: 9px 11px; font-size: 12px; color: var(--dim); }
.empty { padding: 34px 20px; text-align: center; color: var(--dim); font-size: 13px; border: 1px dashed var(--line); border-radius: 11px; }
.rowflex { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(20px); background: #1f2937; color: #fff; padding: 9px 16px; border-radius: 8px; font-size: 13px; opacity: 0; transition: all .2s; pointer-events: none; z-index: var(--z-toast); max-width: 70ch; }
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.err { background: #4a1d20; color: #fecaca; }
.lightbox { position: fixed; inset: 0; z-index: var(--z-lightbox); background: rgba(0,0,0,.86); display: grid; place-items: center; padding: 40px; cursor: zoom-out; }
/* Cap against the VIEWPORT so a tall 4:5 or 9:16 post is fully visible rather than running off
   the bottom. object-fit: contain keeps the aspect ratio inside that box. */
.lightbox img, .lightbox video {
  max-width: calc(100vw - 80px);
  max-height: calc(100vh - 80px);
  width: auto; height: auto;
  object-fit: contain;
  border-radius: 8px;
  background: #000;
  cursor: default;
}

/* ── client picker (topbar) ───────────────────────────────────────────────── */
.clientpick { font: 600 13px -apple-system, sans-serif; color: var(--ink); background: var(--panel2);
  border: 1px solid var(--line); border-radius: 8px; padding: 5px 9px; -webkit-appearance: none;
  cursor: pointer; max-width: 210px; }
.clientpick:hover { border-color: var(--line2); }
/* The same name with no control around it, for a reader who has one brand: same size and weight,
   so the top bar keeps its shape, without the border that promises a menu. */
.clientname { font: 600 13px -apple-system, sans-serif; color: var(--ink); padding: 5px 0; max-width: 210px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Back to the arrangement the operator set. Sits at the end of the table's own header, after the
   format switches it undoes — quiet until there is something to undo, because it is only ever
   rendered to someone who has moved away from the default. */
.viewreset { display: inline-flex; align-items: center; gap: 5px; margin-left: var(--s2);
  font: 600 11px -apple-system, sans-serif; color: var(--dim); background: none;
  border: 1px solid var(--line); border-radius: 20px; padding: 3px 9px; cursor: pointer;
  transition: color .12s ease-out, border-color .12s ease-out; }
.viewreset:hover { color: var(--ink); border-color: var(--line2); }
/* Says where the numbers come from. Replaces the old blanket MOCKUP chip, which lied once one
   client's data became real. */
.datachip { flex: none; font-size: 10px; font-weight: 800; letter-spacing: .8px; padding: 3px 8px; border-radius: 5px; cursor: help; }
.datachip.mock { background: #3a2e12; color: var(--amber); border: 1px solid #7a5f2a; }
.datachip.live { background: #0f2e1b; color: #4ade80; border: 1px solid #2b6b41; }
.datachip.empty { background: #23272f; color: var(--dim); border: 1px solid var(--line); }

/* `margin: auto` is what centres a modal dialog — the browser's own rule, undone by the `* { margin: 0 }`
   reset at the top of this file, which left every dialog in the top-left corner of the viewport. */
.dlg { border: 1px solid var(--line); border-radius: 12px; background: var(--panel); color: var(--ink);
  padding: 18px 20px; min-width: 320px; margin: auto; }
.dlg::backdrop { background: rgba(0,0,0,.6); }
.dlg h3 { font-size: 15px; margin-bottom: 12px; }
.dlg label { display: block; font-size: 11px; color: var(--dim); margin-bottom: 10px; }
.dlg input { display: block; width: 100%; margin-top: 4px; font: 13px -apple-system, sans-serif;
  color: var(--ink); background: var(--panel2); border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; }
/* The primary action is written FIRST so that Enter fires it, and sits LAST so it reads where a
   confirm belongs. Order is presentation; which button Enter means is not. */
.dlg-acts { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }
.dlg-acts .btn.primary { order: 2; }

/* ── attention shape ──────────────────────────────────────────────────────── */
.ladder { display: flex; align-items: stretch; gap: 8px; }
.ladder-step { flex: 1; border: 1px solid var(--line); border-radius: 9px; padding: 12px 13px; background: var(--panel2); }
.ladder-step b { display: block; font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.ladder-step span { font-size: 11.5px; font-weight: 600; }
.ladder-step em { display: block; font-style: normal; font-size: 10.5px; color: var(--dim); margin-top: 4px; line-height: 1.35; }
.ladder-arrow { align-self: center; color: var(--dim); font-size: 15px; }

/* ── creative (attention clients) ─────────────────────────────────────────── */
.postcell { display: flex; gap: 10px; align-items: flex-start; }
.postshot { flex: none; padding: 0; border: 1px solid var(--line); border-radius: 6px; overflow: hidden;
  background: #fff; cursor: zoom-in; width: 44px; height: 56px; }
.postshot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.postmeta { min-width: 0; }
.postcap { font-weight: 600; margin-bottom: 4px; }

.creativegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; }
.creativecard { border: 1px solid var(--line); border-radius: 11px; background: var(--panel2); overflow: hidden;
  display: flex; flex-direction: column; }
.creativeshot { padding: 0; border: none; background: #fff; cursor: zoom-in; display: block; width: 100%; }
.creativeshot img { width: 100%; height: 300px; object-fit: cover; object-position: top; display: block; }
.creativebody { padding: 11px 12px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.creativecopy { font-size: 12px; line-height: 1.5; color: var(--ink2); white-space: pre-line;
  max-height: 8.4em; overflow: hidden; }
.tscroll { overflow-x: auto; }
.tscroll table.t { min-width: 860px; }
.absent ul { padding: 0; }
.absent li { margin: 2px 0; }

/* ── surface grouping in the posts table ──────────────────────────────────── */
.grouprow th.group { font-size: 9.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  padding: 4px 8px 3px; border-bottom: none; color: var(--dim); }
.grouprow th.g-ig  { color: #e26aa0; background: rgba(214,41,118,.07); border-radius: 6px 6px 0 0; }
.grouprow th.g-ads { color: var(--ads); background: rgba(24,119,242,.07); border-radius: 6px 6px 0 0; }
/* a hairline where the surface changes, so the eye never reads across the boundary */
table.t th.gi, table.t td.gi { border-left: 1px solid rgba(214,41,118,.28); }
table.t th.ga, table.t td.ga { border-left: 1px solid rgba(24,119,242,.28); }

/* ── organic vs paid ──────────────────────────────────────────────────────── */
.splitrow { display: grid; grid-template-columns: 92px 1fr 74px 210px; align-items: center; gap: 12px; padding: 5px 0; }
.splitlabel { font-size: 12px; font-weight: 600; text-transform: capitalize; }
.splitbar { display: flex; height: 16px; border-radius: 5px; overflow: hidden; background: var(--panel2); border: 1px solid var(--line); }
.splitbar i { display: block; height: 100%; }
.splitbar i.paid { background: #2c4a6e; }
.splitbar i.org  { background: var(--green); }
.splitnums { text-align: right; font-variant-numeric: tabular-nums; }
.splitnums b { font-size: 14px; }
.splitnums span { font-size: 10px; color: var(--dim); margin-left: 4px; }
.splitdetail { font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums; }
.swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; margin-right: 5px; }
.swatch.paid { background: #2c4a6e; }
.swatch.org { background: var(--green); }
.grouprow th.g-org { color: #4ade80; background: rgba(34,197,94,.08); border-radius: 6px 6px 0 0; }
table.t th.go, table.t td.go { border-left: 1px solid rgba(34,197,94,.28); }

/* ── page head ────────────────────────────────────────────────────────────── */
.pagehead { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 20px; }
.pagewin { font-size: 12px; color: var(--dim); font-variant-numeric: tabular-nums; }

/* ── stacked metric cells: total / ads / organic, one under the other ─────── */
/* The mark sits on the WORD. Set as a flex gap it was pushed the same 6px the swatch uses, so it
   floated halfway between one entry and the next and read as belonging to neither. The swatch
   keeps its gap as a margin; the entries take the wider space instead. */
.stacklegend { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-bottom: 10px; }
/* The unit on a rate, at the size of a decimal rather than of the figure: 12.4% should read as one
   number with a unit, not as a number and a symbol competing for the same weight. */
.pctmark { margin-left: 1px; opacity: .45; }
.legitem { display: inline-flex; align-items: center; font-size: 11px; color: var(--dim); }
.legitem > i { margin-right: 6px; }
.dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }
.dot.t { background: var(--ink); }
.dot.a { background: var(--ads); }
.dot.o { background: var(--green); }
table.t.stacked td { padding: 10px 8px; }
/* THE SLACK GOES TO THE CONTENT COLUMN, not evenly to every numeric one. A table at 100% width
   hands its spare pixels to whichever columns will take them, and a right-aligned figure column
   spends every one of them on the empty left half of itself — so a one-digit Follows count sat a
   hand's width from the channel beside it while the column it lived in was three times the width
   of anything in it. Shrink-to-fit on the figures, and the one column that is actually text takes
   what is left. */
table.t.stacked th.r, table.t.stacked td.r { width: 1%; }
/* THE HINGE COLUMN. The interactions group used to be bracketed by rules on four sides, which is a
   lot of furniture for one relationship — and the chevron that opened it sat inside the header,
   where its width and its footnote mark were charged to every row below. Both are gone: the
   chevron has a column of its own, as narrow as a table cell will go, and it says where the total
   stops and the parts begin by standing between them. */
table.t th.ixgap, table.t td.ixgap { width: 1%; padding-left: 8px; padding-right: 8px;
  text-align: center; cursor: pointer; }
/* THE RECESS. The hinge is not a cell with a chevron in it, it is a channel cut down the table. The
   columns either side fall away into it, and when the parts open they open INSIDE it — which is the
   whole total-and-its-breakdown relationship said once, in a shape, with no rule or label doing the
   saying. Black at a fraction rather than a flat colour, so the row's own shading still reads
   through the floor of the channel: a live row is lit inside the well as well as outside it. */
table.t th.ixwell, table.t td.ixwell { background-color: rgba(0, 0, 0, .26); }
/* Where the neighbour falls in. An inset shadow spread back past its own blur, so it is a fall-off
   at the edge and nothing at all two columns over. */
table.t th.ixw-l, table.t td.ixw-l { box-shadow: inset 14px 0 12px -12px rgba(0, 0, 0, .8); }
table.t th.ixw-r, table.t td.ixw-r { box-shadow: inset -14px 0 12px -12px rgba(0, 0, 0, .8); }
/* Shut, one cell is both edges of the channel, and the two falls have to compose rather than the
   second one replacing the first — but at the depth that reads as a recess across five open
   columns they land on top of each other inside 32px and stack into a black bar. Shallower ground
   and a shorter reach, so a shut channel reads as a seam rather than a hole. */
table.t th.ixw-l.ixw-r, table.t td.ixw-l.ixw-r {
  background-color: rgba(0, 0, 0, .12);
  box-shadow: inset 9px 0 8px -9px rgba(0, 0, 0, .5),
              inset -9px 0 8px -9px rgba(0, 0, 0, .5); }

/* THE EDGE GETS THE CLEARANCE A COLUMN GETS. Two neighbouring columns put 16px between their
   figures, eight from each side of the boundary; the well's edge is a change of ground and got
   eight in total, so a figure sat hard against the channel while the same figure stood clear of
   every ordinary neighbour it had. Both sides of the well, and the last column inside it, now
   measure what everything else on the row measures. */
table.t th.ixw-before, table.t td.ixw-before { padding-right: 16px; }
table.t th.ixw-after, table.t td.ixw-after { padding-left: 16px; }
table.t th.ixw-r:not(.ixgap), table.t td.ixw-r:not(.ixgap) { padding-right: 16px; }
/* Under the cursor the floor lifts toward you. The whole strip is one control, so it answers to
   the pointer the way a button does. */
table.t th.ixgap, table.t td.ixgap { transition: background-color .12s ease-out; }
table.t th.ixgap:hover, table.t td.ixgap:hover { background-color: rgba(0, 0, 0, .10); }
table.t .ixgap .ixbtn { color: var(--dim); }
table.t .ixgap:hover .ixbtn { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  table.t th.ixgap, table.t td.ixgap { transition: none; }
}
/* The total, once its parts are open. Kept and stepped back rather than swapped out: four columns
   decomposing nothing you can see leave no way to check that they sum to what was credited. */
table.t th.ixtotal .sortbtn { color: var(--dim); font-weight: 600; }
table.t th.ixtotal .sortbtn.on { color: var(--ink2); }
td.ixtotal .st.t { color: var(--ink2); font-weight: 600; }
/* A step back, not a fade-out. .85 is where 12px blue on this ground still clears 4.5:1, and the
   real subduing is done by the ground anyway: the four parts sit inside the well and the total
   does not, which separates them before any ink does. */
td.ixtotal .st.a, td.ixtotal .st.o { opacity: .85; }
.ixbtn { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit;
  letter-spacing: inherit; text-transform: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: 3px; }
.ixbtn:hover { color: var(--ink); }
.ixbtn:focus, .ixbtn:focus-visible { outline: none; }
.ixbtn svg { opacity: .75; }
table.t th.cno, table.t td.cno { color: var(--dim); font-variant-numeric: tabular-nums;
  width: 1%; white-space: nowrap; padding-right: 4px; }
td.stack { line-height: 1.35; font-variant-numeric: tabular-nums; }
.st { font-size: 12px; }
.st.t { font-weight: 700; color: var(--ink); font-size: 13px; }
.st.a { color: var(--ads); }
.st.o { color: var(--green); }


/* ── goal + step-down ─────────────────────────────────────────────────────── */
.goal { margin-bottom: 16px; }
.goal-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s3); }
.goal-head b { font-size: 22px; font-variant-numeric: tabular-nums; color: var(--target); }
/* Direct children only. Unscoped, this reached into the figure and shrank "58 /" to a caption,
   and it outranked .goal-gap's own colour. */
.goal-head > span { font-size: 12px; color: var(--dim); }
.goal-head > .goal-gap { margin-left: auto; font-size: 12px; color: var(--target); }
/* `overflow: visible` and the radius moved onto the fill: the markers stand a little proud of the
   track so a notch is legible at 8px, and clipping them to the box would cut the two that matter
   most — the one at the very start and the one at the very end. */
.goal-bar { position: relative; height: 8px; border-radius: 4px; background: var(--panel2);
  border: 1px solid var(--line); }
.goal-bar i { display: block; height: 100%; background: var(--target);
  border-radius: 3px 0 0 3px; }
.goal-bar i[style*="100%"] { border-radius: 3px; }

/* A MILESTONE ON THE BAR IS THE MARKER FROM THE CHART. Same circle, same 9px, same knockout, same
   ink. The 100 that unlocks demographics is ONE fact, and it was a ring on one surface and a
   hairline tick on another, which made the bar look like it was showing something else.
   Shape still carries the state and hue never does: hollow is still ahead, filled is crossed,
   dashed is a stretch nobody is on course for.
   Every selector here leads with `.goal-bar` on purpose. The rule that turns any [data-tip] into a
   footnote asterisk is an ::after of equal weight written later in the file, so at one class these
   grew a '*' hanging off the bar. The extra class settles it, and the mark keeps its tooltip. */
.goal-bar .goal-mark { position: absolute; top: 50%; width: 9px; height: 9px;
  margin: -4.5px 0 0 -4.5px; box-sizing: border-box; border-radius: 50%;
  background: var(--panel2); border: 1.5px solid var(--target); cursor: default; }
.goal-bar .goal-mark[data-tip]::after { content: none; }
/* Crossed. Filled like the chart's, and ringed in the track's own colour because on the bar a
   crossed milestone necessarily sits on the filled half, where red on red is nothing at all. */
/* Crossed. The chart's `done` marker in bar form: the same hollow ring, the target dot's inner
   circle as the inner AREA (a ~4px see-through centre) inside a ~3px ring. A crossed milestone
   always sits on the FILLED (red) half, so the ring inverts to the surface colour — red on red is
   nothing at all, and shape, not hue, is what carries the state. The centre is hollow (transparent),
   so the bar's own red shows through it, matching how the chart's centre shows the plot behind it.
   Centred on top:50% by a margin of half its own box (box-sizing:border-box, so 10px is the whole). */
.goal-bar .goal-mark.done { width: 10px; height: 10px; margin: -5px 0 0 -5px;
  background: transparent; border: 3px solid var(--panel2); }
.goal-bar .goal-mark.stretch { width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px;
  border-style: dashed; border-color: var(--dim); }
/* The target, when a stretch is aimed past it: the chart's ring with a core inside it. */
.goal-bar .goal-mark.atgoal { width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px;
  border-color: var(--ink); }
.goal-bar .goal-mark.atgoal::before { content: ''; position: absolute; inset: 1.5px;
  border-radius: 50%; background: var(--ink); }

.stepflow { display: flex; align-items: stretch; gap: var(--s3); }
.stepcell { flex: 1; border: 1px solid var(--line); border-radius: 9px; padding: var(--s3) 14px; background: var(--panel2); }
.step-n { font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.step-l { font-size: 12px; font-weight: 600; margin-bottom: var(--s2); }
/* Headline and its decomposition are one group each: the rows sit tight together, and the space
   above them is what separates them from the number they sum to. No rule needed. */
.step-body { margin-top: var(--s3); }
.step-half { display: flex; align-items: baseline; gap: var(--s2); font-size: 11.5px; padding: 3px 0; }
.step-half.a .step-v { color: var(--ads); }
.step-half.o .step-v { color: var(--green); }
.step-half.u .step-v { color: var(--ink2); }   /* source not attributable, not zero */
.step-v { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 46px; }
.step-v.dim { color: var(--dim); }
.step-k { color: var(--dim); }
.step-p { margin-left: auto; color: var(--dim); font-variant-numeric: tabular-nums; }
.adapter { align-self: center; text-align: center; padding: 0 4px; }
/* Figure and unit share a line; each ratio is its own line; the arrow takes the last one. */
/* Left-aligned, so stacked ratios sit squarely on top of each other instead of each centring on
   its own width. With a single row the adapter is only as wide as that row, so this is invisible
   there and no conditional is needed. The arrow stays centred under the block. */
.adapter-row { white-space: nowrap; text-align: left; }
.adapter-row + .adapter-row { margin-top: 2px; }
/* Its own colour, and deliberately not blue: an adapter is not a category of anything, it is the
   arithmetic between the two tiles it sits between, and in blue it read as one more paid figure. */
.adapter b { font-size: 13px; color: var(--adapter); font-variant-numeric: tabular-nums; }
.adapter-unit { font-size: 10px; font-style: normal; color: var(--dim); margin-left: 3px; }
/* What has been added to the top of the divide, italic and unbracketed: part of naming the
   measure, not an aside about the number. */
.adapter-qual { font-size: 10px; font-style: italic; color: var(--dim); margin-left: 2px; }
.adapter span { display: block; color: var(--dim); line-height: 0; margin-top: 2px; }

/* The client summary. Neutral, not amber: the amber said "warning" about figures that are simply
   the plan, and it made the one tile a client reads the loudest thing on the panel. */
.projection { margin-top: var(--s5); padding: var(--s4) 16px; border-radius: 9px;
  background: var(--panel2); border: 1px solid var(--line); }
.btn.tiny { font-size: 11px; padding: 4px 9px; }
.redraft { margin-top: var(--s3); }
.projrow { display: flex; gap: 28px; margin: 8px 0 6px; }
.projcell b { display: block; font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.projcell span { font-size: 11px; color: var(--dim); }

/* ── traction charts ──────────────────────────────────────────────────────── */
.tractionlead { margin-bottom: 14px; }
.chartlegend { display: flex; gap: 18px; margin-bottom: 6px; }
.legline { width: 20px; height: 0; border-top: 2px solid; display: inline-block; margin-right: 6px; vertical-align: middle; }
.legline.total { border-color: #7cc0ff; }
.legline.target { border-color: #8a9199; border-top-style: dashed; }
.chartbox { width: 100%; }
.chart { width: 100%; height: auto; display: block; }
.chart .hit { cursor: crosshair; }
.donutbox { display: grid; place-items: center; padding: 6px 0; }
.donut { width: 168px; height: 168px; }
.chartrow { display: flex; gap: 14px; align-items: stretch; }
.chartrow > .sec { margin-bottom: 14px; }
.chartrow > .wide { flex: 3; min-width: 0; }
.chartrow > .narrow { flex: 1.1; min-width: 0; }
.chartrow > .half { flex: 1; min-width: 0; }
@media (max-width: 900px) { .chartrow { flex-direction: column; } }

/* Effort → Outcome → Result stacks once three tiles stop fitting side by side; the connector
   arrow turns to point down the new reading order rather than across a column that isn't there. */
@media (max-width: 720px) {
  .stepflow { flex-direction: column; }
  .adapter { align-self: start; padding: var(--s1) 0 var(--s1) 14px; }
  .adapter span { display: inline-block; transform: rotate(90deg); }
  .projrow { flex-wrap: wrap; gap: var(--s4) var(--s5); }
}

/* ── content-set band ─────────────────────────────────────────────────────── */
.setband { margin-top: 12px; padding: 13px 15px; border-radius: 10px; background: var(--panel); border: 1px solid var(--line); }
.setband-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.setband-head b { font-size: 15px; }
.setband-head span { font-size: 12px; color: var(--dim); }
.setband-gap { margin-left: auto; color: var(--amber) !important; }
.setband-row { display: flex; gap: 26px; margin: 11px 0 4px; flex-wrap: wrap; }

/* ── range picker, ported from WrkBook's calendar ─────────────────────────── */
/* The weeks. This was a popover of its own, anchored under a date button, then a month-at-a-time
   body inside the scope panel; it is a continuous SCROLLER now. Six week rows in view, which is
   the height of one month's grid, and the months run past each other without the calendar ever
   cutting a fortnight in half at a boundary. */
.wt-cal { --cal-wk: 26px; border-top: 1px solid var(--line); margin-top: 8px; padding-top: 4px; }
.wt-cal-bar { display: flex; align-items: center; justify-content: space-between;
  padding: 0 2px 2px; }
.wt-cal-title { font-weight: 500; font-size: 13px; font-variant-numeric: tabular-nums; }
/* Stacked, pointing the way the grid moves. Left and right on a surface that scrolls up and down
   names the wrong axis. */
.wt-cal-steps { display: flex; flex-direction: column; gap: 1px; }
.wt-cal-step { width: 22px; height: 15px; display: grid; place-items: center; border: none;
  border-radius: 5px; background: none; color: var(--dim); cursor: pointer; }
.wt-cal-step svg { width: 13px; height: 13px; }
.wt-cal-step:hover { background: rgba(127,127,127,.14); color: var(--ink); }
.wt-cal-step:focus, .wt-cal-step:focus-visible { outline: none; }

/* The day names sit ABOVE the scroller, not in it: a column heading that scrolls away is a
   heading you have to remember. */
.wt-cal-names { display: grid; grid-template-columns: var(--cal-wk) repeat(7, 1fr); gap: 1px; }
.wt-cal-day-name { display: grid; place-items: center; height: 20px;
  font-family: var(--mono); font-size: .66rem; letter-spacing: .06em; color: var(--dim); }

/* SIX WEEKS: 6 rows of 30px with a 1px gutter, plus room for the one month marker a six-week
   window usually holds. With no marker in view a seventh row shows its top edge, which is the
   cheapest way a scroller can say there is more of it. */
/* `position: relative` is load-bearing, not decoration: the scroller reads its own children's
   offsetTop to frame a period and to name the month in view, and without it those are measured
   from the panel — which is fixed — so every scroll landed a month and a half past its mark. */
/* TWO AND A HALF MONTHS IN ONE LOOK. A week row is 31px with its gap and a month heading 19, so
   eleven weeks and the two headings between them is 380 — against 209, which showed six and a
   half and made picking a range that crossed a month a scrolling exercise. Clamped against the
   viewport so a panel opened low on a short screen shrinks rather than running off the bottom. */
.wt-cal-scroll { position: relative; height: min(380px, calc(100vh - 330px)); min-height: 209px;
  overflow-y: auto; overscroll-behavior: contain;
  border-top: 1px solid var(--line); padding-top: 3px; }
.wt-cal-grid { display: grid; grid-template-columns: var(--cal-wk) repeat(7, 1fr); gap: 1px; }
/* The week the row IS, in the same words every interval and every chart band is named with. It is
   a label and not a target: the row it heads is seven buttons already. */
.wt-cal-wk { display: grid; place-items: center; height: 30px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .02em; color: var(--line2); }
.wt-cal-wk-h { color: var(--line2); }
/* Where a month opens. Full width, so the seven columns pick up on Monday underneath it. */
.wt-cal-mark { grid-column: 1 / -1; padding: 5px 2px 2px;
  font: 700 9px -apple-system, sans-serif; letter-spacing: .07em; text-transform: uppercase;
  color: var(--dim); }
.wt-cal-day { height: 30px; display: grid; place-items: center; border: none; border-radius: 7px;
  background: none; color: var(--ink); font: 400 13px -apple-system, sans-serif; cursor: pointer;
  font-variant-numeric: tabular-nums; }
/* ALTERNATING MONTHS, AND THE HEADING IN THE SAME INK AS ITS OWN DAYS. Split on the warm/cool
   axis rather than by hue: the owner reads red and green as one colour, so a pair that differs
   only in hue is a pair that might not differ at all — cool against warm is the axis that survives
   that, and the two also differ in lightness, which survives having no colour at all. Kept at very
   low chroma so neither can be mistaken for --blue, which on this grid means TODAY and CHOSEN and
   must stay the only saturated thing in it. */
.wt-cal-m0, .wt-cal-mark.wt-cal-m0 { color: oklch(0.925 0.014 250); }
.wt-cal-m1, .wt-cal-mark.wt-cal-m1 { color: oklch(0.845 0.026 82); }
.wt-cal-day:hover { background: rgba(127,127,127,.16); }
.wt-cal-day:focus, .wt-cal-day:focus-visible { outline: none; }
/* STATE OUT-SPECIFIES MONTH. Both are a colour on the same element and the month tint is written
   first, so which one won came down to source order — and it took the wrong one on the state that
   can least afford it: a chosen day is a light blue chip, and a warm near-white number on it read
   as unfilled. The three state rules take a second class each and settle it. */
/* Today is marked, not selected: it says where you are, the fill says what you picked. */
.wt-cal-day.wt-cal-today { color: var(--blue); font-weight: 700; }
.wt-cal-between { background: rgba(124,192,255,.14); border-radius: 0; }
.wt-cal-day.wt-cal-chosen, .wt-cal-day.wt-cal-chosen:hover {
  background: var(--blue); color: #080a0e; font-weight: 700; }
/* NO THIRD INK. A day outside the data window used to be dimmed as well as tinted, and opacity
   MULTIPLIES the colour under it — so two month tints and one dim state rendered as four distinct
   inks on the numbers, plus a fifth where a selected day was exempted from the dim. Two tones were
   asked for and two is also all this grid can carry: the number says which day, the tint says
   which month, and where the content actually falls is stated in words on every interval row
   above. */

/* ── two-path follower attribution ────────────────────────────────────────── */
.twopath { display: flex; align-items: stretch; gap: 10px; }
.pathcell { flex: 1; border: 1px solid var(--line); border-radius: 10px; padding: 13px 14px; background: var(--panel2); }
.pathcell.p-post    { border-color: rgba(124,192,255,.35); }
.pathcell.p-profile { border-color: rgba(34,197,94,.35); }
.pathcell.p-total   { border-color: rgba(240,198,116,.4); background: rgba(240,198,116,.05); }
.path-n { font-size: 28px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.05; }
.p-post .path-n { color: var(--blue); }
.p-profile .path-n { color: var(--green); }
.p-total .path-n { color: var(--amber); }
.path-l { font-size: 12px; font-weight: 600; margin: 2px 0 9px; }
.path-row { display: flex; justify-content: space-between; gap: 10px; font-size: 11.5px; padding: 2px 0; }
.path-k { color: var(--dim); }
.path-v { font-variant-numeric: tabular-nums; }
.pathplus { align-self: center; font-size: 18px; color: var(--dim); padding: 0 2px; }

/* ── Target panel (dashboard element 1) ────────────────────────────────────── */
.metricpick { background: var(--panel2); color: var(--ink); border: 1px solid var(--line);
  border-radius: 7px; padding: 4px var(--s2); font: inherit; font-size: 11px; font-weight: 700;
  letter-spacing: .06em; cursor: pointer; }
.metricpick:focus, .metricpick:focus-visible { outline: none; border-color: var(--blue); }
.metricpick:hover { border-color: var(--line2); }

/* The goal number is the panel's headline and outranks the tile numbers (30 : 24 = 1.25). */
.sec .goal-head { gap: var(--s3); margin-bottom: var(--s2); }
.sec .goal-head b { font-size: 30px; letter-spacing: -.01em; }
.sec .goal-bar + .stepflow { margin-top: var(--s5); }

.step-t { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); margin-bottom: var(--s3); }

.chartwrap { margin-top: var(--s6); }

/* Two independent footnotes, side by side rather than stacked full-bleed: at the panel's full
   width each ran about 150ch. auto-fit collapses them to one column on a narrow viewport. */

/* ── System tooltip (ui.js) ───────────────────────────────────────────────────
   Replaces the browser's OS-styled title bubble. One node, repositioned. */
.tip { position: fixed; z-index: var(--z-tooltip); pointer-events: none;
  max-width: 46ch; padding: 7px 10px; border-radius: 7px;
  background: #0b0d11; border: 1px solid var(--line2); color: var(--ink);
  font-size: 11.5px; line-height: 1.55; font-variant-numeric: tabular-nums;
  /* Tooltips are written as one fact per line, so newlines in data-tip have to survive. pre-line
     collapses the incidental indentation of a template literal but keeps the breaks. */
  white-space: pre-line;
  box-shadow: 0 10px 28px rgba(0,0,0,.6);
  opacity: 0; transform: translateY(3px); transition: opacity .12s ease-out, transform .12s ease-out; }
/* A swatch leading a tooltip line, for the lines that are explaining a colour. `pre-line` on the
   parent keeps the newline after each row, so these still read one fact per line. */
.tip-row { display: inline; }
.tip-sw { display: inline-block; width: 8px; height: 8px; border-radius: 2px;
  margin-right: 6px; vertical-align: 0; }
.tip-sw.a { background: var(--ads); }
.tip-sw.o { background: color-mix(in srgb, var(--green) 72%, transparent); }
.tip.on { opacity: 1; transform: none; }
.tip.below { transform: translateY(-3px); }
.tip.below.on { transform: none; }

/* ── System dropdown (ui.js) ──────────────────────────────────────────────────
   position:fixed so no ancestor's overflow can clip it. */
.pick { display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  background: var(--panel2); color: var(--ink); border: 1px solid var(--line);
  border-radius: 7px; padding: 4px 9px; font: inherit; font-size: 11px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; transition: border-color .12s ease-out; }
.pick:hover { border-color: var(--line2); }
.pick[aria-expanded="true"] { border-color: var(--blue); }
.pick:focus, .pick:focus-visible { outline: none; border-color: var(--blue); }
.pick-caret { display: inline-flex; color: var(--dim); transition: transform .14s ease-out; }
.pick[aria-expanded="true"] .pick-caret { transform: rotate(180deg); }
.pick-label { display: inline-flex; align-items: center; }
/* The picker's label without the picker, for a reader: what is being measured is a fact they need,
   and the choice of it is not theirs. Same type and same place as the control it replaces, so the
   heading keeps its shape between the two views. */
.metriclabel { display: inline-flex; align-items: center; padding: 4px 0; color: var(--ink2);
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }

/* A MENU YOU CAN TYPE INTO. The field sits outside the list box, because the list is rebuilt on
   every keystroke and a field inside it would be replaced mid-word. */
.pickfind { width: 100%; box-sizing: border-box; margin-bottom: 4px;
  padding: 6px var(--s2); border-radius: 7px;
  background: var(--bg); border: 1px solid var(--line); color: var(--ink);
  font: 400 12px -apple-system, sans-serif; }
.pickfind::placeholder { color: var(--dim); }
.pickfind:focus, .pickfind:focus-visible { outline: none; border-color: var(--line2); }
.pickfind::-webkit-search-cancel-button { filter: invert(.6); }
.picklist { max-height: 264px; overflow-y: auto; }
.pickfind-none { padding: var(--s3) var(--s2); font-size: 12px; color: var(--dim); }

.pickmenu { position: fixed; z-index: var(--z-dropdown); min-width: 160px; padding: 5px;
  border-radius: 9px; background: var(--panel2); border: 1px solid var(--line2);
  box-shadow: 0 16px 44px rgba(0,0,0,.6);
  opacity: 0; transform: translateY(-4px) scale(.985); transform-origin: top left;
  transition: opacity .13s ease-out, transform .13s cubic-bezier(.22,1,.36,1); }
.pickmenu.on { opacity: 1; transform: none; }
.pickitem { display: flex; align-items: baseline; gap: var(--s2); width: 100%; cursor: pointer;
  background: none; border: 0; border-radius: 6px; padding: 6px 9px; font: inherit;
  font-size: 12px; color: var(--ink2); text-align: left; }
.pickitem:hover, .pickitem:focus { background: rgba(255,255,255,.06); color: var(--ink); }
.pickitem:focus, .pickitem:focus-visible { outline: none; }
.pickitem.on { color: var(--ink); font-weight: 700; }
.pickitem.on .pickitem-l::after { content: ''; }
.pickitem-n { margin-left: auto; font-size: 11px; color: var(--dim); font-weight: 400; }

/* THE VIEW SWITCHER, at the right end of the top bar. Sentence case at the nav's size: every other
   thing in that bar is sentence case, and an uppercase tracked pill would be the loudest element on
   a page whose job is the numbers below it. */
.whoami .pick.viewmode { text-transform: none; letter-spacing: 0; font-size: 12px; padding: 5px 9px; }
/* THE TRIGGER CARRIES THE STATE. Three of them, and each changes what the next click does:
   sharp green in the client's own view, pale green when standing in the view published to them,
   and nothing in a private cut. The label says which view it is; this says what that view IS. */
.whoami .pick.viewmode.as-client { border-color: var(--green); }
.whoami .pick.viewmode.on-published { border-color: color-mix(in oklab, var(--green) 45%, transparent); }
/* SOMETHING IS WRITTEN AND NOT HANDED OVER. The one state of this control that is a job rather
   than a fact, so it is the one that overrides the others: amber, and a dot, because a border
   colour alone is a difference nobody scans a top bar for. */
.whoami .pick.viewmode.waiting { border-color: var(--amber); color: var(--ink); }
.whoami .pick.viewmode.waiting::before { content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--amber); margin-right: 2px; flex: none; }
/* A rule between two kinds of thing in one menu: the views you can be IN, and everything else. */
.pickdiv { height: 1px; background: var(--line); margin: 6px 5px; }

/* The verbs that act on the view above them. Icons on one row, because they are things you DO to a
   view rather than views you can pick, and five more full-width rows of prose would bury the two
   lines that matter. Each carries its name and its consequence in the tooltip. */
.pickacts { display: flex; gap: 2px; padding: 3px 6px 5px; }
.pickact { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 27px;
  cursor: pointer; background: none; border: 1px solid transparent; border-radius: 7px;
  color: var(--dim); transition: color .12s ease-out, background-color .12s ease-out; }
.pickact:hover:not(:disabled) { background: rgba(255,255,255,.06); color: var(--ink); border-color: var(--line); }
.pickact:focus, .pickact:focus-visible { outline: none; border-color: var(--blue); color: var(--ink); }
/* Destructive, and it says so before it is pressed rather than after — the only place hue is doing
   work in this row, and it is doubled by the icon and by the tooltip. */
.pickact.danger:hover:not(:disabled) { color: var(--red); border-color: var(--red); }
.pickact:disabled { opacity: .3; cursor: default; }
/* Armed: the icon becomes the question, in the same place the finger already is. Red AND the word
   — the colour is never carrying this on its own. */
.pickact.armed { width: auto; padding: 0 10px; color: var(--red); border-color: var(--red);
  background: color-mix(in oklab, var(--red) 16%, transparent); }
.pickact.armed:hover { background: color-mix(in oklab, var(--red) 26%, transparent); }
.pickact-w { font: 700 10px -apple-system, sans-serif; letter-spacing: .06em; text-transform: uppercase; }
/* No footnote asterisk. The mark means "there is context behind this figure"; on a button whose
   whole label IS its tooltip it is a second glyph competing with the icon. */
.pickact[data-tip]::after { content: none; }

/* Who each view belongs to, as subtext under the option rather than a note trailing it: a list of
   names is the answer to "whose view is this", not an aside, and it has to be free to wrap. */
/* The pages control, where the `//` separator was: same weight as the separator it replaces, so the
   bar reads as brand · client · pages · the pages themselves rather than gaining a new landmark. */
.sep .pick.pagepick { padding: 3px 5px; gap: 3px; border-color: transparent; background: none; color: var(--dim); }
.sep .pick.pagepick:hover { color: var(--ink); border-color: var(--line); background: var(--panel2); }
/* Wide enough for a grip, a page name and an eye without the label ever meeting either — the same
   room the Assets menu takes, because it is now the same control doing the same job. */
.pickmenu.pagemenu { min-width: 224px; }
/* Assets: the way out to the brand's own surfaces. Sits with the top bar's other controls rather
   than competing with them — it is a door, not a state. */
.pick.assetpick { color: var(--dim); }
.pick.assetpick:hover { color: var(--ink); }
.pickmenu.assetmenu { min-width: 232px; }

/* A ROW THAT IS MORE THAN ITS LABEL: something to pick it up by on the left, the door in the
   middle, and the eye that parks it on the right. A button cannot contain a button, so these are
   siblings in a flex row rather than one control with things inside it. */
.pickrow { display: flex; align-items: center; gap: 2px; border-radius: 6px; position: relative; }
.pickrow .pickitem { flex: 1; min-width: 0; }
/* The grip appears on hover, and holds its space when it does not, so nothing shifts sideways the
   moment the pointer arrives. */
/* A HANDLE HAS TO BE CATCHABLE. It was a 13px glyph that only appeared once the pointer was
   already on the row, so the thing you reach for was never under the pointer when you pressed.
   Same glyph, a target around it, and it holds its space so nothing shifts sideways on hover. */
.pickgrip { display: inline-flex; align-items: center; justify-content: center; width: 18px;
  align-self: stretch; flex: none; color: var(--dim); opacity: .3; cursor: grab;
  transition: opacity .12s ease-out, color .12s ease-out; }
.pickrow:hover .pickgrip, .pickrow:focus-within .pickgrip { opacity: 1; color: var(--ink2); }
.pickrow.dragging { opacity: .45; }
.pickrow.dragging .pickgrip { cursor: grabbing; opacity: 1; }
/* WHERE IT WILL LAND. A line drawn on the row the pointer is over, on the side it would go — the
   only way a drop has anything to say before the mouse is released. */
.pickrow.dropbefore::before, .pickrow.dropafter::after {
  content: ''; position: absolute; left: 4px; right: 4px; height: 2px; border-radius: 2px;
  background: var(--blue); }
.pickrow.dropbefore::before { top: -2px; }
.pickrow.dropafter::after { bottom: -2px; }
/* Shown or parked, on the row itself. Open eye in green when the asset is offered first, struck
   through and grey when it has been put away under Other — the glyph says it before the hue does. */
.pickeye { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 24px;
  flex: none; cursor: pointer; background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--dim); transition: color .12s ease-out, background-color .12s ease-out; }
.pickeye.on { color: var(--green); }
.pickeye:hover { background: rgba(255,255,255,.07); color: var(--ink); }
.pickeye.on:hover { color: var(--green); }
.pickeye:focus, .pickeye:focus-visible { outline: none; border-color: var(--blue); }
.pickeye[data-tip]::after { content: none; }
/* A STATE THAT IS STATED, NOT OFFERED. The page every route falls back to cannot leave the bar, so
   its eye is drawn and inert. Dimmed rather than omitted: a gap where every other row carries a
   control reads as a control that failed to load, and the open eye is still the true answer to
   "is this page in the bar". */
.pickeye.fixed { cursor: default; opacity: .45; }
.pickeye.fixed:hover, .pickeye.fixed.on:hover { background: none; }
.pickeye.fixed.on:hover { color: var(--green); }
/* The parking bay. A row that opens the rest rather than going anywhere, so it reads as a lid:
   quieter than the doors above it, with the count of what is under it, and what is under it sits
   in from the edge — the indent is what says these belong to the row above rather than following
   it. Nothing in there can be dragged: it is a bay, not a second arrangement. */
.pickmenu .pickitem.otherlid { color: var(--dim); font-size: 11.5px; }
.pickitem-g { display: inline-flex; align-items: center; flex: none; color: var(--dim); }
.pickmenu .pickitem.parkedrow { padding-left: 22px; color: var(--dim); }
.pickmenu .pickitem.parkedrow:hover { color: var(--ink2); }
/* ── the client directory, edited in place ───────────────────────────────────
   A cell IS its field. Invisible until you touch it, so the table reads as a table and not as a
   form; a border on hover says it can be changed before you have committed to changing it. */
/* FIXED COLUMNS, because the rows change. Opening the archived brands puts longer codes and more
   handles into a table whose widths were measured from the live row alone, so every column
   re-measured and the whole thing shifted under the pointer. Fixed layout takes the widths from
   the header and keeps them, so a row appearing below changes nothing above it. */
.dirtable { table-layout: fixed; width: 100%; }
.dirtable td { vertical-align: top; }
.dirtable th:nth-child(1), .dirtable td:nth-child(1) { width: 78px; }
.dirtable th:nth-child(2), .dirtable td:nth-child(2) { width: 64px; }
.dirtable th:nth-child(3), .dirtable td:nth-child(3) { width: 200px; }
.dirtable th:nth-child(4), .dirtable td:nth-child(4) { width: 112px; }
.dirtable th:last-child,   .dirtable td:last-child   { width: 48px; }
/* The two handle columns share whatever the fixed columns leave, which is most of the table —
   they hold the only content that genuinely varies in length. */
.dirtable td.socials { min-width: 150px; }
/* The archived brands, folded into one row. It is a lid, not a heading: full width, quiet, and it
   names what is under it so opening it is a choice rather than a look. */
/* ONE HIGHLIGHT, NOT TWO. The row lit up as a table row and the lid lit up as a button, so hovering
   it drew two overlapping panels of slightly different greys. The lid is the only thing here. */
.archrow td { padding: 0; }
table.t tbody tr.archrow:hover { background: none; }
table.t tbody tr.archrow > td { border-top: 0; }
.archtoggle { display: flex; align-items: center; gap: 7px; width: 100%; padding: 9px 8px;
  background: none; border: 0; font: inherit; font-size: 12px; color: var(--dim);
  text-align: left; cursor: pointer; border-radius: 7px; }
.archtoggle:hover { background: rgba(255,255,255,.04); color: var(--ink2); }

.cellinput { display: block; width: 100%; min-width: 60px; font: inherit; font-size: 12px;
  color: var(--ink); background: none; border: 1px solid transparent; border-radius: 6px;
  padding: 3px 6px; margin: -3px -6px; }
.cellinput:hover { border-color: var(--line); }
.cellinput:focus, .cellinput:focus-visible { outline: none; border-color: var(--blue);
  background: var(--panel2); }
.cellinput::placeholder { color: var(--dim); opacity: .55; }
/* Refused. Held for a moment on the field itself, so the value going back to what it was is
   explained where it happens rather than only in a toast at the foot of the page. */
.cellinput.bad { border-color: var(--red); background: color-mix(in oklab, var(--red) 12%, transparent); }
.cellinput.mono { font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; }
.cellinput.ref { text-transform: uppercase; letter-spacing: .08em; font-weight: 700; }
.cellinput.strong { font-weight: 700; color: var(--ink); }
/* The second line of a cell: the same field, less of it. */
.cellinput.sub { font-size: 11px; color: var(--dim); margin-top: 1px; }
.dirtable .pick.statuspick { text-transform: uppercase; font-size: 10px; padding: 3px 7px; }

/* A brand's accounts: as many as it has, each one a link out, with the platform in the tooltip. */
.socialwrap { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.socialchip { display: inline-flex; align-items: stretch; gap: 2px; font-size: 11px;
  padding: 0 4px 0 0; border-radius: 6px; background: var(--panel2);
  border: 1px solid var(--line); color: var(--ink2); white-space: nowrap; }
.socialchip > *:not(.entrytag) { align-self: center; }
.socialchip a { color: var(--ink2); padding: 2px 0; }
/* THE TAG IS THE END OF THE FIELD, NOT A LABEL FLOATING IN IT. It fills the chip's left edge —
   flush to the top, the bottom and the side, with only the chip's own corner rounding on the
   outside and a straight cut on the inside — so the eye reads a strip and then a value, rather
   than a pill inside a pill. The two characters that say what the value IS, on the chip and on the
   field while it is being typed, which is the moment the platform just picked is otherwise nowhere
   on screen. */
.entrytag { display: inline-flex; align-items: center; flex: none;
  font: 700 9px ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .07em;
  color: var(--dim); background: rgba(255,255,255,.06); padding: 0 5px;
  border-radius: 5px 0 0 5px; margin-right: 4px; }
.socialchip.adding { border-color: var(--blue); background: var(--panel2); padding-right: 4px; }
.socialchip.adding .entrytag { background: color-mix(in oklab, var(--blue) 18%, transparent);
  color: var(--ink2); }
/* What opened, last time anything looked. Absent until a check has run — an unmarked entry is one
   nobody has tried, which is not the same as one that failed. Shape carries it: a tick against an
   octagon, which survives not separating red from green. */
.accessmark { display: inline-flex; align-items: center; margin-left: 1px; }
.accessmark.ok { color: var(--green); }
.accessmark.no { color: var(--red); }
.accessmark[data-tip]::after { content: none; }

/* A sub-account: the brand's, so it sits under it and says less. No name and no status, because it
   has neither — the brand's govern it. */
tr.subaccount td { border-top: 0; padding-top: 2px; }
tr.subaccount .subcode { display: inline-block; margin-left: 14px; font: 12px ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--dim); position: relative; }
/* The bracket that says "under this one" — quieter than an indent alone, and it survives a column
   being resized. */
tr.subaccount .subcode::before { content: ''; position: absolute; left: -10px; top: -4px; bottom: 6px;
  width: 1px; background: var(--line2); }
.subadd { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 24px;
  cursor: pointer; background: none; border: 1px dashed var(--line); border-radius: 7px; color: var(--dim); }
.subadd:hover { color: var(--ink); border-style: solid; border-color: var(--line2); background: rgba(255,255,255,.04); }
.subadd.danger:hover { color: var(--red); border-color: var(--red); }
.subadd[data-tip]::after { content: none; }
.socialchip.named a { color: var(--ink); }
/* META is one word said four times over, so it becomes the strip and the surface keeps its own
   name: META | Ads Manager reads as the same thing "Meta Ads Manager" did, in half the width, and
   Business Suite and Business Manager stop starting with the same two words. */
.socialchip.named .entrytag { letter-spacing: .09em; }
.cellinput.addinput { width: 120px; min-width: 0; border: 0; padding: 1px 4px; margin: 0;
  background: none; }
.cellinput.addinput:focus { border: 0; background: none; }
.socialchip[data-tip]::after { content: none; }
/* THE CONTROL SITS ABOVE THE ROW SURFACE. The minus (and the link beside it) are raised in the
   stacking order so a press on them is the control's, never the draggable row's underneath — the
   interaction can never be "captured at the row level" (owner 2026-09-07). z-index needs a
   position, and the minus outranks the link so that if the two boxes ever touch at some zoom, the
   press still lands on the minus rather than the link it hangs off. */
.socialchip a { position: relative; z-index: 1; }
.socialx { display: inline-flex; align-items: center; padding: 1px; border: 0; border-radius: 4px;
  background: none; color: var(--dim); cursor: pointer; position: relative; z-index: 2; }
/* THE TARGET IS BIGGER THAN THE GLYPH. The minus is only ~13px, easy to miss by a pixel onto the
   row; this invisible pad fills the chip's dead space around it (and a touch beyond) so a press
   near the minus still lands ON it and arms — without moving the glyph or its shading, which stay
   exactly where they read (owner 2026-09-07). A click on the pad targets the button, so it arms
   and the row's guard sees the button too; kept clear of the link on the left, which the minus
   also outranks in z so a shared pixel still goes to the minus. */
.socialx::before { content: ''; position: absolute; top: -5px; bottom: -5px; left: -2px; right: -6px; }
.socialx:hover { color: var(--red); background: color-mix(in oklab, var(--red) 18%, transparent); }
/* ARMED. The glyph has already become a question mark — the fill is the second signal, never the
   only one, so the state survives not separating red from green. */
.socialx.danger, .socialx.danger:hover { background: var(--red); color: var(--bg); border-radius: 4px; }
.socialx-q { display: inline-flex; align-items: center; justify-content: center;
  width: 11px; height: 11px; font: 700 10px/1 -apple-system, sans-serif; }
.pick.socialadd { padding: 3px 5px; border-style: dashed; color: var(--dim); }
.pick.socialadd:hover { color: var(--ink); border-style: solid; }


/* The admin button in the top bar: a glyph, its name in the tooltip, and a border when you are on
   the page it opens. */
.btn.iconbtn { display: inline-flex; align-items: center; justify-content: center; padding: 5px 7px; }
.btn.iconbtn.on { color: var(--ink); border-color: var(--blue); }
.btn.iconbtn[data-tip]::after { content: none; }

/* The directory. A status is a word, never a colour on its own — one of the three is the absence of
   trouble and the other two are different kinds of stop. */
.statuschip { display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 5px; border: 1px solid var(--line2);
  color: var(--dim); }
.statuschip.active { color: var(--green); border-color: color-mix(in oklab, var(--green) 45%, transparent); }
.statuschip.paused { color: var(--amber); border-color: color-mix(in oklab, var(--amber) 45%, transparent); }
.statuschip.archived { color: var(--dim); }
.clientform h4.formhead { grid-column: 1 / -1; margin: var(--s2) 0 0; font-size: 11px;
  text-transform: uppercase; letter-spacing: .06em; color: var(--dim); font-weight: 700; }
.clientform h4.formhead:first-child { margin-top: 0; }
tr.subrow td { padding-top: 0; border-top: 0; }
/* Shown or hidden, at the right end of the row where the note used to be. Open eye in green, struck
   eye in the same grey the note was: the glyph is the state and the colour agrees with it, so
   neither is carrying it alone. */
.pickitem-s { margin-left: auto; display: inline-flex; align-items: center; color: var(--dim);
  transition: color .12s ease-out; }
.pickitem-s.on { color: var(--green); }
.pickitem:hover .pickitem-s { color: var(--ink2); }
.pickitem:hover .pickitem-s.on { color: var(--green); }
.pickitem:disabled { cursor: default; }
.pickitem:disabled:hover { background: none; color: var(--ink2); }

.pickmenu.viewmenu { min-width: 230px; }
/* Each version is a FULL TILE. Hovering it, focusing it, or its being the view you are in shades
   the WHOLE tile — grip and label together — one flat SQUARE shade, never a rounded patch inside
   it. The published/current mark is a SQUARE bar down the left edge (no rounded ends); its COLOUR
   is the fact (blue = you are here, green = published, amber = a report is written and not handed
   over), and the words on the row carry the rest. */
.pickmenu.viewmenu .pickrow { border-radius: 0; }
.pickmenu.viewmenu .pickrow:hover,
.pickmenu.viewmenu .pickrow:focus-within { background: rgba(255,255,255,.05); }
.pickmenu.viewmenu .pickrow:has(> .pickitem.on) { background: rgba(255,255,255,.09); }
.pickmenu.viewmenu .pickrow:has(> .pickitem.on):hover { background: rgba(255,255,255,.12); }
.pickmenu.viewmenu .pickrow:has(> .pickitem.on) { box-shadow: inset 3px 0 0 var(--blue); }
.pickmenu.viewmenu .pickrow:has(> .pickitem.published),
.pickmenu.viewmenu .pickrow:has(> .pickitem.on.published) { box-shadow: inset 3px 0 0 var(--green); }
.pickmenu.viewmenu .pickrow:has(> .pickitem.published.waiting),
.pickmenu.viewmenu .pickrow:has(> .pickitem.on.published.waiting) { box-shadow: inset 3px 0 0 var(--amber); }
.pickmenu.viewmenu .pickitem.published.waiting .pickitem-n { color: var(--amber); }
/* The label gives up its own shade and bar so the TILE carries one flat shade, edge to edge. */
.pickmenu.viewmenu .pickrow > .pickitem { background: none !important; box-shadow: none !important; border-radius: 0; }
.pickmenu.viewmenu .pickitem { flex-direction: column; align-items: flex-start; gap: 1px; border-radius: 0; }
/* Non-draggable version rows (a client's own side, or a client viewing their panel) carry the same
   full-tile shade and square bar on the item itself, since they sit outside a grip row. */
.pickmenu.viewmenu > .pickitem.on { background: rgba(255,255,255,.09); box-shadow: inset 3px 0 0 var(--blue); }
.pickmenu.viewmenu > .pickitem.published,
.pickmenu.viewmenu > .pickitem.on.published { box-shadow: inset 3px 0 0 var(--green); }
.pickmenu.viewmenu > .pickitem.clientview.on { background: color-mix(in oklab, var(--green) 16%, transparent); box-shadow: inset 3px 0 0 var(--green); }
.pickmenu.viewmenu .pickrow .pickgrip { align-self: flex-start; margin-top: 7px; }
.pickmenu.viewmenu .pickitem-n { margin-left: 0; white-space: normal; line-height: 1.35; }

/* The chart's target marker reads as interrogable rather than as another data point. */
.marker-dot { pointer-events: none; }
.chart .hit { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .tip, .pickmenu, .pick-caret { transition: none; }
  .tip { transform: none; }
  .pickmenu { transform: none; }
}

/* ── Data page ────────────────────────────────────────────────────────────── */
/* The Data page's fields are numField()s; only the width differs from the default. */
.dataval { width: 120px; font-size: 13px; font-weight: 700; }
tr.derived td { border-top: 1px solid var(--line); color: var(--ink); font-weight: 700;
  font-variant-numeric: tabular-nums; }
tr.derived .hint { font-weight: 400; }
/* A derived rate sitting beside the label it qualifies: muted and lighter, so the tile still reads
   headline-then-label and the rate is the third thing seen, not the second. */
.step-rate { margin-left: var(--s2); color: var(--dim); font-weight: 400; }

/* ── Tooltip affordance ───────────────────────────────────────────────────────
   A hover readout nobody knows is there is a readout nobody reads. Every text
   element carrying data-tip gets a footnote asterisk, so the context announces
   itself without a hover.

   A plain '*' rather than a symbol glyph: it is a footnote marker in every font
   on every platform, which is exactly the meaning, and it cannot fall back to an
   emoji. Driven off the attribute itself, so a tooltip added later is marked
   automatically and there is no second list to keep in sync. */
[data-tip]::after,
.step-half[data-tip] .step-k::after,
.projcell[data-tip] b::after {
  content: '*';
  font-size: .8em; font-weight: 600; line-height: 0; vertical-align: super;
  color: var(--dim); margin-left: 2px;
}
/* Three containers where the generic ::after lands in the wrong place, and the mark moves onto the
   part of the element it actually qualifies — which is always the NUMBER.

   A flex row puts it past the right-aligned trailing figure, so it marks the label instead. A
   projection cell stacks figure over label, so a mark on the container trails the LABEL, two lines
   from the number it belongs to. An adapter puts it after the arrow glyph, and an adapter is only
   ever "<figure> <unit>" — a trailing mark reads as part of the unit — so it LEADS there, the way
   a footnote marker leads a line of small print. */
.step-half[data-tip]::after, .adapter-row[data-tip]::after, .projcell[data-tip]::after { content: none; }
.adapter-row[data-tip] b::before {
  content: '*';
  font-size: .8em; font-weight: 600; line-height: 0; vertical-align: super;
  color: var(--dim); margin-right: 2px;
}
/* SVG shapes take no generated content; the chart's own marker is the affordance there. */

/* ── Goal editing: target, milestones, stretch goals ──────────────────────── */
.numfield { background: var(--panel2); color: var(--ink); border: 1px solid var(--line);
  border-radius: 6px; padding: 2px 6px; font: inherit; text-align: right;
  font-variant-numeric: tabular-nums; transition: border-color .12s ease-out, background .12s ease-out; }
.numfield:hover { border-color: var(--line2); }
.numfield:focus, .numfield:focus-visible { outline: none; border-color: var(--blue); }
.numfield::placeholder { color: var(--dim); font-weight: 400; }
/* No spinner. The stepper arrows are for nudging by one; these are typed figures, and the control
   eats the right edge of the number. */
.numfield { -moz-appearance: textfield; appearance: textfield; }
.numfield::-webkit-outer-spin-button,
.numfield::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* The target reads as the figure it always was: same size, same weight, on the same baseline as
   the "58 /" beside it. The field only draws itself under the pointer, so the number is a number
   until you go to change it. A transparent border rather than none, so nothing shifts on hover. */
.goal-figure { display: inline-flex; align-items: baseline; }
.goal-target { font: inherit; font-weight: 700; color: inherit;
  letter-spacing: -.01em; text-align: left; padding: 1px 4px; margin-left: 3px;
  background: transparent; border-color: transparent; }
.goal-target:hover { background: var(--panel2); border-color: var(--line); }
.goal-target:focus, .goal-target:focus-visible { background: var(--panel2); border-color: var(--blue); }
.pick.addgoal { padding: 3px 5px; }

/* Icon only: a flag for a waypoint, a rocket for a reach past the target. The word was three times
   the width of the number it introduced, and the tooltip carries it. */
.goalchip { display: inline-flex; align-items: center; gap: 2px; padding: 2px 2px 2px 7px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--panel2); }
.goalchip::after { content: none; }          /* the icon is the affordance; no asterisk needed */
.goalchip.milestone svg { color: var(--blue); }
.goalchip.stretch svg { color: var(--amber); }
.goalchip-v { font-size: 12px; font-weight: 700; padding: 1px 3px; text-align: center;
  background: transparent; border-color: transparent; }
.goalchip-v:hover { background: var(--panel); border-color: var(--line); }
.goalchip-v:focus, .goalchip-v:focus-visible { background: var(--panel); border-color: var(--blue); }
.goalchip-x { display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  width: 18px; height: 18px; border: 0; border-radius: 999px; background: none; color: var(--dim);
  transition: background .12s ease-out, color .12s ease-out; }
.goalchip-x:hover { background: rgba(248,113,113,.16); color: var(--red); }
.goalchip-x:focus, .goalchip-x:focus-visible { outline: none; color: var(--red); }

/* ── Target note ──────────────────────────────────────────────────────────────
   A caption on the goal, between its name and its numbers. Reads as text, not as
   a form field: no border until the pointer is on it, and the toolbar stays out
   of the way until the field has focus. */
.notewrap { position: relative; margin-bottom: var(--s3); }
.note { min-height: 22px; padding: 3px 7px; margin-left: -7px; border-radius: 6px;
  font-size: 12.5px; line-height: 1.5; color: var(--ink2); max-width: 78ch;
  border: 1px solid transparent; transition: border-color .12s ease-out, background .12s ease-out; }
.note:hover { border-color: var(--line); }
.note:focus, .note:focus-visible { outline: none; border-color: var(--blue); background: var(--panel2); }
.note:empty::before { content: 'Add a note'; color: var(--dim); }
.note b, .note strong { color: var(--ink); font-weight: 700; }

/* Directly under the field, left-aligned with it, appearing on focus. Out of flow so it never
   pushes the goal figure down when the note is clicked, which is the kind of jump that makes a
   panel feel unstable. */
.notebar { position: absolute; top: 100%; left: -7px; margin-top: 4px; z-index: var(--z-dropdown);
  display: flex; gap: 1px; padding: 3px; border-radius: 8px;
  background: var(--panel2); border: 1px solid var(--line2); box-shadow: 0 8px 22px rgba(0,0,0,.5);
  opacity: 0; visibility: hidden; transform: translateY(-3px);
  transition: opacity .12s ease-out, transform .12s ease-out, visibility .12s; }
.notewrap:focus-within .notebar { opacity: 1; visibility: visible; transform: none; }
.notebtn { display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  width: 24px; height: 24px; border: 0; border-radius: 5px; background: none; color: var(--dim);
  transition: background .12s ease-out, color .12s ease-out; }
.notebtn:hover { background: rgba(255,255,255,.07); color: var(--ink); }
.notebtn:focus, .notebtn:focus-visible { outline: none; color: var(--ink); }
.notebtn::after { content: none; }   /* the icon is the affordance; no asterisk */

@media (prefers-reduced-motion: reduce) { .notebar { transition: none; } }
/* Lists in the note. Tight leading and a small marker: these are statements to scan, not prose. */
.note ul, .note ol { margin: 2px 0; padding-left: 17px; }
.note li { margin: 1px 0; }
.note li::marker { color: var(--dim); }
/* A draggable chart marker. ew-resize, because only x moves: the value is the target and does not
   change, what the drag expresses is WHEN. */
.marker.draggable .hit { cursor: ew-resize; }
.chart.dragging { cursor: ew-resize; }
.chart.dragging .hit { cursor: ew-resize; }

/* ── Engagement calendar (Data page) ──────────────────────────────────────── */
.calpre { max-width: 92ch; margin-bottom: var(--s3); }
.calyear { display: flex; align-items: center; gap: var(--s2); margin: 2px 0 var(--s3); }
.calyear-l { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--dim); }
.calpre p { font-size: 12.5px; line-height: 1.5; color: var(--ink2); margin: 0 0 2px; }
/* One band per line, down the left. Strung across the width the eye has to travel and re-find the
   baseline for each; stacked, they read in a single pass. */
.callegend { display: flex; flex-direction: column; gap: 3px;
  margin-bottom: var(--s4); padding-bottom: var(--s4); border-bottom: 1px solid var(--line); }
.callegend-h { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); margin-top: var(--s2); }
.callegend-h:first-child { margin-top: 0; }
.callegend-i { display: flex; align-items: baseline; gap: var(--s2); font-size: 12px; }
.callegend-i .callegend-l { min-width: 84px; font-weight: 700; }
.callegend-i .calwhat { color: var(--dim); }
.calswatch { width: 9px; height: 9px; border-radius: 3px; flex: none; display: inline-block;
  transform: translateY(1px); }
/* TWO SCALES, two families, and they must not borrow each other's colours.
   Performance runs red → orange → grey → blue. Green is deliberately absent: it belongs to intent
   now, and a green "Strong" beside a green "B2C" would read as one measure with five values.
   Intent runs green, split by audience. */
.calswatch.t-poor { background: #f87171; }
/* Hollow: same severity, conditional scope. A filled swatch would claim it always applies. */
.calswatch.t-poor-scoped { background: none; box-shadow: inset 0 0 0 2px #f87171; }
.calswatch.t-soft { background: #fb923c; }
.calswatch.t-volatile { background: #6b7280; }
.calswatch.t-strong { background: #7cc0ff; }
.calswatch.t-intent-b2c { background: #4ade80; }
.calswatch.t-intent-b2b { background: #14b8a6; }
/* Shrink-wrapped to its content. table.t is width:100%, which spread four short columns across the
   whole panel and left a hand's width of nothing between each pair. Auto width with one small gap
   is the least that still separates them, and the hover and the current-week tint then stop where
   the data stops instead of running to the panel edge. */
table.cal { width: auto; table-layout: auto; }
table.cal th, table.cal td { padding-left: 0; padding-right: var(--s4); white-space: nowrap; }
table.cal th:last-child, table.cal td:last-child { padding-right: 0; }
table.cal td { padding-top: 4px; padding-bottom: 4px; }

table.cal .calw { font-variant-numeric: tabular-nums; font-weight: 700; }
table.cal tr.banded .calw { color: var(--ink); }
table.cal td i.calswatch { margin-right: 6px; }
.calchip { display: inline-flex; align-items: baseline; margin-right: var(--s3); white-space: nowrap; }
.calchip:last-child { margin-right: 0; }
.calintent { line-height: 1.7; }
/* The week we are in. A tint and a chip rather than a coloured edge: this stylesheet has no
   side-stripes, and the row already carries a band swatch that an edge would compete with. */
table.cal tr.now td { background: rgba(124,192,255,.07); }
table.cal tr.now .calw { color: var(--blue); }
.calnow { margin-left: 7px; padding: 1px 5px; border-radius: 4px; font-size: 9.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--blue);
  background: rgba(124,192,255,.14); }
/* A source link in a note. Underlined rather than coloured: the note is prose, and a blue run of
   text inside it would read as a control. */
.note a { color: var(--ink); text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--dim); }
.note a:hover { text-decoration-color: var(--blue); }

/* ── Target report ────────────────────────────────────────────────────────── */
/* The date moved onto the row that carries the reading, so the head is the name and nothing else. */
/* A frozen note reads; it is not a field. No editing chrome and no placeholder — but it keeps the
   reference pane, because a published claim is the one most likely to be checked. What it drops is
   the writing: nothing here drags to renumber, so the rows point rather than grab. */
.note.frozen { max-width: 84ch; }
.frozensrc .source { cursor: pointer; }
.reportacts { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s3); flex-wrap: wrap; }
.reportacts .redraft { margin-top: 0; }

/* ── The picker: the live estimate, and which reading is open ──────────────
   Under the chart, side by side. The estimate takes the width because it is figures to read; the
   folder is a control and needs only enough room for a date and a word. */
/* One template, so the row under the chart and the button under the reading cannot drift apart. */
.sec { --pickcols: minmax(0, 1fr) minmax(190px, 250px); }
.reportpick { display: grid; grid-template-columns: var(--pickcols);
  gap: var(--s4); align-items: stretch; margin-top: var(--s5); }
/* ONE BAND, NOT TWO BLOCKS. The estimate takes the folder's height so the row reads as a single
   thing under the chart rather than a short card beside a tall one. Stretch works both ways:
   whichever column is taller sets the height, and neither has children that grow, so nothing is
   stretched out of shape.
   THE BOX GROWS; THE CONTENT DOES NOT MOVE. The figures stay at the top left where they have
   always been — a taller box is a taller box, not a reason to recentre what is in it. */
.reportpick > .projection { margin-top: 0; }
@media (max-width: 760px) { .sec { --pickcols: 1fr; } }

/* GENERATE REPORT: bottom right, in the folder's own column and at exactly its width, with the
   section's gap between them. It makes the next reading; the stack above chooses among the ones
   already made — sharing an edge with them would say it is one of them. */
.sec > .reportacts.reporttop { display: grid; grid-template-columns: var(--pickcols);
  gap: var(--s4); margin-top: var(--s4); }
.sec > .reportacts.reporttop .reportgen { grid-column: -2; width: 100%; justify-content: center; }

.reportfolder { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }

/* A TAB IS A READING: its date leading, its state trailing, and the one being read lit in the
   panel's blue — the same "you are here" the view switcher uses, about a different kind of thing. */
/* No `width: 100%`: these are stretch children of a column, so the stack's own inset shrinks
   them. A fixed width would push the margins outside the box instead and every tab would come
   out the same size. */
.rtab { display: flex; align-items: center; gap: var(--s2); padding: 4px 10px; line-height: 18px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel2); color: var(--ink2);
  font: inherit; font-size: 12px; text-align: left; cursor: pointer;
  transition: background-color .14s ease-out, border-color .14s ease-out, color .14s ease-out; }
.rtab:hover { background: #1c212a; color: var(--ink); border-color: var(--line2); }
.rtab:focus, .rtab:focus-visible { outline: none; border-color: var(--blue); }
.rtab.on { background: color-mix(in oklab, var(--blue) 14%, transparent);
  border-color: var(--blue); color: var(--ink); }
.rtab-d { font-weight: 700; font-variant-numeric: tabular-nums; }
/* The state's mark, leading. Every row in the group carries one, the archive included, so the
   labels all start at the same x rather than stepping in and out by whether a row has a glyph. */
.rtab-i { display: inline-flex; align-items: center; flex: none; }
/* A SLOT WITH NOTHING IN IT. Centred rather than set at the label's x, where a dash would read as
   a date that failed to load. Faint, inert, and holding the tile's height so the row under the
   chart is one size for the life of the account. */
.rtab.rnone { justify-content: center; color: var(--edge); cursor: default; }

/* LIVE IS NOT A READING, so it does not stand in the group and it does not look like one. A dark
   green ground, mixed INTO the panel rather than laid over it as a transparency, so it reads as a
   deep base rather than a bright chip: this is the only control here that means "now".
   The green is never the only thing saying so. It is labelled Live, it sits apart from the group,
   and being chosen is a step in LIGHTNESS as much as in hue — which is what the owner's eyes
   actually read, green landing near yellow for them. */
.rlive { justify-content: center;
  background: color-mix(in oklab, var(--green) 9%, var(--panel2));
  border-color: color-mix(in oklab, var(--green) 26%, var(--line)); }
.rlive:hover { background: color-mix(in oklab, var(--green) 15%, var(--panel2));
  border-color: color-mix(in oklab, var(--green) 44%, var(--line)); color: var(--ink); }
.rlive.on { background: color-mix(in oklab, var(--green) 24%, var(--panel2));
  border-color: var(--green); color: var(--ink); }

/* THE STACK IS ONE TILE. Four controls that pick one thing between them are one control, so they
   share a border and are divided by rules. As separate cards with gaps the panel showed through
   the middle of a single component, and four things appeared where there is one.
   Order carries the depth: oldest under the archive, newest at the bottom where a hand lands. */
.rfolder { display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel2); overflow: hidden; }
/* Inside the group a tab owns no border and no ground: the tile owns those. Its only edge is the
   rule up to the one above it.
   THE TWO ROWS AT THE ENDS KEEP A RADIUS, and it is the tile's LESS ITS BORDER — 8 minus 1. The
   focus ring is an inset shadow, so it follows the row's own corner, not the tile's: with the
   rows squared off the ring ran into a rounded corner and stopped dead. Anything a row draws to
   its own edge has the same problem, which is why this sits on the row and not on the ring. */
.rfolder > .rslot { border: 0; border-radius: 0; background: none; }
.rfolder > .rslot:first-child { border-radius: 7px 7px 0 0; }
.rfolder > .rslot:last-child { border-radius: 0 0 7px 7px; }
.rfolder > .rslot:only-child { border-radius: 7px; }
.rfolder > .rslot + .rslot { border-top: 1px solid var(--line); }
/* A READING'S ROW IS ONE CELL, and everything in it belongs to that cell.
   The tab is not a direct child of the stack, so the reset that strips a tab's own border, ground
   and radius never reached it: every row was carrying a bordered, panel-coloured tile with a bin
   parked beside it, which is the two-column look, and the tab's extra border made it 28px against
   the bin's 22. The ROW owns the chrome now; the tab owns the label and the bin owns the glyph. */
.rrow { display: flex; align-items: stretch; position: relative; }
/* `color: inherit` is load-bearing. `.rtab` sets its own --ink2, and the row's state rules — the
   dulling on everything but the latest, the lift on hover, the lift when chosen — are set on the
   ROW. While the tab declared its own colour it ignored all three, so the two older readings and
   the archive read at full strength beside the newest one and the stack had no near end. */
/* `.rfolder` leads so this OUTWEIGHS `.rtab:hover` and `.rtab.on` rather than merely outrunning
   them. Both declare a background and a colour on the tab itself, and at equal weight the winner
   was whichever sat lower in the file — a tie this rule would lose the day someone reorders the
   stylesheet, putting a tab-shaped highlight back inside a row-shaped one. */
.rfolder .rrow > .rtab { flex: 1; min-width: 0; border: 0; border-radius: inherit;
  background: none; color: inherit; }
.rrow > .reportdel { width: 30px; flex: none; align-self: stretch; margin: 0; padding: 0;
  border: 0; border-radius: 0; background: none; }
/* The row lights on hover; the bin lights on its own hover, inside it. */
.rrow > .reportdel:hover:not(:disabled) { color: var(--red); background: rgba(255,255,255,.07); }
.rrow > .reportdel:focus, .rrow > .reportdel:focus-visible { outline: none; border: 0; }
.rrow:hover > .reportdel, .rrow > .reportdel:focus, .rrow > .reportdel.armed { opacity: 1; }
/* ARMED NEVER MOVES THE ROW. As a flex child the pill grew from 30px to 78 and took that width
   off the label beside it, so the tint and the date shuffled left every time the question was
   asked — the row visibly rearranging itself is the worst possible moment to ask someone to
   confirm. Out of flow and pinned to the row's right edge: same width, same height, and the only
   thing that changed is that it is now asking. */
.rrow > .reportdel.armed { position: absolute; right: 3px; top: 3px; bottom: 3px; width: auto;
  padding: 0 9px; border: 1px solid var(--red); border-radius: 6px;
  background: color-mix(in oklab, var(--red) 22%, var(--panel2)); }
.rrow > .reportdel.armed:hover { background: color-mix(in oklab, var(--red) 32%, var(--panel2)); }
.rfolder > .rslot:not(:last-child):not(.on) { color: var(--dim); }
.rfolder > .rslot:hover { background: #1c212a; color: var(--ink); }
/* Chosen is a ground, not a border: the border belongs to the tile now. */
.rfolder > .rslot.on { background: color-mix(in oklab, var(--blue) 14%, transparent); color: var(--ink); }
/* And focus is a ring inside the row, for the same reason. Never an outline — house rule. */
.rfolder > .rslot:focus-within {
  outline: none; box-shadow: inset 0 0 0 1px var(--blue); }
/* The archive trigger is a menu, so it arrives wearing `.pick`. It keeps the caret, which is the
   one thing on the stack that says this opens rather than selects. */
.rarch { justify-content: flex-start; font-size: 12px; }
.rarch .pick-caret { margin-left: auto; }

/* `.pick` sets its triggers in tracked caps. Here the neighbours are dates in sentence case, and
   one shouted word in a stack of four reads as a different kind of control. */
.rarch .pick-label { font-weight: 700; text-transform: none; letter-spacing: normal; }
.archmenu { min-width: 240px; }

/* ── The reading ───────────────────────────────────────────────────────────
   Built only when one is open. On Live it does not exist, so the page ends at the picker rather
   than at an empty frame explaining that nothing is selected. */
.reporttile { margin-top: var(--s5); padding: var(--s4) 16px; border-radius: 9px;
  background: var(--panel2); border: 1px solid var(--line); }
/* THE DOCUMENT CARRIES ITS OWN DATE, at the size of a thing that was published rather than a
   label on a row. */
.reporthead { display: flex; align-items: baseline; gap: var(--s3); margin-bottom: var(--s4);
  padding-bottom: var(--s3); border-bottom: 1px solid var(--line); }
.reporthead-d { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* The period, at the right end of the head. It sits where a status trails a row everywhere else
   on this panel, quiet, and only louder than the date when there is a bin beside it. */
.reporthead-s { margin-left: auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; color: var(--dim); }
.reporthead-s svg { color: var(--dim); }

/* The frozen tiles, aligned to the note's own column so the reading reads as one page rather than
   as two blocks that happen to be stacked. */
.reportproj { display: grid; grid-template-columns: var(--notecols); gap: 0 var(--s4); }
.reportproj > * { grid-column: 1; }
.reportproj .projgroup:first-child { margin-top: 0; }
.reporttile .notecol, .reporttile > .note { margin-top: var(--s5); }
/* What `.summary` used to give the note inside it. The note moved; the rules it needs came with
   it, and the placeholder is not decoration — without it an empty draft is a blank rectangle. */
.reporttile .notewrap, .reportmake .notewrap { margin-bottom: 0; }
.reporttile .note b, .reportmake .note b { color: var(--ink); }
.reporttile .note:empty::before, .reportmake .note:empty::before {
  content: attr(data-placeholder); color: var(--dim); }
.reporttile .note .src, .reportmake .note .src {
  display: block; margin-top: var(--s3); font-size: 11.5px; color: var(--dim); }
.reportproj .projrow { margin: 0 0 var(--s4); }

/* The state of a reading, written out in the document. ONE COLOUR FOR BOTH: draft used to run
   amber, which read as a warning about a reading that is simply not finished yet, and the amber
   was the loudest thing in a head whose job is to say what day this is. Which state it is in is
   the word. */
.reportrow-n { font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--dim); }
/* Permanently, so it asks — in place, the same way every destructive control here does. It keeps
   its own column beside the row rather than inside it, so arming it cannot move the date. */
.reportdel { display: inline-flex; align-items: center; justify-content: center; width: 32px;
  flex: none; background: none; border: 1px solid transparent; border-radius: 7px; color: var(--dim);
  opacity: 0; cursor: pointer; transition: color .12s ease-out, background-color .12s ease-out, opacity .12s ease-out; }
.reportdel:focus, .reportdel.armed { opacity: 1; }
.reportdel:hover { color: var(--red); border-color: var(--red); }
.reportdel:focus, .reportdel:focus-visible { outline: none; border-color: var(--blue); }
.reportdel.armed { width: auto; padding: 0 10px; color: var(--red); border-color: var(--red);
  background: color-mix(in oklab, var(--red) 16%, transparent); }
.reportdel.armed:hover { background: color-mix(in oklab, var(--red) 26%, transparent); }
.reportdel[data-tip]::after { content: none; }
.reportdel-w { font: 700 10px -apple-system, sans-serif; letter-spacing: .06em; text-transform: uppercase; }
/* Writing a report is a deliberate act, so its surface is behind a button rather than standing
   open — and it sits UNDER the record, where the next reading will appear.
   A REAL BUTTON, at the size every other button on the panel is. As a ghost at the tiny size it sat
   flush in the surface and read as a label with an arrow, which is why it was hard to find at all:
   the one control here that MAKES something looked like the least of them. */
.reportacts.reporttop { margin-top: var(--s4); }
/* THE SAME BLUE THE SECTION NUMBERS WEAR. Blue is this panel's "a control, or a place in the
   structure" colour — the numbered chip at the head of every section is the clearest instance of
   it — so the one button that makes something takes the same tint rather than inventing a hue. It
   stops the button reading as another line of the record it sits under. */
.reportgen { display: inline-flex; align-items: center; gap: 7px; color: var(--ink);
  background: rgba(124,192,255,.13); border-color: rgba(124,192,255,.22);
  transition: background-color .14s ease-out, border-color .14s ease-out; }
.reportgen:hover:not(:disabled) { background: rgba(124,192,255,.20); border-color: rgba(124,192,255,.38); }
.reportgen svg { color: var(--blue); }
/* Open: it is holding the writing surface below it, so it reads as pressed rather than available.
   Deeper than hover, and the border closes to full strength, which is the difference a glance
   catches without reading the label. */
.reportgen.on { background: rgba(124,192,255,.26); border-color: var(--blue); }
.reportgen.on:hover { background: rgba(124,192,255,.32); }
.reportgen:disabled { opacity: .45; cursor: default; }
/* WHERE IT LANDS NOW, on the faded comparison line. It breathes rather than blinks: a slow fade
   between two opacities reads as "this one is the live one" without ever being the thing your eye
   is pulled to while you are reading the report it sits beside. Two and a half seconds, no
   movement, and it stops entirely for anyone who has asked motion to stop. */
/* 5px, a shade over forty percent down from the 8.5 it was, with the tracking opened to .12em so
   seven capitals at that size still read as letters rather than as a texture. The PILL keeps its
   old size — the padding in `charts.js` grew to make up exactly what the glyphs gave back — so the
   chip is the same object on the chart and only the word inside it got quieter. */
.marker-now { font: 700 5px ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .12em;
  text-transform: uppercase; animation: nowbreath 2.6s cubic-bezier(.4,0,.6,1) infinite; }
/* The floor was .45, which was legible when the label sat on a plain surface and is not what it
   is for: the word marks WHERE THE ACCOUNT LANDS NOW, and it should be readable at every point in
   the breath, not only at the top of it. */
@keyframes nowbreath { 0%, 100% { opacity: .8; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .marker-now { animation: none; opacity: .9; }
}
.reportgen[data-tip]::after { content: none; }
.reportmake { margin-top: var(--s3); }
/* The rule above the record, not above the button: the button is the last thing in the section. */
/* That rule divides the list from what precedes it in the panel. With the estimate moved out to
   its own tile the list usually leads, and a divider above the first thing in a box is a line
   under nothing plus 28px of empty panel. A client with no reports still gets the note above it,
   and there the rule is doing its job. */
.reportmake .notewrap { margin-bottom: 0; }


/* A citation marker in a note. Sized and coloured like the footnote it is: small enough to read
   past, blue enough to be seen as a control. */
.cref { font-size: .72em; font-weight: 700; color: var(--blue); cursor: pointer;
  padding: 0 2px; border-radius: 3px; user-select: none;
  transition: background-color .12s ease; }
.cref:hover { background: rgba(124,192,255,.16); }
/* Which marker the pane is currently answering. Same tint as the row it lit in the list, so the
   two ends of one citation read as one thing. */
.cref.on { background: rgba(124,192,255,.16); }
/* A marker pointing at a reference that is not in the list. It shows rather than hides, because a
   silent one is a citation the reader is told exists and cannot follow. */
.cref-lost { color: var(--amber); }
/* The reference it lands on says so for a moment. A flash, not a permanent highlight: the row is
   being pointed at, not selected, and a mark that stayed would have to be dismissed. */
.source.flash { animation: srcflash 1.6s cubic-bezier(.16,1,.3,1); border-radius: 5px; }
@keyframes srcflash {
  0%   { background: rgba(124,192,255,.28); box-shadow: 0 0 0 4px rgba(124,192,255,.28); }
  100% { background: transparent; box-shadow: 0 0 0 4px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  /* No fade, but still an answer to the click: it holds, then goes. */
  .source.flash { animation: none; background: rgba(124,192,255,.2); }
}

/* Sources sit at the foot, under a rule: they are apparatus for the copy above, not part of it. */
.sources { margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--line); }
.sources-h { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); margin-bottom: var(--s2); }
.source { display: flex; align-items: baseline; gap: var(--s2); font-size: 12px; margin-bottom: 2px; }
.source-n { min-width: 12px; color: var(--dim); font-variant-numeric: tabular-nums; }
.srcfield { background: var(--panel); color: var(--ink); border: 1px solid var(--line);
  border-radius: 6px; padding: 4px 8px; font: inherit; font-size: 12px; width: 26ch; }
.srcfield:hover { border-color: var(--line2); }
.srcfield:focus, .srcfield:focus-visible { outline: none; border-color: var(--blue); }
.srcfield::placeholder { color: var(--dim); }
/* Two readings of the same metrics. The second is the owner's own date, so it carries the amber
   the intent marker uses on the chart, and nothing else changes: same metrics, same order. */
/* Space separates the two rows, not a rule. They are two readings of one thing, and a line between
   them says they are separate things. */
.projgroup + .projgroup { margin-top: var(--s5); }
.projgroup-h { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--target); margin-bottom: var(--s2); }
.projgroup.retarget .projgroup-h { color: var(--retarget); }


/* ── Collapsible sections ──────────────────────────────────────────────────
   The Data page is a stack of reference tables and it will keep growing. A 53-row calendar
   between the reader and the table under it is the thing being fixed; the toggle sits with the
   title so it reads as part of the heading, not as one more control in the row. */
.sec-toggle { background: none; border: 0; padding: 2px; margin-left: calc(var(--s1) * -1);
  color: var(--dim); cursor: pointer; display: inline-flex; border-radius: 5px;
  transition: color .15s ease, background-color .15s ease; }
.sec-toggle:hover { color: var(--ink); background: var(--panel2); }
/* A closed section keeps its heading but loses the gap under it, or it reads as an empty section
   rather than a shut one. */
.sec:has(> .sec-body[hidden]) .sec-head { margin-bottom: 0; }

/* ── Sources: the citation row ─────────────────────────────────────────────
   "1  Cucu (2026)  link". Two destinations, so two controls: the name opens the record, the word
   after it leaves for the page. One anchor doing both would make those the same click. */
.source-l { background: none; border: 0; padding: 0; font: inherit; color: var(--ink2);
  cursor: pointer; text-align: left; border-bottom: 1px dotted #4a535f; }
.source-l:hover { color: var(--ink); border-bottom-color: var(--dim); }
.source-u { color: var(--blue); text-decoration: none; font-size: 10.5px; letter-spacing: .05em;
  border-bottom: 1px solid rgba(124,192,255,.3); }
.source-u:hover { border-bottom-color: var(--blue); }
/* Citing is a SEARCH, not a menu. The library is built to grow, and a dropdown that works at
   four entries is unusable at forty; what the owner remembers about a source is a word in it. */
.srcpick { position: relative; margin-top: var(--s3); max-width: 34ch; }
.srcsearch { width: 100%; background: var(--panel); color: var(--ink); border: 1px solid var(--line);
  border-radius: 7px; padding: 5px 9px; font: inherit; font-size: 12px; }
.srcsearch::placeholder { color: var(--dim); }
.srcsearch:hover { border-color: var(--line2); }
.srcsearch:focus, .srcsearch:focus-visible { outline: none; border-color: var(--blue); }
.srcsearch::-webkit-search-cancel-button { filter: grayscale(1) opacity(.5); }
/* Absolute, not fixed: this sits at the foot of a tile with room under it and nothing clipping it,
   and an in-flow overlay stays attached to the field when the page scrolls under it. */
.srcres { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: var(--z-dropdown);
  max-height: 240px; overflow-y: auto; padding: 4px; border: 1px solid #333b45; border-radius: 9px;
  background: var(--panel2); box-shadow: 0 12px 30px rgba(0,0,0,.5); }
.srcresi { display: flex; align-items: baseline; gap: var(--s2); width: 100%; text-align: left;
  background: none; border: 0; border-radius: 6px; padding: 5px 8px; font: inherit; font-size: 12px;
  color: var(--ink2); cursor: pointer; }
.srcresi.on, .srcresi:hover { background: #232a34; color: var(--ink); }
.srcresi-l { font-weight: 600; white-space: nowrap; }
.srcresi-n { color: var(--dim); font-size: 11px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.srcresi.none { color: var(--dim); cursor: default; }
.srcresi.none:hover { background: none; }

/* ── Sources: the library table ────────────────────────────────────────────
   Column widths are declared rather than left to the content: the reference is a paragraph, the
   figures are short, and auto layout would give the numbers half the table. */
/* Widths key off the header cell's own class (fixed layout takes the column width from the first
   row), so the owner's extra Access column and the client's shorter row both stay aligned. */
.srct { table-layout: fixed; }
.srct .sc-type { width: 88px; }
.srct .sc-ref  { width: 26%; }
.srct .sc-for  { width: 24%; }
.srct .sc-ctx  { width: 16%; }
.srct .sc-shot { width: 64px; }
.srct .sc-acc  { width: 140px; }
.srct .sc-rm   { width: 28px; }
/* A header longer than its fixed column clips instead of bleeding into the next one. */
.srct th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Where the record came from, which is what decides how far a claim resting on it carries.
   Colour is the shorthand: blue for published outside, neutral for our own, amber for evidence
   that is true of one account only and must never travel. */
.srckind { display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .03em;
  padding: 2px 6px; border-radius: 5px; white-space: nowrap; }
.srckind.k-external { background: rgba(124,192,255,.14); color: var(--blue); }
.srckind.k-internal { background: #232a34; color: var(--ink2); }
.srckind.k-client { background: rgba(240,198,116,.14); color: var(--amber); }
.srct td { vertical-align: top; }
.srcrefc { line-height: 1.45; }
/* The whole reference is the control. Left-aligned and unstyled as a button, because it is a
   paragraph of text that happens to be clickable, not a button that happens to hold text. */
.srcref { display: block; background: none; border: 0; padding: 0; margin-bottom: 3px;
  font: inherit; font-size: 12px; color: var(--ink2); text-align: left; cursor: pointer; }
.srcref:hover { color: var(--ink); }
.srcref .apa-u { color: var(--dim); overflow-wrap: anywhere; }
.apa-u { font-size: .95em; }
a.apa-u { color: var(--blue); text-decoration: none; overflow-wrap: anywhere; }
.srcfor p { font-size: 11.5px; color: var(--ink2); line-height: 1.5; }
/* Value, then unit, then what it measures. A benchmark read without its unit is a rumour. */
.srcfig { margin-bottom: var(--s2); }
.srcfig b { font-size: 13px; font-variant-numeric: tabular-nums; margin-right: var(--s1); }
.srcfigu { font-size: 11px; color: var(--ink2); }
.srcfigl { display: block; font-size: 10.5px; color: var(--dim); }
.srcthumb { position: relative; display: block; width: 56px; height: 40px; padding: 0; overflow: hidden;
  border: 1px solid var(--line); border-radius: 6px; background: var(--panel2); cursor: pointer; }
.srcthumb:hover { border-color: var(--line2); }
.srcthumb img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.srcthumb.none { display: grid; place-items: center; color: var(--dim); border-style: dashed; }
/* The mark survives the reduction, and it is the part that says a figure was actually located. */
.srcmark { position: absolute; border: 1px solid var(--amber); border-radius: 2px;
  background: rgba(240,198,116,.18); }

/* ── Sources: the add form ─────────────────────────────────────────────────
   Two columns, labelled. Six placeholder-only fields would ask the owner to remember which is
   which the moment the first character is typed. */
.srcadd { margin-top: var(--s4); }
.srcform { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: var(--s2) var(--s3); margin-bottom: var(--s3); }
.srcf { display: flex; flex-direction: column; gap: 3px; }
.srcf.wide { grid-column: 1 / -1; }
.srcf-l { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); }
.srcf .srcfield { width: 100%; }

/* ── The gallery ───────────────────────────────────────────────────────────
   A native <dialog>: the top layer, so no ancestor's overflow can clip it, plus modal focus,
   Escape and a backdrop without writing any of them. */
/* margin:auto restores the centring the page reset takes away — the * { margin: 0 } at the top
   of this file also hits <dialog>, whose UA margin:auto is what centres it in the top layer. */
.gal { margin: auto; width: min(1100px, calc(100vw - 48px)); max-height: calc(100vh - 48px); padding: 0;
  border: 1px solid var(--line); border-radius: 14px; background: var(--panel); color: var(--ink);
  font: inherit; overflow: auto; }
.gal::backdrop { background: rgba(0,0,0,.72); }
.gal.dropping { border-color: var(--blue); }
.gal-top { display: flex; align-items: flex-start; gap: var(--s3); padding: var(--s4) var(--s4) var(--s3);
  border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--panel); z-index: 1; }
.gal-t { font-size: 15px; font-weight: 700; }
.gal-a { font-size: 11.5px; color: var(--dim); margin-top: 2px; }
.gal-x { margin-left: auto; background: none; border: 0; color: var(--dim); cursor: pointer;
  padding: 3px; border-radius: 6px; display: inline-flex; }
.gal-x:hover { color: var(--ink); background: var(--panel2); }
/* Image left, record right. It collapses to one column early: the screenshot is the reason the
   gallery exists, and a 300px column of it is not worth reading. */
.gal-body { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(260px, 1fr);
  gap: var(--s4); padding: var(--s4); }
@media (max-width: 860px) { .gal-body { grid-template-columns: 1fr; } }
.gal-frame { position: relative; line-height: 0; border-radius: 9px; overflow: hidden;
  border: 1px solid var(--line); background: var(--bg); }
.gal-frame img { width: 100%; height: auto; display: block; user-select: none; }
.gal-frame.marking { cursor: crosshair; }
/* The mark is a hole in a scrim, not a box on top of one: the ring says where, and the dimmed rest
   of the page says the rest is not what is being cited. A spread box-shadow does both with one
   element, and clips itself to the frame. */
/* THE MARK IS THE FOCUS ITSELF: a hole in a scrim, and nothing drawn on top. The dimmed rest of
   the page is what says "this is the part being cited", and an outline over it only competes with
   the figure it is pointing at. A spread box-shadow cuts the hole with one element. */
.gal-mark { position: absolute; border-radius: 10px;
  box-shadow: 0 0 0 9999px rgba(9,11,14,.72); pointer-events: none; }
/* While DRAGGING it is a selection, not an annotation — a dashed edge, because the shape is
   still changing under the pointer and a finished-looking mark would lie about that. */
.gal-mark.drawing { box-shadow: none; background: rgba(240,198,116,.14);
  border: 1px dashed var(--amber); }
.gal-bar { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s2); flex-wrap: wrap; }
.gal-hint { font-size: 11px; color: var(--dim); }
.gal-drop { display: grid; justify-items: center; gap: 5px; width: 100%; padding: 46px 20px;
  border: 1px dashed var(--line); border-radius: 11px; background: transparent; color: var(--dim);
  font: inherit; font-size: 12px; cursor: pointer; }
.gal-drop:hover { border-color: var(--line2); color: var(--ink2); }
.gal-drop-h { font-size: 13px; font-weight: 600; color: var(--ink2); }
.gal-side { min-width: 0; }
.gal-h { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); margin: var(--s3) 0 var(--s2); }
.gal-side > .gal-h:first-child { margin-top: 0; }
.gal-ctx { width: 100%; background: var(--panel2); color: var(--ink); border: 1px solid var(--line);
  border-radius: 7px; padding: var(--s2); font: inherit; font-size: 12px; line-height: 1.6;
  resize: vertical; }
.gal-ctx:focus, .gal-ctx:focus-visible { outline: none; border-color: var(--blue); }
.gal-ctx::placeholder { color: var(--dim); }
.gal-fig { display: flex; align-items: baseline; gap: var(--s2); margin-bottom: var(--s2); }
.gal-fig b { font-size: 16px; font-variant-numeric: tabular-nums; min-width: 3.5ch; }
.gal-figt { min-width: 0; }
.gal-figl { display: block; font-size: 12px; color: var(--ink2); }
.gal-figu { display: block; font-size: 10.5px; color: var(--dim); }
.gal-ref { font-size: 11.5px; color: var(--ink2); line-height: 1.6; }
.gal-side > .btn { margin-top: var(--s3); }


/* ── A note, its references, and the page they came from ────────────────────
   Copy and reference list on the left, the screenshot on the right. Tiled rather than stacked
   because they are one act of reading: the claim, what backs it, and where that came from. */
.notecol { display: grid; grid-template-columns: var(--notecols); gap: var(--s4); align-items: start; }
/* One column early. The screenshot is the reason the right side exists, and a 260px-wide
   screenshot is not worth the width it takes from the copy. */
@media (max-width: 900px) { .notecol { grid-template-columns: 1fr; } }
.notemain { min-width: 0; }
.noteref { min-width: 0; position: sticky; top: 64px; }

/* ── The reference viewer ─────────────────────────────────────────────────── */
.refpane { border: 1px solid var(--line); border-radius: 10px; background: var(--panel2);
  overflow: hidden; }
.refempty { padding: 28px 16px; text-align: center; font-size: 12px; color: var(--dim); }
/* A fixed window onto a much larger page. Fixed, so the column does not resize every time a
   different reference is picked — the page beside it would reflow on every click. */
.refframe { position: relative; height: 260px; overflow: hidden; background: var(--bg);
  touch-action: none; cursor: default; }
.refframe.grabbable { cursor: grab; }
.refframe.grabbable:active { cursor: grabbing; }
.refstage { position: absolute; top: 0; left: 0; width: 100%; transform-origin: 0 0;
  will-change: transform; }
.refstage img { width: 100%; height: auto; display: block; user-select: none; -webkit-user-drag: none; }
.refbar { display: flex; align-items: center; gap: var(--s1); padding: var(--s2);
  border-bottom: 1px solid var(--line); }
.refzoom { background: none; border: 1px solid var(--line); border-radius: 6px; color: var(--ink2);
  font: inherit; font-size: 11px; line-height: 1; padding: 4px 8px; cursor: pointer;
  transition: border-color .15s ease, color .15s ease; }
.refzoom:hover { border-color: var(--line2); color: var(--ink); }
.refmeta { padding: var(--s3); }
/* Value, then unit, then what it measures. The figure leads because the figure is what was cited. */
.reffig { display: flex; align-items: baseline; gap: var(--s2); }
.reffig b { font-size: 20px; font-variant-numeric: tabular-nums; }
.reffigu { font-size: 11px; color: var(--ink2); }
.reffigl { font-size: 12px; color: var(--ink2); margin-top: 1px; }
.reffign { font-size: 11px; color: var(--dim); margin-top: 2px; }
.refcite { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line);
  font-size: 11px; color: var(--dim); line-height: 1.55; }
.refcite .apa-u { overflow-wrap: anywhere; }

/* ── The reference list ───────────────────────────────────────────────────── */
/* Drag to renumber. The order is the owner's and is deliberately NOT the order the markers appear
   in the copy: a reference cited first is not always the one to lead with. */
.source { cursor: grab; padding: 2px 4px; border-radius: 5px; border: 1px solid transparent; }
.source:hover { background: #1c212a; }
.source.on { background: #1c212a; border-color: #333b45; }
.source.dragging { opacity: .4; }
.source.over { border-color: var(--blue); }
.source-f { font-size: 11px; color: var(--dim); min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.citepop { position: fixed; z-index: var(--z-dropdown); width: 320px; padding: 5px;
  border: 1px solid #333b45; border-radius: 10px; background: var(--panel2);
  box-shadow: 0 16px 40px rgba(0,0,0,.55); }
.citepop .srcres { position: static; margin-top: 4px; border: 0; background: none; box-shadow: none;
  padding: 0; }

/* ── The library editor: one mark per figure ──────────────────────────────── */
/* While editing, every mark shows at once and none of them dims the page: a dozen overlapping
   scrims would black the screenshot out, and the question here is coverage, not focus. */
.gal-mark.flat { box-shadow: none; border: 1.5px solid var(--amber); background: rgba(240,198,116,.08); }
.gal-markn { position: absolute; top: -9px; left: -9px; width: 17px; height: 17px; border-radius: 50%;
  background: var(--amber); color: #17130a; font-size: 10px; font-weight: 700; font-style: normal;
  display: grid; place-items: center; }
.gal-fign { width: 17px; height: 17px; flex: none; border-radius: 50%; background: #2a323d;
  color: var(--ink2); font-size: 10px; font-weight: 700; font-style: normal;
  display: grid; place-items: center; align-self: center; }
.gal-fig { padding: var(--s1) 0; }
.gal-fig .btn { margin-left: auto; align-self: center; }
.gal-fig.marking { outline: 1px solid var(--amber); outline-offset: 4px; border-radius: 6px; }

/* ── The prompt library ────────────────────────────────────────────────────
   Standing instructions, kept beside the sources they cite. */
.prt { table-layout: fixed; }
.prt th:nth-child(1), .prt td:nth-child(1) { width: 88px; }
.prt th:nth-child(2), .prt td:nth-child(2) { width: 22%; }
.prt th:nth-child(3), .prt td:nth-child(3) { width: 38%; }
.prt th:nth-child(4), .prt td:nth-child(4) { width: 26%; }
.prt th:nth-child(5), .prt td:nth-child(5) { width: 28px; }
.prt td { vertical-align: top; }
.prtags { margin-top: var(--s1); display: flex; flex-wrap: wrap; gap: 3px; }
.prtag { font-size: 10px; color: var(--dim); background: #202631; border-radius: 4px; padding: 1px 5px; }
.prwhere .mono { font-size: 10.5px; color: var(--ink2); overflow-wrap: anywhere; }
/* The body is a DOCUMENT, and a document in a 30% column is a column of single words, so it takes
   the full width of the table on its own row. */
tr.prbody td { padding-top: 0; }
tr.prbody pre { margin: 0 0 var(--s3); padding: var(--s3); border: 1px solid var(--line);
  border-radius: 8px; background: var(--panel2); font-family: var(--mono); font-size: 11.5px;
  line-height: 1.6; color: var(--ink2); white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 460px; overflow-y: auto; }

/* The title is the door to the prompt text, so it has to look like one. */
.prtitle { display: flex; align-items: baseline; gap: 5px; }
.prtitle svg { align-self: center; flex: none; color: var(--dim); }
.prread { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--dim); }
.prtitle:hover .prread, .prtitle:hover svg { color: var(--blue); }

/* The legend has a tile of its own, so its head carries no number and its body needs no gap under
   a heading that is only two words wide. */
.sec:has(> .sec-head > .sec-title:only-child) .sec-head { margin-bottom: var(--s2); }

/* Every colour the page uses, named. The owner cannot separate green from yellow, so a hue that is
   not in this list is a hue carrying meaning nobody can read. */
.dot.tg { background: var(--target); }
.dot.rt { background: var(--retarget); }
.dot.ad { background: var(--adapter); }
.stacklegend .legitem { cursor: help; }

/* ── the post viewer ──────────────────────────────────────────────────────────
   Instagram's own web layout: creative left at its own aspect ratio, what we know about it right.
   The overlay keeps its click-out-to-close, but inside it the cursor is a normal one — this is a
   panel to read, not an image to dismiss. */
.lightbox.pvmode { padding: 32px; cursor: default; }
.pv { display: grid; grid-template-columns: auto 360px; grid-template-rows: minmax(0, 1fr);
  max-width: calc(100vw - 64px); height: min(880px, calc(100vh - 64px));
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
/* The pane IS the creative's shape. Sized off the image's own ratio rather than a fixed column,
   so a 4:5 post leaves no black bars either side of it and the panel is never wider than the
   picture in it. */
.pv-media { position: relative; background: #000;
  height: 100%; aspect-ratio: var(--pv-ar, 4 / 5); max-width: calc(100vw - 64px - 360px);
  min-width: 0; overflow: hidden; }
.pv-stage { position: absolute; inset: 0; }
/* Absolutely positioned and margin-auto centred, NOT a centred grid item: a percentage max-height
   on a grid item resolves against a track that is sized by the item, which is circular, so the
   browser drops the constraint and a 4:5 creative runs past the bottom of the panel. Against an
   inset-0 containing block the percentage is definite. */
.lightbox.pvmode .pv-stage img, .lightbox.pvmode .pv-stage video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border-radius: 0; object-fit: contain; display: block; }

.pv-nav { position: absolute; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: rgba(255,255,255,.9); color: #111;
  cursor: pointer; opacity: 0; transition: opacity .15s ease-out; }
.pv-media:hover .pv-nav, .pv-nav:focus-visible { opacity: 1; }
.pv-nav:focus, .pv-nav:focus-visible { outline: none; }
.pv-nav.prev { left: 12px; } .pv-nav.next { right: 12px; }
@media (prefers-reduced-motion: reduce) { .pv-nav { transition: none; } }

.pv-dots { position: absolute; bottom: 12px; left: 0; right: 0;
  display: flex; justify-content: center; gap: 5px; pointer-events: none; }
.pv-dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.35); }
.pv-dots i.on { background: #fff; }

.pv-rail { display: flex; flex-direction: column; min-height: 0;
  border-left: 1px solid var(--line); background: var(--panel); }
.pv-icons { display: flex; gap: var(--s5); padding: var(--s4) var(--s4) var(--s3);
  border-bottom: 1px solid var(--line); }
.pv-ic { display: flex; align-items: center; gap: 6px; color: var(--dim); }
.pv-ic b { color: var(--ink); font-size: 14px; font-variant-numeric: tabular-nums; }

.pv-stats { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--s3) var(--s2); padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line); }
.pv-stat b { display: block; font-size: 15px; color: var(--ink); font-variant-numeric: tabular-nums; }
.pv-stat span { font-size: 11px; color: var(--dim); }

.pv-cap { flex: 1; min-height: 0; overflow-y: auto; padding: var(--s4);
  font-size: 13px; line-height: 1.55; color: var(--ink2); white-space: pre-wrap;
  text-wrap: pretty; }
.pv-cap.none { color: var(--dim); font-style: italic; }

.pv-meta { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s1);
  padding: var(--s3) var(--s4); border-top: 1px solid var(--line); }
.pv-out { margin-left: auto; display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--blue); text-decoration: none; }
.pv-out:hover { text-decoration: underline; }

@media (max-width: 900px) {
  .pv { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr) !important;
    height: min(880px, calc(100vh - 40px)); }
  .pv-media { height: auto; width: 100%; max-width: 100%; }
  .pv-rail { border-left: 0; border-top: 1px solid var(--line); }
}

/* ── the rates column ─────────────────────────────────────────────────────────
   Best and worst are marked by SHAPE (a circle-check against an x-octagon), never by hue: the
   five hues on this page all name roles in the legend, and green-against-red is the one pair the
   reader cannot separate. Secondaries stay neutral for the same reason. */
table.t td.rates { white-space: nowrap; }
.rate-main { display: grid; grid-template-columns: 15px auto; align-items: start;
  justify-content: end; gap: 4px; }
.rate-val { text-align: right; }
.rate-main b { display: block; font-size: 13px; line-height: 16px; color: var(--ink);
  font-variant-numeric: tabular-nums; }
/* The unit under its own number, set in the same type as the secondary labels below it. The mark
   stays on the number's own line: it grades the figure, not the caption. */
.rate-cap { font-size: 10px; letter-spacing: .04em; color: var(--dim); }
.rate-mark { display: inline-flex; align-items: center; justify-content: center; height: 16px;
  color: var(--ink2); }
.rate-sub { display: flex; align-items: baseline; justify-content: flex-end; gap: 6px;
  margin-top: 2px; font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums; }
/* A clear step down from the headline into the list, so the caption reads as belonging to the
   number above it rather than to the first secondary below. */
.rate-main + .rate-sub { margin-top: 5px; }
.rate-sub .rate-k { font-size: 10px; letter-spacing: .04em; }

.ratemenu { position: fixed; z-index: var(--z-dropdown);
  min-width: 210px; padding: 6px; border-radius: 10px;
  background: var(--panel2); border: 1px solid var(--line);
  box-shadow: 0 12px 32px rgba(0,0,0,.5); }
.ratemenu-h { font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--dim); padding: 4px 8px 6px; }
.ratemenu-r { display: flex; align-items: center; gap: 6px; border-radius: 7px; }
.ratemenu-r.on { background: rgba(255,255,255,.045); }
.ratemenu-p { flex: 1; display: flex; align-items: center; gap: 8px;
  background: none; border: 0; padding: 7px 8px; border-radius: 7px;
  font: 500 12px -apple-system, sans-serif; color: var(--ink2); cursor: pointer; text-align: left; }
.ratemenu-p:hover { color: var(--ink); }
.ratemenu-dot { width: 9px; height: 9px; border-radius: 50%; flex: none;
  border: 1px solid var(--edge); }
.ratemenu-dot.on { border-color: var(--ink); box-shadow: inset 0 0 0 2px var(--panel2), inset 0 0 0 9px var(--ink); }
.ratemenu-s { flex: none; margin-right: 6px; padding: 3px 8px; border-radius: 20px;
  background: none; border: 1px solid var(--line);
  font: 600 10px -apple-system, sans-serif; text-transform: uppercase; letter-spacing: .04em;
  color: var(--dim); cursor: pointer; }
.ratemenu-s:hover:not(:disabled) { color: var(--ink); border-color: var(--line2); }
.ratemenu-s.on { color: var(--ink2); border-color: var(--edge); background: rgba(255,255,255,.05); }
.ratemenu-s:disabled { opacity: .45; cursor: default; }
.ratemenu-p:focus, .ratemenu-p:focus-visible,
.ratemenu-s:focus, .ratemenu-s:focus-visible { outline: none; }

/* The footnote asterisk earns its place where context is hidden and the reader would not guess it
   is there. Inside the rates picker every row IS its own explanation, and the good/poor mark is a
   glyph already; an asterisk beside a glyph is a second marker for one fact. */
.ratemenu [data-tip]::after, .rate-mark[data-tip]::after { content: none; }

/* ── collection ──────────────────────────────────────────────────────────── */
.listfield { width: 100%; min-width: 320px; font-family: var(--mono); font-size: 12px; text-align: left; }
.runlog { margin: 0; font: 11.5px/1.5 var(--mono); color: var(--ink2); white-space: pre-wrap; max-height: 320px; overflow: auto; }

/* ── numerals ─────────────────────────────────────────────────────────────────
   Mono, so a digit is the same width wherever it lands, and the fraction a size down so the
   decimal point is findable while the eye runs down the column. Padding every value in a column
   to the same number of fraction digits is what actually aligns the points; the smaller size
   only makes the boundary legible. */
.n { font-family: var(--mono); font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  white-space: nowrap; }
.n .nf { font-size: .8em; letter-spacing: -.01em; }
table.t td.num .n, table.t td.stack .n, table.t td.rates .n { display: inline-block; }
/* No footnote asterisks in the viewer. Every figure there is one line from its own label, and a
   column of them turned the panel into a field of markers. The tooltips still hold the split. */
.pv [data-tip]::after { content: none; }

/* ── post sets ────────────────────────────────────────────────────────────────
   A selected row is tinted neutral, never in one of the five legend hues: selection is a state of
   the interface, not a fact about the account, and giving it a hue would put a sixth colour on a
   page whose whole colour discipline is that every hue names a role. */
table.t tbody tr.sel > td { background: rgba(255,255,255,.055); }
table.t tbody tr.sel > td:first-child { box-shadow: inset 2px 0 0 var(--ink2); }
table.t tbody tr { cursor: default; }
.chip.set { color: var(--ink2); border-color: var(--line2); }

.ctxmenu { position: fixed; z-index: var(--z-dropdown); min-width: 250px; padding: 6px;
  border-radius: 10px; background: var(--panel2); border: 1px solid var(--line);
  box-shadow: 0 12px 32px rgba(0,0,0,.5); }
.ctxmenu-h { font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--dim); padding: 4px 8px 6px; }
.ctxmenu-i { display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  width: 100%; padding: 7px 8px; border: 0; border-radius: 7px; background: none;
  font: 500 12px -apple-system, sans-serif; color: var(--ink2); cursor: pointer; text-align: left; }
.ctxmenu-i i { font-style: normal; font-size: 11px; color: var(--dim);
  font-family: var(--mono); }
.ctxmenu-i:hover:not(:disabled) { background: rgba(255,255,255,.05); color: var(--ink); }
.ctxmenu-i:disabled, .ctxmenu-i.off { opacity: .45; cursor: default; }
.ctxmenu-i:focus, .ctxmenu-i:focus-visible { outline: none; }
.pickitem-c { display: inline-flex; width: 12px; align-self: center; visibility: hidden; }
.pickitem.on .pickitem-c { visibility: visible; }
.pick.small { padding: 4px 8px; font-size: 12px; }
input.interacted { width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: var(--ink2); }

/* Tags on a content row. None of them take a legend hue: they say what an item IS, not what a
   number means, and the five colours on this page are all spoken for. HQ and Partial are the two
   that need to catch the eye, so they get weight and a brighter edge rather than a colour. */
/* HQ reads like the chips beside it — the same border as the date and the boost — with a faint
   pink ground for the lift, never a brighter outline. */
.chip.hq { color: var(--ink); background: rgba(255, 105, 180, .13); font-weight: 600; letter-spacing: .06em; }
/* The identity column's middle line holds its height when empty, so the third line is always third. */
.rowflex.hold:empty { min-height: 16px; }
/* The cycle-name label is frozen until clicked: a quiet control until it is one. */
.prodset-id.editable { cursor: text; }
.prodset-id.editable:hover { border-color: var(--line2); }
/* The frozen cycle name: one large box in the surface's own shading, the whole "IG S1" inside. */
table.t.prodt .prodset-id.frozen { border: 0; padding: 0; background: none; }
table.t.prodt .prodset-id.frozen .prodset-frozen { display: inline-flex; align-items: center; font: 700 14px var(--mono); letter-spacing: .06em; color: var(--ink); background: var(--panel2); border: 1px solid var(--line2); border-radius: 6px; padding: 7px 12px; }
table.t.prodt .prodset-id.frozen[role=button] { cursor: text; }
table.t.prodt .prodset-id.frozen[role=button]:hover .prodset-frozen { border-color: var(--edge); }
/* THE BOOST CHIP. One noun, four glyphs, and no outline: the stroke was drawing a box around a
   state that the glyph already states, on a line that carries two other bordered chips. The fill
   still separates it from the row, and the 1px stays in the box model as transparent so it sits on
   the same baseline and the same height as the date beside it.

   It carries no hue either: "still running" is the only one of the four that changes how the row
   is read, and that row is already the one the table shades — so the state separates on SHAPE,
   which no colour deficiency and no greyscale print can take away, and live steps up in luminance
   for the same reason the row beneath it does. */
/* Set like the format pill beside it: mono and upper case. Two chips on one line saying what kind
   of thing this is should be set the same way, and the state glyph reads better against caps. */
.chip.boost { font-family: var(--mono); text-transform: uppercase; padding-left: 5px;
  border-color: transparent; }
.chip.boost svg { opacity: .85; }
.boost-i { flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 13px; }
.chip.boost.live { color: var(--ink); }
.chip.boost.live svg { opacity: 1; }
.chip.boost.done svg, .chip.boost.paused svg { opacity: .7; }
.chip.warn { color: var(--ink2); border-color: var(--edge); border-style: dashed; }
.chip.link:focus, .chip.link:focus-visible { outline: none; }
table.t tbody tr.flash > td { background: rgba(255,255,255,.1); }
table.t tbody tr > td { transition: background .35s ease-out; }
@media (prefers-reduced-motion: reduce) { table.t tbody tr > td { transition: none; } }

/* The number column holds identity: the number, and what this item is tied to. */
/* The identity column: what it is and where it ran, what kind of thing it is, and what it is tied
   to — three pills down the left, each a different shape so they are not read as one list. */
table.t th.cno, table.t td.cno { text-align: left; }

/* Three lines of tags, each a different question. */
.postmeta .rowflex { margin-top: 3px; }
.postmeta .rowflex:first-child { margin-top: 0; }
/* Analytics sat on this line as bare text while everything beside it was boxed, which read as an
   afterthought rather than as the one control on the row. It takes the tag shape too; what says it
   is a control and not a reading is the chevron, the cursor and a hover the readings do not have. */
.anbtn { text-transform: uppercase; letter-spacing: .06em; color: var(--dim); cursor: pointer; }
.anbtn:hover, .anbtn.on { color: var(--ink); border-color: var(--line2); }
/* How many ad sections the drawer holds, carried by the toggle itself. Set in the toggle's own
   type and dimmed by opacity rather than by a colour of its own, so it tracks the control through
   hover instead of sitting on the line as a second, brighter thing. */
.anbtn-n { opacity: .68; }
.anbtn:focus, .anbtn:focus-visible { outline: none; }
.anbtn svg { opacity: .8; }
.reflink svg { opacity: .7; }

/* ── the analytics drawer ─────────────────────────────────────────────────────
   The curve carries no legend hue. It is not one of the five roles, the envelope is this account
   measured against itself, and a platform is not a kind of delivery — so the lines separate on
   weight and dash, and every one is named in the key underneath. */
table.t tbody tr.anopen > td { border-bottom: 0; }
/* The empty first cell does the indenting, so the drawer starts exactly where the row's content
   starts. The rule down its left edge is what says the two belong together. */
tr.anrow > td.cno { background: rgba(255,255,255,.02); }
tr.anrow > td:not(.cno) {
  padding: var(--s3) var(--s4) var(--s4) var(--s4);
  background: rgba(255,255,255,.02);
  border-left: 1px solid var(--line2);
}
/* A MASONRY PACK, not a row grid. The tiles are six different heights, and rows would leave a
   hole under every short one; each tile spans as many 4px rows as it measures. The board is capped
   rather than run to the table's full width — a chart stretched across 1900px was the complaint
   this answers, and past about 1200 the eye stops being able to compare two tiles side by side. */
/* Zero to the table's width calculation, the cell's full width to lay out in. A colspan cell hands
   whatever its content needs up to the table, and a board is content that can always want more —
   so a board of 268px tracks was quietly demanding 1108px and the table obliged by growing past
   the page. `contain: inline-size` settles it at the source: this element's width is computed
   without asking its contents anything. It is also what the board's own breakpoints measure. */
.anfit { width: 0; min-width: 100%; container-type: inline-size; }

/* A COUNTED NUMBER OF TRACKS, not auto-fill. auto-fill against a floor that can collapse produced
   a fourth track at 0px, and against a floor that cannot, it is the fixed minimum that widens the
   table. The count is written by the same pass that measures the tiles, because a span has to be
   clamped to it in the same breath: minmax(0, 1fr) asks the table for nothing, and `--wc` is the
   width a tile actually gets rather than the one it asked for. */
.tboard { display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
  grid-auto-rows: 4px; gap: 12px; max-width: 1240px; align-items: start; }
.wtile { grid-column: span var(--wc, 2); min-width: 0; }
/* A CONTAINER, so what is inside can size against the tile rather than against the page. A tile is
   one to four columns wide and the reader moves it between them; a chart with fixed bar widths
   fitted the widest of those and ran off the edge of the narrowest. Inline-size only: the height
   still comes from the content, which the board's masonry pass depends on. */
.wtile-in { position: relative; display: flex; flex-direction: column; gap: var(--s2);
  height: 100%; padding: var(--s3); border-radius: 10px; container-type: inline-size;
  background: var(--panel); border: 1px solid var(--line); }
.wtile-in > :nth-child(2) { flex: 1 1 auto; min-height: 0; }
/* A flex item's min-width is auto, which is its content's min-content — so one table inside one
   tile can hold the whole column open and spill over its neighbour. Nothing in a tile may be wider
   than the tile. */
.wtile-in > * { min-width: 0; max-width: 100%; }
.wtile-h { display: flex; align-items: baseline; gap: var(--s2); min-height: 14px; }
/* The handle a tile is picked up by. It holds its slot like the rest of the head's controls, so
   nothing shifts when the cursor arrives, and it is the only part of a tile that starts a drag. */
/* ALWAYS THERE. Hidden until hover, it was an affordance you had to already know about to find:
   nothing on a resting board said a tile could be moved. It sits at the quietest ink on the tile
   instead, one step under the title it precedes, and brightens under the cursor. */
.wtile-g { display: inline-flex; align-self: center; margin: 0 -2px 0 -3px; padding: 0;
  background: none; border: 0; color: var(--dim); cursor: grab;
  opacity: .5; transition: opacity .15s ease-out, color .15s ease-out; }
.wtile:hover .wtile-g, .wtile-in:focus-within .wtile-g { opacity: 1; }
.wtile-g:hover { color: var(--ink); opacity: 1; }
.wtile-g:active { cursor: grabbing; }
.wtile-g:focus, .wtile-g:focus-visible { outline: none; }
.wtile.dragging { opacity: .4; }
/* Where it will land. A rule down the edge it is going to sit against, drawn in the gutter so it
   never covers anything the tile is showing. */
.wtile.dropbefore .wtile-in { box-shadow: -7px 0 0 -5px var(--ink2); }
.wtile.dropafter .wtile-in { box-shadow: 7px 0 0 -5px var(--ink2); }
@media (prefers-reduced-motion: reduce) { .wtile-g { transition: none; } }
.wtile-t { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wtile-n { margin-left: auto; font: 10px var(--mono); font-style: normal; color: var(--dim);
  white-space: nowrap; }
/* The controls hold their slot whether or not the cursor is here, so the note never jumps sideways
   when it arrives. Revealed on hover, and on focus so a keyboard can still reach them. */
.wtile-acts { flex: none; display: flex; align-items: center; gap: 4px; margin-left: auto;
  opacity: 0; transition: opacity .15s ease-out; }
.wtile-n + .wtile-acts { margin-left: 0; }
.wtile:hover .wtile-acts, .wtile-in:focus-within .wtile-acts { opacity: 1; }
/* Except where the control says HOW the tile is to be read rather than offering a second look at
   it. A time scale is not discoverable from the curve — the same line means different things on a
   sampled axis and a linear one — so that switch stands at rest as a plain segmented control. */
.wtile-acts.shown { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .wtile-acts { transition: none; } }
.wtile-sw { padding: 1px 6px; border-radius: 999px; cursor: pointer;
  background: none; border: 1px solid var(--line);
  font: 700 9px var(--mono); letter-spacing: .04em; color: var(--dim); }
.wtile-sw:hover { color: var(--ink2); border-color: var(--line2); }
.wtile-sw.on { color: var(--ink); border-color: var(--edge); background: var(--panel2); }
/* Beside the title, so it holds a slot of its own rather than a place in the controls block. It
   keeps that block's manners: the slot is always there, the glyph appears on hover, and nothing
   moves sideways when the cursor arrives. */
.wtile-x { display: inline-flex; align-self: center; margin-left: -3px; padding: 0;
  background: none; border: 0; cursor: pointer; color: var(--dim);
  opacity: 0; transition: opacity .15s ease-out, color .15s ease-out; }
.wtile:hover .wtile-x, .wtile-in:focus-within .wtile-x { opacity: 1; }
.wtile-x:hover { color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .wtile-x { transition: none; } }
.wtile-sw:focus, .wtile-sw:focus-visible, .wtile-x:focus, .wtile-x:focus-visible { outline: none; }

/* The handles. A tile is widened by dragging its right edge and stretched by dragging its bottom
   one; the mark only appears under the cursor, because an edge that always advertises itself turns
   a board of eight tiles into a board of sixteen controls. */
.wtile-gw, .wtile-gh { position: absolute; padding: 0; background: none; border: 0;
  touch-action: none; opacity: 0; transition: opacity .15s ease-out; }
.wtile-gw { top: 8px; bottom: 8px; right: -6px; width: 12px; cursor: col-resize; }
.wtile-gh { left: 8px; right: 8px; bottom: -6px; height: 12px; cursor: row-resize; }
.wtile-gw::after, .wtile-gh::after { content: ''; position: absolute; border-radius: 2px;
  background: var(--edge); }
.wtile-gw::after { top: 50%; left: 5px; width: 2px; height: 22px; margin-top: -11px; }
.wtile-gh::after { left: 50%; top: 5px; height: 2px; width: 22px; margin-left: -11px; }
.wtile:hover .wtile-gw, .wtile:hover .wtile-gh,
.wtile-gw:focus-visible, .wtile-gh:focus-visible { opacity: 1; }
.wtile.sizing .wtile-gw, .wtile.sizing .wtile-gh { opacity: 1; }
.wtile.sizing .wtile-gw::after, .wtile.sizing .wtile-gh::after { background: var(--ink2); }
.wtile.sizing .wtile-in { border-color: var(--edge); }
.wtile-gw:focus, .wtile-gw:focus-visible, .wtile-gh:focus, .wtile-gh:focus-visible { outline: none; }
@media (prefers-reduced-motion: reduce) { .wtile-gw, .wtile-gh { transition: none; } }

/* What the reader has put away. Named, so the board never silently omits a reading — and one
   click from being back, because the row and the drawer have disagreed before and the check on
   that is to look at both. */
.ttray { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
  max-width: 1240px; margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px solid var(--line); }
.ttray-h { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--dim); }
.ttray-b { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px;
  border-radius: 999px; cursor: pointer; background: none; border: 1px dashed var(--line2);
  font: 500 11px -apple-system, sans-serif; color: var(--dim); }
.ttray-b:hover { color: var(--ink); border-color: var(--edge); border-style: solid; }
.ttray-b:focus, .ttray-b:focus-visible { outline: none; }
.ttray-b svg { opacity: .8; }

.ancurve { min-width: 0; display: flex; flex-direction: column; gap: var(--s2); }

/* ── the split under the curve ────────────────────────────────────────────────
   One track, a name at each end and its figure beneath it. The pair sums to the whole, so a bar
   read from both ends says the share and the totals at once, which two separate bars do not. The
   seam is what carries where one ends: green against blue is the panel's own organic-against-ads
   pair, but nothing here leans on telling them apart. */
.endsbars { display: grid; gap: var(--s3); margin-top: var(--s1); }
.endsbar { display: grid; gap: 3px; }
.endsbar-h, .endsbar-f { display: flex; justify-content: space-between; }
.endsbar-h { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--dim); }
.endsbar-t { display: flex; gap: 1.5px; height: 8px; border-radius: 4px; overflow: hidden;
  background: var(--panel2); }
.endsbar-t i { display: block; }
.endsbar-f { font: 700 11px var(--mono); font-variant-numeric: tabular-nums; }
.endsbar-t .o { background: var(--green); }
.endsbar-t .a { background: var(--ads); }
/* Follower against non-follower is a cut of the audience, not one of the five roles, so it
   separates on luminance. */
.endsbar-t .f { background: var(--ink2); }
.endsbar-t .nf { background: var(--bar); }
/* A FIGURE IS READ, A BAR SEGMENT IS ONLY SEEN, so the two do not always take the same value: the
   neutral that reads as a bar against its track lands at 2.9:1 as an 11px numeral, which is a
   number you cannot read printed under a bar you can. */
.endsbar-f .o { color: var(--green); }
.endsbar-f .a { color: var(--ads); }
.endsbar-f .f { color: var(--ink2); }
.endsbar-f .nf { color: var(--dim); }
.anadlist { display: grid; gap: 1px; }
.curve { width: 100%; display: block; }
.curve-line { fill: none; vector-effect: non-scaling-stroke; }
.curve-total { stroke: var(--ink); stroke-width: 1.6; }
.curve-ig { stroke: var(--ink2); stroke-width: 1.1; stroke-dasharray: 4 3; }
.curve-fb { stroke: var(--dim); stroke-width: 1.1; stroke-dasharray: 1 3; }
.curve-tick { stroke: var(--line); stroke-width: 1; }
/* The elapsed-time labels, in HTML rather than in the svg. Same mono, same size and the same dim
   as every other axis and micro-label on the page, because they are no longer being scaled by
   whatever width the chart happens to have. */
.curve-axis { position: absolute; left: 0; right: 0; bottom: 1px; height: 12px;
  pointer-events: none; }
.curve-axis i { position: absolute; margin-left: 3px; font: 9px var(--mono); font-style: normal;
  letter-spacing: .02em; color: var(--dim); white-space: nowrap; }
.ankey { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s4); }
.ankey-i { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--dim); }
.ankey-m { width: 14px; height: 0; border-top: 2px solid var(--ink); }
.ankey-m.k-ig { border-top: 1px dashed var(--ink2); }
.ankey-m.k-fb { border-top: 1px dotted var(--dim); }
/* An open ad's four figures, on the same board as the post's own and at the same size. Auto-fit,
   so widening the tile spreads them rather than leaving three columns of air. */
.anstats { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
  gap: var(--s2) var(--s3); align-content: start; }
.anread { grid-column: 1 / -1; font-size: 10px; color: var(--dim); font-family: var(--mono);
  word-break: break-all; }
.anempty { padding: 10px 4px; font-size: 12px; color: var(--dim); }
/* Reading the local JSON files rather than the database. Marked because the two hold different
   data — the files carry no ad-level insights — and a panel that looks identical while showing less
   is the hardest kind of wrong to notice. Amber: this is a condition to know about, not a fault. */
.datachip.onfiles { border-color: var(--amber); color: var(--amber); }
/* A reading this post does not carry. The tile keeps its place on the board — the arrangement is
   the reader's and must not reshuffle itself from row to row — and where the chart would be it says
   so. Tall enough to read as a tile rather than a strip, short enough not to spend a screen on
   something that is not there. */
.tvoid { display: flex; align-items: center; justify-content: center; min-height: 92px;
  font: 500 12px var(--mono); letter-spacing: .04em; color: var(--dim); }

/* Ads against organic is the one place in the drawer a legend hue is right: this is bought
   delivery against delivery that arrived on its own, which is exactly what blue and green name. */
.anbars { display: grid; gap: var(--s1); }
.anbar { display: grid; grid-template-columns: 62px minmax(0,1fr) auto;
  align-items: center; gap: var(--s3); }
.anbar-k { font-size: 10px; color: var(--dim); text-transform: uppercase; letter-spacing: .05em; }
/* A seam between the two segments, because where one ends is the whole reading and a reader who
   cannot separate the hues has nothing else to find it by. */
.anbar-t { display: flex; gap: 1.5px; height: 7px; border-radius: 4px; overflow: hidden;
  background: var(--panel2); }
.anbar-a { background: var(--ads); }
.anbar-o { background: var(--green); }
/* Fixed width per figure so the two numbers keep their columns down the stack rather than sliding
   with their own digit count. */
.anbar-n { display: grid; grid-template-columns: repeat(2, 3.4em); gap: var(--s2);
  font: 600 10px var(--mono); text-align: right; }
.anbar-n b.a { color: var(--ads); } .anbar-n b.o { color: var(--green); }

/* Ads against organic as rings. Same numbers as the bars, different question: a ring answers how
   much of this was bought, a bar answers how big it was. Both halves are written out underneath in
   their own colour, so the arc is never the only thing saying which is which. */
.anrings { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: var(--s3); align-content: start; }
.anring { display: grid; justify-items: center; gap: 3px; }
.anring-c { position: relative; width: 84px; max-width: 100%; }
.ringsvg { display: block; width: 100%; transform: rotate(-90deg); }
.ringarc { fill: none; stroke-width: 5; }
.ringarc.r-bg { stroke: var(--panel2); }
.ringarc.r-a { stroke: var(--ads); }
.ringarc.r-o { stroke: var(--green); }
.anring-v { position: absolute; inset: 0; display: grid; place-items: center;
  font: 700 14px var(--mono); color: var(--ink); font-variant-numeric: tabular-nums; }
.anring-k { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--dim); }
.anring-n { display: grid; justify-items: center; gap: 1px; font: 600 10px var(--mono); }
.anring-n b.a { color: var(--ads); } .anring-n b.o { color: var(--green); }

/* SCALED UP AND CLOSED UP. The bars were 9px in a 56px well with 8px between bands, which spent
   most of the tile on the gaps and left the data too small to read a difference off, let alone
   point at. Wider columns, a taller well and tighter gaps put the ink where the reading is. */
/* PACKED, NOT SPREAD. Each band was a 1fr track, so six of them shared the whole tile however wide
   it got and most of that width went between the groups rather than into them. Sized to their
   content and centred: the bands sit together and the tile's spare width stays at its edges.

   THE GAP IS THE GROUPING. 1px inside a band against 20px between them is what makes each band
   read as three bars rather than the whole chart as eighteen — and with the bands run together,
   the near-empty non-binary slot on the right of each one made every group look left-weighted,
   which reads as a label that is off centre even though it is not. */
.anaud-g { display: grid; grid-auto-flow: column; grid-auto-columns: max-content;
  gap: clamp(5px, 2.4cqi, 20px); align-content: end; justify-content: center; }
.anaud-c { display: grid; gap: 3px; justify-items: center; }
.anaud-c span { font-size: 9px; color: var(--dim); font-family: var(--mono); }
.anaud-p { display: flex; align-items: flex-end; gap: 1px; height: var(--audh, 118px);
  width: 100%; justify-content: center; }
/* A floor of 2px, so a band with a real but tiny share is a mark rather than nothing. Zero stays
   zero: an empty band and a nearly-empty one must not look the same. */
/* SIZED AGAINST THE TILE. At 22px fixed, six bands of three bars plus their gaps needed 508px and
   simply overflowed a two-column tile — the last band walked off the right edge. Between 9 and
   22px now, so the chart compresses instead of spilling, and the floor is the width at which a bar
   is still a bar you can point at. */
.anaud-b { width: clamp(9px, 2.7cqi, 22px); border-radius: 3px 3px 0 0; }
/* One letter under each bar, on the bar's own width so it sits centred under it. */
.anaud-l { display: flex; gap: 1px; justify-content: center; }
.anaud-l i { width: clamp(9px, 2.7cqi, 22px); text-align: center; font: 600 9px var(--mono);
  font-style: normal; color: var(--dim); letter-spacing: .02em; }
/* THE ADS SHARE OF A BAND, inside the bar rather than beside it. The bar keeps the height it
   always had — that is the band's share of the audience and it has not changed — and gains a line
   across it where the media's part of that band ends. Gender is which of the three bars this is,
   so the fill is free to say something else. */
.anaud-b.paired { background: color-mix(in srgb, var(--green) 62%, transparent); }
/* A band we only have the ads' word for. Fully the ads colour, because that is all of it we can
   account for, and outlined so it does not read as a complete measurement like its neighbours. */
.anaud-b.adonly { background: none; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ads) 55%, transparent); }
.anaud-b.paired::before { content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--adshare, 0%); background: var(--ads); border-radius: inherit; }
.anaud-b { position: relative; }
/* THE GENDER GREYS ONLY WHERE THERE IS NO SPLIT. These sat after `.paired` at the same weight and
   quietly won, so what should have been the organic green on top of every bar was the old
   women/men/non-binary neutral instead — a colour that stopped meaning anything the moment F, M
   and N were written under the bars, and that read as a fourth thing nobody had defined. Scoped to
   the unpaired case, which is a post with no ad data to split by. */
.anaud-b:not(.paired).w { background: var(--ink2); }
.anaud-b:not(.paired).m { background: var(--edge); }
.anaud-b:not(.paired).n { background: var(--bar); }

/* The two bounds are drawn as LINES, not a shaded band: the tooltip quotes them by name as a lower
   and an upper reading, and a filled region invites reading them as one blurry thing. */
/* LIFTED, BECAUSE THEY NOW SIT ON COLOUR. The two range lines were a neutral edge on a dark
   ground, which was plenty against the panel and 1.03:1 against a filled area — a data line you
   cannot see. Brighter, and still the quietest thing on the chart. */
.curve-hi, .curve-lo { stroke: color-mix(in srgb, var(--ink2) 62%, transparent); stroke-width: 1; }
.curve-frame { position: relative; }
.curve-rule { position: absolute; top: 6px; bottom: 14px; width: 0;
  border-left: 1px dotted var(--dim); opacity: 0; pointer-events: none; }
.curve-dot { position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  border-radius: 50%; background: var(--panel); border: 1.5px solid var(--ink2);
  opacity: 0; pointer-events: none; }
.curve-dot.h-total { border-color: var(--ink); }
.curve-frame.hover .curve-rule, .curve-frame.hover .curve-dot { opacity: 1; }
/* ABOVE THE PLOT, NOT ON IT. The readout used to sit inside the chart and cover the very stretch
   of line the cursor was asking about — the thing you are reading is behind the thing telling you
   about it. It stands clear above the frame now and still tracks the scrub position, so it points
   at where it is reading from. It needs no reserved space: nothing is there but the tile's own
   heading, which is static while you are scrubbing. */
.curve-tip { position: absolute; bottom: calc(100% + 7px); top: auto; z-index: var(--z-tooltip);
  min-width: 178px; padding: 7px 9px; border-radius: 8px;
  background: #1f242c; border: 1px solid var(--line); box-shadow: 0 8px 24px rgba(0,0,0,.5);
  opacity: 0; pointer-events: none; transition: opacity .1s ease-out; }
/* The two halves of the actual line, under it. Indented past the key column and a size down: a
   decomposition of the figure above, not two more readings beside it. */
.curve-tip-r.sub { font-size: 10.5px; line-height: 1.5; color: var(--dim); }
.curve-tip-r.sub b { font-weight: 600; }
.curve-tip-r.sub.a b { color: var(--ads); }
.curve-tip-r.sub.o b { color: var(--green); }
.curve-tip-r.sub .curve-key { border: 0; }
.curve-frame.hover .curve-tip { opacity: 1; }
.curve-tip-h { font-size: 11px; color: var(--dim); margin-bottom: 5px; font-family: var(--mono); }
.curve-tip-r { display: grid; grid-template-columns: 14px 1fr auto; align-items: center; gap: 7px;
  font-size: 11.5px; color: var(--ink2); line-height: 1.7; }
.curve-tip-r b { color: var(--ink); font-family: var(--mono); font-variant-numeric: tabular-nums; }
.curve-key { height: 0; border-top: 2px solid var(--ink); }
.curve-key.h-lo, .curve-key.h-hi { border-top: 1px solid color-mix(in srgb, var(--ink2) 62%, transparent); }
.curve-key.h-ig { border-top: 1px dashed var(--ink2); }
.curve-key.h-fb { border-top: 1px dotted var(--dim); }
@media (prefers-reduced-motion: reduce) { .curve-tip { transition: none; } }

/* A header that orders the table. The arrow shows both which column is sorting and which way, so
   the state is never something you have to remember. */
.sortbtn { background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  display: inline-flex; align-items: center; gap: 3px; }
.sortbtn:hover { color: var(--ink2); }
.sortbtn.on { color: var(--ink); }
.sortbtn:focus, .sortbtn:focus-visible { outline: none; }
table.t th .ixbtn { vertical-align: middle; }
table.t th.ratesth .ixbtn { margin-left: 5px; }
/* THE FOOTNOTE MARK LEADS ON A COLUMN HEAD. Trailing, it added its own width plus a space to a
   column that is sized by its widest cell, and on a right-aligned head it pushed the label off the
   edge the numbers line up on. Leading, it costs the same space the label's own left margin was
   already spending. The number and Content columns carry no tooltip and so no mark: an asterisk on
   a column nobody would question is noise. */
/* `> thead` throughout: descendant reach put this mark on every th of every table nested in a
   drawer row, which is a table inside this one's own tbody. */
table.t.stacked > thead th[data-tip]::after,
table.t.stacked > thead th [data-tip]::after { content: none; }
table.t.stacked > thead th[data-tip]::before {
  content: '*';
  font-size: .8em; font-weight: 600; line-height: 0; vertical-align: super;
  color: var(--dim); margin-right: 3px;
}
/* Not on the hinge. It is a control, not a reading, and a mark there would widen the one column
   whose whole purpose is to be narrow. */
table.t.stacked > thead th.ixgap[data-tip]::before { content: none; }

/* Which surfaces the table is showing. Neutral: a platform is not one of the five roles. */
.platsw { margin-left: auto; display: inline-flex; gap: 4px; }
.platsw { flex-wrap: wrap; justify-content: flex-end; }
.platsw-b { padding: 3px 9px; border-radius: 999px; cursor: pointer; white-space: nowrap;
  background: none; border: 1px solid var(--line);
  font: 700 10px var(--mono); letter-spacing: .04em; color: var(--dim); }
.platsw-b:hover { color: var(--ink2); border-color: var(--line2); }
.platsw-b.on { color: var(--ink); border-color: var(--edge); background: var(--panel2); }
.platsw-b:focus, .platsw-b:focus-visible { outline: none; }
/* Back to the page the drawer was read off, on its own line above the board: provenance, not one
   of the readings. Set as a tag like everything on the row above it, because it is the same kind
   of object — a short label in a small frame — and it was the last thing in here still carrying
   metrics of its own. */
.ansrcrow { display: flex; margin-bottom: 12px; }
.ansrc { font-family: var(--mono); text-transform: uppercase; color: var(--dim);
  text-decoration: none; }
.ansrc:hover { color: var(--ink); border-color: var(--line2); }
.ansrc svg { opacity: .75; }

/* One ad in the list. Two ads on one creative are two decisions with two results, so they are
   listed rather than summed away, and opening one puts its readings on the board beside the post's. */
.adrow { display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 6px;
  border: 0; border-radius: 6px; background: none; cursor: pointer; text-align: left;
  font: 500 12px -apple-system, sans-serif; color: var(--ink2); }
.adrow:hover, .adrow.on { background: rgba(255,255,255,.045); color: var(--ink); }
.adrow:focus, .adrow:focus-visible { outline: none; }
.adrow-n { font: 600 10px var(--mono); letter-spacing: .05em; color: var(--dim);
  text-transform: uppercase; }
.adrow b { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.adrow-s { font-size: 10px; color: var(--dim); }
/* The two ids, on the row they identify. Pushed to the far end so the money and the run length
   keep the left, where the eye starts, and two ads sharing an ad set line up under each other. */
.adrow-id { margin-left: auto; display: flex; gap: 12px; flex: none;
  font: 9px var(--mono); color: var(--dim); letter-spacing: -.01em; }

/* ── what an ad did, under the ad ──────────────────────────────────────────────
   RECESSED, NOT RAISED. These sit inside the Ad List tile, and a card on a card is two frames for
   one thing; a well cut into the surface is how the rest of this table already says "contained".
   The four figures take a row of their own and the delivery split the one under it, which is the
   order they are read in: what it cost and what it bought, then where the money went. */
/* Flush with the row that opened it. The indent was saying "this is nested", which the row above
   it and the frame around both already say — and it cost the four figures inside the width they
   are read at. */
.adbody { display: grid; gap: var(--s2); padding: var(--s2) 6px var(--s3); }
/* Flex, not auto-fit. Four figures want four columns; auto-fit against a wide tile made six and
   collapsed two of them to nothing, which is two gaps of dead space on the end of the row. */
.adstats { display: flex; flex-wrap: wrap; gap: var(--s2); }
.adstat { flex: 1 1 104px; min-width: 0; }
.adstat { padding: 7px 10px; border-radius: 8px; background: rgba(0, 0, 0, .24); }
.adstat b { display: block; font: 700 14px var(--mono); color: var(--ink);
  font-variant-numeric: tabular-nums; }
.adstat span { font-size: 10px; color: var(--dim); }
.adwell { padding: 8px 10px 9px; border-radius: 8px; background: rgba(0, 0, 0, .24); }
.adwell-h { display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: var(--s1); font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--dim); }
.adwell-n { font-style: normal; text-transform: none; letter-spacing: 0; font-family: var(--mono); }

/* Where an ad actually delivered. Ranked by spend, neutral — a placement is a subdivision of paid
   delivery, not one of the five roles, so it separates on length and label rather than on hue. */
/* Where the media delivered. Only the ads have a geography — Meta reports locations for paid
   delivery and nothing else — so the whole list takes the ads hue and the heading says whose it is
   rather than leaving the colour to carry that alone. */
.anwrapv { display: grid; gap: var(--s3); align-content: start; }
/* The shares, at the foot and left. One fact a line: strung along the head they were a sentence
   competing with the tile's own name for the width. The label and the figure sit in two columns so
   the percentages line up under each other rather than wherever their labels happen to end. */
.anshares { display: grid; gap: 1px; justify-self: start; }
.anshares-r { display: grid; grid-template-columns: minmax(0, auto) auto; gap: var(--s2);
  align-items: baseline; font-size: 10px; color: var(--dim); }
.anshares-r b { font: 600 10px var(--mono); color: var(--ink2); font-variant-numeric: tabular-nums;
  text-align: right; }
/* The base is what the three shares are OF, not a fourth share, so it steps back from them. */
.anshares-r.base { margin-top: 3px; }
.anshares-r.base b { color: var(--dim); font-weight: 400; }
.anloc { display: grid; gap: 2px; }
.anloc-h { display: flex; justify-content: space-between; align-items: baseline;
  font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--dim);
  margin-bottom: 2px; }
.anloc-h i { font-style: normal; text-transform: none; letter-spacing: 0; font-family: var(--mono); }
.anloc-r { display: grid; grid-template-columns: 88px minmax(0, 1fr) auto; align-items: center;
  gap: var(--s2); }
.anloc-k { font: 600 10px var(--mono); color: var(--dim); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.anloc-t { height: 7px; border-radius: 4px; background: var(--panel2); overflow: hidden; }
.anloc-t i { display: block; height: 100%; background: var(--ads); }
.anloc-r b { font: 600 10px var(--mono); color: var(--ink2); font-variant-numeric: tabular-nums; }

.anplace { display: grid; gap: 2px; }
.anplace-r { display: grid; grid-template-columns: 96px minmax(0,1fr) auto;
  align-items: center; gap: var(--s2); }
.anplace-k { font: 600 10px var(--mono); color: var(--dim); white-space: nowrap; }
.anplace-t { height: 7px; border-radius: 4px; background: var(--panel2); overflow: hidden; }
.anplace-t i { display: block; height: 100%; background: var(--bar); }
.anplace-r b { font: 600 10px var(--mono); color: var(--ink2); font-variant-numeric: tabular-nums; }
/* The window the boost was running. Behind every line, because the question it answers is what the
   curve does once it stops. */
/* NO `fill` RULE HERE. The rect takes its fill from a `fill="url(#hatch-N)"` attribute pointing at
   this chart's own pattern, and a CSS fill would beat that attribute and paint nothing at all —
   which is exactly what `fill: none` did. The pattern's line is what carries the colour. */
/* FAINT, BECAUSE IT IS BEHIND. It is drawn first and the areas paint over it, but those are 34%
   fills — a hatch bright enough to read on bare ground comes through them at almost full strength
   and reads as the thing on top. At this weight it sits under them, and where there is no fill it
   is still perfectly legible as the boost's window. */
.curve-hatch { stroke: color-mix(in srgb, var(--ads) 26%, transparent); stroke-width: 1.2; }
/* WHAT THE LINE IS MADE OF. Two fills stacked to the post's own total, in the legend's own
   ads-and-organic pair, kept faint: the line is still the reading and these say which half of it
   was bought. No stroke on either, so the only edges on this chart are the ones that are data. */
.curve-area { stroke: none; }
/* MATCHED TO EACH OTHER AND TO THE LEGEND. They were 22 and 18 percent, which is two different
   weights for two halves of one quantity — and both were faint enough that the hues read as tints
   rather than as the legend's own ads and organic. One number for both, high enough to be those
   colours, low enough that the total line and the two range lines still read across them. */
.curve-area.a-ads { fill: color-mix(in srgb, var(--ads) 34%, transparent); }
.curve-area.a-org { fill: color-mix(in srgb, var(--green) 34%, transparent); }
.ankey-m.k-fa { height: 9px; border: 0; border-radius: 2px;
  background: color-mix(in srgb, var(--ads) 34%, transparent); }
.ankey-m.k-fo { height: 9px; border: 0; border-radius: 2px;
  background: color-mix(in srgb, var(--green) 34%, transparent); }
/* The audience chart's own key. Its two fills are solid where the curve's are washes, so its
   swatches are too — a key that does not match the ink it names is a second thing to learn. */
.anaud-g ~ .ankey .ankey-m.k-fa { background: var(--ads); }
.anaud-g ~ .ankey .ankey-m.k-fo { background: color-mix(in srgb, var(--green) 62%, transparent); }
.ankey-m.k-nb { height: 9px; border: 1px solid color-mix(in srgb, var(--ads) 55%, transparent);
  border-radius: 2px; background: none; }

/* TODAY. The post's own line ends at its last reading and the range carries on past it, so without
   a mark here the flattening reads as the post dying rather than as the calendar running out. The
   line pulses to be found before it is hovered; the wash behind it makes "not measured yet" a
   place on the chart rather than an inference. Neutral, and no hue at all: it is not a series and
   it is not one of the five roles. */
.curve-void { position: absolute; top: 6px; right: 2px; bottom: 14px;
  background: rgba(15,17,21,.42); pointer-events: none; }
/* A gradient, not a border, because the rule has to BREAK where the word is: a line struck through
   its own label is a line that cancels it. The break is a band now rather than a foot — the label
   goes wherever the chart leaves room at that x, which is anywhere along the rule — and both its
   start and its length are handed down from the placement. */
.curve-now { position: absolute; top: 2px; bottom: 14px; width: 1px; pointer-events: none;
  background: linear-gradient(to bottom,
              var(--edge) 0, var(--edge) var(--nowlab, 60%),
              transparent var(--nowlab, 60%),
              transparent calc(var(--nowlab, 60%) + var(--nowlen, 44px)),
              var(--edge) calc(var(--nowlab, 60%) + var(--nowlen, 44px)), var(--edge) 100%);
  animation: curve-now-pulse 2.4s ease-in-out infinite; }
/* Turned on its side and centred on the line, standing in whatever clear run the placement found.
   Vertical writing rather than a rotate on a horizontal box: the box comes out tall and narrow on
   its own, so centring it is one translate instead of arithmetic against a rotated bounding box.
   The 180 turn is what makes it read upward, the way an axis label does. */
.curve-now-l { position: absolute; top: var(--nowlab, 0); left: 50%;
  writing-mode: vertical-rl;
  transform: translateX(calc(-50% + var(--nowside, 0px))) rotate(180deg);
  font: 700 8.5px var(--mono); letter-spacing: .06em; text-transform: uppercase; color: var(--ink2);
  white-space: nowrap; }
@keyframes curve-now-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .32; } }
/* Steady rather than absent: the marker is the answer to a question the reader is already asking,
   so what goes is the pulse, never the line. */
@media (prefers-reduced-motion: reduce) { .curve-now { animation: none; } }
/* The key swatch wears the same hatch, so the mark and the thing it names are one shape. */
.ankey-m.k-paid { height: 9px; border: 1px solid var(--line); border-radius: 2px;
  background: repeating-linear-gradient(45deg,
    color-mix(in srgb, var(--ads) 40%, transparent) 0 1.2px,
    transparent 1.2px 5px); }
/* Above the high range, or below the low one. TWO WEIGHTS OF ONE HUE, not two hues: this readout
   already prints ads in blue and organic in green, so a green total would be one colour saying two
   things on one panel. Amber is spoken for by neither, and the pair separates on LIGHTNESS, which
   is the one channel a colour deficiency leaves intact. Both clear 4.5:1 on the readout's ground:
   the dark 5.4:1, the light 9.6:1. The arrow still carries the direction on its own. */
.curve-tip-r.over b { color: var(--over); }
.curve-tip-r.under b { color: var(--under); }
.curve-mark { display: inline-flex; vertical-align: -1px; margin-right: 3px; }
.curve-mark svg { display: block; }
/* Which cycle this item belongs to. Sits with the identity because it says what the item IS part
   of, not how it did — and named by platform, because a set ranges over one surface's sequence. */
.setpill { border-color: var(--line2); color: var(--ink2); }

/* ── the drawer's footnote markers ──────────────────────────────────────────────
   The asterisk means "there is context here you would not guess at". Twenty-five of them in one
   panel means nothing at all: every audience bar, every placement row, every key entry carried
   one. The tooltips stay; the markers go, except on the provenance link where the reading's own
   source genuinely is hidden. */
.tboard [data-tip]::after, .ttray [data-tip]::after { content: none; }
.ansrc[data-tip]::after { content: "*"; }

/* ── the header row ────────────────────────────────────────────────────────────
   A footnote marker on all nine columns says nothing: the asterisk means "context you would not
   guess is here", and when it is everywhere it stops being a signal. A header explains itself by
   being a header, so the tooltips stay and the markers go. They remain on the CELLS, where a
   figure genuinely can hide a definition or a ledger. */
table.t thead th[data-tip]::after { content: none; }

/* ── the range controls ────────────────────────────────────────────────────────
   Four of them, because there are four ways this account gets looked at: a start, an end, a
   production interval, and a post set. */

/* Freshness, not liveness. The chip states when the data was written, so it stops making a claim
   the panel cannot keep — and it stays neutral, because "recently" is not a status. */
.datachip.live { font-variant-numeric: tabular-nums; letter-spacing: .02em; }

/* ── the scope panel ───────────────────────────────────────────────────────────
   One surface for every way a period gets chosen: all time, the two dropdowns that name a period,
   the two fields that hold what a choice resolved to, and the grid that fills them a day at a
   time. Top to bottom is coarse to fine — a whole account, a fortnight, a day — and every row of
   it can be read while any other is being used, which two popovers could not do. */
/* ABSOLUTE, not fixed. The panel belongs to the button that opened it, so it has to travel with
   it: pinned to the viewport it stayed put while the page scrolled out from under it and ended up
   floating over the middle of the dashboard, anchored to nothing. Placed in page coordinates, the
   document carries both of them at once and no scroll handler is needed to keep them together. */
.scopemenu { position: absolute; z-index: var(--z-dropdown); width: 288px; padding: var(--s2);
  border-radius: 12px; background: var(--panel2); border: 1px solid var(--line2);
  box-shadow: 0 18px 48px rgba(0,0,0,.55); font-size: 13px; color: var(--ink); }
.scopetop { display: flex; flex-direction: column; gap: 5px; }
/* The widest cut there is, so it takes the full width above the two that narrow it. */
.scopeclear { display: flex; align-items: center; gap: var(--s2); width: 100%;
  padding: 6px var(--s2); border: 0; border-radius: 7px; background: none; color: var(--ink);
  font: 600 13px -apple-system, sans-serif; text-align: left; cursor: pointer; }
.scopeclear:hover { background: rgba(127,127,127,.14); }
.scopeclear:focus, .scopeclear:focus-visible { outline: none; }
.scopeclear.on { background: rgba(124,192,255,.14); }
.scopeclear .pickitem-n { margin-left: auto; color: var(--dim); font-weight: 400;
  font-family: var(--mono); font-size: 10.5px; }
/* STACKED, not side by side. Half a panel is 130px, and an interval reading "W35–W36" beside a
   caret had nothing left for the count of posts in it — the half-width pair forced every row of
   both menus to be short enough for the narrower of the two. Full width, one over the other, they
   also read as what they are: two alternative namings of one period, not two filters that
   combine. A client with no post sets yet simply has one row here. */
.scopedrops { display: flex; flex-direction: column; gap: 5px; }

/* ── A NAMED PERIOD HAS TWO ENDS ────────────────────────────────────────────
   Intervals are a pair now, so each row is a heading and the controls that answer it. The heading
   sits above rather than beside: at 190px a label and two dropdowns on one line leave the
   dropdowns too narrow to show a fortnight's own name. */
.scopepair { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.scopepair + .scopepair { margin-top: var(--s2); }
.scopepair-h { flex: 0 0 100%; font-size: 10px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--dim); }
.scopepair .scopedrop { flex: 1; min-width: 0; }
.scopepair .scopedrop.wide { flex: 0 0 100%; }
.scopepair-a { color: var(--dim); font-size: 12px; flex: none; }
.scopedrop { width: 100%; justify-content: space-between; padding: 6px var(--s2);
  border-radius: 7px; background: var(--panel); border: 1px solid var(--line);
  font: 600 12px -apple-system, sans-serif; letter-spacing: 0; text-transform: none;
  color: var(--dim); }
.scopedrop:hover { border-color: var(--line2); color: var(--ink2); }
/* Carrying an answer rather than offering one. */
.scopedrop.on { color: var(--ink); border-color: var(--edge); background: var(--panel2); }
.scopedrop .pick-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scopedrop .pick-caret { color: var(--dim); }
.scopedropmenu { width: 244px; max-height: 300px; overflow-y: auto; }
/* A searchable one scrolls its LIST, not itself: the field has to stay put while the rows under
   it move, and two nested scrollers over one list is two scrollbars for one gesture. */
.scopedropmenu.scopedropfind { max-height: none; overflow: visible; }

/* THE TWO ENDS, AND THE CURSOR BETWEEN THEM. The live one is not focus and must not read as it:
   focus is where the keyboard is, this is which end the next click on the grid will fill, and it
   survives the pointer leaving the panel. So it is carried on the border and the ink, never on an
   outline. */
.scopefields { display: flex; align-items: center; gap: 6px;
  margin-top: 6px; padding-top: 8px; border-top: 1px solid var(--line); }
.scopefield { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 6px var(--s2); border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--ink); font: 600 12.5px -apple-system, sans-serif;
  cursor: pointer; transition: border-color .15s ease-out, color .15s ease-out; }
.scopefield svg { color: var(--dim); }
.scopefield:hover { border-color: var(--line2); }
.scopefield.empty { color: var(--dim); }
/* Two rings, not one colour. The owner reads red and green as one hue; blue is safe on that axis,
   but a state carried by hue ALONE is one encoding, so the live field doubles its border weight —
   an inset ring rather than a thicker one, which would move the label a pixel. */
.scopefield.on { border-color: var(--blue); color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--blue); }
.scopefield.on svg { color: var(--blue); }
.scopefield:focus, .scopefield:focus-visible { outline: none; }
.scopefields-a { color: var(--dim); font-size: 12px; }
@media (prefers-reduced-motion: reduce) { .scopefield { transition: none; } }

/* ── the page-head control group ───────────────────────────────────────────────
   All the scoping in one place on the right, reading left to right as the decision is actually
   made: pick the stretch of work, then see the two dates it resolved to.

   The scope picker is the shared `.pick`, which is an 11px uppercase chip — right beside a 13px
   sentence-case date button that reads as a different KIND of control, which these are not. It is
   matched to the date buttons here rather than `.pick` being changed, because `.pick` is shared
   with six other surfaces that do want the chip. */
.headctl { margin-left: auto; display: inline-flex; align-items: center; gap: var(--s2); }
.headctl .scopepick {
  background: var(--panel); border-radius: 9px; padding: 7px 11px;
  font: 600 13px -apple-system, sans-serif; letter-spacing: 0; text-transform: none;
  transition: border-color .15s ease;
}
.headctl .scopepick .pick-caret { color: var(--dim); }
.headctl .scopepick .pick-label { gap: 7px; }
.headctl .scopepick .pick-label svg { color: var(--dim); }

/* ── the pull / time-zone menu ─────────────────────────────────────────────────
   The chip states when data was pulled; pressing it opens the thing a tooltip could not be — when
   the NEXT one lands, whose clock the schedule runs on, and a search to read the whole panel in
   somebody else's. */
.datachip.live { cursor: pointer; }
.tzmenu { position: fixed; z-index: var(--z-dropdown); width: 300px; padding: var(--s2);
  border-radius: 10px; background: var(--panel2); border: 1px solid var(--line);
  box-shadow: 0 12px 32px rgba(0,0,0,.5); }
.tzrow { display: flex; align-items: baseline; gap: var(--s2); padding: 5px var(--s1); }
.tzk { font: 700 10px -apple-system, sans-serif; text-transform: uppercase; letter-spacing: .06em;
  color: var(--dim); }
.tzv { margin-left: auto; display: flex; align-items: baseline; gap: 6px;
  font-family: var(--mono); font-variant-numeric: tabular-nums; }
.tzv b { font-size: 12px; color: var(--ink); font-weight: 700; }
.tzv i { font-style: normal; font-size: 10px; color: var(--dim); }
.tznext { border-bottom: 1px solid var(--line); padding-bottom: var(--s2); margin-bottom: 2px; }
.tzproject { margin-top: var(--s1); }
.tzreset, .tzmake { border: 1px solid var(--line); background: none; border-radius: 20px;
  padding: 2px 7px; cursor: pointer; color: var(--dim);
  font: 600 9px -apple-system, sans-serif; text-transform: uppercase; letter-spacing: .05em; }
.tzreset:hover, .tzmake:hover { color: var(--ink); border-color: var(--line2); }
.tzreset:focus, .tzmake:focus, .tzreset:focus-visible, .tzmake:focus-visible { outline: none; }
.tzfind { width: 100%; box-sizing: border-box; margin: var(--s2) 0 var(--s1);
  padding: 6px var(--s2); border-radius: 7px;
  background: var(--bg); border: 1px solid var(--line); color: var(--ink);
  font: 400 12px -apple-system, sans-serif; }
.tzfind::placeholder { color: var(--dim); }
.tzfind:focus, .tzfind:focus-visible { outline: none; border-color: var(--line2); }
.tzlist { max-height: 260px; overflow-y: auto; }
.tzhead { display: flex; align-items: baseline; gap: var(--s2); padding: 6px var(--s2) 3px;
  font: 700 9px -apple-system, sans-serif; text-transform: uppercase; letter-spacing: .06em;
  color: var(--dim); }
.tzhead i { margin-left: auto; font-style: normal; font-family: var(--mono); letter-spacing: 0; }
.tzitem { display: flex; align-items: baseline; gap: var(--s2); width: 100%; cursor: pointer;
  padding: 5px var(--s2); border: 0; border-radius: 6px; background: none; text-align: left;
  font: 500 12px -apple-system, sans-serif; color: var(--ink2); }
.tzitem:hover { background: rgba(255,255,255,.05); color: var(--ink); }
.tzitem.on { color: var(--ink); font-weight: 700; }
.tzitem:focus, .tzitem:focus-visible { outline: none; }
.tztime { margin-left: auto; font-family: var(--mono); font-size: 10px; color: var(--dim);
  font-variant-numeric: tabular-nums; }
.tzmake { visibility: hidden; margin-left: var(--s2); }
.tzitem:hover .tzmake { visibility: visible; }
.tznone { padding: var(--s3) var(--s2); font-size: 12px; color: var(--dim); }
/* A scheduled run that failed or never happened. Amber is not in the legend's role list, so this
   uses the panel's warning red — the same colour a rejected edit gets — and the WORD says which of
   the two it was, because the colour cannot. */
.tzrow.tzwarn b { color: var(--red); }
/* Clearing the scope sits at the top of the same menu that sets it, above the sections rather than
   inside one — it is not an interval and it is not a set. */

/* ── signing in ───────────────────────────────────────────────────────────── */
/* One centred form on the panel's own ground. No illustration, no product pitch: the person
   reaching this page already knows what is behind it, and the only question is who they are. */
.loginpage { min-height: 100vh; display: grid; place-items: center; padding: var(--s5); }
.loginbox { width: 100%; max-width: 340px; }
.loginbox .brand { display: block; font-size: 22px; margin-bottom: var(--s5); }
.loginbox form { border: 1px solid var(--line); border-radius: 12px; background: var(--panel); padding: var(--s4) var(--s4) var(--s4); }
.loginbox label { display: block; font-size: 11px; color: var(--dim); margin-bottom: var(--s3); }
.loginbox input { display: block; width: 100%; margin-top: var(--s1); font: 13px -apple-system, sans-serif;
  color: var(--ink); background: var(--panel2); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
  transition: border-color .12s ease-out; }
.loginbox input:hover { border-color: var(--line2); }
.loginbox input:focus { border-color: var(--blue); }
.loginbox button { width: 100%; justify-content: center; margin-top: var(--s1); }
.loginbox .hint-dim { margin-top: var(--s3); line-height: 1.6; }
.loginerr { margin-top: var(--s3); font-size: 12px; color: #fecaca; background: #4a1d20;
  border-radius: 8px; padding: 7px 10px; }

/* ── read-only ────────────────────────────────────────────────────────────── */
/* A value someone may see but not change is TEXT, not a disabled input. A greyed-out field is an
   affordance that says "you could type here" and then refuses; the figure is the point, and the
   fact that it is settled is not worth a control to say. Same metrics as .numfield so the row it
   sits in does not move between an editor's view and a reader's. */
.rofield { display: inline-block; padding: 2px 6px; border: 1px solid transparent;
  font-variant-numeric: tabular-nums; text-align: right; }

/* ── admin ────────────────────────────────────────────────────────────────── */
.peoplerow td { vertical-align: middle; }
.peoplerow .who b { display: block; font-size: 13px; }
.peoplerow .who span { font-size: 11px; color: var(--dim); }
/* Edit and Remove were touching. They do opposite things and one of them cannot be undone, so the
   gap between them is not decoration.
   THE COLUMN HOLDS THE WIDER STATE. A table sizes its columns from what is in them, so the icon
   becoming a "Confirm?" pill widened this cell and shoved every column in the table sideways —
   asking a question by moving the thing you were reading. The room the answer needs is reserved
   before it is asked for. */
td.peopleacts { white-space: nowrap; min-width: 158px; }
td.peopleacts > * + * { margin-left: var(--s3); }
/* Remove: a glyph until it is pressed, then the question in its place. Same shape as the view
   menu's trash — a destructive control that asks where the finger already is, not in a window. */
.peoplex { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 26px;
  vertical-align: middle; cursor: pointer; background: none; border: 1px solid transparent;
  border-radius: 7px; color: var(--dim);
  transition: color .12s ease-out, background-color .12s ease-out, border-color .12s ease-out; }
.peoplex:hover { color: var(--red); border-color: var(--red); }
.peoplex:focus, .peoplex:focus-visible { outline: none; border-color: var(--blue); color: var(--ink); }
.peoplex.armed { width: auto; padding: 0 10px; color: var(--red); border-color: var(--red);
  background: color-mix(in oklab, var(--red) 16%, transparent); }
.peoplex.armed:hover { background: color-mix(in oklab, var(--red) 26%, transparent); }
.peoplex-w { font: 700 10px -apple-system, sans-serif; letter-spacing: .06em; text-transform: uppercase; }
.peoplex[data-tip]::after { content: none; }
/* The password, under the email it goes with. Masked, revealed by the eye, copied by a double
   click — a random string being read across two windows is where it gets mistyped. */
.pw { display: flex; align-items: center; gap: 4px; margin-top: 2px; }
.pw-v { font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .04em;
  color: var(--ink2); background: var(--panel2); border: 1px solid var(--line); border-radius: 5px;
  padding: 1px 6px; cursor: copy; user-select: all; }
.pw-v[data-tip]::after { content: none; }
.pw-none { font-size: 10.5px; color: var(--dim); font-style: italic; }
.pw-eye { display: inline-flex; align-items: center; padding: 2px; cursor: pointer; background: none;
  border: 1px solid transparent; border-radius: 5px; color: var(--dim); }
.pw-eye:hover { color: var(--ink); background: rgba(255,255,255,.06); }
.pw-eye:focus, .pw-eye:focus-visible { outline: none; border-color: var(--blue); }
.pw-eye[data-tip]::after { content: none; }
.rolechip { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 5px; border: 1px solid var(--line); background: var(--panel2); color: var(--ink2); }
.rolechip.owner { color: var(--blue); border-color: rgba(124,192,255,.28); background: rgba(124,192,255,.10); }
.rolechip.client { color: var(--amber); border-color: rgba(240,198,116,.28); background: rgba(240,198,116,.10); }
/* The four roles. Blue is the studio (primary strong, internal muted); amber is the client (lead
   strong, team muted). No green anywhere — a role is never told apart by a hue the owner cannot
   read (see the red-green memory); the word on the chip carries it, the colour only agrees. */
.rolechip.primary { color: var(--blue); border-color: rgba(124,192,255,.30); background: rgba(124,192,255,.12); }
.rolechip.internal { color: var(--ink2); border-color: rgba(124,192,255,.18); background: rgba(124,192,255,.05); }
.rolechip.client_lead { color: var(--amber); border-color: rgba(240,198,116,.34); background: rgba(240,198,116,.14); }
.rolechip.client_team { color: var(--amber); border-color: rgba(240,198,116,.18); background: rgba(240,198,116,.06); }
/* An internal viewer's brand set, ticked inline in the account form — the shared checklist in a
   bordered box that scrolls once the studio holds more brands than the form has room for. */
.userform-brands { max-height: 180px; overflow-y: auto; border: 1px solid var(--line); border-radius: 8px; padding: 4px 6px; }
/* The grants read as lines, one fact each — what they see, then what they may do — because that is
   the question being answered, and a comma-run makes it a sentence to parse. */
.grants { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--ink2); }
.grants i { font-style: normal; color: var(--dim); display: inline-block; min-width: 46px; }
.grants em { font-style: normal; color: var(--dim); }
/* THREE ACROSS, capped. A five-field form has no business spanning a 1900px window: the eye has to
   travel the width of the screen between a label and the next one, and the fields that belong
   together end up furthest apart. Three columns to a ceiling, collapsing to two and then one. */
.userform { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3) var(--s4);
  max-width: 880px; align-items: end; }
@media (max-width: 900px) { .userform { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .userform { grid-template-columns: 1fr; } }
.userform label { display: block; font-size: 11px; color: var(--dim); }
.userform label input[type=text], .userform label input[type=email], .userform label input[type=password] {
  display: block; width: 100%; margin-top: var(--s1); font: 13px -apple-system, sans-serif; color: var(--ink);
  background: var(--panel2); border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; }
.userform label input:focus { border-color: var(--blue); }
.userform .wide { grid-column: 1 / -1; }
.ticks { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin-top: var(--s2); }
.tick { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink2); cursor: pointer; }
.tick input { accent-color: var(--blue); }
.tick span em { font-style: normal; color: var(--dim); font-size: 11px; }
/* The button sits in the last column, on the same line as Role and Brand — it is the end of that
   row, not a new one. It goes full width only when the grid has stopped being a grid. */
.formacts { display: flex; align-items: center; gap: var(--s2); }
@media (max-width: 560px) { .formacts { grid-column: 1 / -1; } }
.formacts .hint-dim { margin-left: auto; text-align: right; }
/* Who you are, at the right end of the top bar, next to the freshness chip. */
.whoami { display: flex; align-items: center; gap: var(--s2); font-size: 12px; color: var(--dim); white-space: nowrap; }
.whoami b { color: var(--ink2); font-weight: 600; }

/* SPEND DELIVERY. Spend is per placement because Meta divides the budget across them; everything
   right of it is reported per platform and no deeper, so those cells span the group.

   PAIRS STACK RATHER THAN SPREAD — reach over views, engage over its rate — and the platform sits
   in a band above its rows rather than in a column beside them. Both for the same reason: eight
   columns pushed the last one off the card, and the bar is the only element here with any use for
   width.

   Every rule out-specifies `table.t`, which sets 9px padding and a border on every row of every
   table the panel owns. */
/* FIXED, so the card is the ceiling. Under `auto` the table could not shrink below its own
   content, so on a narrow window it grew past the tile and took the figures with it. Fixed layout
   hands every numeric column exactly what it needs and gives the rest to the bar, which is the one
   thing here that reads fine at any length. */
/* `min-width: 0` because the tile's body is a flexbox and a flex item will not shrink below its
   own min-content by default — which for a table full of nowrap cells is wider than the card. It
   is the missing half of `table-layout: fixed`: fixed says how to divide the width, this says the
   width is the card's. */
table.t.deliv { width: 100%; min-width: 0; border-collapse: collapse; table-layout: fixed;
  font-size: 11.5px; }
/* TOP-ALIGNED, so SPEND reads on the same line as REACH and ENGAGE — and, now that the heads ride
   the first band, on the same line as the platform they sit beside. Vertical padding comes from
   the band rule below, horizontal from the two column rules; a head sets neither itself. */
table.t.deliv th { border-bottom: 0; padding: 0; font-size: 9.5px; line-height: 14px;
  letter-spacing: .05em; text-transform: uppercase; color: var(--dim); font-weight: 700;
  white-space: nowrap; vertical-align: top; }
table.t.deliv th.r, table.t.deliv td.r { text-align: right; }
table.t.deliv td { padding: 2px 0; border-bottom: 0; vertical-align: middle; white-space: nowrap;
  line-height: 1.35; }
table.t.deliv .num { font-variant-numeric: tabular-nums; }
/* The second line of a pair: context for the figure above it, so it steps down in ink and size
   rather than sitting beside it as an equal. */
.deliv-sub { font-size: 10px; color: var(--dim); font-weight: 400; letter-spacing: 0;
  text-transform: none; margin-top: -1px; }
/* The platform, over the placements it paid for — and on its first appearance, the row that also
   carries the column heads. Longhands, so the two column rules keep the horizontal padding and
   this keeps the vertical without either having to out-specify the other. */
/* A LINE BOX SHARED WITH THE HEADS, stated as a length so it survives the three font sizes on
   this row. Left to `1.5`, the band cell's strut came off the table's 11.5px and the head's off
   its own 9.5px — two different boxes, so FACEBOOK sat two pixels below SPEND on the row they now
   share. */
table.t.deliv tr.deliv-band > * { padding-top: 11px; padding-bottom: 2px;
  font-size: 9.5px; line-height: 14px; }
/* THE NAME SITS ON THE ROWS IT HEADS. Top-aligned it floated to the top of a row made tall by the
   two-line heads opposite, a full line clear of the placements underneath — a label hanging in the
   gap rather than belonging to either side of it. Bottom-aligned, the air lands ABOVE it, which is
   where the separation between one platform's rows and the next one's belongs. The heads keep
   `top`, so SPEND, REACH and ENGAGE stay on one line across. */
table.t.deliv tr.deliv-band > td { vertical-align: bottom; }
table.t.deliv tr.deliv-band > th { vertical-align: top; }
table.t.deliv tbody tr.deliv-band:first-child > * { padding-top: 0; }
.deliv-plat { font-size: 9.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink); }
.deliv-plat-s { margin-left: 8px; font-size: 10.5px; color: var(--dim); font-variant-numeric: tabular-nums; }
/* A placement: a surface name from Meta, so it keeps the mono face the panel gives Meta's strings. */
table.t.deliv td.deliv-k { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px; color: var(--ink2); padding-right: 10px; }
/* THE COLUMN THAT ABSORBS THE SLACK, AND FILLS IT. Under `table-layout: fixed` the four columns
   beside it are pinned to stated widths, so this one takes the remainder and a full-width track
   cannot push anything off the edge — which is what capping it at 220px was guarding against,
   back when the cell claimed 99% under `auto`. Capped, the guard became the defect: a 220px bar in
   a 520px cell left 300px of nothing between the longest bar and the figure it explains. */
table.t.deliv td.deliv-bar { padding-right: 12px; }
table.t.deliv td.deliv-bar[data-tip]::after { content: none; }
.deliv-bar .anplace-t { margin: 0; }
/* ONE GUTTER, 12px, ON BOTH SIDES OF EVERY RULE AND AT THE TABLE'S OWN EDGE. Ranged right with
   none of it, each figure sat hard against the line it ranges against: spend against the first
   rule, reach against the second, engage against the edge of the card. */
table.t.deliv th.deliv-s, table.t.deliv td.deliv-s { padding-right: 12px; }
.deliv-s { color: var(--ink); }
/* The group's own figures. A rule down the left says where the per-placement half stops — carried
   by the heads and by every band as well as the figures, so it runs the table's full height
   instead of breaking into a segment per group with a gap at each heading. */
table.t.deliv td.deliv-m, table.t.deliv th.deliv-m {
  padding-left: 12px; padding-right: 12px; border-left: 1px solid var(--line); }
/* NO READING AT THIS LEVEL, which is not the same as zero. */
.deliv-none { color: var(--line2); }

/* ── PRODUCTION ───────────────────────────────────────────────────────────────
   The Content table with the figures taken out and the work put in. Everything that identifies a
   row — the pills, the thumbnail, the table's own furniture — is the Content table's, unchanged,
   so a post looks the same on both pages. Only the six columns after Content are new. */
/* THE COLUMNS DO NOT MOVE WHEN AN ACCORDION OPENS. Under `auto` a table is measured from its own
   contents, so opening a set put nine rows of tokens and pickers into the measurement and every
   column shifted — the reader's eye loses its place on the one gesture where it most needs to keep
   it. Fixed layout takes the widths from the header row alone, and nothing below can move them. */
/* EVERY WIDTH IS ITS OWN CONTENT PLUS THE CELL'S PADDING — measured, not guessed, so no column is
   holding open space that belongs to the span. They come to 998px; the floor is set past that so
   the span is never squeezed to nothing on a narrow window. The gap IS the separation between the
   two halves, and a separation that only appears on a wide screen is not one. Below the floor the
   box scrolls rather than the columns closing up. */
.prodwrap table.t { min-width: 1002px; table-layout: fixed; }
table.t.prodt th.cno { width: 84px; }   /* two pills stack in here: the cycle's, then the run's */
/* The account-wide number, under the cycle's own. The quieter of the two: you look at it when you
   need to say WHICH post, and at the one above it the rest of the time. */
.prodrun { color: var(--dim); }
/* A `#` IS SEVEN PIXELS WIDE, and a seven-pixel target is a control nobody can hit. The header pads
   out to something a cursor can find without the glyph moving off the left edge it shares with the
   pills below it, and the whole cell takes the press. */
table.t.prodt th.cno { padding: 0; }
table.t.prodt th.cno .sortbtn { padding: 6px 8px; min-width: 40px; }
table.t.prodt th.prodshot { width: 60px; }
table.t.prodt th.prodfmt { width: 108px; }
/* THE WORKING TRIO SITS TOGETHER. Each of these three was as wide as its widest POSSIBLE content —
   a filled link token, a picker at its longest label, a heading over a two-letter name — so on the
   ordinary row, where the links are empty and the name is short, they stood a hand's width apart
   and stopped reading as one group. Sized to the row you actually see now; the rare long value
   truncates with an ellipsis, which it already did past its own cap. */
table.t.prodt th.prodlk { width: 176px; }
table.t.prodt th.prodact { width: 148px; }
table.t.prodt th.prodwith { width: 80px; }
table.t.prodt th.c { width: 78px; }
/* The two columns that hold controls take what they need and no more; Content takes the rest, the
   same shrink-to-fit rule the Content table applies to its figure columns. */
/* TWO HALVES WITH A SPAN BETWEEN THEM. Every cell reads left off its own left edge — a column of
   controls ranged right would put each one at a different distance from its own heading. What
   separates the halves is the EMPTY COLUMN between Format and Production Links: it is the only one
   on the table with no width, so it collects whatever the page has left over and the working half
   starts after it. Giving the slack to Format instead made the gap belong to Format — a 92px picker
   in a 160px column, which reads as a wide column rather than as a break between two kinds of
   thing. */
table.t.prodt th, table.t.prodt td { text-align: left; }
table.t.prodt .prodlinks { justify-items: start; }
table.t.prodt th.prodgap, table.t.prodt td.prodgap { width: auto; padding: 0; }
/* THE TABLE HAS TWO HALVES AND THE SLACK GOES BETWEEN THEM. What a post IS — its number, its
   creative, its format — packs against the left edge; what is being DONE to it — the files, the
   rung, whose it is, the three marks — packs against the right. One column carries the gap, and it
   is Format's, so Format still sits against the thumbnail it describes and everything after it is
   pushed out to the far side rather than strung evenly across the width. */
/* THE CREATIVE SITS AT THE TOP OF ITS ROW, like every other cell. The row's first line is where its
   number, its format, its links and its action all read from, and the picture belongs on that line
   with them — centred down a 93px row it was the one thing floating away from the rest. */
table.t.prodt td.prodshot, table.t.prodt td.prodact,
table.t.prodt td.prodwith { white-space: nowrap; }
/* A form whose sequence is not written yet. A dash, not a disabled control: there is nothing to
   operate here, and an empty checkbox would read as work not yet done rather than not applicable. */
table.t.prodt td.prodoff { color: var(--line2); vertical-align: top; text-align: center; }

/* THE SET'S ROW. Not a post — the cycle the posts under it belong to — so it is drawn as a band
   across the table rather than as a row of cells, and it keeps its ground on hover: it is a heading
   you press, not a record you are picking out of a list. */
table.t.prodt tr.prodset > td { padding: 0; background: var(--panel2); border-bottom: 1px solid var(--line); }
table.t.prodt tr.prodset:hover > td { background: var(--panel2); }
table.t.prodt tr.prodset.open > td { border-bottom-color: var(--line2); }
.prodset-bar { display: flex; align-items: center; gap: var(--s4); padding: 9px 8px; }
/* The Hidden drawer at the foot: set apart by a heavier top rule and a muted, quieter bar; the
   cycles nested inside it are indented so they read as "in the drawer" (owner ruling 2026-09-05). */
table.t.prodt tr.prodhiddenbar > td { border-top: 2px solid var(--line2); background: var(--bg); }
table.t.prodt tr.prodhiddenbar:hover > td { background: var(--bg); }
.prodhiddenbar .hiddenlab { display: inline-flex; align-items: center; gap: 6px; color: var(--dim);
  font: 600 11px var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.prodhiddenbar .hiddenlab svg { opacity: .7; }
/* The focus mark on a set bar — the one cycle that opens on load, in the accent so it stands out. */
.prodset-focus { display: inline-flex; align-items: center; color: var(--blue); }
table.t.prodt tr.prodset.nested > td { padding-left: 26px; }
table.t.prodt tr.prodset.nested > td, table.t.prodt tr.nested-row > td { opacity: .82; }
.prodset-t { display: inline-flex; align-items: center; gap: var(--s2); background: none; border: 0;
  padding: 0; margin: 0; color: var(--ink); cursor: pointer; font: inherit; }
.prodset-t svg { color: var(--dim); flex: none; transition: color .12s ease-out; }
.prodset-t:hover svg { color: var(--ink); }
.prodset-t:focus, .prodset-t:focus-visible { outline: none; }
/* The set pill on this row is the heading, not a tag beside a number, so it sits on the text
   baseline rather than under it — the margin-top the identity column gives it is for stacking. */
.prodset-bar .setpill { margin-top: 0; color: var(--ink); border-color: var(--line2); }
/* THE BUILD FILE SITS WITH THE NAME, not out at the far edge: both are facts about this cycle, and
   an empty bar between them read as two unrelated controls. Set BIGGER than the same token in the
   table below — this is a heading you fill in once for ten posts, not a cell you scan past. */
.prodset-f { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
/* Scoped under the table, or the row rules two blocks up win on specificity and this stays the size
   of a cell — which is what it was doing: the token grew and the field it is typed into did not. */
table.t.prodt .prodset-f.big .socialchip { font-size: 12.5px; padding-right: 6px; }
table.t.prodt .prodset-f.big .socialchip a { padding: 4px 0; max-width: 300px; }
table.t.prodt .prodset-f.big .entrytag { font-size: 10px; padding: 0 8px; }
table.t.prodt .prodset-f.big .cellinput.addinput { width: 300px; font-size: 12.5px; padding: 5px 8px; }
table.t.prodt .prodset-f.big .pick.socialadd { padding: 6px 9px; font-size: 12px; }
table.t.prodt .prodset-f.big .socialadd-l { font-size: 10px; }
/* The name reads at the same weight as the file beside it — one heading, two facts. */
table.t.prodt .prodset-bar .setpill { height: 22px; padding: 0 9px; font-size: 10.5px; }
/* Adding to the cycle, at the right edge: the only control on this bar that acts on the rows below
   rather than describing the row it is on. */
/* Beside the build file, not out at the far edge. The bar reads as one group of things you do to
   this cycle — name it, point it at its file, put a post in it — rather than two ends with a gap. */
.prodaddpost { margin-left: 0; }
/* THE CYCLE'S ADD POST — a compact chip sized to the BUILD/STATUS tokens beside it on the heading
   bar, not a call-to-action button. It opens the dropdown rather than adding a blank on the spot. */
.prodaddpost { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 9px;
  font: 600 11px -apple-system, sans-serif; letter-spacing: .02em; color: var(--ink2);
  background: var(--panel2); border: 1px solid var(--line2); border-radius: 6px; cursor: pointer;
  transition: border-color .12s ease, color .12s ease; }
.prodaddpost:hover, .prodaddpost[aria-expanded="true"] { color: var(--ink); border-color: var(--edge); }
.prodaddpost svg { opacity: .75; }
/* The dropdown: the .pickmenu base gives it the panel, position and fade; this is its contents. */
.prodaddmenu { min-width: 216px; padding: 8px; }
.prodadd-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: 2px 4px 9px; }
.prodadd-l { font-size: 12.5px; font-weight: 600; color: var(--ink); }
.prodadd-spin { display: inline-flex; align-items: stretch; border: 1px solid var(--line2); border-radius: 6px; overflow: hidden; }
.prodadd-spin:focus-within { border-color: var(--edge); }
.prodadd-step { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 26px; color: var(--ink2); cursor: pointer; background: none; border: 0; }
.prodadd-step:hover { color: var(--ink); background: rgba(255,255,255,.05); }
.prodadd-n { width: 40px; height: 26px; text-align: center; border: 0; border-inline: 1px solid var(--line2); background: var(--bg); color: var(--ink); font: 600 13px var(--mono); appearance: textfield; -moz-appearance: textfield; }
.prodadd-n::-webkit-outer-spin-button, .prodadd-n::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.prodadd-foot { display: flex; justify-content: flex-end; padding-top: 8px; border-top: 1px solid var(--line); }
.prodadd-go { padding: 6px 16px; font: 600 12px -apple-system, sans-serif; color: #fff; background: var(--blue); border: 0; border-radius: 6px; cursor: pointer; transition: filter .12s ease; }
.prodadd-go:hover { filter: brightness(1.08); }
.prodadd-go:disabled { opacity: .45; cursor: default; filter: none; }
/* The dropdown grows with the cycles it lists, so it scrolls within itself past a point. */
.prodaddmenu { max-width: 300px; max-height: min(70vh, 460px); overflow-y: auto; overscroll-behavior: contain; }
.prodadd-sect { margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--line); }
.prodadd-secth { display: flex; align-items: center; justify-content: space-between; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--dim); padding: 0 4px 4px; }
.prodadd-acch { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 4px; background: none; border: 0; color: var(--ink2); cursor: pointer; text-align: left; border-radius: 6px; }
.prodadd-acch:hover { background: rgba(255,255,255,.05); color: var(--ink); }
.prodadd-acch svg { flex: none; opacity: .7; }
.prodadd-acct { flex: 1; font: 600 12px var(--mono); letter-spacing: .04em; color: var(--ink); }
.prodadd-accn { font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums; }
.prodadd-accb { display: flex; flex-direction: column; gap: 1px; padding: 1px 2px 4px 20px; }
.prodadd-pick { display: grid; grid-template-columns: 22px 1fr 30px 30px; align-items: center; gap: 8px; padding: 3px 4px; border-radius: 6px; }
.prodadd-pick:hover { background: rgba(255,255,255,.04); }
.prodadd-pickthumb { width: 22px; height: 22px; object-fit: cover; border-radius: 4px; background: var(--panel); }
.prodadd-picktag { min-width: 0; font: 600 12px var(--mono); letter-spacing: .04em; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The two link columns — a $0 cross-post (copy) and a full-price reference (link), one pick per row. */
.prodadd-lt { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 26px; border: 1px solid var(--line2); border-radius: 6px; background: none; color: var(--ink2); cursor: pointer; }
.prodadd-lt:hover { color: var(--ink); border-color: var(--edge); }
.prodadd-lt.on { color: #fff; background: var(--blue); border-color: var(--blue); }
.prodadd-leg { display: inline-flex; gap: 8px; }
.prodadd-legcell { display: inline-flex; align-items: center; justify-content: center; width: 30px; color: var(--dim); }
.prodadd-empty { font-size: 11px; color: var(--dim); padding: 3px 6px; }
.prodaddset { margin-left: auto; }
.sec-head .prodaddset + .sec-note, .sec-head .sec-note + .prodaddset { margin-left: var(--s2); }
/* ── the cycle's name, as one token ───────────────────────────────────────────
   The Production Links grammar at heading size: a strip on the left, a field to the right of it.
   The strip is a MENU here rather than a label, so it carries the picker's affordances and none of
   its chrome — no caret, and no pill sitting inside a pill. */
table.t.prodt .prodset-id { font-size: 14px; padding-right: 6px; border-color: var(--line2); }
table.t.prodt .prodset-id .entrytag { font-size: 11px; letter-spacing: .09em; padding: 0 9px; }
.pick.entrytag.prodsurf { border: 0; border-radius: 5px 0 0 5px; margin-right: 6px;
  align-self: stretch; color: var(--ink2); background: rgba(255,255,255,.06);
  text-transform: none; font-weight: 700; cursor: pointer; }
.pick.entrytag.prodsurf:hover { color: var(--ink); background: rgba(255,255,255,.12); }
.pick.entrytag.prodsurf[aria-expanded="true"] {
  background: color-mix(in oklab, var(--blue) 24%, transparent); color: var(--ink); }
table.t.prodt .cellinput.prodset-i { width: 75px; min-width: 0; border: 0; background: none;
  padding: 5px 2px; margin: 0; font-size: 14px; font-weight: 700; color: var(--ink);
  letter-spacing: .02em; }
table.t.prodt .cellinput.prodset-i:focus { border: 0; background: none; }
.prodset-ro { padding: 5px 2px; font: 700 14px var(--mono); color: var(--ink); }

/* ── what the cycle is for right now ──────────────────────────────────────────
   The same token grammar as the build link beside it: a strip naming the field, the value to the
   right of it. The strip is what makes a lone word like "Concept" legible as a status rather than
   as somebody's note.

   NO COLOUR CARRIES ANY OF THIS. The value is a word and the word is the whole encoding — a state
   told apart by hue is one this studio cannot read, and four hues would be four chances to get it
   wrong. */
table.t.prodt .prodset-st { font-size: 13px; padding-right: 4px; border-color: var(--line2); }
table.t.prodt .prodset-st .entrytag { font-size: 9px; letter-spacing: .09em; padding: 0 8px;
  align-self: stretch; }
.pick.prodstatus { border: 0; background: none; padding: 5px 6px 5px 2px;
  font: 700 13px var(--mono); color: var(--ink); text-transform: none; letter-spacing: .02em;
  cursor: pointer; }
.pick.prodstatus:hover { background: rgba(255,255,255,.06); }
.pick.prodstatus[aria-expanded="true"] {
  background: color-mix(in oklab, var(--blue) 22%, transparent); }

/* ── the build link ── the Admin page's Meta convention: a token that opens, not a box holding a
   URL. Its widths are this table's, everything else is inherited from .socialchip. */
.prodnone { color: var(--line2); }
table.t.prodt .socialchip a { max-width: 88px; overflow: hidden; text-overflow: ellipsis; }
table.t.prodt .cellinput.addinput { width: 96px; }
/* Width comes from the header row only — see the fixed-layout block above. A `width: 1%` here was
   a leftover from when the table measured itself, and being later in the file it was winning. */
/* ONE COLUMN, THREE FIXED ROWS — build, caption, archive, in that order, filled or not. The slots
   hold their lines so the eye can read across the table at one height and know which file it is
   looking at; a stack that closed up around its empty slots would put a different thing on the
   second line of every row. */
.prodlinks { display: grid; gap: 3px; justify-items: start; }
.prodlinks > .socialwrap { min-height: 20px; }
/* Which slot a `+` belongs to. Named on every empty one now that the three share a heading: the
   column says these are the production links, and the row has to say which of them this is. */
.socialadd-l { font: 700 9px var(--mono); letter-spacing: .07em; margin-right: 4px; }
.pick.socialadd { min-width: 0; }
/* ── format ── starts on what the pull says the post is, and is the operator's to change. Sized
   like the two pickers beside it, so the three read as one row of controls. */
table.t.prodt td.prodfmt, table.t.prodt th.prodfmt { white-space: nowrap; }
.pick.prodformat { text-transform: none; letter-spacing: 0; font-size: 11.5px; font-weight: 600;
  min-width: 92px; justify-content: space-between; }
.pick.prodformat[disabled] { cursor: default; opacity: 1; }
.pick.prodformat[disabled] .pick-caret { visibility: hidden; }

/* ── the ladder ── the picker, and under it where on the ladder this post stands. Segments, not a
   bar: a bar reads as a percentage of the work done and the rungs are not equal amounts of work. */
.pick.prodstage { text-transform: none; letter-spacing: 0; font-size: 11.5px; font-weight: 600;
  min-width: 132px; max-width: 132px; justify-content: space-between; }
.pick.prodstage .pick-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nothing yet. The dash is the value, so it is set in the ink an empty cell is set in everywhere
   else on this page rather than in the ink of the eight rungs it is not one of. */
.pick.prodstage.none .pick-label { color: var(--dim); }
.pick.prodstage[disabled] { cursor: default; opacity: 1; }
.pick.prodstage[disabled] .pick-caret { visibility: hidden; }
.prodrungs { display: flex; gap: 2px; margin-top: 6px; transition: opacity .1s ease-out; }
.prodrungs i { display: block; width: 12px; height: 3px; border-radius: 2px; background: var(--line); }
.prodrungs i.on { background: var(--ink2); }
/* WHILE ITS OWN PICKER IS OPEN THE STRIP GOES. The popup is placed against the trigger and drawn
   over everything under it, but not quite over this — a two-pixel crescent of the strip showed
   past the panel's rounded corner, which reads as something behind the menu leaking rather than as
   a control. Hidden rather than nudged: any gap big enough to clear the corner is a gap you can
   see when the menu is shut, and the strip says where the post is on a ladder the open menu is
   already showing in full. */
.prodact:has(> .pick[aria-expanded="true"]) .prodrungs { opacity: 0; }

/* ── who it fell to ── read, never chosen: the stage writes it. Plain text, because a control here
   would offer a choice that moving the stage has already made. */
.prodwho { display: inline-block; max-width: 64px; font-size: 11.5px; color: var(--ink2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }


/* ── the marks ── a tick that records who and when. The BOX is the state: filled and checked, or
   empty. Nothing about it is carried by colour, and the tooltip is the receipt. */
.markbox { display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; padding: 0; border: 1px solid var(--line2); border-radius: 5px;
  background: var(--panel2); color: var(--ink); cursor: pointer;
  transition: border-color .12s ease-out, background .12s ease-out; }
.markbox:hover:not([disabled]) { border-color: var(--edge); background: var(--panel); }
.markbox:focus, .markbox:focus-visible { outline: none; }
.markbox.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.markbox.on:hover:not([disabled]) { background: var(--ink2); border-color: var(--ink2); }
.markbox[disabled] { cursor: default; border-style: dashed; }
/* DRAWN, AND NOT YET ITS TURN. Not the same as a box somebody else owns: this one is coming, and
   it belongs to this rung — so it is set back rather than struck out, and it lights up on its own
   the moment the box before it is ticked. */
.markbox.waiting { border-style: dashed; border-color: var(--line); opacity: .5; }
.markbox[disabled].on { border-style: solid; }
/* CENTRED IN ITS COLUMN, not left-aligned in it. The box is 20px in a 78px track, so where it sits
   is entirely up to this rule — and a column of boxes that do not share a vertical line reads as a
   column of accidents. `text-align` alone does not move an inline-flex button reliably across the
   states this cell takes (a box, a dash), so the cell centres its own content.
   ...AND TOP-ALIGNED DOWN THE ROW, which is not the same question and had the opposite answer.
   These rows are tall — the three link slots set their height — so a box centred in its row sat
   forty pixels below the Action picker it belongs to, and read as the tick for the post underneath.
   It is the only thing on the row that is not on the row's first line. The 3px lines its middle up
   with the picker's, which is what "same line" has to mean when the two are different heights. */
table.t.prodt td.prodmark { vertical-align: top; text-align: center; }
table.t.prodt td.prodmark .markbox { margin-top: 3px; }
table.t.prodt td.prodmark .prodnone, table.t.prodt td.prodoff { line-height: 26px; }
table.t.prodt td.prodmark, table.t.prodt th.c { padding-left: 0; padding-right: 0; }
.markbox { margin: 0 auto; }

/* The account whose tick moves a post past a gate on the Production board. Beside the role, because
   it is a kind of role: the same chip, in the ink the panel uses for something that is true rather
   than something that is a state. */
.rolechip.primary { background: rgba(124,192,255,.12); color: var(--blue); border-color: rgba(124,192,255,.28); }
/* The signature the board waits on. The client's own ink — it is the one thing on that board that
   belongs to their side of the line, and the CLIENT chip beside it is already set in it. */
.rolechip.signer { background: rgba(240,198,116,.12); color: var(--amber); border-color: rgba(240,198,116,.28); }

/* ── a post that has not been made yet ────────────────────────────────────────
   Same row, same controls, no creative and no number: the numbering is publication order and this
   has not published. It is numbered within its own set instead, which is enough to point at it. */
.idpill.planned { color: var(--dim); border-style: dashed; }
tr.prodrow.planned .postshot.empty { border-style: dashed; background: none; cursor: default; }
/* An ALIASED instance: the creative it reuses, marked so it never reads as the original itself —
   a soft inset edge on the picture and a blue back-reference beside the identity. */
.postshot.aliased { box-shadow: inset 0 0 0 1.5px color-mix(in oklab, var(--blue) 50%, transparent); }
/* THE PICTURE'S STATUS, on its own line under it: CREATIVE while the picture is the build-file
   frame, POST once the post is live and pulled. The WORD carries the meaning (it survives not
   separating one colour from another — the owner's red/green note); weight and fill only second it.
   Sized to the 44px shot above it so the two read as one stack. */
.prodstate { display: block; width: 44px; margin-top: 4px; text-align: center; border-radius: 4px;
  font: 700 8px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .08em;
  text-transform: uppercase; border: 1px solid var(--line); color: var(--dim); background: var(--panel2); }
.prodstate.post { color: var(--ink); border-color: var(--line2); background: rgba(255,255,255,.07); }
.setpill.aliasref { color: var(--blue); border-color: color-mix(in oklab, var(--blue) 40%, var(--line2)); letter-spacing: .03em; }
/* Clickable link tags in the sequence column (PRODUCTION.md §7a): a derived post's `original`, an
   original's tag to each post linked off it, and a releasable count of pinned-back iterations. */
.setpill.origref, .setpill.linkref, .setpill.pinref { cursor: pointer; }
.setpill.origref, .setpill.linkref { color: var(--blue); border-color: color-mix(in oklab, var(--blue) 40%, var(--line2)); }
.setpill.origref:hover, .setpill.linkref:hover { color: var(--ink); border-color: var(--blue); background: color-mix(in oklab, var(--blue) 16%, var(--panel2)); }
.setpill.pinref:hover { color: var(--ink); border-color: var(--edge); background: color-mix(in oklab, var(--ink) 8%, var(--panel2)); }
.setpill.origref svg, .setpill.linkref svg, .setpill.pinref svg { opacity: .75; }

/* The Brand Details schema editor (owner) — a modal of field rows: label, unit, type, and for a
   computed field the source field(s) it reads. */
dialog.deteditor { width: min(780px, 92vw); max-height: 86vh; overflow: auto; border: 1px solid var(--line2);
  border-radius: 14px; background: var(--panel); color: var(--ink); padding: 20px; }
dialog.deteditor::backdrop { background: rgba(0, 0, 0, .5); }
.deteditor-h { margin: 0 0 4px; font-size: 16px; }
.deteditor-list { margin-top: 8px; }
.deteditor-field { border: 1px solid var(--line); border-radius: 8px; padding: 8px; margin-top: 8px; }
.deteditor-field.core { border-color: var(--line2); background: rgba(255, 255, 255, .02); }
.deteditor-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.deteditor-in { height: 28px; padding: 0 8px; border: 1px solid var(--line2); border-radius: 6px;
  background: var(--bg); color: var(--ink); font: inherit; }
.deteditor-in:not(.narrow):not(.tiny):not(.wide) { flex: 1; min-width: 120px; }
.deteditor-in.narrow { width: 116px; } .deteditor-in.tiny { width: 60px; } .deteditor-in.wide { width: 100%; margin-top: 6px; }
.deteditor-op { color: var(--dim); padding: 0 2px; }
.deteditor-rm { margin-left: auto; width: 26px; height: 26px; border: 1px solid var(--line2); border-radius: 6px;
  background: none; color: var(--ink2); cursor: pointer; }
.deteditor-rm:disabled { opacity: .4; cursor: default; }
.deteditor-foot { display: flex; gap: 8px; align-items: center; margin-top: 14px; }
.deteditor-spacer { flex: 1; }

/* Section access dropdown (owner) — who sees Prompts / the Performance Calendar: Public, else a set
   of brand codes (hidden while Public is on). */
.accessbtn { display: inline-flex; align-items: center; gap: 4px; }
.accessmenu { position: fixed; z-index: var(--z-dropdown); min-width: 200px; max-height: 60vh; overflow-y: auto;
  padding: 6px; border-radius: 12px; background: var(--panel); border: 1px solid var(--line2);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45); }
.accessmenu-row { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 7px;
  cursor: pointer; font-size: 12.5px; color: var(--ink); }
.accessmenu-row:hover { background: rgba(255, 255, 255, .05); }
.accessmenu-row input { accent-color: var(--blue); }
.accessmenu-row input:disabled { cursor: default; }
.accessmenu-brands { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line); }
/* "cites this" beside a brand that is granted automatically — the checkbox is fixed on. */
.accessmenu-auto { margin-left: auto; font-size: 10px; color: var(--ink2); text-transform: uppercase; letter-spacing: .03em; }
/* Archived brands: only active clients sit in the list directly; the rest fold under this accordion
   and read greyed, so a grant to an archived brand persists and stays visible without cluttering. */
.accessmenu-arch { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line); }
.accessmenu-arch > summary { list-style: none; cursor: pointer; padding: 5px 8px; border-radius: 7px;
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink2); }
.accessmenu-arch > summary::-webkit-details-marker { display: none; }
.accessmenu-arch > summary:hover { background: rgba(255, 255, 255, .05); }
.accessmenu-row.archived span:not(.accessmenu-auto) { color: var(--ink2); }
/* SOURCE access cell — an "Add" dropdown over a stack of brand pills. A pill added from the dropdown
   drops out of it; its × revokes the grant. A "cites" pill is automatic and carries no × (owner
   ruling 2026-09-05). */
.srcacccell { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.srcacc-add { display: inline-flex; align-items: center; gap: 3px; padding: 3px 8px; border-radius: 7px;
  border: 1px solid var(--line2); background: var(--panel2, #1a1f27); color: var(--ink); font-size: 11.5px; cursor: pointer; }
.srcacc-add:hover { border-color: var(--blue); }
.srcacc-pills { display: flex; flex-wrap: wrap; gap: 4px; }
.srcacc-pill { display: inline-flex; align-items: center; gap: 4px; padding: 2px 4px 2px 7px; border-radius: 6px;
  background: rgba(124, 192, 255, .12); color: var(--ink); font-size: 11px; font-weight: 600; }
.srcacc-pill.cited { background: #232a34; color: var(--ink2); padding-right: 7px; }
.srcacc-cites { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--ink2); }
.srcacc-x { display: inline-grid; place-items: center; width: 15px; height: 15px; padding: 0; border: 0; border-radius: 4px;
  background: none; color: var(--ink2); cursor: pointer; }
.srcacc-x:hover { background: rgba(255, 255, 255, .1); color: var(--ink); }
.srcacc-none { font-size: 11px; color: var(--ink2); }

/* Brand accordions on the owner's Data page — one per client for Brand Details (and Content Ops):
   a code header that opens the brand's own contents. */
.brandacc { border: 1px solid var(--line); border-radius: 8px; margin-top: 8px; overflow: hidden; }
.brandacc.open { border-color: var(--line2); }
.brandacc-h { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; background: none;
  border: 0; color: var(--ink); cursor: pointer; text-align: left; font: inherit; }
.brandacc-h:hover { background: rgba(255, 255, 255, .03); }
.brandacc-name { color: var(--dim); font-size: 12.5px; }
.brandacc-b { padding: 0 10px 10px; }
.brandacc-add { margin-top: 10px; }
/* Clickable sequence-column link tags (PRODUCTION.md §7a): a derived post's `original`, an original's
   tag back to each linked post, and a count of pinned-back iterations to release. */
.setpill.origref, .setpill.linkref, .setpill.pinref { appearance: none; cursor: pointer; }
.setpill.origref, .setpill.linkref { color: var(--blue); border-color: color-mix(in oklab, var(--blue) 40%, var(--line2)); }
.setpill.origref:hover, .setpill.linkref:hover { color: var(--ink); border-color: var(--blue); background: color-mix(in oklab, var(--blue) 16%, var(--panel2)); }
.setpill.pinref { color: var(--ink2); }
.setpill.pinref:hover { color: var(--ink); border-color: var(--edge); background: color-mix(in oklab, #fff 6%, var(--panel2)); }
.setpill.origref svg, .setpill.linkref svg, .setpill.pinref svg { width: 10px; height: 10px; opacity: .8; }
.socialx.prodrm { margin-top: 3px; }

/* ── selected, and hidden ─────────────────────────────────────────────────────
   The same treatment the Content table gives a selected row, because it is the same gesture. */
table.t.prodt tbody tr.prodrow.sel > td { background: rgba(124,192,255,.09); }
table.t.prodt tbody tr.prodrow.sel:hover > td { background: rgba(124,192,255,.13); }
/* OUT OF THE WAY, AND SAYING SO. Only ever on screen while "show hidden" is on, where the point is
   to see WHICH rows those are — so it is set back rather than removed. */
table.t.prodt tbody tr.hid > td { opacity: .45; }
table.t.prodt tbody tr.hid .idpill, table.t.prodt tbody tr.hid .setpill { border-style: dashed; }
.prodhidden.on { color: var(--ink); border-color: var(--line2); }

/* ── the right-click menu ─────────────────────────────────────────────────────
   WrkBook's own spec, ported (`.wb-menu` in its editor, WrkMenu.swift in its shell): 26px rows on a
   6px inset, sections headed in mono caps with a hairline between them, and a 14px icon slot that
   is KEPT whether or not a row fills it — so the words in one section line up with each other
   rather than with nothing. Drawn, never asked for: a browser's own menu cannot be styled. */
.wbmenu { position: fixed; z-index: var(--z-dropdown); min-width: 210px; max-height: 70vh;
  overflow-y: auto; padding: 6px; border-radius: 14px;
  background: var(--panel); border: 1px solid var(--line2);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45); font-size: 13px; color: var(--ink); }
.wbmenu-g + .wbmenu-g { margin-top: 5px; padding-top: 3px; border-top: 1px solid var(--line); }
.wbmenu-l { font: 500 10px var(--mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--dim); padding: 2px 6px 3px; }
.wbmenu-i { display: flex; align-items: center; gap: 7px; width: 100%; height: 26px;
  padding: 0 6px; border: 0; border-radius: 7px; background: none; color: inherit;
  font: inherit; text-align: left; cursor: pointer; }
.wbmenu-i:hover:not(:disabled) { background: var(--blue); color: var(--bg); }
.wbmenu-i:hover:not(:disabled) .wbmenu-ic { opacity: 1; }
.wbmenu-i:hover:not(:disabled) i { color: var(--bg); opacity: .7; }
.wbmenu-i:focus, .wbmenu-i:focus-visible { outline: none; }
.wbmenu-i:disabled { opacity: .38; cursor: default; }
/* ARMED, AND SAYING SO IN WORDS. The row has already changed its label to "Confirm?" — the fill is
   the second signal, never the only one, so the state survives not separating red from green. */
.wbmenu-i.danger, .wbmenu-i.danger:hover:not(:disabled) { background: var(--red); color: var(--bg); }
.wbmenu-i.danger .wbmenu-ic { opacity: 1; }
.wbmenu-i.danger .wbmenu-t { font-weight: 700; }
/* The slot is kept empty rather than collapsed, which is the whole reason the labels line up. */
.wbmenu-ic { display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; flex: none; opacity: .75; }
.wbmenu-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wbmenu-i i { font-style: normal; font-size: 11px; color: var(--dim); flex: none; }
/* The set picker off "Move to set…": a pinned search field over a scrolling list, so the input
   stays put however many cycles a client has. The shell is a .wbmenu, so it dismisses like one. */
.wbmenu.setpick { overflow: visible; }
.setpick-q { margin: 0 0 4px; }
.setpick-l { max-height: 320px; overflow-y: auto; }
.setpick-none { font-size: 11px; color: var(--dim); padding: 4px 6px; }
/* The Cross-Post / Reference destination picker: sets as accordions, posts inside, multi-select + Apply. */
.setpick.linkpick { min-width: 252px; }
.setpick-foot { display: flex; justify-content: flex-end; padding-top: 6px; margin-top: 4px; border-top: 1px solid var(--line); }
.setpick-apply { padding: 6px 16px; font: 600 12px -apple-system, sans-serif; color: #fff; background: var(--blue); border: 0; border-radius: 6px; cursor: pointer; }
.setpick-apply:hover { filter: brightness(1.08); }
.setpick-apply:disabled { opacity: .45; cursor: default; filter: none; }
.setpick-acch { display: flex; align-items: center; gap: 6px; padding: 5px 4px; border-radius: 6px; cursor: pointer; color: var(--ink); }
.setpick-acch:hover { background: rgba(255,255,255,.05); }
.setpick-acch svg { flex: none; opacity: .7; }
.setpick-acct { flex: 1; font: 600 12px var(--mono); letter-spacing: .04em; }
.setpick-accn { font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums; }
.setpick-accb { display: flex; flex-direction: column; gap: 1px; padding: 1px 2px 4px 24px; }
.setpick-accb[hidden] { display: none; }
.setpick-post { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border-radius: 6px; cursor: pointer; }
.setpick-post:hover { background: rgba(255,255,255,.05); }
.setpick-postt { font: 600 12px var(--mono); letter-spacing: .04em; color: var(--ink); }
/* The IG (feed) tag, second and dimmer — the set tag is the one you point at, the IG tag confirms it. */
.setpick-postig { font: 600 11px var(--mono); letter-spacing: .04em; color: var(--ink3, var(--ink2)); margin-left: auto; }
.setpick-cb { width: 14px; height: 14px; accent-color: var(--blue); flex: none; cursor: pointer; margin: 0; }

/* ── the set's surface ────────────────────────────────────────────────────────
   Beside the file the cycle is cut from, because both are facts about the cycle. Sized with the
   bar's other controls, not with the table's. */
.pick.prodsurface { text-transform: none; letter-spacing: 0; font-size: 12px; font-weight: 600;
  padding: 5px 9px; }
.prodsurface-ro { font-size: 12px; color: var(--ink2); }

/* ── dragging a post into another cycle ───────────────────────────────────────
   The row being carried goes quiet; where it would land is a hard line. A line rather than a filled
   band because the drop is BETWEEN two rows, and a filled row says "into this one". */
table.t.prodt tbody tr.dragging > td { opacity: .4; }
table.t.prodt tbody tr.prodrow.dropat > td { box-shadow: inset 0 2px 0 0 var(--blue); }
table.t.prodt tbody tr.prodset.dropinto > td { box-shadow: inset 0 0 0 1px var(--blue); }


/* ── the cycle's own controls ─────────────────────────────────────────────────
   The row's controls one track up, acting on everything under them. They sit in the set row's own
   ground so they read as part of the heading rather than as an eleventh post, and they line up with
   the columns they act on because they are in the same tracks. */
table.t.prodt tr.prodset > td.prodset-x { padding: 9px 8px; background: var(--panel2); }
table.t.prodt tr.prodset:hover > td.prodset-x { background: var(--panel2); }
table.t.prodt tr.prodset > td.c.prodset-x { text-align: center; }
.pick.prodstage-all { min-width: 132px; font-size: 11px; }
/* PART OF THE SET, NOT ALL OF IT. A minus rather than a tick, because the question the column asks
   is "are these done" and the honest answer to some-of-them is neither yes nor no. */
.markbox.some { background: var(--ink2); border-color: var(--ink2); color: var(--bg); }
/* Nothing ticked yet: the glyph is there so the control has a shape to press, and set back so an
   empty batch box is never mistaken for a ticked one at a glance. */
.markbox.ghost { color: var(--line2); }
.markbox.ghost:hover:not([disabled]) { color: var(--ink2); }

/* ── the row's timeline ───────────────────────────────────────────────────────
   A Gantt line across a calendar: where every checkpoint landed, how long each rung took, and how
   long the open one has been open. It carries no tooltips, by the same ruling the rest of this page
   follows, so every fact is written on the panel — the bars name their rung, the marks carry a
   letter, and the ledger under the chart states each one in full. */
.prodtl-b { margin-top: 7px; }
table.t.prodt tbody tr.tlopen > td { border-bottom-color: transparent; }
table.t.prodt tbody tr.prodtl + tr > td { border-top: 1px solid var(--line); }
/* PART OF THE ROW, NOT A CARD INSIDE IT. No border, no ground of its own, nothing enclosing it —
   the row opening IS the container, and a bordered panel within a bordered row was two boxes for
   one thing. The empty identity cell is the whole of the indent: it starts under the creative, in
   line with the post it belongs to, and runs to the table's right edge like every other row. */
table.t.prodt tbody tr.prodtl > td { padding: 0; background: none; border-bottom: 0; }
table.t.prodt tbody tr.prodtl:hover > td { background: none; }
table.t.prodt tbody tr.prodtl > td.prodtl-c { padding: 0 16px 16px 0; }
.prodtl-in { padding: 6px 0 0; }
.prodtl-none { font-size: 12px; color: var(--dim); padding: 10px 0 4px; max-width: 68ch; line-height: 1.5; }

/* The calendar. Ticks hang from the labels so the label reads as belonging to the line under it. */
.prodtl-axis { position: relative; height: 22px; margin-bottom: 2px; }
.prodtl-day { position: absolute; top: 0; transform: translateX(-50%); display: flex;
  flex-direction: column; align-items: center; gap: 2px; white-space: nowrap; }
/* The two ends keep their tick on the mark and their words inside the panel. */
.prodtl-day.first { transform: none; align-items: flex-start; }
.prodtl-day.last { transform: translateX(-100%); align-items: flex-end; }
.prodtl-day span { font: 500 9.5px var(--mono); color: var(--dim); letter-spacing: .03em; }
.prodtl-day i { display: block; width: 1px; height: 5px; background: var(--line2); }
/* The date, once the axis is counting hours and has stopped naming one. */
.prodtl-date { position: absolute; right: 0; top: 1px; font: 500 9.5px var(--mono);
  color: var(--line2); letter-spacing: .03em; }

/* The line itself. Bars abut, so a rung's start IS the previous one finishing and the row reads as
   one continuous run rather than as separate events that happen to be near each other. */
.prodtl-track { position: relative; height: 30px; border-radius: 6px;
  background: linear-gradient(var(--panel), var(--panel)) center/100% 2px no-repeat; }
.prodtl-bar { position: absolute; top: 4px; height: 22px; border-radius: 5px; overflow: hidden;
  display: flex; align-items: center; padding: 0 7px; box-sizing: border-box; z-index: 1;
  background: var(--panel); border: 1px solid var(--line2);
  transition: border-color .1s ease-out, background-color .1s ease-out; }
/* A GATE READS AS A GATE without leaning on hue: the two rungs the client signs are hatched, which
   survives not separating red from green and survives a greyscale print. */
.prodtl-bar.gate { background: var(--panel) repeating-linear-gradient(45deg,
  rgba(255,255,255,.05) 0 4px, transparent 4px 8px); }
.prodtl-bar.done { border-color: var(--edge); }
/* Still running. Open at the right edge, because it has not ended — a bar with a right edge is a
   bar claiming a finish time it does not have. */
.prodtl-bar.open { border-right-style: dashed; border-color: var(--blue); }
/* The name, under the bar the cursor is on. Its own row so the chart's height never changes as it
   comes and goes — a label that pushed the ledger down every time you crossed a bar would make the
   whole panel twitch. Centred on the bar and held off both edges, so it is never clipped. */
.prodtl-caprow { position: relative; height: 18px; margin-top: 3px; }
.prodtl-cap { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap;
  font: 600 10.5px -apple-system, sans-serif; color: var(--ink); letter-spacing: .01em; }
.prodtl-cap::before { content: ''; position: absolute; left: 50%; top: -4px; width: 1px; height: 3px;
  background: var(--blue); }
/* A checkpoint. The LETTER is the fact — C completed, A approved, P primed, L live — so the mark
   is legible at 14px without a legend and without a colour doing the work. */
/* THE MARKS SIT ON TOP. A checkpoint lands where two rungs meet, so it is always over a bar's edge
   — and painted in DOM order the next bar covered the mark that belongs to the one before it. The
   mark is the fact; the bar is the duration it sits inside. */
.prodtl-mark { position: absolute; top: 7px; transform: translateX(-50%); z-index: 2;
  width: 16px; height: 16px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center;
  font: 700 9px var(--mono); background: var(--ink); color: var(--bg); border: 1px solid var(--bg);
  transition: transform .1s ease-out, box-shadow .1s ease-out; }
.prodtl-mark.client { background: var(--amber); }
.prodtl-now { position: absolute; top: -4px; bottom: -4px; width: 1px; background: var(--blue); z-index: 3; }
/* WHAT THE LINE UNDER THE CHART IS POINTING AT. Read a row, see its bar. The mark grows rather than
   changes colour, because it is already carrying a letter and a second meaning in its fill would be
   one too many — and growth reads at 16px where a hue shift does not. */
.prodtl-bar.lit { border-color: var(--blue); background: color-mix(in oklab, var(--blue) 14%, var(--panel)); }
.prodtl-mark.lit { transform: translateX(-50%) scale(1.35); box-shadow: 0 0 0 2px var(--blue); }
.prodtl-row { border-radius: 5px; padding: 2px 6px; margin: 0 -6px; width: fit-content;
  transition: background-color .1s ease-out; }
.prodtl-row:hover { background: rgba(124,192,255,.09); }
.prodtl-row:hover .prodtl-what, .prodtl-row:hover b { color: var(--ink); }

/* The ledger. One line per checkpoint, in the order they happened — what, on which rung, by whom,
   when, and how long since the one before it, which is the figure a timeline is actually read for
   and the one thing no eye measures off a bar. */
.prodtl-led { display: grid; gap: 2px; margin-top: 14px; }
/* THE LEDGER PACKS LEFT. Stretched across the panel's full width, "Content Map · Completed" sat at
   one end and "Aug 23" at the other with a hand's width of nothing between them, and reading a line
   meant tracking across the gap and hoping you were still on the same row. Columns are sized to
   what goes in them instead, so each line reads as one sentence. The chart above is the thing that
   wants the whole width; a list is not. */
.prodtl-row { display: grid; grid-template-columns: 18px 208px 66px 132px 52px;
  gap: var(--s4); justify-content: start; align-items: baseline;
  font-size: 11.5px; color: var(--ink2); cursor: default; }
.prodtl-row b { font: 600 11px var(--mono); color: var(--ink2); font-variant-numeric: tabular-nums; }
.prodtl-k { display: inline-flex; align-items: center; justify-content: center; width: 16px;
  height: 16px; border-radius: 4px; background: var(--panel); border: 1px solid var(--line2);
  font: 700 9px var(--mono); color: var(--ink); }
.prodtl-what { text-transform: capitalize; }
.prodtl-who, .prodtl-gap { font-size: 11px; color: var(--dim); }
.prodtl-gap { text-align: right; }
/* An approval in the ledger wears the same amber its mark does on the chart, so the two halves
   agree about which of the two boxes on a gated rung a line is talking about. */
.prodtl-k.client { border-color: rgba(240,198,116,.45); color: var(--amber); }
.prodtl-row.open .prodtl-k { border-style: dashed; color: var(--dim); }
.prodtl-row.open b { color: var(--blue); }

/* ── the Action Builder ───────────────────────────────────────────────────────
   A sheet that holds a form, so it does NOT dismiss on a click outside — every other overlay on
   this panel does, and every one of those is a thing you can reopen for free. This one can lose a
   ladder somebody has just written, so it closes by one button and that button asks first. */
.abopen { text-transform: none; letter-spacing: 0; font-size: 11.5px; font-weight: 600;
  margin-left: var(--s2); padding: 4px 11px; }
.abback { position: fixed; inset: 0; z-index: var(--z-modal-back); display: flex;
  align-items: center; justify-content: center; padding: 32px;
  background: rgba(0, 0, 0, .58); backdrop-filter: blur(2px); }
.abmodal { position: relative; z-index: var(--z-modal); display: flex; flex-direction: column;
  width: min(1040px, 100%); max-height: 100%; background: var(--panel);
  border: 1px solid var(--line2); border-radius: 14px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, .6); overflow: hidden; }
.abhead { display: flex; align-items: center; gap: var(--s3); padding: 14px 16px;
  border-bottom: 1px solid var(--line); }
.abhead h2 { font-size: 15px; font-weight: 700; margin: 0; }
/* EVERY CHANGE IS WRITTEN, so this reports rather than asks. It is the only thing left of a Save
   button: the word that says the store already agrees with the screen. */
.abstate { font-size: 11px; color: var(--dim); }
.abstate.on { color: var(--green); }
.abx { margin-left: auto; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: 1px solid var(--line2); border-radius: 50%;
  background: var(--panel2); color: var(--dim); cursor: pointer; }
.abx:hover { color: var(--ink); border-color: var(--edge); }
.abx:focus, .abx:focus-visible { outline: none; }

.abbody { display: grid; grid-template-columns: 216px minmax(0, 1fr); gap: 0; min-height: 0; flex: 1;
  overflow: hidden; }
/* The column itself no longer scrolls: New format and the two format operations are fixtures, and
   scrolling them away to reach a format is how a reader loses the button they were going to press.
   The list of formats scrolls inside it instead. */
.abside { display: flex; flex-direction: column; gap: var(--s2); padding: 14px;
  border-right: 1px solid var(--line); overflow: hidden; min-height: 0; }
.ablabel { font: 500 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--dim); }
.ablist { display: flex; flex-direction: column; gap: 2px; }
.abitem { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; width: 100%;
  padding: 7px 9px; border: 1px solid transparent; border-radius: 8px; background: none;
  color: var(--ink2); cursor: pointer; font: inherit; text-align: left; }
.abitem:hover { background: var(--panel2); }
.abitem.on { background: var(--panel2); border-color: var(--line2); color: var(--ink); }
.abitem b { font-size: 12.5px; font-weight: 600; }
.abitem i { font-style: normal; font-size: 10.5px; color: var(--dim); }
.abitem:focus, .abitem:focus-visible { outline: none; }
.abadd { display: inline-flex; align-items: center; gap: 5px; padding: 6px 9px;
  border: 1px dashed var(--line2); border-radius: 8px; background: none; color: var(--dim);
  cursor: pointer; font: 600 11px -apple-system, sans-serif; }
.abadd:hover { color: var(--ink); border-style: solid; }
.abadd:focus, .abadd:focus-visible { outline: none; }
/* Under the last rung, on the left: the ladder is written downwards, so the way to write the next
   one is where the next one will be. */
.abaddact { align-self: flex-start; }
/* WHAT IS DONE TO A FORMAT LIVES WHERE THE FORMATS ARE. Under the ladder these read as operations
   on the actions above them, which is the one thing they are not. */
.absideacts { display: flex; flex-direction: column; gap: 4px; margin-top: auto;
  padding-top: var(--s3); border-top: 1px solid var(--line); }
.absideact { display: inline-flex; align-items: center; gap: 6px; padding: 6px 9px;
  border: 1px solid var(--line); border-radius: 8px; background: none; color: var(--dim);
  cursor: pointer; font: 600 11px -apple-system, sans-serif; }
.absideact:hover { color: var(--ink); border-color: var(--line2); }
.absideact:focus, .absideact:focus-visible { outline: none; }
.absideact.danger, .absideact.danger:hover { background: var(--red); border-color: var(--red); color: var(--bg); }

.abpane { padding: 14px 16px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: var(--s3); }
.abgrid { display: grid; grid-template-columns: auto minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--s3); align-items: end; }
/* Two letters, so it takes two letters' worth of room. Centred because a code is a label rather
   than a value being read down a column. */
.pick.abpick.abplat { min-width: 74px; }
.pick.abpick.abplat .pick-label { font-family: var(--mono); letter-spacing: .04em; }
/* Size and frames take a row of their own: a size is two fields and a menu, and it was being
   squeezed into a third of a row beside two dropdowns. */
.abgrid-2 { grid-template-columns: auto auto 1fr; }
.abfield { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.abfield > span { font: 500 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--dim); }

/* A FIELD THAT LOOKS LIKE ONE. `.cellinput` is a table affordance — near-invisible until hovered,
   which is right in a dense row and wrong in a form, where a reader has to see where to type
   before deciding to. Full border, own ground, and a blue edge when it has focus. */
.abinput { background: var(--panel2); color: var(--ink); border: 1px solid var(--line2);
  border-radius: 7px; padding: 7px 9px; font: 12.5px -apple-system, "Segoe UI", Roboto, sans-serif;
  transition: border-color .12s ease-out, background .12s ease-out; }
.abinput::placeholder { color: var(--dim); }
.abinput:hover { border-color: var(--edge); }
.abinput:focus, .abinput:focus-visible { outline: none; border-color: var(--blue); background: var(--panel); }
/* Typed as text with a numeric keypad, so no browser spinner exists to suppress and no arrows
   crowd a three-digit field. */
.abnum { width: 74px; text-align: right; font-variant-numeric: tabular-nums; }
.abframes { width: 68px; text-align: right; font-variant-numeric: tabular-nums; }
.absize { display: flex; align-items: center; gap: 7px; }
.absize-x { color: var(--dim); font-size: 12px; }
.absizefield { min-width: 0; }
/* The sizes a studio actually reaches for, under the two fields that also take anything typed. */
.absizemenu { min-width: 190px; }
.absizemenu .pickitem { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); }
.absizemenu .pickitem i { font-style: normal; font-size: 10.5px; color: var(--dim); }
.absizemenu .pickitem.on { color: var(--ink); }

.pick.abpick { text-transform: none; letter-spacing: 0; font-size: 12.5px; font-weight: 600;
  justify-content: space-between; padding: 7px 9px; }
.pick.abpick.abcol { min-width: 138px; }

.abacts { display: flex; flex-direction: column; gap: 6px; }

/* THE EDGE OF A LIST THAT CONTINUES. The wrapper draws the shading, not the scroller, because the
   rows inside carry their own ground and would paint over anything drawn beneath them. Both lips
   are inert to the pointer and their opacity is set from the scroll position, so at rest there is
   nothing to see and no claim being made. A darkening rather than a colour: the panel reads as an
   instrument, and content passing under a lip is the instrument's way of saying it continues. */
/* A BLOCK, NOT A FLEX BOX. As a flex child of the column above it with nothing to size against,
   the wrapper takes min-height: 0 literally and collapses; as a block it is exactly as tall as the
   list inside it, and the list's own cap is the only thing that limits it. */
.abscrollbox { position: relative; min-height: 0; }
.abscroll { overflow-y: auto; overscroll-behavior: contain; }
/* 8px of thumb plus 4px of air. Set on the scroller so the rows shrink to clear it, not on the
   wrapper, where it would only move the whole list and leave the thumb over the rows regardless. */
.abscroll.scrolls { padding-right: 12px; }
/* The lips stop where the rows stop, so the thumb is never dimmed by the thing telling you to
   scroll it. */
.abscrollbox:has(.scrolls)::before, .abscrollbox:has(.scrolls)::after { right: 12px; }
.abscrollbox::before, .abscrollbox::after {
  content: ''; position: absolute; left: 0; right: 0; height: 22px; pointer-events: none;
  z-index: 1; transition: opacity .12s ease-out; }
.abscrollbox::before { top: 0; opacity: var(--up, 0);
  background: linear-gradient(var(--panel), rgba(15, 17, 21, .55) 45%, rgba(15, 17, 21, 0)); }
.abscrollbox::after { bottom: 0; opacity: var(--down, 0);
  background: linear-gradient(rgba(15, 17, 21, 0), rgba(15, 17, 21, .55) 55%, var(--panel)); }
/* One action, one line: where it sits, what it is called, what finishes it, whether the client has
   to sign, and the way to take it off. Read left to right that is the sentence the rung makes. */
.abact { display: flex; align-items: center; gap: var(--s2); padding: 6px 8px;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel2); }
.abact.abnew { border-color: var(--blue); }
.abmove { display: inline-flex; flex-direction: column; gap: 1px; width: 20px; flex: none; }
.abmini { display: inline-flex; align-items: center; justify-content: center; padding: 1px;
  border: 0; border-radius: 4px; background: none; color: var(--dim); cursor: pointer; }
.abmini:hover:not(:disabled) { color: var(--ink); background: rgba(255,255,255,.06); }
.abmini:disabled { opacity: .3; cursor: default; }
.abmini:focus, .abmini:focus-visible { outline: none; }
.abmini.abdel:hover { color: var(--red); background: color-mix(in oklab, var(--red) 18%, transparent); }
/* A ROW READS AS ONE CONTROL. In the format form a field is alone and has to announce itself; in
   the ladder it sits beside a picker and a toggle, six rows deep, and the brighter border turns the
   list into a grid of boxes. Same ground and same line weight as the picker next to it, so the row
   reads across instead of down. */
.abinput.abactname { flex: 1; min-width: 0; border-color: var(--line); }
.abinput.abactname:hover { border-color: var(--line2); }
.abtoggle { display: inline-flex; align-items: center; gap: 5px; flex: none; padding: 5px 10px;
  border: 1px solid var(--line2); border-radius: 20px; background: var(--panel);
  color: var(--dim); cursor: pointer; font: 600 11px -apple-system, sans-serif; }
.abtoggle:hover { color: var(--ink); }
.abtoggle.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.abtoggle:focus, .abtoggle:focus-visible { outline: none; }
.abempty { padding: 12px 14px; border: 1px dashed var(--line); border-radius: 9px;
  color: var(--ink2); font-size: 12px; line-height: 1.5; max-width: 72ch; }

/* ── the readings ledger: one accordion per post, a metric spanning its parts ── */
.rdlist { display: flex; flex-direction: column; }
.rditem { border-top: 1px solid var(--line); padding: 4px 0; }
/* A cycle groups its posts: the header stands out a touch, its members indent under it. */
.rditem.rdset > .rdhead { font-weight: 600; }
.rdbody > .rditem { border-top-color: var(--line2); }   /* a cycle's nested posts */
.rdhead { gap: 8px; }
.rdhead .hint { margin-left: 4px; }
/* The brand's code on every entry: mono, small, the same chip as the rest of the panel. */
.rdhead .rdcode { font-family: var(--mono); font-size: 10px; letter-spacing: .06em; margin-right: 2px; padding: 1px 6px; }
.rdbody { padding: 4px 0 8px 21px; }
.rdadh { margin: 8px 0 2px; }
/* Shrink-wrapped, one line a row: the table is a ledger of short figures, and stretched across the
   panel the metric, its part and its value sit a hand's width apart (PRODUCT.md, density). */
table.rdt { width: auto; }
table.rdt th, table.rdt td { white-space: nowrap; padding: 3px 10px; }
table.rdt td.rdmetric { vertical-align: top; font-weight: 600; color: var(--ink); }
table.rdt td.r { padding-left: 18px; }
table.rdt tr.rdpart td { border-top: 0; }
table.rdt td .hint { margin-left: 5px; }
table.rdt tr.rdgroups th { border-bottom: 0; padding-bottom: 0; color: var(--ink); letter-spacing: .04em; }
table.rdt th.rdgroup { border-left: 1px solid var(--line); }
/* Where a triple-clicked figure lands: a border, held a few seconds. */
tr.rdfocus > td { box-shadow: inset 0 1px 0 var(--ink), inset 0 -1px 0 var(--ink); }
.rditem.rdfocus > .rdhead { box-shadow: inset 0 0 0 1px var(--ink); border-radius: 4px; }
/* A figure that takes a triple click shows a GRAY pointer over it, so the affordance is visible
   without a hue (owner ruling 2026-09-04). The system pointer cannot be recoloured, so it is the
   panel's own: a gray arrow with a dark edge, hotspot at the tip. */
[data-reading] { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='18' height='24' viewBox='0 0 18 24'><path d='M2 1.5 L2 19 L6.4 14.8 L9.3 21.8 L12.2 20.6 L9.4 13.8 L15.6 13.8 Z' fill='%239aa0a8' stroke='%23111318' stroke-width='1.3' stroke-linejoin='round'/></svg>") 2 2, auto; }

/* ══ DEPLOYMENT ═══════════════════════════════════════════════════════════════
   The studio's bench for putting a finished post out. Two blocks: the routes strip (which ways out
   exist and which of them are real) and the queue of cards.

   STATE IS NEVER A HUE HERE. Every pill carries the word — "wired", "not wired", "ready to deploy",
   "waiting on Content Build" — so the page reads with the colour channel gone (owner ruling: green
   reads as yellow). Colour is a second signal on top of a word that already said it. */
.deployroutes { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; margin: 14px 0 22px; }
.deployroute { border: 1px solid var(--line); border-radius: 8px; padding: 11px 12px; background: var(--panel); }
.deployroute.on { border-color: rgba(34, 197, 94, .35); }
.deployroute.off { border-color: rgba(240, 198, 116, .35); }
.deployroute-h { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.deployroute-n { font-weight: 600; color: var(--ink); }
.deployroute-w { margin: 0 0 5px; color: var(--ink2); font-size: 12px; line-height: 1.45; }
.deployroute .hint-dim { font-size: 11px; line-height: 1.4; }

.depgrid { display: grid; gap: 10px; margin-top: 10px; }
.depcard { display: grid; grid-template-columns: 108px 1fr; gap: 14px; padding: 12px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
/* Not ready is DIMMER, not red: it is a post mid-flight, which is the normal state of most of them,
   and an alarm colour on the ordinary case teaches the reader to ignore the colour. */
.depcard.pending { opacity: .72; }

.depshot { width: 108px; aspect-ratio: 3 / 4; border-radius: 6px; overflow: hidden; padding: 0;
  border: 1px solid var(--line); background: var(--panel2); display: block; }
.depshot img { width: 100%; height: 100%; object-fit: cover; display: block; }
button.depshot { cursor: pointer; }
button.depshot:hover { border-color: var(--edge); }
.depshot.empty { border-style: dashed; }

.depbody { min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.dephead { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.dephead .spacer { flex: 1; }
.depid { font-weight: 600; color: var(--ink); letter-spacing: .02em; }

/* The three files on one line, each keeping its slot whether or not it is filled — a missing one is
   a fact worth seeing, so it prints its name greyed rather than vanishing and letting the row lie. */
.depfiles { display: flex; gap: 6px; flex-wrap: wrap; }
.depfile { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--dim); font-size: 11px; background: none; }
.depfile.on { color: var(--ink2); border-color: var(--line2); }
.depfile.link { cursor: pointer; }
.depfile.link:hover { border-color: var(--edge); color: var(--ink); }

.depcap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--ink2); }
.depcap-l, .depmoney-l { color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; min-width: 44px; }
.depcap.none { color: var(--dim); }
.depcap-open { display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: none;
  color: var(--blue); cursor: pointer; font: inherit; }
.depcap-open:hover { text-decoration: underline; }
.depcap .hint-dim { font-size: 11px; }

.depmoney { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; }
.depmoney b { color: var(--ink); }
.depmoney .hint-dim { font-size: 11px; }

.depbrief { margin-top: 2px; border-top: 1px solid var(--line); padding-top: 8px; }
.depbrief > summary { display: flex; align-items: center; gap: 6px; cursor: pointer; color: var(--dim);
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em; list-style: none; }
.depbrief > summary::-webkit-details-marker { display: none; }
.depbrief > summary:hover { color: var(--ink2); }
.depbrief-b { margin: 9px 0 0; display: grid; gap: 3px; }
.depbrief-r { display: grid; grid-template-columns: 88px 1fr; gap: 10px; font-size: 12px; }
.depbrief-r > span { color: var(--dim); }
.depbrief-r > b { color: var(--ink2); font-weight: 500; }
.depacts { display: flex; gap: 8px; margin-top: 11px; }

/* ══ ACCESS ═══════════════════════════════════════════════════════════════════
   One card per provider, in a grid that finds its own column count. The control IS the heading:
   it names the provider and declares its state, so there is no separate title and no status pill
   repeating what the button already says.

   STATE IS A WORD FIRST. "Connect Meta" and "Meta connected" differ in text, not only in tint —
   this account reads green as yellow, so nothing here may rest on the colour alone. The tint only
   ever agrees with the label. */
.accessnote { display: flex; align-items: center; gap: 8px; margin: 14px 0 4px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 8px; font-size: 13px; color: var(--ink2); background: var(--panel); }
.accessnote.ok { border-color: rgba(34, 197, 94, .4); }
.accessnote.warn { border-color: rgba(240, 198, 116, .4); }
.accessnote.bad { border-color: rgba(248, 113, 113, .45); }

/* auto-fit, so the count follows the width rather than a breakpoint guessing at it: three columns
   on a wide screen, two mid, one on a phone. Capped, because four providers stretched across a
   27-inch panel is a row of billboards, not a settings page. */
.accessgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
  gap: 12px; margin-top: 18px; max-width: 720px; align-items: start; }

.accesscard { padding: 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
  display: flex; flex-direction: column; gap: 11px; }
.accesscard.on { border-color: var(--line2); }
/* A placeholder recedes by losing its FILL, not by fading. `opacity` on the whole card takes the
   text down with it — half-strength --dim over the page ground lands near 2.5:1, and "not wired
   yet" is information, not a disabled control label, so it does not get an exemption. Dropping the
   panel fill lets the card sit back against the page while every word in it stays legible. */
.accesscard.soon { background: none; }

.accesshead { display: flex; align-items: center; gap: 8px; }

.accessbtn { flex: 1 1 auto; min-width: 0; padding: 7px 12px; border-radius: 6px; cursor: pointer;
  border: 1px solid transparent; background: var(--green); color: #0c1410;
  font: inherit; font-size: 13px; font-weight: 600; text-align: center;
  transition: background 160ms cubic-bezier(.22,1,.36,1), border-color 160ms cubic-bezier(.22,1,.36,1); }
.accessbtn:hover:not(:disabled) { background: color-mix(in oklab, var(--green) 88%, white); }
/* Focus is a border, never an outline (standing rule). */
.accessbtn:focus-visible { border-color: var(--ink); }
/* CONNECTED IS QUIET. The loud treatment belongs to the thing still asking to be done; a settled
   state shouting in accent green is a page that never stops selling its own success. */
.accessbtn.on { background: none; border-color: var(--line2); color: var(--ink2); font-weight: 500; }
.accessbtn.on:hover:not(:disabled) { border-color: var(--edge); color: var(--ink); background: none; }
.accessbtn:disabled { background: none; border-color: var(--line); color: var(--dim); cursor: default; }

.accessoff { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--line); background: none; color: var(--dim);
  transition: color 160ms cubic-bezier(.22,1,.36,1), border-color 160ms cubic-bezier(.22,1,.36,1); }
.accessoff:hover { color: var(--red); border-color: rgba(248, 113, 113, .45); }
.accessoff:focus-visible { border-color: var(--ink); }

.accessgrants { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
.accessgrant { display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--line2); color: var(--ink2); font-size: 11px; letter-spacing: .01em; white-space: nowrap; }

.accessfacts { display: grid; gap: 4px; padding-top: 10px; border-top: 1px solid var(--line); }
.accessfact { display: grid; grid-template-columns: 74px 1fr; gap: 8px; font-size: 12px; }
.accessfact > span { color: var(--dim); }
.accessfact > b { color: var(--ink2); font-weight: 500; overflow-wrap: anywhere; }

.accesssoon { font-size: 11px; color: var(--dim); }

@media (prefers-reduced-motion: reduce) {
  .accessbtn, .accessoff { transition: none; }
}
