:root{
  color-scheme: light;
  --bg:#f5f6fa; --card:#ffffff; --ink:#0d1730; --muted:#5f677a; --line:#e6e8f0;
  /* Fallbacks only — applyBrandColors() overwrites these from brand_guidelines
     at boot. --uh was #725EFE, a colour UptimeHealth retired; the 2026 book says
     #6f5ff5, which is what the JS fallback already used. A stylesheet default
     that contradicts the brand book paints the wrong purple on every first
     paint, and permanently if /api/brand ever fails. */
  --uh:#6f5ff5; --uh2:#C15BE3; --us:#0A9396; --for:#FE7F4F; --for2:#FFB438;
  /* Chrome — toasts, chips, the segmented "on" state — not a brand-store value,
     so it stays a token rather than following Settings → Brand. The VALUE was
     still wrong: #000E29 is the pre-2026 navy, and 006_brand.sql records that
     the book moved to #030e27 (it is UptimeHealth's colors[0] in the database).
     Chrome doesn't track the brand store, but it shouldn't contradict the brand
     book either. */
  --navy:#030e27;
  --green:#1a9d5a; --amber:#e08c00; --red:#e04848; --blue:#3a6df0; --grey:#8a91a3;
  --shadow:0 1px 3px rgba(13,23,48,.08),0 6px 20px rgba(13,23,48,.05);
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.45}
/* The bottom padding is for the chat FAB, which is position:fixed at
   bottom:22px with a 44px box and z-index 40 — so anything that scrolls under
   it becomes unclickable. Measured at 1280x800: it was stealing the click from
   Calendar's Month and Week buttons and Timeline's "Show all time", via
   elementFromPoint on each control's own centre.

   88px = 22 bottom offset + 44 tall + 22 clear. This does not make it
   impossible for a control to pass beneath the FAB mid-scroll — nothing short
   of moving the FAB would — but it guarantees every control can be scrolled
   out from under it, including the last one on the page, which previously
   could not. */
.wrap{max-width:1400px;margin:0 auto;padding:20px 20px 88px}
/* Three children, two rows: title and account controls share row one, the
   brand legend takes row two on its own. align-items:flex-start rather than
   center so the account chip sits at the TOP right — with `center` it drifted
   down to the vertical middle of a two-line title block. */
header.top{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:16px}
h1{font-size:22px;margin:0;letter-spacing:-.3px}
.sub{color:var(--muted);font-size:12.5px;margin-top:3px}
/* flex-basis:100% is what guarantees its own row. Without it the legend
   competes with the title and the account chip for row one, and the number of
   brands — data, not a constant — decides the layout. */
.legend{display:flex;gap:14px;flex-wrap:wrap;align-items:center;flex-basis:100%}
.legend .dot{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
.legend .sw{width:11px;height:11px;border-radius:3px;display:inline-block}
/* Two rows, two questions. The plan tiles (how much work, how far along, what
   it costs) and the per-brand counts are different kinds of number, and a
   single auto-fit run let the viewport width decide where the break fell —
   which meant the grouping read as accidental, because it was. Each row is now
   its own grid and wraps independently. */
.kpis{display:flex;flex-direction:column;gap:12px;margin-bottom:16px}
.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(138px,1fr));gap:12px}
/* The brand row is secondary: same tile, quieter frame, and it never stretches
   its tiles to fill a half-empty row the way auto-fit alone would. */
.kpi-row-brands{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.kpi{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;box-shadow:var(--shadow)}
.kpi .n{font-size:26px;font-weight:700;letter-spacing:-.5px}
.kpi .l{font-size:11.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.4px;margin-top:2px}
.kpi .bar{height:4px;border-radius:4px;margin-top:10px;background:var(--line);overflow:hidden}
.kpi .bar>i{display:block;height:100%}
/* Tinted toward the house purple so the row reads as one group at a glance,
   rather than as five cards that happen to sit together. Kept light: each tile
   still carries its own brand colour in the bar beneath it, and a strong wash
   here would fight the five different hues sitting on top of it.

   Placed AFTER the base .kpi rules on purpose. `.kpi-row-brands .l` and
   `.kpi .l` are both specificity (0,2,0), so source order is the only
   tiebreak — sitting above them, this block lost every declaration it shared
   with the base and the label silently stayed grey. */
.kpi-row-brands .kpi{background:#f5f3fe;border-color:#e5e0fa}
.kpi-row-brands .kpi .l{color:#6a6288}
.kpi-row-brands .kpi .n{font-size:21px}
/* An explicit grid, not flex-wrap. The six filters occupy six fixed tracks and
   the actions sit on their own full-width row, so the View switcher, Clear
   filters and + Campaign land in the same place regardless of view, viewport,
   or how long a channel name happens to be. Flex-wrap reflowed on all three,
   and the controls visibly jumped every time you clicked between views. */
.toolbar{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px;box-shadow:var(--shadow);margin-bottom:14px;
  display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:10px 12px;align-items:end}
.tb-actions{grid-column:1/-1;display:flex;gap:10px;align-items:end;flex-wrap:wrap}
/* `0 1 auto`, not `0 0 auto`, and min-width:0 — this pair is what actually
   fixes the 375px overflow, and the reason is worth writing down because the
   obvious fix does nothing.

   Putting flex-wrap:wrap on .seg computes correctly and changes NOTHING while
   this rule says `0 0 auto`: an inflexible item sizes to max-content, so the
   segmented control is handed its full 373px inside a 351px box and is never
   under the width pressure that would make it wrap. min-width:0 is the second
   half — a flex item's automatic minimum size is its content, so it refuses to
   shrink below that even when shrinking is allowed. */
.tb-actions .grp{flex:0 1 auto;min-width:0}
.tb-actions .grp:first-child{margin-right:auto}
@media (max-width:1100px){.toolbar{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:640px){.toolbar{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* Captions inside a view bar, matching the toolbar's. */
.vb-lbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.view-bar select{font:inherit;font-size:13px;padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink)}
/* Sticky chrome — the tab bar (which carries global search) and the board's
   filters stay reachable while scrolling a long board. --tabs-h and --stick-h
   are measured in app.js: both bars wrap, so their heights aren't knowable
   from CSS.

   Only above 900px. Below it both bars wrap into several rows — the tab bar
   alone reached 348px in testing — and pinning something that tall leaves no
   room to read what you scrolled to. Small screens scroll normally. */
@media (min-width:900px){
  .tabs{position:sticky;top:0;z-index:32;padding-top:10px}
  .toolbar{position:sticky;top:var(--tabs-h,46px);z-index:31;max-height:42vh;overflow-y:auto}
  /* Third layer, and the one that makes the table's own column headers work:
     .table-scroll needs a height before they have anything to stick to.
     `overflow-x:auto` already forced its overflow-y to compute to `auto`, so it
     is a scrollport either way — but with no cap it never scrolls, the PAGE
     scrolls instead, the whole table travels, and the sticky th never moves.
     Capping it to what the pinned pair leaves puts the scrolling inside the
     table, headers pinned under the chrome rather than sliding away behind it.

     The 96px is not breathing room, it is a floor. Whatever sits BELOW the
     table — the footnote, plus .wrap's bottom padding — is exactly how far the
     page can still scroll once the table is capped, and every one of those
     pixels drags the pane and its pinned header up behind the toolbar. Measured
     at 71px on a 1280 viewport and 89px at 920, where the footnote wraps to a
     fourth line; 96 clears the worst of it. Cap by less and the last flick of
     page scroll eats the header you just fixed.

     Only in here: below 900px nothing above is pinned, --stick-h is 0, and a
     nested scroller a whole viewport tall is worse than the page scroll it
     would replace. overscroll-behavior stops a flick past the last row from
     chaining into the page and spending that scroll for you. */
  #table-view .table-scroll{max-height:calc(100vh - var(--stick-h,140px) - 96px);
    overscroll-behavior-y:contain}

  /* The kanban needs the same cap, for a reason the table does not have: its
     horizontal scrollbar lives at the BOTTOM of the grid. Uncapped, a column
     holding 53 drafts made the grid thousands of pixels tall and put the only
     control for scrolling sideways below all of them — you had to scroll the
     whole page to the end to reach the bar that moves the columns.

     Capped, the bar sits at the bottom of what you can see. 170px rather than
     the table's 96, and the extra is not padding: the Working-at tier row and the
     campaign chip bar sit above this one inside the same card and neither is
     pinned, and --stick-h itself grows to ~200px around 1000px where the toolbar
     wraps to three rows. Measured at 150 the bar still fell 48px below the fold
     at that width.

     It cannot be made exact by arithmetic. Capping the grid shortens the page,
     which reduces how far the page can scroll, which changes how much chrome is
     still on screen when you reach the board — the inputs depend on the output.
     On a narrow screen the chrome above is taller than the viewport anyway, so
     some page scrolling is unavoidable there no matter what this number is. What
     the cap buys is that once the board IS on screen, all of it is: ~450-560px
     with its scrollbar, instead of a column of 53 cards with the bar past the
     end of them.

     Same 900px gate as everything else in this block: below it nothing above is
     sticky, so a nested scroller a viewport tall would just be worse than the
     page scroll it replaces. */
  #table-view .camp-kanban{max-height:calc(100vh - var(--stick-h,140px) - 170px);
    overflow:auto;overscroll-behavior:contain}

  /* Once the grid scrolls vertically, the column headings scroll away with it —
     and a kanban whose columns are unlabelled after one flick is worse than one
     that is too tall. Sticky against .camp-kanban, which is now the scrollport.
     Opaque background because the cards would otherwise read straight through
     it, and the column's own #fbfbfe is what it sits on. */
  #table-view .camp-kanban .ck-head{position:sticky;top:0;z-index:1;
    background:#fbfbfe;border-radius:11px 11px 0 0}
}
.toolbar .grp{display:flex;flex-direction:column;gap:3px;min-width:0}
.toolbar .grp select,.toolbar .grp input{width:100%}
.toolbar label{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
select,input[type=text],input[type=date]{font:inherit;font-size:13px;padding:7px 9px;border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink);min-width:130px}
input[type=text]{min-width:190px}
/* The search box's 190px floor is a desktop nicety and a second source of
   overflow on a phone, where the toolbar is two columns of ~170px. */
@media (max-width:640px){input[type=text]{min-width:0}}

/* X-7. #view-seg holds five buttons that cannot fit a phone. Wrapping rather
   than scrolling the strip, because a segmented control that scrolls sideways
   hides the options it exists to show, and these five are the app's primary
   navigation between views. The radius is on the outer box only, so a wrapped
   second row still reads as one control.

   NOTE: this declaration alone does nothing. See `.tb-actions .grp` above —
   wrap only takes effect once the parent stops being `flex:0 0 auto`.

   The comment that used to sit here claimed "measured before and after — 23px,
   then 0". That was false. The measurement was taken with #tab-board
   display:none, because X-1 made My Work the opening tab, so it measured a
   hidden panel and found the 0 it expected. The board still overflowed by
   exactly 23px on all five views. Measure the surface the change applies to,
   and confirm it is on screen before believing the number. */
.seg{display:inline-flex;flex-wrap:wrap;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.seg button{border:0;background:#fff;padding:8px 14px;font:inherit;font-size:13px;cursor:pointer;color:var(--muted)}
.seg button.on{background:var(--navy);color:#fff}
/* Segmented buttons had no hover either. .shape-seg IS a .seg (board.js), so
   without this the view switcher and the tier-shape switcher — often on screen
   together — behaved differently under the pointer. */
.seg button:hover:not(.on):not(:disabled){color:var(--ink);background:#f6f7fb}

/* X-7. One focus ring for every control that hasn't earned its own.
   Six components had a :focus-visible rule and the rest had nothing, so
   tabbing through the toolbar, the tab bar or a modal left no indication of
   where you were — the app was keyboard-operable and not keyboard-navigable.
   :focus-visible, not :focus, so a mouse click doesn't leave a ring behind.
   Anything with its own rule still wins on specificity. */
button:focus-visible, a:focus-visible, select:focus-visible,
input:focus-visible, textarea:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--uh); outline-offset:2px; border-radius:4px;
}
/* Inside a segmented control the ring must sit within the clipped box, or
   `overflow:hidden` on .seg shaves the outer half of it off. */
.seg button:focus-visible{outline-offset:-2px}
.btn{border:1px solid var(--line);background:#fff;border-radius:8px;padding:8px 12px;font:inherit;font-size:13px;cursor:pointer;color:var(--ink)}
.btn:hover:not(:disabled){background:#f0f1f6}
.btn.primary{background:var(--uh);color:#fff;border-color:var(--uh)}
/* Primary had NO hover state at all. `.btn:hover` above and `.btn.primary` are
   both specificity (0,2,0), and .btn.primary is declared second, so it won every
   time — the 37 primary buttons in this app (every modal Save, Send, the login
   submit) gave no pointer feedback whatsoever. #5f4bea is already in use as
   .chat-fab:hover, so this introduces no new colour. */
.btn.primary:hover:not(:disabled){background:#5f4bea;border-color:#5f4bea}
/* .btn sets background, color, border and cursor explicitly, which overrides
   the browser's own disabled greying — so a disabled button was pixel-identical
   to an enabled one across ~27 sites (every modal save, approvals.js:162,
   board.js:1356, tasks.js:267, chat.js:120). One rule covers all of them. */
.btn:disabled{opacity:.5;cursor:default}
.spacer{flex:1}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);overflow:hidden}
table{width:100%;border-collapse:collapse}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:top}
/* top:0 and not an offset, deliberately. These headers stick to .table-scroll,
   not to the page — `overflow-x:auto` there forces overflow-y to `auto`, so it
   is the nearest scrollport — and the top of that box is already below the
   sticky tabs and toolbar. The offset for the chrome therefore belongs on the
   scrollport's height (see #table-view .table-scroll, above), not here: a th
   pushed down by --stick-h would sit --stick-h *inside* its own table and hide
   the first rows behind itself.
   z-index:1 is likewise right, and low on purpose. Nothing between here and the
   root builds a stacking context, so this competes directly with .tabs (32) and
   .toolbar (31) and must lose to both — it only has to beat the unpositioned
   rows sliding underneath. */
th{font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);cursor:pointer;white-space:nowrap;position:sticky;top:0;background:#fbfbfe;z-index:1}
th .ar{opacity:.4;font-size:9px}
td .camp{font-weight:600}
td .cid{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}
.brandtag{display:inline-block;font-size:11px;font-weight:600;padding:2px 8px;border-radius:20px;white-space:nowrap;color:#fff}
.pill{display:inline-block;font-size:11px;font-weight:600;padding:2px 9px;border-radius:20px;white-space:nowrap}
.pill.dot::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:5px;vertical-align:middle;background:currentColor}
.txt-muted{color:var(--muted)}
.small{font-size:12px}
/* Long CTAs get three lines and a tooltip rather than a 200px-tall row. The
   clamp lives on a span, not the td: -webkit-box on a table cell drops it out
   of table layout and it paints outside the row. */
.cc-cta{font-size:11.5px;color:#4a5266;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.cc-cta b{color:var(--muted);font-weight:700;font-size:10px;text-transform:uppercase;letter-spacing:.4px;margin-right:4px}
tr.tr-group{cursor:default}
tr.tr-group[data-foldable]{cursor:pointer}
tr.tr-group td{background:#f6f7fb}
tr.tr-group .twist{display:inline-block;width:14px;color:var(--muted);font-size:10px}
tr.tr-deliverable td{background:#fff;font-size:12.5px;padding-top:7px;padding-bottom:7px}
tr.tr-deliverable td.dtitle{padding-left:34px}
.timeline{padding:14px 16px}
.tl-head{display:grid;gap:0;margin-left:230px;border-bottom:1px solid var(--line)}
.tl-month{font-size:12px;font-weight:600;text-align:center;padding:4px 0;border-left:1px solid var(--line)}
.tl-row{display:flex;align-items:center;border-bottom:1px solid var(--line);min-height:40px}
.tl-label{width:230px;flex:none;padding-right:10px;font-size:12.5px}
.tl-track{position:relative;flex:1;height:40px}
.tl-bar{position:absolute;top:9px;height:22px;border-radius:6px;color:#fff;font-size:11px;font-weight:600;display:flex;align-items:center;padding:0 8px;overflow:hidden;white-space:nowrap;box-shadow:var(--shadow)}
/* Squared-off, notched edge = this campaign runs past the visible window.
   Without it a campaign already under way reads as starting on the 1st. */
.tl-bar.runs-in{border-top-left-radius:0;border-bottom-left-radius:0;border-left:3px double rgba(255,255,255,.75)}
.tl-bar.runs-out{border-top-right-radius:0;border-bottom-right-radius:0;border-right:3px double rgba(255,255,255,.75)}
.tl-grid{position:absolute;inset:0;pointer-events:none}
.tl-grid i{position:absolute;top:0;bottom:0;border-left:1px solid var(--line)}
.empty-state{padding:40px;text-align:center;color:var(--muted)}

/* ---- three tiers -------------------------------------------------------- */
/* Campaigns, deliverables and tasks used to be one table with a different row
   count, which is why they all read as the same screen. Each tier now has its
   own shape: cards you choose between, a table you work in, a list of what's
   due. The view bar above them stays identical in all three so the switcher
   and the hint never move. */
.vb-hint{font-size:12px;color:var(--muted)}
@media (max-width:760px){.vb-hint{display:none}}

/* All three tiers now draw as tiles, a board or a list, so the card box, the
   tile grid, the board columns and the list chrome are shared rather than
   copied per tier — a deliverable card that drifts from a campaign card makes
   the two read as two products. The three card classes exist only so the
   behaviour that DOES differ has something to hang off: which of them is
   clickable, and which sit inside a board column. */
.camp-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(272px,1fr));gap:12px;padding:14px 16px}
.camp-card,.del-card,.tk-card{text-align:left;font:inherit;background:var(--card);border:1px solid var(--line);
  border-left:4px solid var(--accent,var(--grey));border-radius:11px;padding:12px 14px;display:flex;flex-direction:column;gap:7px;
  transition:box-shadow .12s ease,transform .12s ease,border-color .12s ease}
/* Only the ones that actually open something claim the cursor. A task card has
   no drill-in and a deliverable card has none for a viewer, and a pointer over
   something that does nothing is a promise the card can't keep. */
.camp-card,.del-card[role=button]{cursor:pointer}
.camp-card:hover,.del-card[role=button]:hover{box-shadow:var(--shadow);transform:translateY(-1px);border-color:#cfd4e4}
.camp-card:focus-visible,.del-card:focus-visible{outline:2px solid var(--uh);outline-offset:2px}
/* Actions on a deliverable or task card stay visible, unlike .cc-act on a
   campaign card: Edit and + Task are the reason you are looking at the card,
   not a second thought after reading it. */
.item-act{display:flex;gap:6px;flex-wrap:wrap;margin-top:2px}
.cc-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.cc-ok{color:var(--green);font-weight:700;margin-left:auto}
.cc-name{font-weight:650;font-size:14.5px;line-height:1.3;letter-spacing:-.2px}
.cc-id{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}
.cc-desc{font-size:12px;line-height:1.4;color:#4a5266;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.cc-prog{margin:1px 0}
.cc-facts{display:flex;flex-wrap:wrap;gap:4px 12px;font-size:12px;color:var(--muted)}
.cc-facts b{color:var(--ink);font-weight:650}
.cc-when{font-size:11.5px;color:var(--muted);border-top:1px solid var(--line);padding-top:7px;margin-top:auto}
.cc-arch{font-size:10px;text-transform:uppercase;letter-spacing:.4px;font-weight:700;color:var(--muted);background:#eceef4;border-radius:20px;padding:2px 8px}
/* Actions sit quiet until you're on the card — they're the second thing you
   want, after reading it. Always present in the layout so nothing reflows. */
.cc-act{display:flex;gap:6px;margin-top:8px;opacity:0;transition:opacity .12s ease}
.camp-card:hover .cc-act,.camp-card:focus-within .cc-act{opacity:1}
@media (hover:none){.cc-act{opacity:1}}
.camp-card.archived{background:#fafbfd;border-style:dashed}
.camp-card.archived .cc-name{color:#4a5266}
.scope-note{font-size:11.5px;color:var(--muted);align-self:center;margin-left:4px}
@media (max-width:700px){.scope-note{display:none}}

/* Tiles / Board / List, at the far end of whichever chip bar the tier owns —
   the scope bar on Campaigns, the campaign chips on Deliverables and Tasks.
   margin-left:auto holds it right without leaving the flow, so it wraps under
   on a narrow pane rather than sitting on top of the chips. */
.shape-seg{margin-left:auto;align-self:center}
/* Icon-only, so the padding has to carry the hit area the text used to. Square
   and >=32px a side keeps it thumb-sized on a tablet. */
.shape-seg button{padding:7px 11px;display:inline-flex;align-items:center;justify-content:center;
  min-width:36px;min-height:32px;color:var(--muted)}
.shape-seg button.on{color:#fff}
.shape-seg button:hover:not(.on){color:var(--ink)}
.shape-seg svg{display:block}

/* Board — one column per campaign status. grid-auto-columns with a floor rather
   than flex, so every column is the same width and the row scrolls sideways
   once there are more statuses than fit. minmax(0,…) for the usual reason: a
   long campaign name must not widen its column past its share. */
.camp-kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(238px,1fr);
  gap:11px;padding:14px 16px;overflow-x:auto;align-items:start}

/* Wide screens should not scroll sideways at all.

   238px is the floor that keeps a card readable when the row DOES scroll, and it
   is the right number on a laptop. It is the wrong number at 1500px, where six
   deliverable statuses need 238*6 + five gaps + padding = ~1515px and the card
   gives them ~1360 — so the widest screens were the ones scrolling.

   Above 1180px the floor drops to 170, which fits all six (170*6 + 87 = 1107)
   and still refuses to crush them: it is a floor, not a fixed width, so `1fr`
   shares the real width out and the columns land near 210px on a 1500px screen.
   Kept as minmax rather than minmax(0,1fr) deliberately — if a seventh status is
   ever added the row goes back to scrolling instead of squeezing to nothing. */
@media (min-width:1180px){
  .camp-kanban{grid-auto-columns:minmax(170px,1fr)}
}
.ck-col{background:#fbfbfe;border:1px solid var(--line);border-radius:11px;min-width:0;
  display:flex;flex-direction:column}
.ck-head{display:flex;align-items:center;gap:7px;padding:9px 11px;border-bottom:1px solid var(--line)}
.ck-dot{width:8px;height:8px;border-radius:50%;flex:none}
.ck-name{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;
  color:var(--muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ck-n{margin-left:auto;font-size:10.5px;font-weight:700;color:var(--muted);
  background:#eceef4;border-radius:20px;padding:1px 7px;flex:none}
.ck-body{display:flex;flex-direction:column;gap:8px;padding:9px;min-width:0}
/* The cards inside a column are already bounded by it, so they drop the grid's
   min-width and let their own ellipsis do the work. */
.ck-body .camp-card,.ck-body .del-card,.ck-body .tk-card{min-width:0}
.ck-empty{color:var(--muted);font-size:12px;text-align:center;padding:10px 0}

/* List — one row per item. Reuses the table chrome; the row is the control,
   the same as a card. */
.camp-list{padding:0 0 4px}
.camp-list td{vertical-align:middle}
/* th carries cursor:pointer globally because the board's headers sort. These
   don't, and a pointer over a header that ignores the click is a lie. */
.camp-list th{cursor:default}
.camp-list .tk-chk{width:1%;padding-right:0}
.camp-list tr.camp-lrow{cursor:pointer}
.camp-list tr.camp-lrow:hover td{background:#fafbff}
.camp-list tr.camp-lrow:focus-visible{outline:2px solid var(--uh);outline-offset:-2px}
.camp-list tr.camp-lrow.archived{background:#fafbfd}
.camp-list .cl-name{font-weight:600;font-size:13px}
.camp-list th.cl-n,.camp-list td.cl-n{text-align:right;font-variant-numeric:tabular-nums}
/* Duplicate preview */
.dup-prev{font-size:12.5px;color:#3a4257;background:#f4f4fb;border:1px solid var(--line);
  border-radius:8px;padding:9px 11px;margin:4px 0 2px;min-height:20px}

/* Chip bars — campaign narrowing above the table, assignee filter above tasks. */
.chipbar{display:flex;flex-wrap:wrap;gap:6px;padding:11px 16px;border-bottom:1px solid var(--line);background:#fbfbfe}
.chip{font:inherit;font-size:12px;cursor:pointer;background:#fff;border:1px solid var(--line);border-radius:20px;
  padding:4px 11px;color:var(--muted);display:inline-flex;align-items:center;gap:6px;max-width:280px}
.chip:hover{border-color:#c8cddf;color:var(--ink)}
.chip.on{background:var(--navy);border-color:var(--navy);color:#fff}
.chip i{width:8px;height:8px;border-radius:2px;flex:0 0 auto}
.chip.more{border-style:dashed}

/* Tasks — a status board by default, with the deadline grouping still one click
   away as Tiles. #task-groups already pads, so the shapes drop the padding they
   carry for the Campaigns tier rather than doubling it. */
#task-groups{padding:14px 16px}
#task-groups .camp-kanban,#task-groups .camp-grid{padding:0}
/* The deadline group heading supplies the gap above; the grid supplies the rest. */
.tg-grid{margin-top:9px}
.tg{margin-bottom:16px}
.tg-head{display:flex;align-items:center;gap:8px;font-size:11px;text-transform:uppercase;letter-spacing:.5px;
  font-weight:700;color:var(--muted);padding-bottom:6px;border-bottom:2px solid var(--line);margin-bottom:2px}
.tg-head span{background:#eceef4;border-radius:20px;padding:1px 8px;font-size:10.5px;letter-spacing:0}
.tg-overdue .tg-head{color:var(--red);border-bottom-color:#f6d3d3}
.tg-overdue .tg-head span{background:#fdeaea;color:var(--red)}
.tg-today .tg-head{color:var(--amber);border-bottom-color:#f6e6c6}
.tg-today .tg-head span{background:#fdf3e0;color:var(--amber)}
.tg-check{font:inherit;font-size:16px;line-height:1;cursor:pointer;background:none;border:0;color:var(--muted);padding:0 2px;flex:0 0 auto}
.tg-check:hover{color:var(--green)}
.tg-check:disabled{opacity:.4;cursor:default}
.tg-check.off{cursor:default;opacity:.3}
.tg-who{background:#f2f3f8;border-radius:20px;padding:2px 9px;color:#3a4257}
.tg-who.none{background:none;font-style:italic}
.tg-due{font-variant-numeric:tabular-nums;min-width:52px;text-align:right}
/* Dotted = borrowed from the deliverable, so nobody reads it as a commitment
   somebody actually made on the task itself. */
.tg-due.inherited{border-bottom:1px dotted var(--grey);color:var(--grey)}
.tg-done{font-size:12px;color:var(--muted);padding-top:4px}
/* Brand guidelines */
.brand-card{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);margin-bottom:12px;overflow:hidden}
.bh{display:flex;align-items:center;gap:12px;padding:12px 16px}
.bh-swatches{display:flex;gap:3px;flex:0 0 auto}
.bh-swatches i{width:16px;height:26px;border-radius:3px;border:1px solid rgba(13,23,48,.12)}
.b-body{padding:0 16px 16px;border-top:1px solid var(--line)}
/* One class, two features: the Brand panel's headings and Performance's section
   subtitles. They were declared separately 600 lines apart, so the later block
   silently won font-size and contributed the flex layout to both. Merged here,
   carrying the values that were actually in force — 12.5px, a 10px bottom
   margin, and the flex row. */
.sec-sub{font-size:12.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);font-weight:700;
  margin:16px 0 10px;display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.sec-sub .txt-muted{text-transform:none;letter-spacing:0;font-weight:400;font-size:11.5px}
.b-colors,.b-fonts{width:100%;border-collapse:collapse;margin-bottom:6px}
.b-colors td,.b-fonts td{padding:3px 6px 3px 0;vertical-align:middle}
.b-colors input[type=text],.b-fonts input{width:100%;font:inherit;font-size:12.5px;padding:6px 8px;border:1px solid var(--line);border-radius:7px}
.b-colors input[type=color]{width:42px;height:32px;padding:2px;border:1px solid var(--line);border-radius:7px;background:#fff;cursor:pointer}
.b-colors .bc-hex{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.b-body textarea{width:100%;font:inherit;font-size:13px;padding:9px 11px;border:1px solid var(--line);border-radius:8px;resize:vertical}
.b-board{display:flex;align-items:center;gap:8px;margin-bottom:4px}
.b-board input[type=color]{width:42px;height:32px;padding:2px;border:1px solid var(--line);border-radius:7px;background:#fff;cursor:pointer}
.b-board input[type=text]{width:120px;font:inherit;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px;padding:6px 8px;border:1px solid var(--line);border-radius:7px}
.b-links{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}
.b-links label{display:flex;flex-direction:column;gap:4px;font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.b-links input{font:inherit;font-size:12.5px;padding:7px 9px;border:1px solid var(--line);border-radius:7px;text-transform:none;letter-spacing:0;color:var(--ink)}
.b-act{display:flex;align-items:center;gap:12px;margin-top:16px}
.b-guide{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.b-file{cursor:pointer}
/* What the model read out of the PDF, before any of it is saved. */
.b-extract{margin-top:10px}
.bx{border:1px solid var(--uh);border-radius:10px;background:#fbfaff;padding:12px 14px}
.bx-head{font-size:11px;text-transform:uppercase;letter-spacing:.4px;font-weight:700;color:var(--uh);margin-bottom:8px}
.bx-head .txt-muted{text-transform:none;letter-spacing:0;font-weight:400;font-size:12px}
.bx-row{display:flex;align-items:baseline;gap:9px;padding:5px 0;border-bottom:1px solid var(--line);cursor:pointer;font-size:12.5px}
.bx-row:last-of-type{border-bottom:0}
.bx-label{font-weight:650;flex:0 0 108px}
.bx-val{color:#4a5266;min-width:0;overflow:hidden;text-overflow:ellipsis}
.bx-val i{display:inline-block;width:10px;height:10px;border-radius:2px;margin:0 4px 0 8px;vertical-align:baseline}
.bx-notes{font-size:12px;color:var(--muted);margin-top:8px;line-height:1.5}
.bx-act{display:flex;gap:8px;margin-top:12px}

/* Task templates */
.tpl-card{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);padding:14px 16px;margin-bottom:12px}
.tpl-card.retired{opacity:.6}
.tpl-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:10px}
.tpl-act{display:flex;gap:6px;flex-shrink:0}
.tpl-steps{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
.tpl-step{font-size:12px;background:#f4f4fb;border:1px solid var(--line);border-radius:7px;padding:4px 9px}
.tpl-step b{color:var(--muted);font-weight:700;margin-right:3px}
.tpl-step em{font-style:normal;color:var(--uh);margin-left:5px}
.tpl-step i{font-style:normal;color:var(--muted);margin-left:5px}
.tpl-items{margin-top:12px}
.tpl-items table{width:100%;border-collapse:collapse}
.tpl-items th{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);text-align:left;padding:4px 6px;position:static;background:none;cursor:default}
.tpl-items td{padding:3px 6px;vertical-align:middle}
.tpl-items input,.tpl-items select{width:100%;font:inherit;font-size:12.5px;padding:6px 8px;border:1px solid var(--line);border-radius:7px}
.tpl-items .ti-hours,.tpl-items .ti-weight{width:76px}
.tpl-preview{margin:10px 0;border:1px solid var(--line);border-radius:9px;padding:8px 10px;background:#fbfbfe}
.tpl-prev-row{font-size:12.5px;padding:2px 0}
.tpl-prev-row b{color:var(--muted);margin-right:5px}
.tpl-prev-row span{margin-left:6px;font-size:11.5px}
.pill-grey{background:#eceef4;color:var(--muted);border-radius:20px;padding:1px 8px;font-size:10.5px;margin-left:6px}
.cal{padding:14px 16px}
.cal-month{margin-bottom:22px}
/* .timeline and .cal are padded, unlike the .card the social bar sits in, so
   the bar has to be pulled back out to the card edges or its rule floats. */
.timeline>.view-bar,.cal>.view-bar{margin:-14px -16px 14px}
/* minmax(0,1fr), never a bare 1fr: a `1fr` track takes an automatic minimum of
   its content's width, so a long entry title spilled across neighbouring days.
   minmax(0,…) lets the track shrink and the chip's own ellipsis do its job. The
   paired min-width:0 on the cells is the same fix one level down. */
.cal-dow{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;margin-bottom:6px}
.cal-dow span{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);text-align:center}
.cal-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px}
.cal-cell{min-height:92px;min-width:0;border:1px solid var(--line);border-radius:8px;padding:5px 6px;background:#fcfcfe;display:flex;flex-direction:column;gap:3px}
.cal-cell.out{background:#f5f6fa;opacity:.5}
.cal-cell.today{border-color:var(--uh);box-shadow:0 0 0 1px var(--uh)}
.cal-date{font-size:11px;color:var(--muted);font-weight:600}
/* A button, not a div: every chip now opens its campaign in the table. */
.cal-chip{font:inherit;font-size:10.5px;font-weight:600;color:#fff;border:0;border-radius:5px;padding:3px 6px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;text-align:left;display:block;width:100%}
.cal-chip:hover{filter:brightness(1.12);box-shadow:0 1px 4px rgba(13,23,48,.25)}
.cal-chip:focus-visible{outline:2px solid var(--ink);outline-offset:1px}
.cal-chip.cont{opacity:.55}
/* Week mode: seven days get the whole width, so a day's entries can breathe
   instead of being clipped to one line in a 90px box. */
.cal-week .cal-cell{min-height:180px}
.cal-week .cal-chip{white-space:normal}
.cal-week .cal-date{font-size:12px;color:var(--ink)}
/* Where a calendar click landed. The Deliverables tier draws as cards too now,
   so the flash has to have somewhere to land in every shape — on a card it is
   the element itself, on a row it is the cells, because a tr paints no
   background of its own. */
@keyframes rowflash{from{background:#efe9ff}to{background:transparent}}
tr.row-flash td{animation:rowflash 1.6s ease-out}
.del-card.row-flash{animation:rowflash 1.6s ease-out}
.social-bar,.view-bar{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--line);flex-wrap:wrap}
.wk-label,.vb-label{font-size:13px;font-weight:700;min-width:150px}
.social-legend{display:flex;gap:10px;flex-wrap:wrap}
.social-legend .dot{display:inline-flex;align-items:center;gap:5px;font-size:11px;color:var(--muted)}
.social-legend .sw{width:9px;height:9px;border-radius:3px}
.swim-scroll{overflow-x:auto}
.swim{min-width:760px}
/* minmax(0,1fr) and the min-width:0 on .swim-cell/.post-chip — see the note on
   .cal-dow above. A bare 1fr here let a long post title spill into the next day. */
.swim-head{display:grid;grid-template-columns:120px repeat(7,minmax(0,1fr));border-bottom:1px solid var(--line)}
.swim-head>div{padding:8px 6px;font-size:11px;font-weight:600;text-align:center;color:var(--muted);border-left:1px solid var(--line)}
.swim-head>div:first-child{border-left:0;text-align:left}
.swim-head .wd small{display:block;font-weight:400;font-size:10px}
.swim-head .wd.today{color:var(--uh)}
.swim-row{display:grid;grid-template-columns:120px repeat(7,minmax(0,1fr));border-bottom:1px solid var(--line);min-height:56px}
.swim-lane{padding:8px 6px;font-size:12px;font-weight:600;display:flex;align-items:center;gap:6px}
.swim-lane .sw{width:9px;height:9px;border-radius:3px;flex:none}
.swim-cell{border-left:1px solid var(--line);min-width:0;padding:5px 5px;display:flex;flex-direction:column;gap:4px}
.swim-cell.today{background:#faf9ff}
.post-chip{color:#fff;border-radius:6px;padding:4px 7px;font-size:11px;line-height:1.25;box-shadow:var(--shadow)}
/* Now a <button>: reset the UA styling, and make it look clickable, because it
   is — it navigates to the deliverable's row like a calendar chip does. */
.post-chip{display:block;width:100%;min-width:0;max-width:100%;border:0;text-align:left;font-family:inherit;cursor:pointer;
  transition:transform .08s,box-shadow .08s}
.post-chip:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(13,23,48,.22)}
.post-chip:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.post-chip .pc{opacity:.95;display:block;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.post-chip .pstat{display:inline-block;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.3px;background:rgba(255,255,255,.28);border-radius:4px;padding:0 4px;margin-top:2px}
.footnote{color:var(--muted);font-size:11.5px;margin-top:14px;line-height:1.6}
.sec-title{font-size:13px;font-weight:700;margin:20px 0 8px;letter-spacing:.2px}
/* Only the two section titles that carry an action button, so the other eleven
   keep their inline layout untouched. float:right took the button out of flow,
   so on a narrow viewport it sat on top of the subtitle instead of wrapping
   under it; margin-left:auto holds it right without leaving the flow. */
.sec-title.with-act{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px}
.sec-title.with-act .btn{margin-left:auto}
/* ==========================================================================
   v2 — auth, tabs, task tier, progress, ideas, squad, chat.
   Everything above this line is the v1 board and is unchanged.
   ========================================================================== */

/* A class rule that sets `display` outranks the browser's low-specificity
   [hidden] rule, so `hidden` silently stops working on .modal-backdrop,
   .chat-drawer, .alert-bar and friends. This restores it globally. */
[hidden]{display:none !important}

/* ------------------------------------------------------------ boot / login */

.boot{position:fixed;inset:0;display:grid;place-items:center;background:var(--bg);z-index:50}
.boot-inner{color:var(--muted);font-size:13px}

.login-body{display:grid;place-items:center;min-height:100vh;background:linear-gradient(160deg,#f5f6fa,#eceefb)}
.login-card{background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:0 12px 40px rgba(13,23,48,.1);padding:32px;width:min(400px,92vw)}
.login-card h1{font-size:20px;margin:0 0 2px}
.login-card .sub{margin-bottom:22px}
.login-brand{display:flex;gap:5px;margin-bottom:16px}
.login-brand .sw{width:26px;height:6px;border-radius:3px}
.login-card label{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);margin-bottom:12px}
.login-card input{display:block;width:100%;margin-top:4px;font:inherit;font-size:14px;padding:9px 11px;border:1px solid var(--line);border-radius:8px}
.login-error{background:#fdecec;color:var(--red);border:1px solid #f8d0d0;border-radius:8px;padding:8px 10px;font-size:12.5px;margin-bottom:12px}
.login-foot{margin:16px 0 0;text-align:center}
.btn.block{width:100%;padding:10px;font-weight:600}

/* ---------------------------------------------------------------- chrome -- */

/* margin-left:auto keeps it hard right even on a narrow viewport where it
   wraps onto a row of its own. .top-right is gone — it was the column that
   coupled the account controls to the brand legend. */
.user-chip{display:flex;align-items:center;gap:8px;font-size:12.5px;margin-left:auto;white-space:nowrap}
.role-badge{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;padding:2px 7px;border-radius:20px;background:#eef0f7;color:var(--muted)}
.role-badge.role-admin{background:#efeaff;color:#5b3fd6}
.role-badge.role-director{background:#e7f6ee;color:#12794a}
.role-badge.role-marketing{background:#e8effd;color:#2854c5}
.role-badge.role-contributor{background:#fff3e0;color:#a35f00}
.btn-sm{padding:4px 9px;font-size:12px}
/* The top bar's "?" — square rather than a text button, so it reads as an icon
   beside "Sign out" instead of competing with it. Circular and monospaced
   because a lone "?" in the body face sits off-centre in a rounded rect. */
.btn-icon{
  width:26px;height:26px;padding:0;border-radius:50%;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13px;font-weight:700;line-height:1;
  display:inline-grid;place-items:center;flex:none}
.btn-icon:hover{border-color:var(--uh);color:var(--uh)}
/* The gear is an SVG where the "?" is a glyph; inline SVG keeps its line-height
   otherwise and sits a pixel low inside the circle. */
.btn-icon svg{display:block}
/* Settings and Help have no tab to highlight, so the control itself carries the
   state — without it there is no answer on screen to "which of these am I in". */
.btn-icon.on{background:var(--uh);border-color:var(--uh);color:#fff}
.btn-icon.on:hover{color:#fff}
.btn-ghost{background:transparent;border-color:transparent;color:var(--muted)}
.btn-ghost:hover:not(:disabled){background:#f0f1f6}
.btn.danger,.btn-danger-ghost{color:var(--red);border-color:#f3cfcf}
.btn.danger:hover:not(:disabled),.btn-danger-ghost:hover:not(:disabled){background:#fdecec}

.alert-bar{display:flex;align-items:center;gap:12px;background:#fff8e8;border:1px solid #f2dfae;border-radius:12px;padding:10px 14px;margin-bottom:14px}
.alert-icon{width:22px;height:22px;border-radius:50%;background:var(--amber);color:#fff;font-weight:700;display:grid;place-items:center;flex:none;font-size:13px}
.alert-body{flex:1}
.alert-body strong{font-size:13px}

.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:16px;flex-wrap:wrap;background:var(--bg)}
.tabs button{border:0;background:none;font:inherit;font-size:13.5px;padding:9px 14px;cursor:pointer;color:var(--muted);border-bottom:2px solid transparent;margin-bottom:-1px;display:inline-flex;align-items:center;gap:6px}
.tabs button:hover{color:var(--ink)}

/* Global search. Lives in the sticky tab bar so it is reachable from every tab
   and at any scroll position. */
.gs{position:relative;margin-left:auto;flex:1 1 260px;max-width:520px;display:flex;align-items:center;padding-bottom:6px}
#gs-input{width:100%;font:inherit;font-size:13px;padding:8px 44px 8px 12px;border:1px solid var(--line);border-radius:9px;background:#fff;color:var(--ink)}
#gs-input:focus{outline:none;border-color:var(--uh);box-shadow:0 0 0 3px rgba(114,94,254,.14)}
.gs-kbd{position:absolute;right:10px;top:50%;transform:translateY(-60%);font-size:10.5px;color:var(--muted);border:1px solid var(--line);border-radius:5px;padding:1px 5px;background:#fbfbfe;pointer-events:none}
.gs-panel{position:absolute;top:calc(100% + 2px);right:0;width:min(560px,92vw);max-height:min(60vh,520px);overflow-y:auto;background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 40px rgba(13,23,48,.18);z-index:36;padding:6px}
.gs-group + .gs-group{border-top:1px solid var(--line);margin-top:4px;padding-top:4px}
.gs-head{display:flex;justify-content:space-between;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);padding:6px 10px 4px}
.gs-head span{font-weight:700}
.gs-row{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;border:0;background:none;font:inherit;padding:7px 10px;border-radius:8px;cursor:pointer}
.gs-row:hover,.gs-row.on{background:#f2f0ff}
.gs-row .gs-t{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto}
.gs-row .gs-s{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}
.gs-row mark{background:#ffe9a8;color:inherit;border-radius:2px;padding:0 1px}
.gs-more{display:block;width:100%;text-align:left;border:0;background:none;font:inherit;font-size:11.5px;color:var(--uh);padding:5px 10px;cursor:pointer}
.gs-empty{padding:16px;text-align:center;color:var(--muted);font-size:12.5px}
@media (max-width:820px){.gs{flex-basis:100%;max-width:none;margin-left:0}.gs-kbd{display:none}}
.tabs button.on{color:var(--ink);font-weight:600;border-bottom-color:var(--uh)}
.badge{background:var(--uh);color:#fff;font-size:10px;font-weight:700;border-radius:20px;padding:1px 6px;min-width:17px;text-align:center}
/* 037 · the attention bell on the Ideas nav entry, beside the pill it varies.
   A count pill and an attention marker want opposite things: the pill is a
   figure you read, the bell is a shape you notice once and then stop looking at.
   So it drops the background and padding and keeps only the position — the glyph
   carries itself, and a coloured lozenge around an emoji reads as a badge that
   failed to load. The count is not lost, it moves to the title attribute. */
/* 038 · a drawn icon rather than the emoji. An emoji is the font's artwork, not
   the page's: it arrives full-colour, sits on its own baseline and looks
   different on every OS — beside a flat nav strip it read as a sticker. The SVG
   inherits currentColor, so it takes the brand purple here and would follow any
   later change to it. */
.badge.bell{background:none;padding:0;min-width:0;line-height:0;
  display:inline-flex;align-items:center;vertical-align:-2px;
  color:var(--uh);cursor:default}
.badge.bell svg{width:14px;height:14px;display:block}
/* X-5. Red is reserved for work that is actually LATE — due-today and pending
   approvals stay in the ordinary badge colour. A badge that is always red is a
   badge nobody reads, which is the failure this item exists to prevent. */
.badge.urgent{background:#e04848}

.pad{padding:14px 16px}

/* Settings — one tab, four sections, nav on the side so it doesn't compete
   with the top-level tabs for the header strip. */
.settings{display:grid;grid-template-columns:196px minmax(0,1fr);gap:20px;align-items:start}
.subnav{display:flex;flex-direction:column;gap:2px;position:sticky;top:var(--tabs-h,0px);padding-top:20px}
.subnav button{border:0;background:none;font:inherit;font-size:13.5px;text-align:left;padding:8px 11px;
  cursor:pointer;color:var(--muted);border-radius:8px;border-left:2px solid transparent}
.subnav button:hover{color:var(--ink);background:#eef0f7}
.subnav button.on{color:var(--ink);font-weight:600;background:#fff;border-left-color:var(--uh);box-shadow:var(--shadow)}
.settings-body .sec-title:first-child{margin-top:20px}
@media (max-width:820px){
  /* minmax(0,1fr), not 1fr — the same reason the desktop rule above says it.
     A bare 1fr track refuses to shrink below its content, so the vocabulary
     table pushed this column wider than the viewport and took the whole page
     with it, defeating the .table-scroll the table is already wrapped in. The
     stray horizontal scroll on Settings at narrow widths was this, not the
     buttons that appeared to be sticking out. */
  .settings{grid-template-columns:minmax(0,1fr);gap:6px}
  .subnav{flex-direction:row;flex-wrap:wrap;position:static;padding-top:12px}
  .subnav button{border-left:0;border-bottom:2px solid transparent}
  .subnav button.on{border-left:0;border-bottom-color:var(--uh)}
}
/* Definitions on column headers. abbr's default dotted underline reads as a
   typo in a table head; the marker is the cursor and the muted color. */
th abbr[title]{text-decoration:none;border-bottom:1px dotted var(--grey);cursor:help}

.table-scroll{overflow-x:auto}

/* A collapsed border belongs to the TABLE, not to the cell it was declared on,
   so it does not travel with a sticky th: the header pins, its underline stays
   behind at the top of the table, and rows scroll flush against the labels with
   nothing separating them. An inset shadow is painted by the cell, so it moves.
   The border has to come off first or the two stack into a 2px rule. */
#table-view thead th{border-bottom:0;box-shadow:inset 0 -1px 0 var(--line)}

/* The 1320px floor belongs to the BOARD table, which carries 13-15 columns —
   crushing a campaign name to three words per line is worse than a scrollbar.
   It used to be on `.table-scroll>table`, i.e. every table in the app: the
   five-column audit log, the six-column vocabulary and the seven-column user
   list all inherited a floor twice their natural width and scrolled sideways
   inside a pane wide enough to hold them, with the row actions clipped off the
   right edge. Scoped to the one table that needs it. */
#table-view .table-scroll > table{min-width:1040px}

/* …and then earn that floor back. Every cell carries data-c naming its column
   (tagCells in board.js), so the table sheds columns by priority as the pane
   narrows rather than shoving the whole thing under a scrollbar.

   The breakpoints are measured, not guessed: at 1200px the eleven columns come
   to 1266px against a 1160px container, and each rung below drops the widest
   remaining column that isn't load-bearing. Brand, Campaign, Deliverable,
   Progress and Status never drop.

   CTA and Assets are not in this table at all — see the note above columns()
   in board.js. .wrap caps at 1400px so the container never exceeds ~1360, and
   those two could not fit at any real viewport. */
@media (max-width:1300px){
  /* Approval is the widest optional column and the most redundant: it sits
     next to Status as a second pill, and the campaign card shows both. */
  #table-view [data-c="approvalStatus"]{display:none}
  #table-view .table-scroll > table{min-width:0}
}
@media (max-width:1250px){
  #table-view [data-c="owner"]{display:none}
}
@media (max-width:1160px){
  /* End date and Channel go together: the pair is what tips a 960px container
     over, and Start plus the deliverable's own date already carry the timing. */
  #table-view [data-c="endDate"],#table-view [data-c="channel"]{display:none}
  #tbody td:nth-child(2){min-width:150px}
  #tbody td:nth-child(3){min-width:160px}
  #table-view th,#table-view td{padding-left:8px;padding-right:8px}
}
@media (max-width:940px){
  /* Last one out. Budget survives longest of the optional columns because the
     people who can see it are the ones who came for it — but a table that
     scrolls sideways hides it just as thoroughly as dropping it does. */
  #table-view [data-c="_budget"]{display:none}
  #tbody td:nth-child(2){min-width:130px}
  #tbody td:nth-child(3){min-width:140px}
}

/* These sit AFTER the breakpoints above and carry the same specificity, so 196
   and 214 win at every width and the 150/160 and 130/140 floors inside the
   1160px and 940px blocks never apply. That is the behaviour 0b2f505 verified
   overflow against, at 1440/1280/1100/1000/900 — so it stays. Do not "fix" it by
   deleting this block instead: that silently switches on two breakpoints whose
   layout nobody has ever seen. */
td.nowrap,th.nowrap{white-space:nowrap}
#tbody td:nth-child(2){min-width:196px}
#tbody td:nth-child(3){min-width:214px}
/* Everything else sizes to its content and wraps instead of scrolling. */
#users-table,#vocab-table,#audit-table,#actuals-table{min-width:0;table-layout:auto}
#users-table td,#vocab-table td,#audit-table td{word-break:break-word}
#users-table .u-act{display:flex;gap:6px;flex-wrap:wrap}
/* The wrap-instead-of-scroll rule above is right for text and wrong for the one
   cell that holds controls. Between the 1080px breakpoint and roughly 1400px the
   seven columns squeezed this one to ~54px, and `word-break:break-word` then
   broke the labels mid-word — "Deactivat e" on three lines, which reads as a
   rendering fault rather than a button. Labels stay whole and the cell takes the
   room they need; .table-scroll handles the overflow that causes, which is what
   it is for. flex-wrap still stacks them when that is all the room there is. */
#users-table td:last-child{word-break:normal}
#users-table .u-act .btn{white-space:nowrap}
#users-table select{max-width:100%}

/* Below this the seven-column user list stops fitting no matter what, so drop
   the two columns that restate something already on the row: "Sees" is a gloss
   on Role, and last sign-in is reference data, not something you act on. */
@media (max-width:1080px){
  #users-table th:nth-child(4),#users-table td:nth-child(4){display:none}
}
@media (max-width:900px){
  #users-table th:nth-child(6),#users-table td:nth-child(6){display:none}
  #audit-table th:nth-child(5),#audit-table td:nth-child(5){display:none}
}
@media (max-width:760px){
  #users-table th:nth-child(2),#users-table td:nth-child(2){display:none}
  #vocab-table th:last-child,#vocab-table td:last-child{display:none}
}

#tbody .camp{line-height:1.3}
.loading{color:var(--muted);font-size:13px;padding:22px;text-align:center}
.error-box{background:#fdecec;border-radius:8px;padding:12px;color:var(--red);font-size:13px}
.hint{font-size:11.5px;color:var(--muted);margin:8px 0 0;line-height:1.5}
/* Two outcomes worth colouring, both in the invite modal: the mail went, or it
   didn't and the copied link is now the way through. Tinted rather than plain
   coloured text — this is the line that changes what the admin does next, and it
   sits above a link box that is already visually busy. */
.hint.ok{color:#14713f;background:#e8f6ee;border:1px solid #bfe3cd;
  border-radius:6px;padding:7px 10px;font-size:12px}
.hint.warn{color:#7a4a00;background:#fff6e6;border:1px solid #f0dcb4;
  border-radius:6px;padding:7px 10px;font-size:12px}
.kpi-note{font-size:10px;color:var(--muted);margin-top:3px}

/* -------------------------------------------------------------- progress -- */

.prog{display:flex;align-items:center;gap:7px;min-width:96px}
.prog-track{flex:1;height:6px;border-radius:6px;background:var(--line);overflow:hidden}
.prog-track>i{display:block;height:100%;border-radius:6px}
.prog-track.big{height:9px;margin-top:6px}
.prog-track.big>i{background:var(--uh)}
.prog-num{font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--muted);min-width:32px;text-align:right}
/* Hatched = the percentage is derived from status, not measured from tasks. */
.prog.estimated .prog-track{background:repeating-linear-gradient(45deg,var(--line),var(--line) 3px,#f0f1f6 3px,#f0f1f6 6px)}
.prog.estimated .prog-num{opacity:.7}
.prog.compact{min-width:74px}
.progress-summary{border-top:1px solid var(--line);margin-top:16px;padding-top:12px;font-size:13px}

/* ------------------------------------------------------ table task tier --- */

tr.tr-task td{background:#fbfbfe;font-size:12px;padding-top:5px;padding-bottom:5px;border-bottom:1px dashed var(--line)}
tr.tr-task td.ttitle{padding-left:56px}
.task-status-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;margin-top:5px;vertical-align:middle;flex:none}
.plat-dot{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:7px;vertical-align:middle}
.task-count{font-size:10.5px;font-weight:700;color:var(--uh);background:#efeaff;border-radius:20px;padding:1px 6px;margin-left:6px}
/* A fixed box either way, so a deliverable with tasks and one without still
   line their titles up. Only the version that can actually open carries
   .twist-task, and the pointer lives on that class alone — the empty spacer
   must not read as something to click. */
.dtitle .twist{display:inline-block;width:14px;color:var(--muted);font-size:10px}
.twist-task{cursor:pointer;user-select:none}
/* Sorting is offered by data-k. th{cursor:pointer} is global, so a header that
   isn't a control has to opt out — the campaign list does the same at
   .camp-list th. */
#thead-row th:not([data-k]){cursor:default}
/* Row actions were opacity:0 until the row was hovered, so nothing on the board
   advertised that a campaign could be edited or a deliverable added — you had
   to already know. They are always visible now, and shaped like buttons rather
   than like text, but kept quiet enough not to compete with the row content. */
.row-act{display:inline-flex;align-items:center;gap:3px;border:1px solid var(--line);
  background:#fff;color:var(--ink);font:inherit;font-size:11.5px;line-height:1.6;
  cursor:pointer;padding:2px 8px;margin:2px 4px 0 0;border-radius:6px;
  transition:background .12s,border-color .12s,color .12s}
.row-act:hover{background:var(--navy);border-color:var(--navy);color:#fff}
/* The "add" actions carry the brand tint so the creating action reads first. */
.row-act.add{color:var(--uh);border-color:#d8d2ff;background:#f6f4ff}
.row-act.add:hover{background:var(--uh);border-color:var(--uh);color:#fff}
/* Template actions are secondary to editing; present, but visibly lighter. */
.row-act.subtle{color:var(--muted)}
.row-act.subtle:hover{background:var(--muted);border-color:var(--muted);color:#fff}

/* Timeline: fill shows progress inside the campaign bar. */
.tl-bar{position:relative}
.tl-fill{position:absolute;inset:0;right:auto;background:rgba(255,255,255,.32);border-radius:6px 0 0 6px}
.tl-bar-label{position:relative;z-index:1}
.tl-today{position:absolute;top:0;bottom:0;width:2px;background:var(--red);opacity:.5;z-index:2}

.cal-chip.done{opacity:.55;text-decoration:line-through}
.cal-chip-pct{float:right;font-weight:700;opacity:.85;margin-left:4px}

/* --------------------------------------------------------------- my work -- */

/* X-1 removed the campaign grouping (.mw-group/.mw-group-head) and the two
   ad-hoc overdue colour classes; My work groups by deadline now and wears the
   board's .tg shell for it. See the X-1 block at the foot of this file. */
.mywork-summary{display:flex;gap:18px;font-size:13px;padding-bottom:12px;border-bottom:1px solid var(--line);margin-bottom:14px}
.mw-task{display:flex;align-items:flex-start;gap:9px;padding:9px 10px;border:1px solid var(--line);border-radius:9px;margin-bottom:6px;background:#fff}
.mw-task.is-done{opacity:.55}
.mw-task.is-overdue{border-color:#f3cfcf;background:#fffafa}
.mw-main{flex:1;min-width:0}
.mw-title{font-size:13px;font-weight:600}
.mw-task.is-done .mw-title{text-decoration:line-through}
.mw-meta{font-size:11.5px;color:var(--muted);margin-top:2px;display:flex;align-items:center;gap:5px;flex-wrap:wrap}
.mw-status{min-width:120px;font-size:12px;padding:5px 7px}

/* ----------------------------------------------------------------- ideas -- */

.idea-card{padding:16px 18px;margin-bottom:12px;border-left:3px solid var(--line)}
.idea-card.status-proposed{border-left-color:var(--blue)}
.idea-card.status-accepted{border-left-color:var(--green)}
.idea-card.status-rejected{border-left-color:var(--red);opacity:.72}
.idea-card.status-refined{border-left-color:var(--grey);opacity:.72}
.idea-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:8px}
.idea-head>div:first-child{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.idea-status{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.idea-lineage{font-size:10.5px;color:var(--muted);background:#f0f1f6;border-radius:20px;padding:1px 7px}
.plat-tag{font-size:10.5px;font-weight:700;border-radius:20px;padding:1px 8px}
/* 033 · clamped to two lines. The body has been clamped since 030 while the title
   rendered in full, so a long "title" — which the old form's placeholder actively
   encouraged — pushed the card taller than the three-line body it sat above. The
   form caps it at 120 and the server truncates there too; this is what keeps a
   pre-existing row from an earlier import from breaking the layout. */
.idea-title{font-size:15px;margin:0 0 8px}
/* The count under the title field. Quiet until it matters — see bindTitleCount. */
.fld-count{align-self:flex-end;font-size:10.5px;color:var(--muted);
  text-transform:none;letter-spacing:0;min-height:13px}
.fld-count.near{color:#b8860b;font-weight:600}
/* A description worth writing needs room to write it in, and room to resize. */
#idea-body{min-height:170px;resize:vertical;line-height:1.55}
.idea-body{font-size:13px;line-height:1.6;color:#2c3446;background:#fbfbfe;border:1px solid var(--line);border-radius:8px;padding:11px 13px;margin-bottom:9px;white-space:pre-wrap}
.idea-actions{display:flex;gap:7px;margin-top:12px;flex-wrap:wrap;align-items:center}

/* 028 · the queue got scannable, and grew a backlog and an archive.

   THE CLAMP IS THE POINT of most of this. A generated idea's body runs to a
   paragraph or three and the panel rendered all of it, so four ideas filled a
   screen. -webkit-line-clamp rather than a max-height in ems: the clamp cuts on a
   line boundary at whatever the column width turns out to be, where a height
   guesses and leaves a half-line showing. Both prefixed and unprefixed
   properties, because the unprefixed `line-clamp` is the standard and the
   prefixed pair is what actually ships in every engine today.

   Same technique as `.cc-desc` on the campaign cards (line-clamp:3, ~line 311),
   which is also where the note lives about this needing a block container rather
   than a table cell. */
.idea-body.clamped{
  display:-webkit-box;-webkit-box-orient:vertical;
  -webkit-line-clamp:3;line-clamp:3;
  overflow:hidden;margin-bottom:6px;
}
/* Looks like the link it behaves as, but it is a button — a control that opens a
   popup is not a navigation. */
.idea-more{background:none;border:0;padding:0;margin:0 0 4px;cursor:pointer;
  font:inherit;font-size:12px;font-weight:600;color:var(--blue)}
.idea-more:hover{text-decoration:underline}

/* The title opens the idea, so it has to be a real button — keyboard-reachable,
   with a focus ring — while looking exactly like the heading it replaced. */
/* The clamp goes on the BUTTON, not the <h3> around it, because the button is
   what holds the text. Putting -webkit-box on the heading did nothing: its only
   child is an inline-block <button>, which becomes a single unclamped box child
   rather than the lines the clamp counts — measured at 348px on a long title,
   exactly as if the rule were absent. */
.idea-open{background:none;border:0;padding:0;margin:0;cursor:pointer;
  font:inherit;color:inherit;text-align:left;
  display:-webkit-box;-webkit-box-orient:vertical;
  -webkit-line-clamp:2;line-clamp:2;overflow:hidden}
.idea-open:hover{color:var(--blue)}

.idea-tags{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.idea-pick{width:15px;height:15px;cursor:pointer;flex:none}
.idea-card.backlogged{border-left-color:var(--purple,#6b5ce7)}

/* The three views. Sits above the toolbar, so it reads as "which list" rather
   than "which filter". */
.ideas-views{margin-bottom:10px}
.ideas-views .badge{margin-left:5px}

/* The bulk bar. Sticks under the measured header stack the same way the board's
   own sticky rows do — a selection you made forty cards ago must still be
   actionable without scrolling back up to find the button. */
/* z-index above .toolbar's 31, and BELOW it in the offset — the bar pins under
   the filters, not over them. Both halves matter: --stick-h now measures the
   visible panel's toolbar (see app.js), so this lands just under the Ideas
   toolbar rather than 4px above the tab bar and behind it. The 4px negative
   overlap is kept so no hairline of page shows between the two. */
.idea-bulk{position:sticky;top:calc(var(--stick-h,140px) - 4px);z-index:32;
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  background:#eef1ff;border:1px solid #c9d2ff;border-radius:9px;
  padding:8px 12px;margin-bottom:11px;font-size:12.5px;font-weight:600}
.idea-bulk .spacer{flex:1}

/* Running-order controls. Fixed width so the four buttons do not change size
   between rows as the arrows enable and disable, which would make the column
   ripple as you move things. */
.idea-rank{display:flex;gap:3px;margin-right:6px}
.idea-rank .btn{min-width:28px;padding:3px 6px;line-height:1.1}
.idea-rank .btn:disabled{opacity:.32;cursor:default}
.idea-target-wrap{display:flex;align-items:center;gap:5px;margin-right:8px;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.idea-target-wrap input{font-size:12px;padding:3px 6px}
.idea-target-ro{margin-right:8px}

/* The detail popup. Blocks rather than one wall of text, because the four fields
   answer different questions and a reader is usually after one of them. */
.idea-detail .idea-tags{margin-bottom:12px}
.idea-dblock{margin-bottom:14px}
.idea-dlabel{font-size:10.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.4px;color:var(--muted);margin-bottom:4px}
.idea-dblock>div:last-child{font-size:13.5px;line-height:1.65;color:#2c3446}
.idea-dblock.is-feedback>div:last-child{background:#fff8e8;border:1px solid #f2dfae;
  border-radius:8px;padding:10px 12px}
.idea-dmeta{border-top:1px solid var(--line);padding-top:10px;margin-top:4px}

/* Narrow screens: the head already wraps, but the action row holds up to seven
   controls and the rank buttons must not be the thing that squeezes the date
   input to nothing. */
@media (max-width:620px){
  .idea-actions{flex-direction:column;align-items:stretch}
  .idea-rank{margin-right:0}
  .idea-target-wrap{margin-right:0}
}

/* ----------------------------------------------------------------- squad -- */

.squad-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:14px;margin-bottom:18px}
.squad-card{padding:18px}
.squad-card h3{margin:0 0 6px;font-size:15px}
.squad-card p{margin:0 0 14px;line-height:1.55}
.squad-form{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:14px}
.squad-form label,.actual-form label,.user-form label,.form-grid label{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);display:flex;flex-direction:column;gap:3px}
.squad-form .full,.actual-form .full,.form-grid .full{grid-column:1/-1}
.squad-form input,.squad-form select,.squad-form textarea,
.actual-form input,.actual-form select,
.user-form input,.user-form select,
.form-grid input,.form-grid select,.form-grid textarea{width:100%;font:inherit;font-size:13px;padding:7px 9px;border:1px solid var(--line);border-radius:8px;text-transform:none;letter-spacing:0;color:var(--ink);background:#fff}
.form-grid input:disabled{background:#f5f6fa;color:var(--muted)}
.squad-result-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px;flex-wrap:wrap}
.squad-strategy{font-size:13.5px;line-height:1.6;color:#2c3446;background:#fbfbfe;border-left:3px solid var(--uh);padding:10px 14px;border-radius:0 8px 8px 0;margin:0 0 14px}
.idea-preview-list{display:grid;gap:10px}
.idea-preview{border:1px solid var(--line);border-radius:9px;padding:12px 14px}
.ip-head{display:flex;align-items:center;gap:7px;margin-bottom:6px;flex-wrap:wrap}
.ip-body{font-size:12.5px;line-height:1.55;color:#3a4257;margin:5px 0}
.ip-why{margin-top:5px;font-style:italic}

.analysis-headline{font-size:16px;font-weight:600;line-height:1.45;margin-bottom:8px}
.analysis-quality{font-size:12.5px;color:var(--muted);background:#fbfbfe;border-radius:8px;padding:10px 12px;margin-bottom:16px;line-height:1.55}
.finding{border:1px solid var(--line);border-left-width:3px;border-radius:9px;padding:12px 14px;margin-bottom:10px}
.finding.sev-critical{border-left-color:var(--red)}
.finding.sev-warning{border-left-color:var(--amber)}
.finding.sev-watch{border-left-color:var(--blue)}
.finding.sev-positive{border-left-color:var(--green)}
.finding-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.sev-badge{color:#fff;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;padding:2px 7px;border-radius:20px}
.finding-row{display:flex;gap:10px;font-size:12.5px;line-height:1.55;margin-bottom:4px}
.fr-label{flex:none;width:74px;font-size:10px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);padding-top:2px}
.exp-list{display:grid;gap:11px}
.experiment{border:1px solid var(--line);border-radius:9px;padding:13px 15px}
.exp-head{display:flex;align-items:center;gap:8px;margin-bottom:7px;flex-wrap:wrap}
.exp-hypothesis{font-size:13px;line-height:1.55;font-style:italic;color:#2c3446;margin-bottom:9px}
.exp-grid{display:grid;grid-template-columns:1fr 1fr;gap:7px 16px;font-size:12.5px;line-height:1.5}
.exp-grid>div{display:flex;gap:8px}
.effort-badge{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;padding:2px 7px;border-radius:20px;background:#f0f1f6;color:var(--muted)}
.effort-badge.effort-low{background:#e7f6ee;color:#12794a}
.effort-badge.effort-high{background:#fdecec;color:#b03030}
.run-row{display:grid;grid-template-columns:150px 165px 130px 1fr;gap:10px;font-size:12.5px;padding:7px 0;border-bottom:1px solid var(--line);align-items:baseline}
.run-row.is-error{color:var(--red)}
.run-kind{font-weight:600}
.run-summary{color:var(--muted)}

/* ----------------------------------------------------------------- admin -- */

.role-help{line-height:1.9;padding-bottom:12px;border-bottom:1px solid var(--line);margin-bottom:14px}
.user-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:9px;align-items:end}
.user-form button{height:34px}
.actual-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:9px;align-items:end}
.actual-form button{height:34px}
.via-claude{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;background:#efeaff;color:#5b3fd6;border-radius:20px;padding:1px 6px;margin-left:5px}
.detail-cell{max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ----------------------------------------------------------------- modal -- */

.modal-backdrop{position:fixed;inset:0;background:rgba(13,23,48,.42);display:grid;place-items:center;z-index:60;padding:20px}
.modal{background:var(--card);border-radius:14px;box-shadow:0 20px 60px rgba(0,0,0,.3);width:min(560px,100%);max-height:88vh;display:flex;flex-direction:column}
.modal.wide{width:min(820px,100%)}
.modal-head,.modal-foot{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;flex:none}
.modal-head{border-bottom:1px solid var(--line)}
.modal-foot{border-top:1px solid var(--line);justify-content:flex-end;gap:8px}
.modal-foot:empty{display:none}
.modal-body{padding:18px;overflow-y:auto}
.modal-body h4{margin:16px 0 8px;font-size:12.5px}
.modal-body h4:first-child{margin-top:0}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:11px}
.inline-tasks{border-top:1px solid var(--line);margin-top:16px;padding-top:12px}
.inline-tasks-head{font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);margin-bottom:8px}
.inline-task{display:flex;align-items:center;gap:8px;font-size:12.5px;padding:5px 0;border-bottom:1px dashed var(--line)}
.it-title{flex:1}
.it-meta,.it-status{color:var(--muted);font-size:11.5px}
/* ------------------------------------------- links and feedback · A-1 ----- */

/* Row-level notes and feedback. The caption is the point: two lines of grey
   text under a title told nobody which was the brief and which was a review
   comment somebody is waiting on. Feedback gets the magenta rule so it reads as
   "somebody is asking for a change" at a glance. */
.row-note{display:flex;gap:6px;align-items:baseline;font-size:11.5px;color:#4a5266;
  margin-top:3px;padding-left:8px;border-left:2px solid var(--line)}
.row-note.fb{border-left-color:var(--uh2)}
.note-cap{flex:none;font-size:9.5px;text-transform:uppercase;letter-spacing:.5px;
  color:var(--muted);padding-top:1px}
.row-note.fb .note-cap{color:var(--uh2)}
.note-body{min-width:0}

/* Inline link chips on a board row. */
.link-chips{display:inline-flex;flex-wrap:wrap;gap:4px;margin-left:6px;vertical-align:middle}
.link-chip{font-size:10.5px;line-height:1;padding:3px 7px;border-radius:10px;
  background:#eef0f6;color:#3a4256;text-decoration:none;white-space:nowrap;
  border:1px solid transparent}
a.link-chip:hover{border-color:var(--uh);color:var(--uh)}
.link-chip.pending{color:var(--muted);font-style:italic}
.link-chip.more{color:var(--muted)}

/* The links editor inside a modal. */
.links-block,.feedback-block,.review-note-block{border-top:1px solid var(--line);margin-top:16px;padding-top:12px}
/* A label uppercases everything inside it, which is right for the field name
   and wrong for a sentence — the submit-for-review hint has been rendering as
   "A DIRECTOR DECIDES THE OUTCOME…" since it was written. Hints opt back out. */
.squad-form label .hint,.actual-form label .hint,.user-form label .hint,.form-grid label .hint{
  text-transform:none;letter-spacing:0}
.c-reason{margin-top:6px}
.c-reason-note{resize:vertical}
.links-head{font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);
  margin-bottom:8px;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
.links-hint{text-transform:none;letter-spacing:0;font-size:11px;color:var(--muted);opacity:.8}
.links-list{display:flex;flex-direction:column;gap:2px;margin-bottom:8px}
.link-row{display:flex;align-items:center;gap:8px;font-size:12.5px;padding:4px 0;
  border-bottom:1px dashed var(--line)}
.link-row a{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.link-pending{flex:1;color:var(--muted)}
.link-x{flex:none;border:0;background:none;color:var(--muted);cursor:pointer;
  font-size:15px;line-height:1;padding:0 4px}
.link-x:hover{color:var(--red)}
/* Three tracks so the label doesn't collapse on a narrow modal. */
.link-add{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr) auto;gap:6px}
.link-add input{min-width:0}

.feedback-text{width:100%;resize:vertical}
.feedback-foot{display:flex;align-items:center;gap:10px;margin-top:6px}

/* Read-only deliverable, for a reviewer who can't edit. */
.read-only{display:flex;flex-direction:column;gap:6px;font-size:12.5px}
.ro-row{display:flex;gap:10px;align-items:baseline}
.ro-k{flex:none;width:110px;color:var(--muted);font-size:11px;text-transform:uppercase;
  letter-spacing:.4px}

@media (max-width:620px){
  .link-add{grid-template-columns:1fr}
}

.cc-needs{margin-top:8px;padding:7px 9px;border-radius:6px;background:#fff8ec;
  border:1px solid #f0dcb4;font-size:11.5px;color:#6b5423;line-height:1.4;
  display:flex;flex-wrap:wrap;align-items:center;gap:7px}

/* The Social empty state carries a button now, so it needs to lay out as a row
   rather than a paragraph. */
#social-empty{display:flex;flex-wrap:wrap;align-items:center;gap:8px;justify-content:center}

/* --------------------------------------------------- approvals · A-2 ----- */

#mywork-approvals{margin-bottom:14px}
.ap-count{background:var(--uh);color:#fff;border-radius:10px;padding:1px 8px;
  font-size:11px;font-weight:700}
.ap-alone{font-size:11.5px;color:#6b5423;background:#fff8ec;border:1px solid #f0dcb4;
  border-radius:6px;padding:7px 9px;margin-bottom:10px}

.ap-row{display:flex;gap:12px;align-items:flex-start;padding:10px 0;
  border-bottom:1px dashed var(--line)}
.ap-row:last-child{border-bottom:0;padding-bottom:0}
.ap-main{flex:1;min-width:0}
.ap-title{display:flex;gap:7px;align-items:center;flex-wrap:wrap}
/* The work itself — labelled links straight from the deliverable, so a reviewer
   can look at what they are judging without opening the form. */
.ap-proof{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px}
.ap-plink{font-size:11.5px;border:1px solid var(--line);border-radius:20px;
  padding:2px 9px;color:var(--uh);background:#fff;text-decoration:none;
  max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ap-plink:hover{border-color:var(--uh);background:#f6f4ff}
.ap-pmore{font-size:11px;color:var(--muted);align-self:center}
/* Also a <button> in the approvals queue (the title opens the deliverable), so
   this carries the reset — a second .ap-name block competing with this one over
   font-size is how you get a title that renders at the browser's button default
   in one place and 13px in another. It stays a <span> in the violations panel;
   the reset is harmless there. */
.ap-name{font:inherit;font-weight:600;font-size:13px;color:var(--ink);
  background:none;border:0;padding:0;text-align:left;min-width:0}
button.ap-name{cursor:pointer}
.ap-name.ap-open:hover{color:var(--uh);text-decoration:underline}
.ap-name.ap-open:focus-visible{outline:2px solid var(--uh);outline-offset:2px;border-radius:3px}
.ap-kind{font-size:9.5px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);
  border:1px solid var(--line);border-radius:4px;padding:1px 5px}
.ap-meta{font-size:11.5px;color:var(--muted);margin-top:2px}
/* Risk, from the due date — the only honest clock available. */
.ap-meta.late{color:var(--red);font-weight:600}
.ap-meta.soon{color:var(--amber);font-weight:600}

.ap-acts{display:flex;gap:6px;flex:none;flex-wrap:wrap;justify-content:flex-end}
/* Approve and Send back are the two things a phone thumb has to hit. */
.ap-acts .btn{min-height:34px}

.ap-send{margin-top:8px;display:flex;gap:6px;align-items:flex-start;flex-wrap:wrap}
.ap-send textarea{flex:1;min-width:220px;resize:vertical}

.ap-empty{padding:14px 0;font-size:12.5px;display:flex;flex-direction:column;gap:3px}

/* ---- approvals tab · 022 ---- */

/* The toolbar is a six-column grid of minmax(0,1fr), so a .grp is ~190px on a
   1280 viewport. Four scope labels do not fit that, and .seg wraps — so the
   control rendered as a vertical stack sitting on top of the row beside it.
   Spanning three columns gives it the width to stay one strip; below 1100 the
   grid drops to three columns and it takes the whole row. */
.toolbar .grp.ap-scope-grp{grid-column:span 3}
@media (max-width:1100px){.toolbar .grp.ap-scope-grp{grid-column:1/-1}}

/* The pointer left in My work where A-2's full queue used to be. */
.ap-point{display:flex;gap:10px;align-items:center;flex-wrap:wrap;font-size:13px}
.ap-point .btn{margin-left:auto}

/* Red on a count means past the approval deadline, matching .badge.urgent.
   Everything else stays brand purple — an always-red counter is one nobody
   reads, which is the rule X-5 set for the tab badges. */
.ap-count.urgent{background:var(--red)}

/* Small state flags on a row: unassigned, round 2, campaign not approved. */
.ap-flag{font-size:9.5px;text-transform:uppercase;letter-spacing:.4px;
  color:var(--muted);background:#f2f3f8;border-radius:4px;padding:1px 6px}
.ap-flag.warn{color:#9d6200;background:#fff8ec}

/* Who has said what this round. Glyph plus name, wrapping as a group. */
.ap-trail{display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:4px}
.ap-who{font-size:11px;color:var(--muted)}
.ap-who.approved{color:#157e48}
.ap-who.changes_requested,.ap-who.rejected{color:#c13e3e}
.ap-who.overridden{color:#6755e5}
.ap-who.pending{color:var(--muted);font-style:italic}
/* 023. Self-approval is allowed, so this reads as a label rather than a warning
   — outlined in the muted ink of the row it sits on, not red. */
.ap-self{font-size:9px;text-transform:uppercase;letter-spacing:.5px;margin-left:4px;
  border:1px solid currentColor;border-radius:3px;padding:0 3px;opacity:.75;
  vertical-align:1px}

/* Bulk approve, social only. Sits at the right of the .sec-sub flex row. */
.ap-bulk{margin-left:auto;display:flex;gap:6px;flex-wrap:wrap}
.ap-pick{flex:none;width:15px;height:15px;accent-color:var(--uh)}

/* Published without approval. Its own card above the queue, red-ruled, because
   it is a finding rather than an ordinary queue item. Since 024 each row DOES
   have an action — acknowledging it — so it takes the same two-column shape as
   .ap-row rather than the flat line it used to be. */
.ap-viol{margin-bottom:14px;border-color:#f3cfcf}
/* Cleared findings are history, not a warning: the red rule would keep shouting
   about work somebody has already dealt with. */
.ap-viol.resolved{border-color:var(--line)}
.ap-vrow{display:flex;gap:12px;align-items:flex-start;padding:10px 0;
  border-bottom:1px dashed var(--line)}
.ap-vrow:last-child{border-bottom:0;padding-bottom:0}
.ap-vmain{flex:1;min-width:0}
/* 026. The stronger of the two findings in this card, named on the row. Red-on-
   tint rather than a second panel: the heading already says these went out
   without a sign-off, and this says a reviewer had actively refused. */
.ap-vkind{font-size:10.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.4px;color:var(--red);background:#fdeaea;border:1px solid #f3cfcf;
  border-radius:9px;padding:1px 7px;white-space:nowrap;cursor:help}

/* ---- approval history, inside the deliverable form · 024 ---- */

.ap-hist{margin:14px 0 0;border-top:1px solid var(--line);padding-top:12px}
.ap-hist summary{cursor:pointer;font-size:12px;font-weight:600;color:var(--uh);
  list-style:none}
.ap-hist summary::-webkit-details-marker{display:none}
.ap-hist summary::before{content:"▸ ";color:var(--muted)}
.ap-hist[open] summary::before{content:"▾ "}
.ap-hist-body{margin-top:10px;display:flex;flex-direction:column;gap:12px}

/* One block per round — the unit people think in ("it took three passes"), which
   a flat list of decisions loses. */
.ap-round{border-left:2px solid var(--line);padding-left:10px}
.ap-round-head{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;
  font-size:12.5px;margin-bottom:5px}
.ap-hrow{display:flex;gap:7px;align-items:baseline;flex-wrap:wrap;font-size:12px;
  padding:2px 0}
.ap-hwho{font-weight:600}
.ap-hverb{color:var(--muted)}
/* The verdict colours the row's own text, matching the queue's trail glyphs, so
   a scan down the history reads as a sequence of outcomes. */
.ap-hrow.approved .ap-hverb{color:#157e48}
.ap-hrow.changes_requested .ap-hverb,.ap-hrow.rejected .ap-hverb{color:#c13e3e}
.ap-hrow.overridden .ap-hverb{color:#6755e5}
.ap-hrow.pending .ap-hverb{font-style:italic}
/* Full width so a long comment wraps under the name rather than squeezing it. */
.ap-hnote{flex-basis:100%;color:#4a5266;padding-left:8px;
  border-left:2px solid var(--uh2)}

/* The approval block on the campaign form: the yes/no question, then the
   people it turns on. Boxed and tinted so it reads as one decision rather than
   as three more fields at the bottom of a twelve-field form. */
/* --bg, not --uh2: the brand accents are saturated (--uh2 is #C15BE3) and one
   of them behind a form section paints a purple slab through the middle of the
   modal. This wants the page's own off-white. */
.approval-need{margin-top:10px;padding:10px 12px;border:1px solid var(--line);
  border-radius:8px;background:var(--bg)}
.approval-need .links-head{margin-bottom:2px}

/* The question sits on one line with its two answers; it wraps to two lines on
   a narrow modal rather than squeezing the radios. */
.need-ask{display:flex;flex-wrap:wrap;align-items:center;gap:6px 14px;margin:6px 0}
.need-q{font-size:12.5px;color:var(--ink)}
.need-opts{display:flex;gap:14px}
.need-body{margin-top:2px}
.need-none,.need-warn{margin:4px 0 0}
.need-warn{color:#8a6d0b}

/* One row per approver, stacked. Each is a normal .form-grid-style stacked
   field (caption above control), which is right here — "APPROVER" over a
   dropdown — so these keep the inherited look rather than overriding it.

   .ap-slot, NOT .ap-pick: that name is already the bulk-select checkbox in the
   approvals queue (approvals.js), and it carries width:15px;height:15px. Reused
   here it sized these labels to 15 square and every dropdown escaped its own
   row and painted over the caption below it. */
.ap-slots{display:flex;flex-direction:column;gap:8px;margin:8px 0 10px;max-width:320px}
.form-grid .ap-slot{gap:3px}

/* These are <label>s inside .form-grid, which styles every label as a stacked
   field — column flex, 10.5px uppercase muted caption, control underneath.
   That is right for "CAMPAIGN NAME" above an input and wrong for a checkbox
   beside a sentence: it put the box above the text and shouted it in caption
   grey. Overridden by .form-grid specificity rather than by adding !important,
   and every inherited caption property has to be named — resetting only
   flex-direction leaves the uppercase behind. */
.form-grid .need-opt,.form-grid .ap-social{
  flex-direction:row;text-transform:none;letter-spacing:0;font-size:12.5px;
  font-weight:400;color:var(--ink)}
.form-grid .need-opt{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.form-grid .ap-social{display:flex;align-items:flex-start;gap:8px;margin-top:4px}
/* The caption is a real element, not a bare text node beside the box. As a text
   node it was an anonymous flex item that would not shrink, so on a 375px modal
   it took its own line under the checkbox and pushed the ⓘ onto a third. Given
   a span it wraps inside its own column with the box still beside it — which is
   also why .ap-social no longer needs flex-wrap. */
.ap-social-txt{flex:1;min-width:0}
/* The wrapped hint that used to live here became a tooltip on the checkbox
   (025), so the flex-basis rule that gave it its own row under the box is gone
   with it. .ap-social keeps flex-wrap for the label text itself. */
/* .form-grid sets width:100% on inputs — right for a text box filling its
   column, catastrophic for a checkbox, which stretched to the full 769px of the
   modal and painted itself straight through the name beside it. Sized
   explicitly, and `flex:none` alone was not enough: the box was not flexing, it
   was obeying a width. */
.form-grid .need-opt input[type=radio],
.form-grid .ap-social input[type=checkbox]{
  flex:none;width:15px;min-width:15px;height:15px;margin:0;accent-color:var(--uh)}
/* The dropdowns are the one control here that SHOULD fill its column, so they
   keep .form-grid's width:100% — .ap-slots caps the column instead. */

@media (max-width:620px){
  /* .ap-vrow rides along with .ap-row, and had to: the `.ap-acts{width:100%}`
     below is not scoped to either, so in a .ap-vrow that was still a flex ROW it
     took the whole line and collapsed .ap-vmain (flex:1, min-width:0) to zero —
     the buttons painted straight over the brand tag and title, and the meta line
     wrapped one word per line. 024 gave this card .ap-row's two-column shape but
     not its mobile rule. */
  .ap-row, .ap-vrow{flex-direction:column;gap:8px}
  .ap-acts{justify-content:flex-start;width:100%}
  .ap-acts .btn{flex:1;min-height:44px}
  /* A checkbox is the one control here a thumb has to hit precisely. */
  .ap-pick{width:20px;height:20px}
  .ap-bulk{margin-left:0;width:100%}
}

/* ----------------------------------------------- performance · D-4 ------ */

.perf-tools{float:right;display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
/* Full-width so it wraps onto its own line under the controls rather than
   widening the row. Right-aligned to sit under the Sync button it describes. */
.pf-lastsync{flex-basis:100%;text-align:right;font-size:11.5px;font-weight:400;
  color:var(--muted);margin-top:2px}
.pf-lastsync.bad{color:var(--red,#e04848)}
.perf-tools select{font-size:12.5px}

/* Provenance strip. Deliberately the first thing under the title — every number
   below is only as good as the feed it came from, and the feeds are stale by
   different amounts. */
.src-strip{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 14px}
.src{display:flex;flex-direction:column;gap:1px;padding:7px 11px;border-radius:8px;
  font-size:11.5px;border:1px solid var(--line);background:var(--card);min-width:190px}
.src b{font-size:12px}
.src span{color:var(--muted)}
.src.ok{border-left:3px solid var(--green)}
.src.warn{border-left:3px solid var(--amber)}
.src.bad{border-left:3px solid var(--red)}
/* A failure message has to be readable, so this card is allowed to grow. */
.src.bad{max-width:520px}
.src-err{color:var(--red);white-space:normal;line-height:1.35}
.src.warn{max-width:520px}
.src-note{color:var(--amber);white-space:normal;line-height:1.35;margin-top:2px}
.src.none{border-left:3px solid var(--grey)}

.perf-kpis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:10px}
.pk{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:11px 13px;
  display:flex;flex-direction:column;gap:2px;box-shadow:var(--shadow)}
.pk-lbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.pk-val{font-size:21px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.15}
.dl{font-size:11px;font-weight:600}
.dl.good{color:var(--green)}
.dl.bad{color:var(--red)}
.dl.none{color:var(--muted);font-weight:400}

.card.gap{margin-top:14px}

/* Hover detail for every chart on this screen. position:fixed and driven from
   clientX/clientY, so it is never clipped by .table-scroll or by the card's own
   overflow the way an absolutely-positioned tip inside the chart would be.
   pointer-events:none is load-bearing — without it the tip sits under the
   cursor, takes the mouseout, and flickers. */
.perf-tip{position:fixed;z-index:70;pointer-events:none;max-width:300px;
  background:var(--navy);color:#fff;border-radius:9px;padding:9px 11px;
  box-shadow:0 10px 30px rgba(13,23,48,.28);font-size:12px;line-height:1.45}
.perf-tip .pt-head{font-weight:700;font-size:12.5px;margin-bottom:3px}
.perf-tip .pt-total{font-size:15px;font-weight:700;font-variant-numeric:tabular-nums;
  margin-bottom:6px}
.perf-tip .pt-total span{font-size:11px;font-weight:400;opacity:.7;margin-left:5px}
.perf-tip .pt-rows{display:flex;flex-direction:column;gap:3px}
.perf-tip .pt-row{display:flex;align-items:center;gap:7px}
.perf-tip .pt-dot{width:8px;height:8px;border-radius:2px;flex:none}
.perf-tip .pt-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.85}
.perf-tip .pt-val{margin-left:auto;font-variant-numeric:tabular-nums;font-weight:600;flex:none}
.perf-tip .pt-none{opacity:.7}
/* Provenance. Dimmer than the numbers above it, and always last — it answers
   "where is this from", which you read after the value, not before. */
.perf-tip .pt-src{margin-top:7px;padding-top:6px;border-top:1px solid rgba(255,255,255,.18);
  font-size:10.5px;opacity:.75;line-height:1.4}

.bar-row[data-tip]{cursor:help;border-radius:5px}
.bar-row[data-tip]:hover{background:#f4f4fb}
.chart-hit{cursor:crosshair}
.chart-cursor{stroke:var(--muted);stroke-width:1;stroke-dasharray:3 3;opacity:.7;pointer-events:none}

/* Hand-rolled SVG. No build step and a CSP that blocks CDNs, so a charting
   library would mean vendoring a blob nobody updates. */
.chart{width:100%;height:220px;display:block}
.chart .axis{stroke:var(--line);stroke-width:1}
.chart .tick{fill:var(--muted);font-size:11px;text-anchor:middle}
/* The peak label sits at the left edge, so it must not be centred on its x. */
.chart .tick.lead{text-anchor:start}

.perf-cols{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);gap:14px;margin-top:14px}

.perf-table{width:100%;border-collapse:collapse;font-size:12.5px}
.perf-table th{text-align:left;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;
  color:var(--muted);padding:5px 8px;border-bottom:1px solid var(--line);white-space:nowrap}
.perf-table td{padding:7px 8px;border-bottom:1px solid var(--line);vertical-align:top}
.perf-table th.n,.perf-table td.n{text-align:right;font-variant-numeric:tabular-nums}
.perf-table tr:last-child td{border-bottom:0}
.post-cell{max-width:340px}
.post-cell a{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.unmapped{color:var(--amber);font-weight:600;cursor:help}

.bar-row{display:grid;grid-template-columns:82px minmax(0,1fr) 64px;gap:9px;align-items:center;
  font-size:12px;margin-bottom:7px}
.bar-lbl{color:var(--muted)}
.bar-track{background:var(--line);border-radius:5px;height:9px;overflow:hidden}
.bar-track i{display:block;height:100%;border-radius:5px}
.bar-val{text-align:right;font-variant-numeric:tabular-nums}

.mini-stats{display:flex;gap:14px;flex-wrap:wrap;margin-top:12px;padding-top:10px;
  border-top:1px solid var(--line);font-size:12px;color:var(--muted)}

.unmeasured{margin:0 0 10px;padding-left:20px;font-size:12.5px}
.unmeasured li{margin-bottom:4px}

.manual-actuals{margin-top:14px}
.manual-actuals summary{padding:12px 16px;cursor:pointer;font-size:12.5px;font-weight:600}
.manual-actuals summary::marker{color:var(--muted)}

@media (max-width:1200px){
  .perf-kpis{grid-template-columns:repeat(3,minmax(0,1fr))}
  .perf-cols{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:620px){
  .perf-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}
  .perf-tools{float:none;margin-top:8px}
  .pf-custom{flex-wrap:wrap}
  .lg{gap:4px}
}

/* Custom date range. Inline next to the preset select, hidden until chosen —
   two date inputs permanently on the toolbar would imply they're the primary
   control when the presets answer the question almost every time. */
.pf-custom{display:inline-flex;align-items:center;gap:6px;margin-left:6px}
.pf-custom input[type=date]{font-size:12.5px;padding:3px 6px;border:1px solid var(--line);
  border-radius:4px;background:#fff;color:var(--ink)}

/* Chart legend, which is also the filter. Buttons rather than divs so keyboard
   and screen-reader users get the same toggle, with aria-pressed carrying the
   state that colour alone would otherwise convey. */
.lg{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.lg-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 9px 3px 6px;
  border:1px solid var(--line);border-radius:999px;background:#fff;cursor:pointer;
  font:inherit;font-size:12px;color:var(--ink);line-height:1.6}
.lg-chip:hover{border-color:var(--muted)}
.lg-chip.off{opacity:.55}
.lg-chip.off .lg-name{text-decoration:line-through}
.lg-dot{width:10px;height:10px;border-radius:3px;border:1.5px solid;flex:none}
.lg-val{color:var(--muted);font-variant-numeric:tabular-nums}
/* Breakdown picker, sitting in the section subtitle rather than the toolbar —
   it changes what the chart underneath it means, not what the page shows. */
.sec-sub-ctl{margin-left:8px;font-size:12px;padding:2px 5px;border:1px solid var(--line);
  border-radius:4px;background:#fff;color:var(--ink);font-weight:400}
/* Campaign multi-select. The menu is absolutely positioned inside an
   inline-block anchor so it hangs off the toggle rather than reflowing the
   chip bar open — the reflow is exactly what made the old inline expansion
   push the table off screen. */
.cpick{position:relative;display:inline-block}
/* position:FIXED, and top/left are set by placeMenu() in board.js.
   Absolute positioning is clipped by any ancestor with overflow:hidden, and
   this menu lives inside #table-view, which is a .card — exactly that. Flipping
   it upward pushed it through the card's top edge and hid 19 of 21 options,
   while getBoundingClientRect went on reporting it as fully on screen. */
.cpick-menu{position:fixed;z-index:36;min-width:290px;
  max-width:min(420px,90vw);background:var(--card);border:1px solid var(--line);
  border-radius:10px;box-shadow:var(--shadow);padding:8px;
  display:flex;flex-direction:column;overflow:hidden}
.cpick-head{display:flex;justify-content:space-between;align-items:center;gap:10px;
  font-size:11.5px;color:var(--muted);padding:2px 6px 8px}
/* Full width and its own row: at 21 campaigns the list is the reason this menu
   exists, and searching it is the fastest way in. flex:0 0 auto so it never
   shrinks when the list below is long. */
.cpick-search{flex:0 0 auto;width:100%;margin:0 0 6px;font:inherit;font-size:12.5px;
  padding:6px 9px;border:1px solid var(--line);border-radius:8px;background:#fff;
  color:var(--ink);min-width:0}
.cpick-none{padding:10px 6px;font-size:12.5px;color:var(--muted)}
/* Capped and scrollable: 21 campaigns today and it only grows, and a menu
   taller than the viewport cannot be dismissed by anyone who scrolled into it. */
/* No max-height of its own any more — placeMenu clamps the MENU to the room
   available, and the list takes what is left. Two competing caps meant the
   shorter one always won and the menu could be short in a tall viewport. */
.cpick-list{flex:1 1 auto;min-height:0;overflow-y:auto;padding-top:6px}
.cpick-opt{display:flex;align-items:center;gap:8px;padding:6px 6px;border-radius:7px;
  font-size:12.5px;cursor:pointer;white-space:nowrap}
.cpick-opt:hover{background:#f4f5fa}
.cpick-opt.on{background:#f1efff}
.cpick-opt input{margin:0;flex:0 0 auto}
.cpick-opt i{width:9px;height:9px;border-radius:2px;flex:0 0 auto}
.cpick-opt span{overflow:hidden;text-overflow:ellipsis}

/* A segmented control inside a section subtitle. .sec-sub-ctl is styled for the
   <select> it was written for — border, padding, background — all of which
   double up on a .seg that brings its own. And .sec-sub is uppercase with
   letter-spacing, which `font:inherit` on .seg button would otherwise inherit
   and render "MOST RECENT" in a control that is not a heading. */
.sec-sub .seg.sec-sub-ctl{padding:0;background:none;text-transform:none;letter-spacing:0;
  align-self:center}
.sec-sub .seg.sec-sub-ctl button{padding:3px 10px;font-size:11.5px;font-weight:600;
  text-transform:none;letter-spacing:0}

.linkish{background:none;border:0;padding:0;font:inherit;color:var(--accent,#6f5ff5);
  text-decoration:underline;cursor:pointer}

/* T-3 · per-person capacity, in the admin users table. */
.u-cap-wrap{display:inline-flex;align-items:center;gap:4px}
.u-cap-h{width:66px;font-size:12.5px;padding:3px 6px;border:1px solid var(--line);
  border-radius:4px;color:var(--ink)}
.u-cap-p{font-size:12px;padding:3px 4px;border:1px solid var(--line);border-radius:4px;
  background:#fff;color:var(--ink)}
.u-cap-h:disabled,.u-cap-p:disabled{opacity:.55}

/* ------------------------------------------ workload view · T-1/T-2 ----- */

.wl-cap{display:flex;align-items:center;gap:6px;font-size:11px;text-transform:uppercase;
  letter-spacing:.4px;color:var(--muted)}
.wl-cap input{width:56px;font-size:12.5px;text-transform:none;letter-spacing:0;color:var(--ink);
  padding:3px 6px;border:1px solid var(--line);border-radius:4px}
.wl-summary{display:flex;flex-wrap:wrap;gap:18px;padding:12px 14px 0;font-size:12.5px}
.wl-summary strong{font-size:15px}
.wl-warn{color:var(--amber,#e08c00)}
.wl-note{margin:10px 14px 0;padding:9px 11px;border:1px solid var(--amber,#e08c00);
  border-radius:8px;background:#fffaf0;font-size:12.5px}

.wl-rows{padding:12px 14px 4px;display:flex;flex-direction:column;gap:6px}
.wl-row{display:grid;grid-template-columns:120px minmax(0,1fr) 130px;align-items:center;gap:10px}
.wl-label{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* The track is the full width; the bar inside it is the share of the peak, so
   every row is measured against the same scale and the capacity marker means
   the same thing on each. */
.wl-track{position:relative;height:20px;background:#f4f5fa;border-radius:5px;overflow:hidden}
.wl-bar{display:flex;height:100%;border-radius:5px;overflow:hidden}
.wl-seg{display:block;height:100%}
.wl-cap-line{position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--ink);opacity:.55}
.wl-row.over .wl-cap-line{background:#e04848;opacity:1;width:2px}
.wl-num{font-size:12px;text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.wl-row.over .wl-num b{color:#e04848}
.wl-unest{margin-left:5px;color:var(--amber,#e08c00);font-weight:700;cursor:help}

.wl-risks{margin:14px 14px 4px;border-top:1px solid var(--line);padding-top:12px}
.wl-risks ul{margin:6px 0 0;padding-left:18px;font-size:12.5px;line-height:1.65}
.wl-risks li{margin-bottom:3px}
.wl-clear{margin:14px 14px 4px}
#wl-body .lg{padding:0 14px}

@media (max-width:620px){
  .wl-row{grid-template-columns:84px minmax(0,1fr);row-gap:2px}
  .wl-num{grid-column:2;text-align:left}
}

/* --------------------------------------------------- integrations · D-1 -- */

.int-card{margin-bottom:12px}
.card.warn{border-color:var(--amber);background:#fffaf0}
.int-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:8px}
.int-note{font-size:11.5px;color:var(--muted);margin:0 0 10px}
/* A credential that survived a key rotation as ciphertext nobody can read. Not
   an error — there is one clear action — so amber, and readable rather than a
   muted aside, because it is the only thing on the card that matters. */
.int-note.warn-note{color:var(--ink);background:#fffaf0;border:1px solid var(--amber,#e08c00);
  border-radius:6px;padding:8px 10px}
.int-note code{font-size:11px}
.int-form{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px}
.int-form label.full{grid-column:1/-1}
.int-form label{display:flex;flex-direction:column;gap:3px;font-size:11px;
  text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.int-form input{font-size:13px;text-transform:none;letter-spacing:0;color:var(--ink)}
/* The one credential that is a whole file rather than a token. Monospace and
   pre-wrap so a pasted JSON key is legible enough to see whether it arrived
   intact — which is the entire reason this is a textarea and not a masked
   single-line input. resize:vertical because 2KB of key is a lot of rows. */
.int-form textarea.int-secret-big{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:11.5px;line-height:1.45;color:var(--ink);
  text-transform:none;letter-spacing:0;white-space:pre;overflow-wrap:normal;
  resize:vertical;min-height:96px}
.int-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap}

/* One heading over several credentials — the three Slack webhooks. The card's
   own .pad holds the heading and the setup steps; each slot is a full-bleed row
   under it, divided by a rule, so the group reads as one thing you scroll rather
   than three cards that look alike. */
.int-group > .pad{padding-bottom:4px}
.int-slot{padding:14px 16px;border-top:1px solid var(--line)}
.int-slot-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.int-slot > .hint{margin:2px 0 8px}
/* The slot's own form sits tighter than a standalone card's — the heading above
   it already carries the context a card header would repeat. */
.int-slot .int-form{margin-bottom:8px}

/* Collapsed by default: it is five steps you need once and never again. */
.int-setup{margin:0 0 10px}
.int-setup summary{cursor:pointer;font-size:12px;font-weight:600;color:var(--uh);
  list-style:none}
.int-setup summary::-webkit-details-marker{display:none}
.int-setup summary::before{content:"▸ ";color:var(--muted)}
.int-setup[open] summary::before{content:"▾ "}
.int-setup ol{margin:8px 0 0;padding-left:20px;font-size:12px;color:var(--ink);
  display:flex;flex-direction:column;gap:5px}
.int-setup ol li::marker{color:var(--muted)}
.int-setup .int-note{margin:10px 0 0}

/* Sync schedule. Sits between the provenance note and the credential form, so
   "how often" reads as a property of the connection rather than of the token. */
.int-sched{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:8px 10px;margin:0 0 10px;border:1px solid var(--line);border-radius:6px;
  background:var(--bg-soft,#f7f8fb)}
.int-sched > label{display:flex;align-items:center;gap:6px;font-size:11px;
  text-transform:uppercase;letter-spacing:.4px;color:var(--muted);margin:0}
.int-sched select{font-size:12.5px;text-transform:none;letter-spacing:0;color:var(--ink);
  padding:3px 6px;border:1px solid var(--line);border-radius:4px;background:#fff}
.int-sched select:disabled{opacity:.55;cursor:not-allowed}
.int-sched-toggle{white-space:nowrap}
.int-sched-toggle input{margin:0}
/* The cost note wraps under the controls rather than stretching the row. */
.int-sched .hint{flex-basis:100%;margin:0;font-size:11.5px}

@media (max-width:620px){
  .int-form{grid-template-columns:1fr}
  .int-sched{gap:8px}
}

/* ------------------------------- D-5 / D-6 · reverse-sync and announcing --- */

/* Mode picker. Radios rather than a select, because each option needs a
   sentence beside it and one of them has to be visibly unavailable with a
   reason — neither of which fits in an <option>. */
.an-modes{display:flex;flex-direction:column;gap:6px;margin:0 0 10px}
.an-mode{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.45;
  padding:7px 10px;border:1px solid var(--line);border-radius:6px;cursor:pointer}
.an-mode:hover{border-color:var(--muted)}
.an-mode input{margin:3px 0 0;flex:none}
/* Unavailable, not merely off. Greyed AND not clickable, with the reason in the
   warn note under the group — a control that looks live and does nothing is the
   thing this screen exists to stop shipping. */
.an-mode.off{opacity:.55;cursor:not-allowed;background:var(--bg,#f5f6fa)}
.an-mode.off:hover{border-color:var(--line)}

.an-log{margin-top:12px;border-top:1px solid var(--line);padding-top:10px}
.an-log-head{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;
  color:var(--muted);margin-bottom:8px}
.an-log-empty{margin-top:12px;border-top:1px solid var(--line);padding-top:10px}
.an-item{margin-bottom:10px}
.an-item:last-child{margin-bottom:0}
.an-when{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;font-size:11.5px;
  margin-bottom:3px}
.an-when b{font-variant-numeric:tabular-nums}
/* pre-wrap, and this is the point of the whole log: in preview mode `note`
   holds the entire multi-line Slack message that WOULD have been sent, and a
   preview collapsed onto one line is not a preview of anything. Wraps as well
   as preserving newlines, so a long line doesn't push the card sideways. */
.an-note{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;line-height:1.5;
  background:var(--bg,#f5f6fa);border:1px solid var(--line);border-radius:6px;
  padding:8px 10px;color:var(--ink)}
.an-note.sent{color:var(--muted);font-style:italic;white-space:normal}
.int-sched #an-age{width:70px;font-size:12.5px;padding:3px 6px;border:1px solid var(--line);
  border-radius:4px;background:#fff;color:var(--ink)}
.int-sched input[type=date]{font-size:12.5px;padding:3px 6px;border:1px solid var(--line);
  border-radius:4px;background:#fff;color:var(--ink);text-transform:none;letter-spacing:0}

/* ------------------------------------------ D-5 · the unplanned queue ----- */

.rq-counts{font-size:13px;margin:0 0 12px}
.rq-list{display:flex;flex-direction:column;gap:8px}
.rq-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px 14px;align-items:start;
  padding:9px 11px;border:1px solid var(--line);border-radius:8px}
.rq-main{min-width:0}
.rq-text{display:block;font-size:13px;line-height:1.4;overflow-wrap:anywhere}
.rq-meta{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:4px;
  font-size:11.5px;color:var(--muted)}
.rq-nobrand{color:var(--amber);font-weight:600}
.rq-act{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-end}
/* Capped, or a long campaign name stretches the actions column until the
   excerpt beside it has nowhere left to go. */
.rq-camp{max-width:230px;font-size:12.5px;padding:4px 6px;border:1px solid var(--line);
  border-radius:6px;background:#fff;color:var(--ink)}
/* The server's refusal, on the row rather than in a toast that has faded by the
   time you try again. Spans both columns so it sits under the select it is about. */
.rq-msg{grid-column:1/-1;color:var(--red);line-height:1.4}

.rq-empty{border:1px dashed var(--line);border-radius:8px;padding:16px;
  font-size:12.5px;color:var(--muted);line-height:1.5}
.rq-empty b{color:var(--ink)}
.rq-empty.off{border-style:solid;border-color:var(--amber);background:#fffaf0}

.rq-unattr{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.rq-sub{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);
  margin-bottom:6px}
.rq-chan{margin:0;padding-left:18px;font-size:12.5px}
.rq-chan li{margin-bottom:3px}
.rq-chan .txt-muted{margin-left:6px;font-size:11.5px}

@media (max-width:760px){
  .rq-row{grid-template-columns:minmax(0,1fr)}
  .rq-act{justify-content:flex-start}
  .rq-camp{max-width:100%}
}

/* D-5 · the board's two markers. Quiet on purpose: a catch-all campaign and an
   auto-filed deliverable have to be distinguishable from planned work at a
   glance, and no more prominent than that — the bucket is the board's most
   common campaign the moment reverse-sync is on. */
.origin-chip{display:inline-block;margin-left:6px;padding:1px 7px;border-radius:999px;
  border:1px dashed var(--line);background:var(--bg,#f5f6fa);color:var(--muted);
  font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;
  vertical-align:middle;white-space:nowrap;cursor:help}

.cf-block{margin-bottom:14px;font-size:12.5px}
.cf-block ul{margin:6px 0 0;padding-left:20px;color:var(--muted)}
.cf-block li{margin-bottom:2px}
.cf-line{font-size:12.5px;margin-bottom:6px}

/* ---------------------------------------------------------------- toasts -- */

.toast-stack{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:8px;z-index:80;align-items:center;pointer-events:none}
.toast{background:var(--navy);color:#fff;font-size:13px;padding:9px 16px;border-radius:24px;box-shadow:0 8px 26px rgba(0,0,0,.24);max-width:min(520px,90vw);transition:opacity .25s,transform .25s}
.toast.out{opacity:0;transform:translateY(8px)}
.toast-success{background:#12794a}
.toast-error{background:#b03030}
/* 028. "It worked, and there is something you should know" — a synthesised
   campaign created with nobody named as approver did what was asked and will
   auto-approve everything inside it. Green would bury that and red would claim
   the campaign wasn't created, so it gets its own colour. Amber on near-black
   text because this is the one toast people are meant to stop and read. */
.toast-warn{background:#b8860b;color:#fff}

/* ------------------------------------------------------------------ chat -- */

.chat-fab{position:fixed;bottom:22px;right:22px;z-index:40;border:0;background:var(--uh);color:#fff;font:inherit;font-size:13.5px;font-weight:600;padding:12px 20px;border-radius:26px;cursor:pointer;box-shadow:0 8px 26px rgba(114,94,254,.4)}
.chat-fab:hover{background:#5f4bea}
.chat-fab.disabled{background:var(--grey);box-shadow:none}

.chat-drawer{position:fixed;top:0;right:0;bottom:0;width:min(440px,100vw);background:var(--card);border-left:1px solid var(--line);box-shadow:-8px 0 40px rgba(13,23,48,.14);z-index:45;display:flex;flex-direction:column}
.chat-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line);flex:none}
.chat-head-actions{display:flex;gap:6px;flex:none}
.chat-log{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px}
.msg{max-width:92%}
.msg-user{align-self:flex-end}
.msg-user .msg-body{background:var(--uh);color:#fff;border-radius:14px 14px 4px 14px}
.msg-claude{align-self:flex-start}
.msg-claude .msg-body{background:#f2f3f8;color:var(--ink);border-radius:14px 14px 14px 4px}
.msg-body{padding:10px 13px;font-size:13px;line-height:1.55;white-space:pre-wrap;word-wrap:break-word}
.chat-notice{font-size:12px;color:var(--muted);background:#fbfbfe;border:1px solid var(--line);border-radius:9px;padding:9px 11px;line-height:1.5}
.chat-notice.error{background:#fdecec;border-color:#f3cfcf;color:#b03030}
.chat-notice.hint{background:#f7f6ff;border-color:#e3dffb}
.tool-line{display:flex;align-items:flex-start;gap:7px;font-size:11.5px;color:var(--muted);background:#fbfbfe;border:1px solid var(--line);border-radius:8px;padding:6px 10px;flex-wrap:wrap}
.tool-line.ok .tool-mark{color:var(--green);font-weight:700}
.tool-line.failed{background:#fdecec;border-color:#f3cfcf;color:#b03030}
.tool-line.failed .tool-mark{color:var(--red);font-weight:700}
.tool-detail{width:100%;font-size:11px;color:var(--muted);margin-top:3px;padding-left:14px}
.spinner{width:9px;height:9px;border:1.5px solid var(--line);border-top-color:var(--uh);border-radius:50%;display:inline-block;animation:spin .7s linear infinite;flex:none;margin-top:3px}
@keyframes spin{to{transform:rotate(360deg)}}
.chat-suggest{display:flex;flex-wrap:wrap;gap:6px;padding:0 16px 12px;flex:none}
.chat-suggest button{border:1px solid var(--line);background:#fff;border-radius:20px;padding:5px 11px;font:inherit;font-size:11.5px;cursor:pointer;color:var(--muted)}
.chat-suggest button:hover{background:#f0f1f6;color:var(--ink)}
.chat-input{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--line);flex:none;align-items:flex-end}
.chat-input textarea{flex:1;font:inherit;font-size:13px;padding:8px 10px;border:1px solid var(--line);border-radius:9px;resize:none;max-height:120px}

@media (max-width:1100px){
  .exp-grid{grid-template-columns:1fr}
  .run-row{grid-template-columns:1fr;gap:2px;padding:10px 0}
}
@media (max-width:640px){
  .wrap{padding:12px}
  .squad-form,.form-grid{grid-template-columns:1fr}
  .chat-drawer{width:100vw}
  .mw-task{flex-wrap:wrap}
  .mw-status{min-width:0;flex:1}
}

/* Vocabulary table on the Setup tab */
#vocab-table td,#vocab-table th{padding:7px 10px;text-align:left}
#vocab-table th{position:static;background:none;cursor:default}
#vocab-table tr.needs-review{background:#fff8e8}
.vt-dot{display:inline-block;width:9px;height:9px;border-radius:3px;margin-right:7px;vertical-align:middle}
.vf{display:inline-flex;align-items:center;gap:5px;cursor:pointer}

/* Campaign builder (S-1) */
.build-card{margin-bottom:14px}
.cb-draft{margin-top:16px;border-top:1px solid var(--line);padding-top:14px}
.cb-pillar{background:#f6f4ff;border:1px solid #d8d2ff;border-radius:9px;padding:10px 12px;margin-bottom:10px}
.cb-pillar b{display:block;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--uh);margin-bottom:3px}
.cb-del{border:1px solid var(--line);border-radius:9px;padding:9px 11px;margin-bottom:7px;background:#fff}
.cb-why{font-size:11.5px;color:var(--muted);margin-top:3px}
/* The draft is an editable form now (S-1), so a deliverable is four fields on
   one line rather than a read-only summary. Without this the shared .form-grid
   puts Effort weight alone on its own row and every deliverable block grows by
   a row it doesn't need. The @media rule below still flattens this to 1fr. */
.cb-del .form-grid{grid-template-columns:2fr 1fr 1fr;gap:8px}
.cb-del .tpl-items,.cb-del .cb-actions{margin-top:8px}
.cb-actions{display:flex;gap:8px;align-items:center;margin-top:12px;flex-wrap:wrap}
/* Checkboxes have to sit OUTSIDE .form-grid — its `label{flex-direction:column}`
   and `input{width:100%}` stretch a checkbox across the whole cell. */
.vt-add-flags{display:flex;flex-wrap:wrap;gap:16px;margin-top:12px}

/* ------------------------------------------ X-1 · My work, the front door -- */
/* My work groups by deadline now, reusing the board's .tg group shell verbatim
   so the two surfaces can't drift apart visually either. Only the parts a task
   row needs that a board row doesn't live here: the stat rail (which doubles as
   the Blocked filter), the in-place status control, and the completed
   disclosure. */
.mywork-summary{align-items:center;flex-wrap:wrap;gap:14px}
.mw-stat{display:inline-flex;align-items:baseline;gap:5px;color:var(--muted)}
.mw-stat b{color:var(--ink);font-size:15px}
.mw-stat.late,.mw-stat.late b{color:var(--red)}
.mw-stat.due,.mw-stat.due b{color:var(--amber)}
/* A count you can click. Dashed until it's doing something, so it doesn't read
   as a button you must press before the list is trustworthy. */
button.mw-stat{font:inherit;font-size:13px;cursor:pointer;background:none;
  border:1px dashed var(--line);border-radius:20px;padding:2px 11px}
button.mw-stat:hover{border-color:#c8cddf}
button.mw-stat.on{background:#fdeaea;border-style:solid;border-color:#f3cfcf}
button.mw-stat.on b{color:var(--red)}

/* The left edge carries the status colour (set inline from TASK_STATUS_COLORS),
   which is why the border is thick on that side only. */
.mw-task{border-left-width:3px}
.mw-task.is-blocked{background:#fffafa}
.tg .mw-task:first-of-type{margin-top:9px}
.mw-task .tg-check{margin-top:1px}
.mw-acts{display:flex;align-items:center;gap:7px;flex:0 0 auto}
.mw-due{font-variant-numeric:tabular-nums}
/* Dotted = borrowed from the deliverable, same signal the board's .tg-due uses,
   so nobody reads it as a commitment made on the task itself. */
.mw-due.inherited{border-bottom:1px dotted var(--grey);color:var(--grey)}
button.tg-done{background:none;border:0;font:inherit;font-size:12px;color:var(--muted);
  cursor:pointer;padding:4px 0;text-decoration:underline;text-decoration-style:dotted}
button.tg-done:hover{color:var(--ink)}

@media (max-width:700px){
  .mw-acts{width:100%;padding-left:26px}
  .mw-status{flex:1}
}

/* ============================================ T-4 · load-balancing suggestions
   The advisory panel at the foot of the Workload card. Sits below the chart and
   the risk list it is answering, and is quiet by default: this is a proposal,
   not a verdict, so nothing in here is coloured like an alert except the one
   thing that IS one — a destination period already over its ceiling. */
.wl-balance{margin:16px 14px 4px;border-top:1px solid var(--line);padding-top:12px}
.bal-bar{display:flex;align-items:center;gap:8px;margin-top:8px}
.bal-result{margin-top:12px;display:flex;flex-direction:column;gap:10px}
.bal-summary{font-size:13px;line-height:1.6}
.bal-unknown{font-size:12.5px;line-height:1.6;color:var(--amber);
  border-left:2px solid var(--amber);padding-left:9px}
.bal-stale{font-size:12.5px;background:#fff8e8;border:1px solid #f0dcae;
  border-radius:8px;padding:8px 10px}

.bal-sug{border:1px solid var(--line);border-radius:10px;padding:11px 12px;
  display:flex;flex-direction:column;gap:6px;background:var(--card)}
.bal-sug.done{opacity:.6}
.bal-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.bal-who{font-size:12px;color:var(--muted)}
.bal-hours{font-size:12px;font-variant-numeric:tabular-nums;color:var(--muted);
  border:1px solid var(--line);border-radius:20px;padding:1px 8px}
/* An unestimated task is UNKNOWN hours, not zero — the same claim the chart's
   "+2?" badge makes, worn the same colour so they read as one idea. */
.bal-hours.unknown{color:var(--amber);border-color:#f0dcae;cursor:help}

.bal-path{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px;font-size:12.5px}
.bal-date{font-variant-numeric:tabular-nums}
.bal-date.to{font-weight:600}
.bal-arrow{color:var(--muted)}

.bal-loads{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.bal-load{font-size:12px;font-variant-numeric:tabular-nums;background:#f4f5fa;
  border-radius:6px;padding:2px 8px;display:inline-flex;align-items:baseline;gap:6px}
.bal-load.over{background:#fdeaea;color:var(--red)}
.bal-load-when{color:var(--muted)}
.bal-q{color:var(--amber);font-weight:700;cursor:help}

.bal-why{font-size:12.5px;line-height:1.6}
.bal-relieves{line-height:1.55}
.bal-note{font-size:12px;line-height:1.55;color:var(--muted);
  border-left:2px solid var(--line);padding-left:9px}
.bal-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:2px}
.bal-done{font-size:12.5px;color:var(--green);font-weight:600}

.bal-dropped ul{margin:6px 0 0;padding-left:18px;font-size:12px;line-height:1.6;
  color:var(--muted)}
.bal-foot{line-height:1.55}

/* ===== Z-1 set-password links · F-2 reassignment ========================== */

/* Confirmation on /login after someone sets a password. Green rather than the
   red .login-error, and its own element rather than reusing that one: the two
   appear in the same slot and a success painted in the failure colour reads as
   a failure. */
.login-note{background:#e9f7ef;color:#12794a;border:1px solid #c6e9d6;border-radius:8px;
  padding:8px 10px;font-size:12.5px;margin-bottom:12px}

/* The Team table's "can this person actually get in?" sub-line. Sits under the
   name because it is a fact about the account, not a column anybody sorts by —
   and because the users-table already drops columns at three breakpoints, so a
   ninth one would be the first to disappear on the laptop this is used on. */
.u-note{display:block;font-size:11px;font-weight:500;margin-top:2px}
.u-note.pending{color:var(--amber)}
.u-note.none{color:var(--muted)}

/* The link itself. Deliberately a full-width readonly input rather than styled
   text: it has to be selectable by hand when the clipboard API is unavailable,
   and it has to be visibly a single opaque secret rather than something to read
   and retype. */
.link-box{display:flex;gap:8px;align-items:stretch;margin:10px 0 2px}
.link-box input{flex:1;min-width:0;font:inherit;font-size:12.5px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:#f7f8fc;
  color:var(--ink)}
.link-box button{white-space:nowrap}

/* Reassignment. One row per kind of move, each an opt-in checkbox with its own
   source picker, so ticking "tasks" and ticking "campaign owner" can never be
   the same gesture — they are different operations on different columns and the
   layout says so. */
.ra-to{margin-bottom:14px}
.ra-to label{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);
  display:flex;flex-direction:column;gap:3px}
.ra-to select{font:inherit;font-size:13px;padding:7px 9px;border:1px solid var(--line);
  border-radius:8px;text-transform:none;letter-spacing:0;color:var(--ink);background:#fff}
.reassign{display:flex;flex-direction:column;gap:12px}
.ra-row{border:1px solid var(--line);border-radius:9px;padding:10px 12px;background:#fff}
/* The checkbox sits outside any .form-grid on purpose — that rule set makes
   labels column-flex and inputs width:100%, which stretches a checkbox across
   the whole cell. Same trap as .vt-add-flags. */
.ra-check{display:flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;
  cursor:pointer;margin-bottom:7px}
.ra-check input{width:auto;margin:0}
.ra-row select{width:100%;font:inherit;font-size:13px;padding:7px 9px;
  border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink)}
.ra-row select:disabled{background:#f5f6fa;color:var(--muted)}
.ra-row .hint{margin-top:6px}
.ra-summary{margin:14px 0 0;font-size:12.5px;color:var(--muted);
  border-top:1px solid var(--line);padding-top:11px}
.ra-summary.armed{color:var(--ink);font-weight:600}

/* ---------------------------------------------------------- C-3 · tags ---- */

/* Tag chips, wherever a deliverable's tags render — the board row, the
   read-only reviewer view. Colour comes from the vocabulary via an inline
   style, so this file only owns the shape. Deliberately not .link-chip: a link
   leaves the app and a tag narrows it, and two controls that do opposite things
   should not look identical. */
.tag-chips{display:inline-flex;flex-wrap:wrap;gap:4px;margin-left:6px;vertical-align:middle}
.tag-chip{font-size:10.5px;line-height:1;padding:3px 8px;border-radius:4px;
  white-space:nowrap;border:1px solid transparent;font-weight:600;
  letter-spacing:.2px}
/* Only the clickable ones invite a click. A chip in the read-only view has no
   data-tagfilter and keeps the default cursor. */
.tag-chip[data-tagfilter]{cursor:pointer}
.tag-chip[data-tagfilter]:hover,.tag-chip[data-tagfilter]:focus-visible{
  filter:brightness(.92);outline:none;box-shadow:0 0 0 2px var(--bg),0 0 0 3px currentColor}

/* The tag picker in the deliverable editor. Real checkboxes styled as chips —
   the input stays in the DOM and focusable (opacity, not display:none, or it
   drops out of the tab order and off screen readers). */
.tag-pick{border-top:1px solid var(--line);margin-top:16px;padding-top:12px}
.tag-pick-list{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.tagpick{position:relative;display:inline-flex;cursor:pointer}
.tagpick input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}
.tagpick span{font-size:12px;line-height:1;padding:6px 10px;border-radius:5px;
  border:1px solid var(--line);background:#fff;color:var(--muted);user-select:none}
/* --tag and --tagbg are both set inline by the picker. Two properties rather
   than one plus color-mix(), because the eight-digit-hex tint is what UI.pill
   and the tag chips above already do and one technique is easier to keep
   consistent than two. */
.tagpick input:checked + span{
  border-color:var(--tag);color:var(--tag);background:var(--tagbg);font-weight:600}
.tagpick input:focus-visible + span{box-shadow:0 0 0 2px var(--bg),0 0 0 3px var(--uh)}
/* A retired tag still applied to this deliverable. Shown, never offered — same
   rule as a retired platform on the Social lanes. */
.tagpick.retired span{border-style:dashed;font-style:italic}

/* ------------------------------------------ tooltips & attachments · 025 -- */

/* The ⓘ. A real button so it is in the tab order — several of these sit on
   section headings, which are not focusable, and a `title` on a non-focusable
   element is unreachable by keyboard. */
.tip{
  display:inline-flex;align-items:center;justify-content:center;
  width:14px;height:14px;flex:none;margin-left:5px;padding:0;
  border:1px solid var(--line);border-radius:50%;
  background:#fff;color:var(--muted);
  font:600 9.5px/1 var(--font-mono,inherit);font-family:inherit;
  cursor:help;vertical-align:1px;
  text-transform:none;letter-spacing:0;
}
.tip:hover,.tip:focus-visible{border-color:var(--uh);color:var(--uh);background:#faf9ff}

/* One shared host on <body>. Fixed, so nothing can clip it — .card sets
   overflow:hidden and the modal body scrolls, which between them cover every
   form these icons appear on. */
.tipbox{
  position:fixed;z-index:200;max-width:280px;
  background:var(--navy);color:#fff;
  font-size:12px;line-height:1.45;font-weight:400;
  text-transform:none;letter-spacing:0;
  padding:7px 10px;border-radius:6px;
  box-shadow:0 4px 14px rgba(13,23,48,.22);
  pointer-events:none;
}
/* A caret, flipped when the bubble had to open downward. */
.tipbox::after{
  content:"";position:absolute;left:50%;margin-left:-4px;
  border:4px solid transparent;border-top-color:var(--navy);
  top:100%;
}
.tipbox.below::after{top:auto;bottom:100%;border-top-color:transparent;border-bottom-color:var(--navy)}

/* ---- attachments on a deliverable ---- */

.files-block{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}
.files-list{display:flex;flex-direction:column;gap:2px;margin:6px 0 8px}
.file-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12.5px;
  padding:4px 0;border-bottom:1px dashed var(--line-2,#eef0f7)}
.file-row:last-child{border-bottom:0}
/* Fixed width so a column of PDF/PNG/JPEG labels lines up rather than ragging. */
.file-kind{flex:none;width:40px;text-align:center;font-size:9px;font-weight:700;
  letter-spacing:.4px;color:var(--muted);border:1px solid var(--line);
  border-radius:3px;padding:1px 0}
.file-name{font-weight:600;min-width:0;overflow-wrap:anywhere}
.file-meta{color:var(--muted);font-size:11.5px}
.file-row .btn-sm{margin-left:auto}
.file-add{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.file-add input[type=file]{font-size:12px;max-width:100%}
/* Caption + ⓘ on one row inside a column-flex form label. Only present when the
   field has a tooltip — see field() in editors.js. */
.fcap{display:flex;align-items:center;gap:0;min-height:13px}

/* The help panel's own chrome — the bar above the document, and the states it
   shows while the fetch is in flight or after it failed. Separate from the
   .help-doc block below, which styles the fetched markup and nothing else. */
.help-bar{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding-bottom:14px;margin-bottom:18px;border-bottom:1px solid var(--line)}
.help-load{padding:26px 0;display:flex;flex-direction:column;align-items:flex-start;gap:8px}

/* ================================================== help · the approvals guide

   The in-product copy of the published "Approvals Loop" guide, served as markup
   by GET /api/help and injected into #help-body.

   EVERY rule here is scoped under .help-doc, and every class in the partial is
   prefixed hd-. That is not tidiness: the guide was written as a standalone page
   and independently used .wrap, .top, .sub, .note, .chip, .label, .role and .a —
   all of which mean something else in this stylesheet. Unscoped, the two would
   have fought over the whole app's spacing and colour.

   Colours come from this file's :root, so the guide follows the brand store like
   everything else and there is no second palette to keep in step. Only the few
   values the app has no token for are declared locally, on .help-doc. The app is
   light-only (color-scheme: light, no prefers-color-scheme anywhere), so this is
   too — the standalone artifact's dark theme is deliberately not carried over. */

.help-doc{
  /* Doc-local only where the app has no equivalent. */
  --hd-card2:#fbfbfe;
  --hd-ink2:#3a4258;
  --hd-faint:#8a91a3;
  --hd-line2:#eef0f7;
  --hd-accent-ink:#5647d6;
  --hd-wash:#f1eeff;
  --hd-warn-bg:#fff8ec;
  --hd-warn-line:#f0dcb4;
  --hd-warn-ink:#6b5423;
  --hd-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;

  font-size:15px;
  line-height:1.62;
  color:var(--ink);
  padding-bottom:8px;
}

/* Reading measure. The panel is full width for tables and screenshots; running
   text is held near 68 characters, which is what makes a 7-minute read tolerable
   on a 1440px monitor. */
.help-doc .hd-prose,
.help-doc .hd-standfirst,
.help-doc .hd-sub,
.help-doc > section > p,
.help-doc .hd-end{max-width:68ch}

.help-doc p{margin:0 0 14px}
.help-doc .hd-prose p:last-child,
.help-doc .hd-note p:last-child{margin-bottom:0}
.help-doc strong{font-weight:650;color:var(--ink)}
.help-doc em{font-style:italic}
.help-doc a{color:var(--hd-accent-ink)}
.help-doc code{
  font-family:var(--hd-mono);font-size:.88em;
  background:var(--hd-wash);color:var(--hd-accent-ink);
  padding:2px 6px;border-radius:4px;white-space:nowrap}

/* ---- masthead + section headings ---- */
.help-doc .hd-head{margin-bottom:30px}
.help-doc h2{
  font-size:26px;line-height:1.12;letter-spacing:-.02em;font-weight:750;
  margin:0 0 12px;text-wrap:balance;max-width:26ch}
.help-doc .hd-standfirst{font-size:16px;line-height:1.55;color:var(--hd-ink2);margin:0 0 14px}
.help-doc .hd-meta{
  font-family:var(--hd-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--hd-faint);margin:0}
.help-doc section{margin:0 0 44px}
.help-doc h3{
  font-size:21px;line-height:1.18;letter-spacing:-.018em;font-weight:730;
  margin:0 0 5px;text-wrap:balance}
.help-doc h3 + .hd-sub{color:var(--muted);font-size:14px;margin:0 0 22px}
.help-doc h4{font-size:16px;letter-spacing:-.008em;font-weight:700;margin:28px 0 7px}
.help-doc .hd-rule{border:0;border-top:1px solid var(--line);margin:0 0 40px}

/* ---- the two-lane opener ---- */
.help-doc .hd-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:14px}
.help-doc .hd-lane{
  background:var(--card);border:1px solid var(--line);
  border-radius:8px;padding:18px 20px 20px}
.help-doc .hd-lane-h{
  display:block;font-family:var(--hd-mono);
  font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--hd-accent-ink);
  padding-bottom:11px;margin-bottom:13px;border-bottom:1px solid var(--hd-line2)}
.help-doc ol.hd-mini{
  list-style:none;counter-reset:m;margin:0;padding:0;
  display:flex;flex-direction:column;gap:15px}
.help-doc ol.hd-mini > li{
  counter-increment:m;position:relative;padding-left:26px;
  font-size:14px;line-height:1.55;color:var(--hd-ink2)}
.help-doc ol.hd-mini > li::before{
  content:counter(m) ".";position:absolute;left:0;top:0;
  font-family:var(--hd-mono);font-size:12.5px;font-weight:700;color:var(--hd-faint)}
.help-doc ol.hd-mini > li b{display:block;color:var(--ink);font-weight:660;margin-bottom:3px}

/* ---- status chips · the app's own pill colours ---- */
.help-doc .hd-chip{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--hd-mono);font-size:11.5px;font-weight:600;
  border-radius:20px;padding:3px 11px 3px 9px;white-space:nowrap}
.help-doc .hd-chip::before{
  content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
.help-doc .hd-c-draft{color:var(--grey);background:color-mix(in srgb,var(--grey) 12%,transparent)}
.help-doc .hd-c-changes{color:var(--red);background:color-mix(in srgb,var(--red) 12%,transparent)}
.help-doc .hd-c-needs{color:var(--amber);background:color-mix(in srgb,var(--amber) 12%,transparent)}
.help-doc .hd-c-approved{color:var(--uh);background:color-mix(in srgb,var(--uh) 14%,transparent)}
.help-doc .hd-c-scheduled{color:var(--blue);background:color-mix(in srgb,var(--blue) 12%,transparent)}
.help-doc .hd-c-published{color:var(--green);background:color-mix(in srgb,var(--green) 12%,transparent)}

/* ---- the pipeline diagram ---- */
.help-doc .hd-figure{margin:0 0 12px}
.help-doc .hd-scroller{overflow-x:auto;padding-bottom:4px}
.help-doc .hd-pipe{
  display:block;min-width:860px;width:100%;height:auto;
  background:var(--card);border:1px solid var(--line);border-radius:8px}
.help-doc .hd-pipe .hd-box-fill{fill:var(--hd-card2)}
.help-doc .hd-pipe .hd-box-line{stroke:var(--line)}
.help-doc .hd-pipe .hd-label{fill:var(--ink);font-family:var(--hd-mono);font-size:13px;font-weight:600}
.help-doc .hd-pipe .hd-sublabel{fill:var(--muted);font-size:11.5px}
.help-doc .hd-pipe .hd-flow{stroke:var(--hd-faint);stroke-width:1.5;fill:none}
.help-doc .hd-pipe .hd-flow-note{fill:var(--muted);font-size:11.5px;font-style:italic}
.help-doc .hd-pipe .hd-arrowhead{fill:var(--hd-faint)}
.help-doc figcaption{font-size:13px;color:var(--muted);margin-top:9px;max-width:68ch}
.help-doc figcaption + h4{margin-top:26px}

/* ---- the status reference table ---- */
.help-doc .hd-tablewrap{
  overflow-x:auto;border:1px solid var(--line);border-radius:8px;background:var(--card)}
.help-doc .hd-tablewrap table{border-collapse:collapse;width:100%;min-width:620px;font-size:14px}
.help-doc .hd-tablewrap th,
.help-doc .hd-tablewrap td{
  text-align:left;padding:12px 15px;border-bottom:1px solid var(--hd-line2);vertical-align:top}
.help-doc .hd-tablewrap thead th{
  font-family:var(--hd-mono);font-size:10px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--hd-faint);font-weight:600;background:var(--hd-card2);
  border-bottom:1px solid var(--line);white-space:nowrap}
.help-doc .hd-tablewrap tbody tr:last-child td{border-bottom:0}
.help-doc .hd-tablewrap td.hd-who-col{color:var(--muted);white-space:nowrap}

/* ---- screenshots ---- */
.help-doc .hd-shot{
  margin:0 0 12px;border:1px solid var(--line);border-radius:8px;
  overflow:hidden;background:var(--card);line-height:0}
.help-doc .hd-shot img{display:block;width:100%;height:auto}
.help-doc .hd-shot.hd-tight{max-width:720px}

/* ---- callouts. Left rule carries the severity, same idea as .ap-viol. ---- */
.help-doc .hd-note{
  border-left:3px solid var(--uh);background:var(--card);
  border-radius:0 6px 6px 0;padding:13px 17px;margin:0 0 16px;font-size:14.5px}
.help-doc .hd-note b{display:block;margin-bottom:4px}
.help-doc .hd-note.hd-warn{
  border-left-color:var(--amber);background:var(--hd-warn-bg);color:var(--hd-warn-ink)}
.help-doc .hd-note.hd-warn b{color:var(--hd-warn-ink)}
.help-doc .hd-note.hd-warn code{background:transparent;color:inherit;border:1px solid var(--hd-warn-line)}
.help-doc .hd-note.hd-stop{border-left-color:var(--red)}

/* ---- role cards ---- */
.help-doc .hd-roles{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.help-doc .hd-role{
  background:var(--card);border:1px solid var(--line);border-radius:8px;padding:17px 17px 19px}
.help-doc .hd-role .hd-r{
  font-family:var(--hd-mono);font-size:10px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--hd-faint);display:block;margin-bottom:9px}
.help-doc .hd-role b{display:block;font-size:15.5px;margin-bottom:7px;letter-spacing:-.01em}
.help-doc .hd-role ul{margin:0;padding-left:17px;font-size:14px;color:var(--hd-ink2)}
.help-doc .hd-role li{margin-bottom:4px}
.help-doc .hd-role li::marker{color:var(--hd-faint)}
.help-doc .hd-role .hd-cant{
  color:var(--muted);font-size:13px;margin-top:9px;padding-top:9px;
  border-top:1px solid var(--hd-line2)}

/* ---- numbered how-to ---- */
.help-doc ol.hd-howto{list-style:none;counter-reset:h;margin:0;padding:0}
.help-doc ol.hd-howto > li{
  counter-increment:h;position:relative;padding-left:44px;margin-bottom:28px}
.help-doc ol.hd-howto > li::before{
  content:counter(h);position:absolute;left:0;top:1px;
  width:28px;height:28px;display:grid;place-items:center;border-radius:50%;
  background:var(--hd-wash);color:var(--hd-accent-ink);
  font-family:var(--hd-mono);font-size:12.5px;font-weight:700}
.help-doc ol.hd-howto > li > b{display:block;font-size:16px;margin-bottom:5px;letter-spacing:-.012em}
.help-doc ol.hd-howto > li > p{max-width:66ch}
.help-doc ol.hd-howto > li > p:last-child{margin-bottom:0}
.help-doc ol.hd-howto .hd-note{margin-top:13px;margin-bottom:15px}

/* ---- Q&A ---- */
.help-doc .hd-qa{border-top:1px solid var(--line);margin-top:8px}
.help-doc .hd-qa details{border-bottom:1px solid var(--line)}
.help-doc .hd-qa summary{
  cursor:pointer;padding:14px 34px 14px 0;position:relative;
  font-weight:640;font-size:15px;letter-spacing:-.01em;list-style:none}
.help-doc .hd-qa summary::-webkit-details-marker{display:none}
.help-doc .hd-qa summary::after{
  content:"+";position:absolute;right:6px;top:50%;transform:translateY(-50%);
  font-family:var(--hd-mono);font-size:16px;color:var(--hd-faint);font-weight:400}
.help-doc .hd-qa details[open] summary::after{content:"–"}
.help-doc .hd-qa summary:hover{color:var(--hd-accent-ink)}
.help-doc .hd-qa .hd-a{padding:0 0 16px;max-width:66ch;color:var(--hd-ink2);font-size:14.5px}
.help-doc .hd-qa .hd-a p:last-child{margin-bottom:0}

.help-doc blockquote{
  margin:0 0 18px;padding:0 0 0 18px;border-left:2px solid var(--line);
  font-family:Georgia,"Iowan Old Style","Times New Roman",serif;
  font-style:italic;font-size:17px;line-height:1.5;color:var(--hd-ink2);max-width:58ch}
.help-doc blockquote cite{
  display:block;margin-top:7px;font-family:inherit;font-style:normal;
  font-size:12px;color:var(--hd-faint)}

.help-doc .hd-end{
  border-top:1px solid var(--line);margin-top:4px;padding-top:20px;
  font-size:13px;color:var(--muted)}

@media (max-width:620px){
  .help-doc{font-size:14.5px}
  .help-doc h2{font-size:22px}
  .help-doc h3{font-size:19px}
  .help-doc ol.hd-howto > li{padding-left:36px}
}

/* 028 · the synthesis modals. A numbered list of what is going in, and of what
   came out — both read as sequences, which is the whole claim a combined campaign
   makes over the ideas it was built from. */
.idea-synth-list{margin:6px 0 0;padding-left:22px;font-size:13px;line-height:1.7}
.idea-synth-list li{margin-bottom:2px}
.idea-synth select[multiple]{min-height:74px;padding:5px}

/* 030 · references on an idea: the staged lists in the create form, the counts on
   a card, and the lists in the popup. */
.stage-block{border-top:1px solid var(--line);margin-top:14px;padding-top:12px}
.stage-list{display:flex;flex-direction:column;gap:4px;margin:4px 0 8px}
.stage-row{display:flex;align-items:center;gap:8px;font-size:12.5px;
  background:#fbfbfe;border:1px solid var(--line);border-radius:7px;padding:5px 8px}
/* The label is the fixed part and the URL is the part that can be enormous, so
   the URL is what truncates — a 200-character tracking link must not push the
   remove button off the row. */
.stage-lbl{font-weight:600;flex:none}
.stage-url{color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}
.stage-add{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.stage-add input[type=text]{flex:1;min-width:140px;font-size:12.5px;padding:5px 7px}
.stage-add input[type=file]{font-size:12.5px}
/* 035 · a reference that is not stored yet. Dashed, not coloured: the distinction
   is promise-versus-fact rather than good-versus-bad, and in the create form —
   where every row is new — a page of amber rows would read as a page of warnings. */
.stage-row.is-new{border-style:dashed}
/* The stored ones are links now, so you can check WHICH attachment before you
   remove it. Underlined on hover only: a row of permanently underlined URLs in a
   list this dense is noise. */
a.stage-url{text-decoration:none}
a.stage-url:hover{text-decoration:underline}

/* Reads as a count, not a verb — it opens the popup where the list actually is. */
.idea-refs{background:#eef1ff;border:1px solid #c9d2ff;color:#3a45a8;cursor:pointer;
  font:inherit;font-size:11.5px;font-weight:600;border-radius:20px;padding:2px 9px;
  margin:0 0 6px}
.idea-refs:hover{background:#e2e7ff}

.idea-reflist{margin:0;padding-left:18px;font-size:13px;line-height:1.75}
.idea-reflist li{margin-bottom:2px}
/* The URL beside a labelled link is context, not the link — it truncates so a
   long one cannot widen the popup. */
.idea-reflist .small{display:inline-block;max-width:min(46ch,100%);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}

/* 032 · the campaign-level bulk assign bar in the Campaign Pack review. Reads as
   a shortcut over the pickers below rather than a field of its own — it moves
   them, it does not hold a value that gets committed. */
.cb-assign{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  background:#eef1ff;border:1px solid #c9d2ff;border-radius:9px;
  padding:9px 12px;margin:10px 0 14px}
.cb-assign label{display:flex;align-items:center;gap:6px;margin:0;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.cb-assign select{font-size:12.5px;padding:4px 7px}
/* The Who column holds a select in a table sized for two short inputs, so it
   needs a floor or the picker collapses to an unusable sliver. */
.tpl-items .ti-assignee{min-width:118px;font-size:12px;padding:3px 5px}

/* 032 · the tier selector, top right under the KPIs.
   Right-aligned so it reads as a mode selector against the KPI rail rather than
   as another filter. row-reverse rather than a spacer div: the hint sits to the
   LEFT of the control it describes, and wraps under it on a narrow screen instead
   of pushing the segmented control off the edge. */
.tier-bar{display:flex;flex-direction:row-reverse;align-items:center;gap:9px;
  flex-wrap:wrap;margin:0 0 14px}
.tier-bar .vb-hint{margin-right:auto;text-align:left}
@media (max-width:620px){
  .tier-bar{flex-direction:row;justify-content:flex-start}
  .tier-bar .vb-hint{margin-right:0;flex-basis:100%}
}

/* The bulk assign bar in the task tier.
   ANCHORED TO THE BOTTOM, not sticky at the top like its twin on the ideas panel,
   and the difference is forced rather than stylistic. A top-sticky bar floats over
   whatever is beneath it once the page scrolls, and what is beneath this one is
   the task table's own sticky header — which pins to `.table-scroll` at `top:0`
   because that box is its nearest scrollport, NOT to the page. So it cannot be
   pushed down to make room: the comment on `th` above spells out why an offset
   there would hide the first rows behind the header instead. The ideas panel has
   no sticky header under its bar, which is why the same treatment works there.
   Bottom-anchored solves it outright — it collides with nothing, and a pending
   selection is always one glance away rather than one scroll.
   z-index below .toast-stack's 80: a toast is transient and should pass in front.
   The right margin keeps it clear of the fixed chat launcher. */
.task-assign{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);
  z-index:70;max-width:min(720px,calc(100vw - 130px));
  display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  background:#eef1ff;border:1px solid #c9d2ff;border-radius:24px;
  box-shadow:0 8px 26px rgba(0,0,0,.18);
  padding:9px 16px;font-size:12.5px}
.task-assign .ta-count{font-weight:600}
.task-assign label{display:flex;align-items:center;gap:6px;margin:0;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)}
.task-assign select{font-size:12.5px;padding:4px 7px}

/* The row controls. A fixed-width checkbox column so the table does not reflow
   as rows are ticked, and a floor on the inline picker or it collapses to a
   sliver between two narrow cells. */
.tk-pick{width:26px;text-align:center}
.tk-pick input{width:15px;height:15px;cursor:pointer}
.task-list .tk-assign{min-width:130px;max-width:170px;font-size:12px;padding:3px 5px}
/* Visually hidden but read aloud — the checkbox column header has no visible text
   and an unlabelled column is a column a screen reader cannot announce. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* 032 · the same two controls on a task card. Sized down: a card is ~240px wide
   and the picker shares its facts row with hours and a date. */
.tk-card .tk-sel{width:14px;height:14px;cursor:pointer;margin-right:2px;flex:none}
.tk-card .tk-assign{max-width:140px;font-size:11.5px;padding:2px 4px}

/* Clearance under the fixed bulk bar, only while it is up. The bar is out of
   flow, so without this the final row is unreachable behind it. */
#tasks-view.has-bulk{padding-bottom:64px}
/* 038 · the campaign step in "Schedule this idea", lifted out of the grid into its
   own block with a rule under it. It is the decision the rest of the dialog
   depends on and it used to be one cell of three — the biggest question wearing
   the smallest control. */
.promote-campaign{border-bottom:1px solid var(--line);padding-bottom:13px;margin-bottom:14px}
.promote-campaign > label{display:flex;flex-direction:column;gap:5px;font-size:12.5px;
  font-weight:600;color:#3a4257}
.promote-campaign select{font:inherit;font-size:13.5px;padding:8px 9px;
  border:1px solid var(--line);border-radius:8px;background:#fff;width:100%}

/* 032 · the Schedule modal, which now builds a campaign as well as a deliverable.
   Both blocks are set off from the form above them with a rule rather than a
   panel: they are further steps in one decision, not separate cards, and boxing
   them would make the modal read as three forms stacked. */
.idea-newcamp,.idea-companions{border-top:1px solid var(--line);margin-top:16px;padding-top:13px}
.idea-newcamp .form-grid{margin-top:8px}

/* 038 · the select-all row. Sits between the toolbar and the cards and reads as
   part of the list rather than as another filter, so it takes the list's left
   edge and none of the toolbar's chrome — no card, no border, no background. */
.idea-selectall{padding:2px 4px 10px}
.idea-selectall[hidden]{display:none}
.idea-selectall label{display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  font-size:12.5px;color:var(--muted);user-select:none;
  text-transform:none;letter-spacing:0;font-weight:500}
.idea-selectall label:hover{color:var(--ink)}
.idea-selectall input{width:15px;height:15px;cursor:pointer;flex:none;accent-color:var(--uh)}

.comp-list{list-style:none;margin:10px 0 0;padding:0;display:grid;gap:7px}
/* Title and reason on the left, its own date on the right. The date sits on the
   row rather than under it because every row has one and a column of them is
   scannable — a campaign built here is read as a sequence of dates. */
.comp-row{display:flex;align-items:flex-start;gap:12px;justify-content:space-between;
  border:1px solid var(--line);border-radius:9px;padding:9px 11px}
.comp-row:has(.comp-pick:checked){border-color:var(--uh);background:#fbfbfe}
.comp-main{display:flex;align-items:flex-start;gap:9px;flex:1;min-width:0;cursor:pointer;
  font:inherit;text-transform:none;letter-spacing:0}
.comp-main input{margin-top:2px;flex:none}
.comp-main>span{display:grid;gap:2px;min-width:0}
.comp-title{font-size:13px;font-weight:600;color:var(--ink);line-height:1.4}
.comp-row .comp-date{flex:none;width:145px;font:inherit;font-size:12.5px;padding:5px 7px;
  border:1px solid var(--line);border-radius:7px;color:var(--ink);background:#fff}

@media (max-width:620px){
  .comp-row{flex-direction:column;gap:8px}
  .comp-row .comp-date{width:100%}
}

/* ── 033 · Blog studio ─────────────────────────────────────────────────────
   Reuses .card, .squad-card, .squad-form, .form-grid, .seg, .btn and .pill for
   everything they already cover. What is here is only what the studio adds: the
   outline's repeating section rows, the placeholder/score badges, the Webflow
   field list and the article preview. */

.bl-context{margin:-4px 0 12px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.bl-link-chip{font-size:12.5px;background:#f2f0ff;border:1px solid #ded8ff;color:#3a3268;
  border-radius:20px;padding:4px 12px}

.bl-panel{margin-top:14px}
.bl-h{font-size:14px;font-weight:600;color:var(--ink)}
.bl-badges{display:flex;gap:7px;align-items:center;flex-wrap:wrap}

/* The score, banded on the source repo's rubric: 90 publish, 80 minor tweaks,
   70 iterate, below that rewrite. The gate is a setting, so `good` is applied by
   the JS against minScore rather than hardcoded at 90 here. */
.bl-score{font-size:11.5px;font-weight:600;border-radius:20px;padding:3px 10px;white-space:nowrap}
.bl-score-good{background:#e6f6ec;color:#137a44}
.bl-score-ok{background:#fff4e0;color:#8a5a00}
.bl-score-warn{background:#fff0e6;color:#a34b16}
.bl-score-bad{background:#fdecec;color:var(--red)}
.bl-score-none{background:#eceef4;color:var(--muted)}

/* Unresolved [IMAGE:]/[INTERNAL-LINK:] placeholders. Amber rather than grey on
   purpose — these are the reason a blog is not finished, and the push refuses
   while any remain. */
.bl-mark{font-size:11.5px;border-radius:20px;padding:3px 10px;background:#fff4e0;color:#8a5a00;white-space:nowrap}
.bl-mark-clear{background:#eef1f6;color:var(--muted)}
.bl-warn{color:var(--red)}

.bl-sec-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin:18px 0 8px;padding-top:14px;border-top:1px solid var(--line)}

/* One outline section. A card rather than a table row: each has four fields and
   a multi-line points box, and the template editor's .tpl-items table is for
   rows of single short values. */
.bl-sections{display:grid;gap:10px}
.bl-section{border:1px solid var(--line);border-radius:9px;padding:10px 11px;display:grid;gap:7px;background:#fcfcfe}
.bl-section-top{display:flex;gap:7px;align-items:center}
.bl-section-top input{flex:1;min-width:0;font-weight:600}
.bl-section-top select{flex:0 0 104px}
.bl-section-foot{display:grid;grid-template-columns:1fr 1fr;gap:7px}
.bl-section input,.bl-section select,.bl-section textarea,
.bl-faq input{width:100%;font:inherit;font-size:12.5px;padding:6px 8px;
  border:1px solid var(--line);border-radius:7px;color:var(--ink);background:#fff}
.bl-section textarea{resize:vertical}

.bl-faqs{display:grid;gap:7px}
.bl-faq{display:grid;grid-template-columns:1fr 1fr auto;gap:7px;align-items:center}

/* The remove affordance, shared by section rows, FAQ rows and source rows. */
.bl-x{border:1px solid var(--line);background:#fff;border-radius:7px;width:26px;height:26px;
  flex:0 0 auto;cursor:pointer;color:var(--muted);font-size:15px;line-height:1;padding:0}
.bl-x:hover{border-color:#f3cfcf;color:var(--red);background:#fdecec}

.bl-sources{display:grid;gap:7px}
.bl-source{display:grid;grid-template-columns:1fr auto;gap:4px 10px;align-items:center;
  border:1px solid var(--line);border-radius:9px;padding:8px 11px}
.bl-source>div{grid-column:1}
.bl-source .bl-x{grid-column:2;grid-row:1/3}
.bl-src-stat{font-size:12.5px;line-height:1.5;color:#2c3446}
.bl-source a{color:var(--uh)}

.bl-gaps{margin:0;padding-left:20px;font-size:12.5px;line-height:1.6;color:#3a4257}
.bl-research{margin-top:14px;font-size:12.5px}
.bl-research summary{cursor:pointer;color:var(--muted)}
.bl-research pre,.bl-review pre{white-space:pre-wrap;word-break:break-word;font:inherit;
  font-size:12.5px;line-height:1.6;background:#fbfbfe;border:1px solid var(--line);
  border-radius:9px;padding:11px 13px;margin:8px 0 0;color:#2c3446}

.bl-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:16px}
.bl-seg{margin:12px 0}
.bl-pane{min-height:80px}

/* Character counters on the two fields with a real limit. .bl-over is the only
   thing on this screen that turns red for a value that is merely long — meta
   descriptions are truncated by Google, not rejected, so it warns and does not
   block. */
.bl-count{float:right;font-size:10px;color:var(--muted);letter-spacing:0}
.bl-count.bl-over{color:var(--red);font-weight:600}

/* The article body. Monospace because the placeholders and the markdown
   structure are what somebody is scanning for in this box, not the prose. */
.bl-md{width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px;line-height:1.65;padding:12px;border:1px solid var(--line);
  border-radius:9px;color:var(--ink);background:#fff;resize:vertical}

/* The preview renders the STORED html — the same string that gets pasted into
   Webflow — so these rules deliberately do not restyle it into something the CMS
   will not reproduce. They set readable measure and spacing, nothing more. */
.bl-preview{font-size:14px;line-height:1.7;color:#20293d;max-width:70ch;padding:4px 2px}
.bl-preview h1{font-size:23px;line-height:1.3;margin:0 0 14px}
.bl-preview h2{font-size:18px;line-height:1.35;margin:26px 0 10px}
.bl-preview h3{font-size:15px;margin:20px 0 8px}
.bl-preview p{margin:0 0 13px}
.bl-preview ul,.bl-preview ol{margin:0 0 13px;padding-left:22px}
.bl-preview li{margin-bottom:5px}
.bl-preview a{color:var(--uh)}
.bl-preview blockquote{margin:0 0 16px;padding:12px 16px;background:#fbfbfe;
  border-left:3px solid var(--uh);border-radius:0 9px 9px 0}
.bl-preview blockquote p{margin:0 0 6px;font-weight:600}
.bl-preview blockquote ul{margin:0;padding-left:20px}
.bl-preview img{max-width:100%;height:auto;border-radius:9px}
.bl-preview pre{overflow-x:auto;background:#fbfbfe;border:1px solid var(--line);
  border-radius:9px;padding:11px 13px;font-size:12.5px}
.bl-preview hr{border:0;border-top:1px solid var(--line);margin:22px 0}
/* Every unresolved placeholder, in both the preview and the copyable HTML.
   Deliberately loud: this is the class the server stamps on markers, and the
   whole reason it exists is that a placeholder which reads like prose gets
   published. */
.bl-preview .blog-todo,.bl-preview em.blog-todo{background:#fff4e0;color:#8a5a00;
  border-radius:5px;padding:1px 6px;font-style:normal;font-size:12.5px}

.bl-wf-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:12px}
.bl-wf-strip{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--ink);cursor:pointer}
.bl-wf-strip input{margin:0}
.bl-fields{display:grid;gap:11px}
.bl-field{border:1px solid var(--line);border-radius:9px;padding:9px 11px}
.bl-field-top{display:flex;align-items:center;gap:9px;margin-bottom:5px}
.bl-field-top b{font-size:12.5px}
.bl-field-top code{font-size:11px;color:var(--muted);background:#f2f3f8;border-radius:5px;padding:1px 6px}
.bl-field-top .btn{margin-left:auto}
.bl-field textarea{width:100%;margin-top:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:11.5px;line-height:1.55;padding:8px 10px;border:1px solid var(--line);
  border-radius:7px;background:#fbfbfe;color:#2c3446;resize:vertical}

.bl-rubric{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:4px}
.bl-rub{border:1px solid var(--line);border-radius:8px;padding:6px 10px;font-size:12px;
  display:flex;gap:7px;align-items:baseline;background:#fbfbfe}
.bl-rub b{font-weight:600;color:var(--muted);text-transform:capitalize}

.bl-list{display:grid;gap:7px}
.bl-row{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  text-align:left;font:inherit;cursor:pointer;background:#fff;border:1px solid var(--line);
  border-radius:9px;padding:9px 12px;color:var(--ink)}
.bl-row:hover{border-color:#c8cddf;background:#fbfbfe}
.bl-row-main{display:grid;gap:2px;min-width:0}
.bl-row-title{font-size:13px;font-weight:600;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bl-row-meta{display:flex;gap:6px;align-items:center;flex:none}

@media (max-width:620px){
  .bl-section-foot,.bl-faq{grid-template-columns:1fr}
  .bl-faq .bl-x{justify-self:end}
  .bl-row{flex-direction:column;align-items:flex-start;gap:7px}
}
/* The CSP note above the preview. `img-src 'self' data:` means an article with a
   real image URL in it renders broken here and fine in Webflow; the note says so
   rather than leaving somebody to conclude the tool dropped the image. */
.bl-csp{background:#fbfbfe;border:1px solid var(--line);border-left:3px solid var(--amber);
  border-radius:0 8px 8px 0;padding:9px 12px;margin-bottom:12px;color:var(--muted);line-height:1.5}

/* ── 033b · the score gate, the field map, the settings screen ───────────── */

/* The flag on anything under the bar. A banner rather than a badge colour: a
   badge is a number somebody reads past, and this is the sentence that stops
   them. It never blocks — see the note in blog.js. */
.bl-gate{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:12px 0 0;
  padding:10px 13px;border-radius:9px;font-size:12.5px;line-height:1.55;
  background:#fff4e0;border:1px solid #f0dcb4;color:#7a4f00}
.bl-gate b{color:#5f3d00}
.bl-gate .btn{margin-left:auto;background:#fff;border-color:#e2c894}
.bl-gate-none{background:#fdecec;border-color:#f3cfcf;color:#8a2222}

/* A Webflow field name that has been overridden in Settings. Marked in both
   places — on the field in the studio's Webflow pane, and on the input in
   Settings — because "which of these did we change" is the question a failed
   push makes somebody ask. */
/* `.bl-field-top code` above sets the background at (0,1,1); a bare `.bl-mapped`
   is (0,1,0) and lost every time, so the highlight never painted. Qualified to
   (0,2,1). Caught in the browser, not in review. */
.bl-field-top code.bl-mapped{background:#ede9ff;color:#4a3aa8}
.bl-mapped-in{border-color:#c9c0f5;background:#fbfaff}

.bl-wf-state{font-size:12.5px;line-height:1.55;color:var(--muted);background:#fbfbfe;
  border:1px solid var(--line);border-left:3px solid var(--amber);
  border-radius:0 8px 8px 0;padding:9px 12px;margin-bottom:12px}
.bl-wf-state.ok{border-left-color:var(--green);color:#137a44}

.bl-map{margin-top:6px;font-size:12.5px}
.bl-map summary{cursor:pointer;color:var(--muted);padding:4px 0}
.bl-map-rows{display:grid;gap:6px;margin:8px 0 12px}
.bl-map-row{display:grid;grid-template-columns:190px 1fr 190px;gap:10px;align-items:center;
  font-size:12.5px;text-transform:none;letter-spacing:0;color:var(--ink)}
.bl-map-row input{font:inherit;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12px;padding:6px 8px;border:1px solid var(--line);border-radius:7px;
  color:var(--ink);background:#fff;width:100%}

@media (max-width:760px){
  .bl-map-row{grid-template-columns:1fr}
  .bl-gate .btn{margin-left:0}
}

/* ── 034 · the visuals pane ──────────────────────────────────────────────── */

.bl-empty{padding:6px 2px;max-width:62ch}
.bl-empty p{margin:0 0 10px;line-height:1.6;font-size:13px}
.bl-empty code,.bl-vis-note code{font-size:11.5px;background:#f2f3f8;border-radius:5px;padding:1px 5px}

.bl-visuals{display:grid;gap:14px}
.bl-vis-note{line-height:1.55;background:#fbfbfe;border:1px solid var(--line);
  border-left:3px solid var(--amber);border-radius:0 8px 8px 0;padding:10px 13px;max-width:78ch}

.bl-vis{border:1px solid var(--line);border-radius:10px;padding:11px 13px;background:#fff}
.bl-vis-none{background:#fbfbfe}
.bl-vis-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:9px}
.bl-vis-head b{font-size:13px}
.bl-vis-head .btn{margin-left:auto}

/* The drawing itself. A max-width so a 1200-wide hero does not dominate the
   pane, and a checked ground so a chart drawn on transparent white is still
   visibly a rectangle rather than floating. */
.bl-vis-art{border:1px solid var(--line);border-radius:8px;padding:10px;
  background:#fff;overflow-x:auto;max-width:640px}
.bl-vis-art-wide{max-width:100%}
.bl-vis-art svg{display:block;width:100%;height:auto;max-width:100%}
.bl-vis-cap{margin-top:8px;color:#3a4257;line-height:1.5}
.bl-vis-cap a{color:var(--uh)}
.bl-vis-alt{margin-top:5px;line-height:1.5}

.bl-brief-list{display:grid;gap:8px}
.bl-brief{display:flex;gap:11px;align-items:flex-start;border:1px solid var(--line);
  border-radius:9px;padding:10px 12px;background:#fbfbfe}
.bl-brief-title{font-size:12.5px;font-weight:600;margin-bottom:3px}
.bl-brief .small{line-height:1.55}
.bl-kind{flex:0 0 auto;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;
  border-radius:20px;padding:2px 9px;background:#eceef4;color:var(--muted)}
.bl-kind-image{background:#e8f0fe;color:#1a4fa0}
.bl-kind-chart{background:#ede9ff;color:#4a3aa8}
.bl-kind-callout{background:#fff4e0;color:#8a5a00}
.bl-kind-table{background:#e6f6ec;color:#137a44}

@media (max-width:620px){
  .bl-vis-head .btn{margin-left:0}
  .bl-vis-art{max-width:100%}
}

/* ── 034b · the preview with the drawings in ─────────────────────────────── */

.bl-preview-host{min-height:80px}

/* The hero, above the H1, the way it sits on the published page. Full width of
   the measure rather than the 70ch prose column, because that is what a hero is. */
.bl-preview .blog-hero,.bl-hero-wrap{margin:0 0 22px}
.bl-preview figure.blog-hero{max-width:100%}
.bl-preview figure{margin:22px 0}
.bl-preview figure svg{display:block;width:100%;height:auto;border-radius:9px}
.bl-preview figure.blog-fig{padding:0}
.bl-preview figcaption{font-size:12.5px;color:var(--muted);line-height:1.5;margin-top:8px}

/* A visual that still needs a human: the frame it will sit in, with the brief
   inside. Deliberately looks unfinished — dashed, amber — because it is. */
.bl-preview figure.blog-fig-todo{border:1px dashed #e2c894;background:#fffaf0;
  border-radius:9px;padding:18px 16px;min-height:96px;display:flex;align-items:center}
.blog-fig-brief{font-size:12.5px;line-height:1.6;color:#7a4f00}
.blog-fig-brief b{text-transform:uppercase;letter-spacing:.4px;font-size:10.5px;
  background:#f6e6c8;border-radius:20px;padding:2px 9px;margin-right:7px}

/* ── 036 · the "draw the visuals too" checkbox ───────────────────────────── */

/* A checkbox inside .squad-form, which is a grid of column-flex labels — so it
   needs the row direction back, and the uppercase caption styling off. */
.bl-check{flex-direction:row!important;align-items:flex-start;gap:8px;
  text-transform:none!important;letter-spacing:0!important;color:var(--ink)!important;
  font-size:12.5px!important;line-height:1.5;cursor:pointer}
.bl-check input{margin:1px 0 0;flex:0 0 auto;width:auto!important}
.bl-check .txt-muted{display:block;margin-top:2px}
/* The copy beside Write the blog, where it actually fires. Inline, no block hint. */
.bl-check-inline{align-items:center}
.bl-check-inline input{margin:0}
.bl-check-inline .txt-muted{display:inline;margin:0}

/* ── 037 · a stored credential reads as stored ────────────────────────────── */

/* It used to render an open input whose placeholder said "Leave blank to keep the
   current one" — which reads as nothing saved, because a placeholder is the one
   piece of text on a form that looks like absence. Dots and a Replace button
   instead. The input is still in the DOM, hidden, because save() reads it and an
   empty save is the meta-only path. */
.int-locked{display:flex;align-items:center;gap:11px;flex-wrap:wrap;
  border:1px solid var(--line);border-radius:9px;padding:9px 12px;background:#fbfbfe}
.int-mask{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13px;letter-spacing:1px;color:#8a91a3;user-select:none}
.int-locked .btn{margin-left:auto}
.int-locked code{font-size:11.5px;background:#f2f3f8;border-radius:5px;padding:1px 5px}

@media (max-width:620px){
  .int-locked .btn{margin-left:0}
}

/* ── 039 · the Marketing Squad's four sub-views ───────────────────────────── */

/* Same `.seg` role=tablist the Ideas tab uses; only the spacing differs, because
   this one sits under a section title rather than at the top of a card. */
.squad-views{margin:0 0 18px}

/* A lede above a panel that opens on a grid of cards, so the panel says what it
   is for before it says what it can do. */
.sq-lede{max-width:74ch;line-height:1.6;margin:0 0 14px}

@media (max-width:620px){
  /* .seg already wraps; at this width four labels want the full width each so
     they stay readable rather than truncating to two words. */
  .squad-views{display:grid;grid-template-columns:1fr 1fr}
  .squad-views button{border-radius:0}
}
