/* Monkey Mail's own rules. Everything structural — masthead, sidebar, landing, cards, badges,
   buttons, tables, forms — comes from brand.css, which is the Wycombe Abbey design system
   vendored unmodified from App-13 Keystone Support. Only add here what that file does not
   already provide, so the two apps keep looking like one system. */

.shell-root { display: flex; flex-direction: column; min-height: 100vh; }

/* brand.css styles `input, textarea, select` as a block with width:100%, padding and a border —
   right for text fields, wrong for checkboxes, which it also catches. Left alone, every checkbox
   is a full-width padded box that pushes its own label across the row. The Support Desk has no
   bare checkboxes to expose it; Monkey Mail's audience picker is nothing but checkboxes.
   Fixed here rather than in brand.css, which stays a verbatim copy. */
input[type="checkbox"],
input[type="radio"] {
  width: auto;
  flex: none;
  padding: 0;
  margin: 0;
  border: none;
  background: none;
  accent-color: var(--navy);
  /* Big enough to hit on a phone without needing a wrapper. */
  inline-size: 17px;
  block-size: 17px;
}

/* Campaign status, on the same solid brand-secondary fills the Support Desk uses for ticket
   status — spotted at a glance down a list rather than read. */
.badge.mm-draft      { background: var(--navy-10); color: var(--navy-50); }
.badge.mm-queued     { background: var(--saffron); color: var(--navy); }
.badge.mm-sending    { background: var(--clementine); color: var(--white); }
.badge.mm-paused     { background: var(--violet); color: var(--white); }
.badge.mm-sent       { background: var(--skye); color: var(--white); }
.badge.mm-failed     { background: var(--scarlett); color: var(--white); }
.badge.mm-cancelled  { background: var(--navy-10); color: var(--navy-50); }
.badge.mm-pending    { background: var(--navy-10); color: var(--navy-50); }
.badge.mm-suppressed { background: var(--rose); color: var(--navy); }
.badge.mm-skipped    { background: var(--navy-10); color: var(--navy-50); }

/* Operational is deliberately loud. It means "no unsubscribe link, the Do Not Contact list does
   not apply" — the composer should never pick it by accident. */
.badge.mm-operational { background: var(--scarlett); color: var(--white); }
.badge.mm-marketing   { background: var(--grey-soft); color: var(--navy-50); }

.notice {
  padding: 11px 14px; border-radius: 6px; margin: 14px 0;
  font-size: calc(14px + var(--font-boost));
}
.notice.warn  { background: var(--saffron-light); border: 1px solid var(--gold); }
.notice.error { background: var(--rose-light); border: 1px solid var(--scarlett); }
/* Neutral, for a statement that is neither reassurance nor a problem — "it reached their
   provider, and what happened next is not something we can see". Used by Investigate and by the
   Billing documents page, both of which were rendering it unstyled. */
.notice.info { background: #eef2f9; border: 1px solid #c9d6ea; color: var(--navy); }
.notice.ok    { background: var(--skye-light); border: 1px solid var(--skye); }

.progress { height: 8px; background: var(--navy-10); border-radius: 99px; overflow: hidden; margin: 12px 0; }
.progress > div { height: 100%; background: var(--skye); transition: width .3s var(--ease); }

.hint  { font-size: calc(12px + var(--font-boost)); color: var(--text-muted); margin-top: 5px; }
.muted { color: var(--text-muted); }
.right { text-align: right; }
.stack > * + * { margin-top: 16px; }

/* Multi-select filter pills, matching the Support Desk's filter bar. */
.checks { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.checks label {
  display: flex; align-items: center; gap: 7px; margin: 0; cursor: pointer;
  padding: 6px 13px; border: 1px solid var(--navy-20); border-radius: 99px;
  background: var(--white); font-weight: 400; font-size: calc(13px + var(--font-boost));
}
.checks label.on { background: var(--navy); border-color: var(--navy); color: var(--white); }
.checks input { margin: 0; }

/* Roughly what the recipient sees. Never a substitute for the test send — only a real email
   client is an honest preview — so it is framed as a panel, not presented as the email. */
.preview {
  border: 1px solid var(--border); border-radius: 6px; padding: 18px;
  background: var(--white); max-height: 480px; overflow: auto;
}

.field-row { display: flex; gap: 16px; flex-wrap: wrap; }
.field-row > * { flex: 1; min-width: 220px; }

.actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 18px; align-items: center; }

.address-dump {
  max-height: 220px; overflow: auto; margin-top: 10px; padding: 10px;
  background: var(--grey-soft); border-radius: 6px;
  font-size: calc(12px + var(--font-boost)); color: var(--text-muted); word-break: break-all;
}

/* The composer's body field is HTML until the block editor lands in stage 2. */
textarea.html-source {
  min-height: 300px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: calc(12px + var(--font-boost)); line-height: 1.5;
}

.empty { padding: 30px; text-align: center; color: var(--text-muted); }

/* ---- Recipient picker -------------------------------------------------------------------------
   A single column of sixteen year groups wastes most of a laptop screen and all of a 27" one, and
   gives no sense of which half of the school is being addressed. Grouped by division, in a grid
   that takes as many columns as the window allows. */

.scope-buttons { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }

/* The "family contacts last updated" line. Quiet when all is well; the warning state reuses the
   ordinary .notice.warn, so it needs nothing here. */
.mm-freshness-line { margin: 10px 0; }

.audience-summary {
  margin: 0 0 16px; padding: 10px 14px; border-radius: 6px;
  background: var(--grey-soft); font-size: calc(14px + var(--font-boost));
}

.yg-groups { display: flex; flex-direction: column; gap: 20px; }
.yg-group-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.yg-group-head .eyebrow { margin: 0; }
.yg-group-head .muted { margin-left: auto; font-size: calc(12px + var(--font-boost)); }
.yg-pick { padding: 3px 10px; font-size: calc(11px + var(--font-boost)); }

.yg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; }

.yg-card {
  border: 1px solid var(--navy-20); border-radius: 8px; background: var(--white);
  padding: 10px 12px; transition: border-color .12s var(--ease), background .12s var(--ease);
}
.yg-card:hover { border-color: var(--navy-50); }
.yg-card.on { border-color: var(--gold-deep); background: var(--saffron-light); }
/* Some forms picked but not the whole year — visibly different from both states. */
.yg-card.part { border-color: var(--gold-soft); background: #fffdf6; }

.yg-card-head {
  display: flex; align-items: flex-start; gap: 9px; margin: 0; cursor: pointer; font-weight: 400;
}
.yg-card-head input { margin-top: 2px; }
.yg-name { display: block; font-weight: 700; font-size: calc(14px + var(--font-boost)); line-height: 1.25; }
.yg-count { display: block; color: var(--text-muted); font-size: calc(12px + var(--font-boost)); }

.yg-forms { display: flex; flex-wrap: wrap; gap: 5px; margin: 8px 0 0 28px; }
.yg-form {
  display: flex; align-items: center; gap: 5px; margin: 0; cursor: pointer;
  background: var(--grey-soft); padding: 2px 8px; border-radius: 6px;
  font-size: calc(11px + var(--font-boost)); font-weight: 600;
}
.yg-form input { margin: 0; }
.yg-form.on { background: var(--navy); color: var(--white); }
/* A form inside an already-ticked year group is shown ticked but not clickable — the year group
   has already included it, and un-ticking would imply an exclusion the query cannot express. */
.yg-form:has(input:disabled) { opacity: .6; cursor: default; }

/* ---- Block editor -------------------------------------------------------------------------- */

/* Blocks on the left, the real compiled email on the right. .main is flex:1 up to 1800px, so on a
   laptop there is room for both; below that the preview drops underneath rather than squeezing the
   thing being edited. */
.mm-editor { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 20px; align-items: start; }
@media (max-width: 1100px) { .mm-editor { grid-template-columns: minmax(0, 1fr); } }

.mm-editor-preview { position: sticky; top: 12px; }
.mm-preview-head {
  font-size: calc(12px + var(--font-boost)); font-weight: 700; text-transform: uppercase;
  letter-spacing: .4px; color: var(--text-muted); margin-bottom: 8px;
}
.mm-preview-frame {
  width: 100%; height: 640px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--paper);
}

.mm-blocks { display: flex; flex-direction: column; gap: 12px; }
/* NOT overflow:hidden. It was there to clip the header's background to the rounded corners, and it
   silently broke position:sticky for everything inside — an overflow ancestor becomes the sticky
   container, so the formatting bar scrolled away with its own block instead of pinning to the
   viewport. The corners are rounded on the header itself instead. */
.mm-block { border: 1px solid var(--border); border-radius: 6px; background: var(--white); }
.mm-block-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 12px; background: var(--grey-soft); border-bottom: 1px solid var(--border);
  border-radius: 5px 5px 0 0;
}
.mm-block-type {
  font-size: calc(11px + var(--font-boost)); font-weight: 700; text-transform: uppercase;
  letter-spacing: .5px; color: var(--text-muted);
}
.mm-block-tools { display: flex; gap: 4px; }
.mm-icon {
  border: 1px solid var(--navy-20); background: var(--white); border-radius: 5px;
  width: 26px; height: 26px; padding: 0; line-height: 1; cursor: pointer; color: var(--navy);
  font-size: calc(12px + var(--font-boost));
}
.mm-icon:hover:not(:disabled) { background: var(--navy-10); }
.mm-icon:disabled { opacity: .3; cursor: not-allowed; }
.mm-icon.danger:hover:not(:disabled) { background: var(--scarlett); border-color: var(--scarlett); color: var(--white); }
.mm-block-body { padding: 12px 14px; }

.mm-field + .mm-field { margin-top: 10px; }
.mm-field label { margin-top: 0; }
.mm-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-start; margin-top: 10px; }
.mm-row > * { flex: 1; min-width: 180px; }
/* Fields SIDE BY SIDE in a row are still adjacent siblings, so the stacking margin above applied
   between them too and pushed the second one down by 10px — which is what made "Alignment" sit
   lower than "Size". */
.mm-row > .mm-field + .mm-field { margin-top: 0; }

.mm-add-row { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 12px; align-items: center; }
.mm-add { font-size: calc(12px + var(--font-boost)); padding: 6px 12px; }

/* --- rich text --- */
.mm-rte-bar {
  display: flex; gap: 4px; flex-wrap: wrap; padding: 6px; margin-bottom: 8px;
  background: var(--grey-soft); border-radius: 6px;
}
.mm-rte-btn {
  border: 1px solid transparent; background: transparent; border-radius: 4px; cursor: pointer;
  padding: 4px 9px; font-size: calc(13px + var(--font-boost)); color: var(--navy); line-height: 1.2;
}
.mm-rte-btn:hover { background: var(--white); }
.mm-rte-btn.on { background: var(--navy); color: var(--white); }

/* The editing surface itself. Sized and spaced like the compiled email so what is typed already
   looks close to what is sent — the preview beside it is the exact version. */
.mm-rte .tiptap {
  min-height: 90px; padding: 12px 14px; border: 1px solid var(--navy-20); border-radius: 6px;
  background: var(--white); outline: none;
  font-family: Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* The campaign's OWN base size, set per editor from the theme — not a fixed 16px, and not
     boosted by the A-/A/A+ control.

     Both of those were wrong in the same way. A story card inheriting a 15px base was drawn at
     16px + 2px of boost = 18px, while a text block explicitly set to 16px was drawn at exactly
     16px, so the smaller text looked bigger and the toolbar appeared to be lying. An editing
     surface that misreports relative sizes is worse than one that is simply small.

     The accessibility boost is not lost: it is applied as zoom below, which scales the inline
     px sizes TipTap writes as well as this one, so the proportions survive it. */
  font-size: var(--mm-base-size, 16px);
  zoom: var(--mm-editor-zoom, 1);
  line-height: 1.55; color: var(--navy);
}
.mm-rte .tiptap:focus-within { border-color: var(--gold-deep); }
.mm-rte .tiptap p { margin: 0 0 10px; }
.mm-rte .tiptap p:last-child { margin-bottom: 0; }
.mm-rte .tiptap a { color: var(--indigo); }
.mm-rte .tiptap ul, .mm-rte .tiptap ol { margin: 0 0 10px; padding-left: 22px; }

.mm-columns { display: flex; gap: 14px; flex-wrap: wrap; }
.mm-column { flex: 1; min-width: 200px; }
.mm-subblock { border: 1px solid var(--border); border-radius: 6px; padding: 8px; margin-bottom: 8px; }
.mm-subblock-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.mm-subblock-head .muted { font-size: calc(11px + var(--font-boost)); text-transform: uppercase; letter-spacing: .4px; }
textarea.mm-mini { min-height: 70px; }

/* --- text toolbar: sizing, images, tables --- */

/* brand.css makes every select and input full width, which inside a toolbar means one control per
   line. Same root cause as the checkbox fix above. */
.mm-rte-bar select,
.mm-rte-context input {
  width: auto;
  padding: 3px 8px;
  font-size: calc(12px + var(--font-boost));
  border-radius: 4px;
}
/* width:auto because brand.css makes every select 100% wide, and this one now has to fit
   "16px (default)" and "Mixed" as well as "24px" — a fixed 74px clipped the longest of them. */
.mm-size { width: auto !important; min-width: 74px; }
.mm-size-input { width: 68px !important; }

.mm-sep { width: 1px; align-self: stretch; background: var(--navy-20); margin: 0 3px; }

/* Only appears when the cursor is actually inside an image or a table. */
.mm-rte-context:not(:empty) {
  display: flex; gap: 4px; flex-wrap: wrap; align-items: center;
  padding: 6px; margin: -4px 0 8px; border-radius: 6px;
  background: var(--saffron-light); border: 1px solid var(--gold-soft);
}
.mm-context-label {
  font-size: calc(11px + var(--font-boost)); font-weight: 700; text-transform: uppercase;
  letter-spacing: .5px; color: var(--gold-deep); margin-right: 4px;
}

/* Tables while editing. Styled to look like the compiled email's default so the canvas does not
   promise something the output will not deliver. */
.mm-rte .tiptap table { border-collapse: collapse; width: 100%; margin: 0 0 12px; table-layout: fixed; }
.mm-rte .tiptap th,
.mm-rte .tiptap td {
  border: 1px solid var(--navy-10); padding: 8px 10px; vertical-align: top; position: relative;
}
.mm-rte .tiptap th { background: var(--grey-soft); text-align: left; font-weight: 700; }
.mm-rte .tiptap .selectedCell::after {
  content: ""; position: absolute; inset: 0; background: rgba(222, 172, 53, .22); pointer-events: none;
}
.mm-rte .tiptap .column-resize-handle {
  position: absolute; right: -2px; top: 0; bottom: 0; width: 4px;
  background: var(--gold); cursor: col-resize;
}

.mm-rte .tiptap img { max-width: 100%; height: auto; border-radius: 2px; }
/* ProseMirror's selected-node class — makes it obvious which image the context bar is acting on. */
.mm-rte .tiptap img.ProseMirror-selectednode { outline: 3px solid var(--gold); outline-offset: 2px; }

.mm-theme-bar {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end;
  padding: 10px 12px; margin-bottom: 12px;
  background: var(--grey-soft); border-radius: 6px;
}
.mm-theme-field label { margin: 0 0 3px; }
.mm-theme-field select { width: auto; min-width: 170px; padding: 5px 9px; }
.mm-theme-note { flex: 1 1 260px; margin: 0; }

/* The preview panel is narrower than the email, so the frame is rendered at TRUE width and scaled
   to fit rather than squeezed — squeezing it would trigger the email's own mobile media query and
   show the phone layout while claiming to be the email. */
.mm-preview-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.mm-preview-modes { display: flex; gap: 4px; }
.mm-preview-mode {
  border: 1px solid var(--navy-20); background: var(--white); border-radius: 4px; cursor: pointer;
  padding: 3px 9px; font-size: calc(11px + var(--font-boost)); font-weight: 600;
  text-transform: none; letter-spacing: 0; color: var(--navy);
}
.mm-preview-mode.on { background: var(--navy); border-color: var(--navy); color: var(--white); }

.mm-preview-stage {
  overflow: hidden; border: 1px solid var(--border); border-radius: 6px; background: var(--paper);
}
.mm-preview-frame {
  border: 0; background: var(--paper); transform-origin: top left; display: block;
}

/* Autosave state, beside the campaign title. Silent autosave is indistinguishable from no
   autosave, which is why this is always visible rather than a toast that disappears. */
.mm-save {
  font-size: calc(12px + var(--font-boost)); font-weight: 600;
  padding: 4px 10px; border-radius: 99px; white-space: nowrap;
}
.mm-save.ok      { background: var(--skye-light); color: var(--skye); }
.mm-save.pending { background: var(--saffron-light); color: var(--gold-deep); }
.mm-save.error   { background: var(--rose-light); color: var(--scarlett); }

/* Merge-field pickers. Nobody should have to remember that the token is {{contact.salutation}}
   and not {{salutation}} — a typo there reaches every inbox as an empty gap. */
.mm-field-picker { width: auto !important; min-width: 96px; padding: 3px 8px; font-size: calc(12px + var(--font-boost)); }
.mm-subject-row { display: flex; gap: 8px; align-items: stretch; }
.mm-subject-row > input { flex: 1; }
.mm-subject-row > select { flex: 0 0 auto; }
.hint code {
  background: var(--grey-soft); padding: 1px 5px; border-radius: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .92em;
}

/* The send confirmation. Deliberately a panel on the page rather than a browser prompt: a prompt
   can be dismissed by accident, and its cancel path used to end the send with no message at all. */
.mm-confirm { border: 2px solid var(--scarlett); margin-top: 16px; }
.mm-confirm h3 { margin-top: 0; }
.mm-confirm-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.mm-confirm-row label { margin: 0; white-space: nowrap; }
.mm-confirm-problem { color: var(--scarlett); font-weight: 700; margin-top: 10px; }

/* The formatting bar follows you down a long story rather than making you scroll back up to bold
   a word. Offset by the masthead, which is sticky too. Can be switched off — a stuck bar is a
   nuisance on a short block, and on a laptop screen every pinned row costs reading height. */
/* The masthead scrolls away with the page rather than holding 58px of every screen — on a laptop
   that is a whole story card's worth of height, and the crest does not need to be permanently
   visible. brand.css pins it; this unpins it for this app only.

   The formatting bar therefore sticks to the very top, with nothing above it to clear. */
.sticky-header-group { position: static; }

.mm-rte-bar { position: sticky; top: 0; z-index: 4; }
body.mm-no-sticky .mm-rte-bar { position: static; }
.mm-rte-context { position: sticky; top: 44px; z-index: 3; }
body.mm-no-sticky .mm-rte-context { position: static; }

.mm-colour { width: auto !important; min-width: 84px; padding: 3px 8px; font-size: calc(12px + var(--font-boost)); }

.mm-wordcount {
  font-size: calc(11px + var(--font-boost)); color: var(--text-muted);
  text-align: right; margin-top: 5px;
}

/* Quotes and rules, matched to what the compiler emits so the canvas does not promise something
   the email will not deliver. */
.mm-rte .tiptap blockquote {
  margin: 14px 0; padding: 2px 0 2px 16px; border-left: 3px solid var(--gold); font-style: italic;
}
.mm-rte .tiptap hr { border: 0; border-top: 1px solid var(--navy-10); margin: 16px 0; }

/* ---- Wizard ---------------------------------------------------------------------------------- */

.mm-wizard h3 { margin: 14px 0 6px; font-weight: 300; font-size: calc(22px + var(--font-boost)); }
.mm-wizard-bar { height: 4px; background: var(--navy-10); border-radius: 99px; overflow: hidden; margin: 8px 0 4px; }
.mm-wizard-bar > div { height: 100%; background: var(--gold); transition: width .25s var(--ease); }

.mm-choices { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.mm-choice {
  display: flex; align-items: flex-start; gap: 11px; margin: 0; cursor: pointer; font-weight: 400;
  padding: 12px 14px; border: 1px solid var(--navy-20); border-radius: 8px; background: var(--white);
}
.mm-choice:hover { border-color: var(--navy-50); }
.mm-choice.on { border-color: var(--gold-deep); background: var(--saffron-light); }
.mm-choice input { margin-top: 3px; }
.mm-choice .hint { margin-top: 3px; }
.mm-choice strong { font-size: calc(15px + var(--font-boost)); }

/* The app's own mark. The school crest stays where it is — that is the institution; this is the
   tool. Sized to sit on the baseline of the app title without pushing the masthead taller. */
.mm-mark { height: 1.15em; width: auto; vertical-align: -0.18em; margin-left: 6px; }
.landing-hero-mark { display: block; margin: 0 auto 14px; }

/* Bold has to LOOK bold. brand.css loads Lato at 400/700/900, so the editing canvas asks for the
   real weights rather than letting the browser synthesise a smeared fake one from Regular. */
.mm-rte .tiptap { font-synthesis-weight: none; }
.mm-rte .tiptap strong, .mm-rte .tiptap b { font-weight: 700; }
.mm-rte .tiptap h1, .mm-rte .tiptap h2, .mm-rte .tiptap h3 { font-weight: 700; }
.mm-weight { min-width: 106px; }

/* ---- Collapsible sidebar ---------------------------------------------------------------------
   brand.css hides the hamburger above 900px, where the sidebar is a permanent column rather than
   a drawer. It earns its place there too: collapsed, the menu is a rail of icons and the editor
   gets the 172px back, which on a 13" laptop is most of a story card. */
.masthead .menu-toggle { display: inline-flex; }

.sidebar nav a { display: flex; align-items: center; gap: 9px; }
.nav-icon { flex: 0 0 auto; width: 20px; text-align: center; font-size: 1.05em; }
.nav-text { white-space: nowrap; overflow: hidden; }

body.mm-rail .sidebar { width: 56px; padding-left: 6px; padding-right: 6px; }
body.mm-rail .nav-text,
body.mm-rail .sidebar-section-label { display: none; }
body.mm-rail .sidebar nav a { justify-content: center; padding: 10px 0; }
/* Without this the divider is the only thing left in a rail with no labels, and reads as a fault. */
body.mm-rail .sidebar-divider { margin: 8px 4px; }

.sidebar { transition: width .18s var(--ease); }

/* Under 900px the sidebar is an overlay drawer and the button opens it, so the rail must not
   apply — a 56px drawer would be absurd. */
@media (max-width: 900px) {
  body.mm-rail .sidebar { width: 172px; padding-left: 10px; padding-right: 10px; }
  body.mm-rail .nav-text, body.mm-rail .sidebar-section-label { display: revert; }
  body.mm-rail .sidebar nav a { justify-content: flex-start; padding: 8px 10px; }
}

/* On a narrow screen the subject and its field picker cannot share a line — the picker's minimum
   width squeezed the input down to a single visible character. */
@media (max-width: 700px) {
  .mm-subject-row { flex-wrap: wrap; }
  .mm-subject-row > input { flex: 1 1 100%; }
  .mm-subject-row > select, .mm-subject-row > button { flex: 1 1 auto; }
  .mm-theme-bar { gap: 10px; }
  .mm-theme-field select { min-width: 0; width: 100%; }
}

/* Proof that a picture is actually in there. Without it the only evidence is the word "Replace"
   on a button, and identifying WHICH picture means reading the preview and working backwards. */
.mm-thumb-link { display: block; flex: 0 0 auto; line-height: 0; }
.mm-thumb {
  height: 128px; width: auto; max-width: 260px; object-fit: cover;
  border: 1px solid var(--navy-20); border-radius: 6px; background: var(--grey-soft);
  display: block;
}
.mm-thumb-link:hover .mm-thumb { border-color: var(--gold-deep); }
.mm-thumb-missing {
  width: 200px; height: 128px; display: flex; align-items: center; justify-content: center;
  font-size: calc(12px + var(--font-boost)); color: var(--scarlett);
  border-style: dashed; border-color: var(--scarlett);
}
/* The row wraps rather than squeezing the picture when the buttons do not fit beside it. */
.mm-add-row:has(.mm-thumb-link) { align-items: flex-start; }

/* ---- Full-screen preview ----------------------------------------------------------------------
   The side panel is about 400px of a laptop screen — fine for glancing at while writing, useless
   for deciding whether the email actually looks right. Same compiled HTML, whole screen, nothing
   else on it. */
.mm-full {
  position: fixed; inset: 0; z-index: 200; background: var(--navy);
  display: flex; flex-direction: column;
}
.mm-full-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 16px; background: var(--navy-90); color: var(--white);
  border-bottom: 1px solid var(--navy-70);
}
.mm-full-bar strong { font-size: calc(14px + var(--font-boost)); letter-spacing: .3px; }
.mm-full-bar .spacer { flex: 1; }
.mm-full-sizes { display: flex; gap: 6px; }
.mm-full-sizes .btn { padding: 5px 12px; font-size: calc(12px + var(--font-boost)); }
.mm-full-sizes .btn.on { background: var(--gold); border-color: var(--gold); color: var(--navy); }

/* Scrolls on its own, so the whole email is reachable however long it is. */
.mm-full-stage { flex: 1; overflow: auto; display: flex; justify-content: center; padding: 20px 12px 40px; }
.mm-full-frame {
  border: 0; background: var(--paper); height: 100%; min-height: 100%;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .45); border-radius: 4px;
}
.mm-preview-full { white-space: nowrap; }

/* ---- Events list ------------------------------------------------------------------------------ */
.mm-events { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.mm-event {
  border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; background: var(--paper);
}
.mm-event-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.mm-event-when { flex: 1 1 140px; }
.mm-event-when label { margin: 0 0 3px; }
.mm-event-tools { display: flex; gap: 4px; margin-left: auto; padding-bottom: 2px; }
.mm-event label { margin-top: 8px; }

.mm-header-opts { flex: 1 1 100%; border-top: 1px solid var(--navy-20); padding-top: 10px; margin-top: 4px; }
.mm-header-opts > div { margin-top: 4px; }
/* Two columns of switches rather than one tall column — four of these read as a 2x2 block.
   auto-fit was tried and gave three columns on a wide screen, which puts the fourth switch on a
   row of its own. Below 560px they stack, where two columns would wrap every label anyway. */
.mm-opt-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 18px; }
.mm-opt-grid > div { margin-top: 0; }
@media (max-width: 560px) { .mm-opt-grid { grid-template-columns: 1fr; } }

/* ---- The three sections of a campaign ----------------------------------------------------------
   Settings, writing, and choosing who gets it are three distinct jobs. As a run of identical white
   cards they read as one long form with no landmarks; tinted and numbered, the eye can find its
   place after scrolling. Tints are deliberately near-white — enough to separate, not enough to
   compete with the content sitting on them. */

.mm-section {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 18px 20px; margin-bottom: 20px;
}
.mm-section-setup   { background: #fbf8f2; border-color: #ece4d6; }
.mm-section-content { background: #f6f8fc; border-color: #dde5f0; }
.mm-section-audience{ background: #f5f9f3; border-color: #dbe7d7; }
.mm-section-review  { background: var(--white); border-color: var(--gold); border-width: 2px; }

.mm-section-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px; }
.mm-section-num {
  flex: 0 0 auto; width: 27px; height: 27px; border-radius: 50%;
  background: var(--navy); color: var(--white);
  display: flex; align-items: center; justify-content: center;
  font-size: calc(13px + var(--font-boost)); font-weight: 700;
}
.mm-section-title {
  margin: 0; font-size: calc(18px + var(--font-boost)); font-weight: 700;
  letter-spacing: .01em; line-height: 1.2;
}
.mm-section-head .hint { margin-top: 2px; }

/* Cards and inputs sitting on a tint stay white, so fields still read as fields. */
.mm-section .card { background: var(--white); }
.mm-section input, .mm-section select, .mm-section textarea { background: var(--white); }

/* Block cards, tinted by what they are ---------------------------------------------------------
   A newsletter under construction is a column of a dozen near-identical grey cards, and finding
   the one story card among nine text blocks meant reading every header. The tints come from the
   brand's own pastel set — the same values brand.css uses for notices — so the palette stays the
   school's rather than a set of colours invented here. Each pairs a pastel head with a stronger
   version of the same hue for the label and the left edge, which is what carries the distinction
   for anyone who cannot separate the pastels themselves. */
.mm-block-head { border-left: 3px solid var(--navy-20); }
.mm-block-heading  .mm-block-head { background: var(--saffron-light);  border-left-color: var(--gold); }
.mm-block-text     .mm-block-head { background: var(--grey-soft);      border-left-color: var(--navy-50); }
.mm-block-image    .mm-block-head { background: var(--info-blue-light); border-left-color: var(--info-blue); }
.mm-block-card     .mm-block-head { background: var(--lilac-light);    border-left-color: var(--violet); }
.mm-block-events   .mm-block-head { background: var(--skye-light);     border-left-color: var(--skye); }
.mm-block-button   .mm-block-head { background: var(--rose-light);     border-left-color: var(--sienna); }
.mm-block-columns  .mm-block-head { background: var(--pink-light);     border-left-color: var(--ruby); }
.mm-block-html     .mm-block-head { background: var(--grey-warm);      border-left-color: var(--navy-50); }
/* A divider and a spacer are furniture, not content. Left deliberately plain so the eye skips
   them, which is what they are for. */
.mm-block-divider  .mm-block-head,
.mm-block-spacer   .mm-block-head { background: var(--white); border-left-color: var(--navy-20); }

.mm-block-heading .mm-block-type { color: var(--gold-deep); }
.mm-block-image   .mm-block-type { color: #2c5c8a; }
.mm-block-card    .mm-block-type { color: #7d4576; }
.mm-block-events  .mm-block-type { color: #246300; }
.mm-block-button  .mm-block-type { color: var(--sienna); }
.mm-block-columns .mm-block-type { color: #b2416c; }

/* The per-type number. Same size as the label, deliberately lighter: "TEXT 3" should read as one
   thing with an index, not as two words of equal weight. */
.mm-block-n { font-weight: 400; opacity: .75; }

/* Primary actions ------------------------------------------------------------------------------
   brand.css makes .btn navy with white text. On a page of white cards that reads as black — and
   Send, Review recipients and Save all looked like the same heavy slab. Gold with navy text is
   the school's own accent, it is unmistakably a button, and it leaves navy free for the one
   button that should still feel grave. Overridden here rather than in brand.css, which stays a
   verbatim copy of the Support Desk's. */
.btn {
  background: var(--gold);
  color: var(--navy);
  border: 1px solid var(--gold-deep);
}
.btn:hover:not(:disabled) { background: var(--gold-soft); }
.btn.secondary { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
.btn.secondary:hover:not(:disabled) { background: var(--grey-soft); border-color: var(--navy-50); }
/* Kept loud: these two are the irreversible ones. */
.btn.danger  { background: var(--scarlett); border-color: var(--scarlett); color: var(--white); }
.btn.success { background: var(--skye); border-color: var(--skye); color: var(--white); }

/* The whole-school shortcuts, each carrying the number of pupils it covers. The count sits in a
   chip rather than in the label text so it stays legible when the button is the dark "on" state
   and so the three buttons keep the same shape whether the number is 8 or 238. */
.scope-btn { display: inline-flex; align-items: center; gap: 9px; }
.scope-n {
  background: var(--navy); color: var(--white); border-radius: 99px;
  padding: 1px 8px; font-size: calc(11px + var(--font-boost)); font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Send report and statistics ------------------------------------------------------------------
   Both pages are long, and a long page of identical white cards is one the eye slides off. Each
   block gets a title with a gold rule under it, so scrolling past gives a sense of place. */
.rep-block {
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
  padding: 18px 20px; margin-bottom: 16px;
}
.rep-title {
  margin: 0 0 4px; font-size: calc(17px + var(--font-boost)); font-weight: 700; color: var(--navy);
  padding-bottom: 8px; border-bottom: 2px solid var(--gold);
}
.rep-note { margin: 10px 0 14px; max-width: 78ch; }
.rep-block h4 {
  margin: 20px 0 8px; font-size: calc(12px + var(--font-boost)); text-transform: uppercase;
  letter-spacing: .6px; color: var(--text-muted);
}

/* Facts about one thing: a label column that stays narrow and a value column that wraps. Grid
   rather than a table because a table would let one long value widen the labels. */
.rep-facts { display: grid; grid-template-columns: minmax(150px, 210px) 1fr; gap: 1px 20px; margin: 0; }
.rep-facts dt {
  font-size: calc(13px + var(--font-boost)); color: var(--text-muted); padding: 7px 0;
  border-top: 1px solid var(--border);
}
.rep-facts dd {
  margin: 0; padding: 7px 0; border-top: 1px solid var(--border);
  font-size: calc(14px + var(--font-boost)); overflow-wrap: anywhere;
}
.rep-facts dt:first-of-type, .rep-facts dd:first-of-type { border-top: none; }
/* Not known is a real answer and looks like one — never an empty cell, which reads as zero. */
.rep-unknown { color: var(--text-muted); font-style: italic; }

.rep-figures { display: flex; flex-wrap: wrap; gap: 10px; margin: 4px 0 6px; }
.rep-figure {
  flex: 1 1 140px; background: var(--paper); border: 1px solid var(--border);
  border-radius: 8px; padding: 12px 14px; border-top: 3px solid var(--navy-20);
}
.rep-figure.good { border-top-color: var(--skye); }
.rep-figure.bad  { border-top-color: var(--scarlett); }
.rep-figure-n {
  font-size: calc(26px + var(--font-boost)); font-weight: 700; line-height: 1.1;
  color: var(--navy); font-variant-numeric: tabular-nums;
}
.rep-figure-l { font-size: calc(13px + var(--font-boost)); color: var(--text); margin-top: 2px; }
.rep-figure-s { font-size: calc(11px + var(--font-boost)); color: var(--text-muted); margin-top: 3px; }

/* Two columns on a laptop, one on a phone. 420px is where a table inside one starts to squeeze. */
/* min() is load-bearing. A bare minmax(420px, 1fr) can never be narrower than 420px, so on a
   375px phone the grid itself was wider than the screen and pushed the whole report sideways —
   which is what made the send report the only page in the app that scrolled horizontally.
   min(420px, 100%) lets the track collapse to the available width instead. */
.rep-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); gap: 16px; }
/* Bar lists need far less width than a table does, so they can sit side by side much sooner.
   auto-fit with a 1fr max means it drops to one column on a phone by itself — the layout never
   scrolls sideways, it just reflows. */
.rep-cols.tight { grid-template-columns: repeat(auto-fit, minmax(min(270px, 100%), 1fr)); gap: 14px 26px; }
.rep-cols > .rep-block { margin-bottom: 0; }

/* overflow-x alone is not enough. A grid or flex child defaults to min-width:auto, which means
   it refuses to shrink below its own content — so a 694px recipient table pushed the whole PAGE
   sideways instead of scrolling inside its own box. Same bug as the About page's 660px table.
   The wide table still scrolls within itself; the page never does. */
.rep-scroll { overflow-x: auto; min-width: 0; max-width: 100%; }
.rep-tall { max-height: 520px; overflow-y: auto; }
.rep-scroll table { width: 100%; }
.rep-tall thead th { position: sticky; top: 0; background: var(--surface); z-index: 1; }

.rep-bars { display: flex; flex-direction: column; gap: 6px; }
/* The number travels with the end of its bar rather than sitting in a right-aligned column.
   With the track removed there is nothing between a short bar and a distant figure, and the eye
   has to cross an empty space to pair them. Attached, each row reads as one object. */
.rep-bar-row { display: grid; grid-template-columns: minmax(90px, 160px) 1fr; align-items: center; gap: 10px; }
.rep-bar-meter { display: flex; align-items: center; gap: 9px; min-width: 0; }
.rep-bar-label { font-size: calc(13px + var(--font-boost)); overflow-wrap: anywhere; }
/* NO TRACK behind the fill, deliberately.
   A filled bar sitting inside a grey track is the visual language of PROGRESS — "42 of 55 done"
   — and every bar in this report is a MAGNITUDE: how many families are in Year 2W, how many read
   it on a phone. Drawn with a track, a send report appeared to say that some year groups were
   only partly delivered, which is the one thing this app must never imply by accident.
   Without the track it is an ordinary bar chart and reads as size, which is what it is. */
.rep-bar-track { background: transparent; border-radius: 99px; height: 10px; overflow: hidden; flex: 0 0 auto; }
.rep-bar-fill { height: 100%; background: var(--navy); border-radius: 99px; }
.rep-bar-value {
  /* Never wraps: a two-line value ("28 · 1 bounced") grew its row taller than the others and
     broke the rhythm the eye uses to scan down the column. */
  white-space: nowrap;
  font-size: calc(13px + var(--font-boost)); font-weight: 700; font-variant-numeric: tabular-nums;
  min-width: 3ch; text-align: right;
}

/* First contact — one real person, one moment. Navy so it reads as the find it is. */
.rep-first { background: var(--navy); color: var(--paper); border-radius: 8px; padding: 14px 16px; margin: 14px 0; }
.rep-first-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.rep-first-head .eyebrow { color: var(--gold); margin: 0; }
.rep-first-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px 18px; }
.rep-first-k { font-size: calc(11px + var(--font-boost)); color: #9aa0b4; text-transform: uppercase; letter-spacing: .5px; }
.rep-first-v { font-size: calc(14px + var(--font-boost)); font-weight: 700; margin-top: 1px; }
.rep-first .hint { color: #9aa0b4; margin-top: 12px; }

/* Charts. Inline SVG, sized by its viewBox, so it scales with the card and prints. */
.chart { width: 100%; margin: 6px 0 2px; }
.chart svg { width: 100%; height: auto; display: block; }
.chart-tick { font-size: 10px; fill: var(--text-muted); }
.chart-centre { font-size: 22px; font-weight: 700; fill: var(--navy); }
.chart-donut { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.chart-donut svg { width: 150px; height: 150px; flex: none; }
.chart-key { list-style: none; margin: 0; padding: 0; flex: 1 1 160px; }
.chart-key li { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: calc(13px + var(--font-boost)); }
.chart-key li strong { margin-left: auto; font-variant-numeric: tabular-nums; }
.chart-swatch { width: 11px; height: 11px; border-radius: 3px; flex: none; }

/* Past campaigns ------------------------------------------------------------------------------
   Cards rather than rows: what identifies last term's newsletter is its name and its subject
   line, and both need more than a table cell's worth of room. */
.arch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin-top: 12px; }
.arch-card {
  border: 1px solid var(--border); border-radius: 8px; padding: 14px; background: var(--paper);
  display: flex; flex-direction: column; gap: 6px;
}
.arch-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: calc(12px + var(--font-boost)); }
/* Overrides `.rep-block h4`, which is a small uppercase section label — right for "BY YEAR
   GROUP", wrong for a campaign's own name, which came out shouting. */
/* Two classes deep on purpose: `.rep-block h4` is (0,1,1) and outranks a bare `.arch-name`
   whatever the order in this file. */
.arch-card .arch-name {
  margin: 2px 0 0; font-size: calc(16px + var(--font-boost)); font-weight: 700;
  text-transform: none; letter-spacing: 0; color: var(--navy);
}
.arch-subject { margin: 0; color: var(--text-muted); font-size: calc(13px + var(--font-boost)); }
.arch-count { margin: 0; font-size: calc(13px + var(--font-boost)); font-variant-numeric: tabular-nums; }
.arch-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: auto; padding-top: 8px; }
.arch-actions .btn { padding: 6px 11px; font-size: calc(12px + var(--font-boost)); }

/* The frozen pre-send checks. Amber rather than red: these were known and sent anyway, which is
   a record of a decision, not an error to be alarmed about now. */
.rep-checks { margin: 6px 0 0; padding-left: 20px; }
.rep-checks li { margin: 4px 0; font-size: calc(13px + var(--font-boost)); color: var(--gold-deep); }

/* Placeholders ---------------------------------------------------------------------------------
   Light enough to read as an example rather than as content, dark enough to actually read. navy-50
   is the same muted tone the hints use and clears 4.5:1 on white; the greys browsers default to do
   not. Nothing is ever pre-filled into a field for someone to delete — see NEW_BLOCK. */
::placeholder { color: var(--navy-50); opacity: 1; }
input::placeholder, textarea::placeholder { font-style: italic; }

/* The rich-text areas are contenteditable and take no placeholder attribute, so the hint is drawn
   over the empty editor. pointer-events:none keeps the click going through to the text. */
.mm-rte[data-empty]::before {
  content: attr(data-placeholder);
  /* z-index is load-bearing: TipTap's own editor element is position:relative with a white
     background, so without it the placeholder is painted and then covered over. */
  position: absolute; z-index: 1; pointer-events: none;
  color: var(--navy-50); font-style: italic;
  padding: 12px 14px;
  font-size: calc(15px + var(--font-boost));
}
.mm-rte { position: relative; }

/* Which block the preview was just clicked on. A ring that fades, not a selection that stays:
   it answers "which one is this" and then gets out of the way. */
@keyframes mm-flash {
  from { box-shadow: 0 0 0 3px var(--gold); }
  to   { box-shadow: 0 0 0 3px rgba(222, 172, 53, 0); }
}
.mm-block.mm-flash { animation: mm-flash 1.4s var(--ease); }
/* The preview's own cursor, so it reads as something you can click into rather than a picture. */
.mm-preview-frame { cursor: pointer; }

/* The add buttons, in the colour of the block each one makes -------------------------------------
   Same tints as the block cards below them, so pressing "+ Story card" and seeing a lilac card
   appear is one idea rather than two. Kept as tinted outlines rather than solid fills: nine solid
   buttons would shout louder than the campaign itself. */
/* Every rule below is written two classes deep. `.btn.secondary` is (0,2,0) and would otherwise
   win against a single class whatever the order, which is what made the first attempt at this
   render nine white buttons. */
.mm-add-palette .mm-add { border-width: 1px; border-style: solid; font-weight: 700; }
.mm-add-palette .mm-add-heading { background: var(--saffron-light);   border-color: var(--gold);      color: var(--gold-deep); }
.mm-add-palette .mm-add-text    { background: var(--grey-soft);       border-color: var(--navy-50);   color: var(--navy); }
.mm-add-palette .mm-add-image   { background: var(--info-blue-light); border-color: var(--info-blue); color: #2c5c8a; }
.mm-add-palette .mm-add-card    { background: var(--lilac-light);     border-color: var(--violet);    color: #7d4576; }
.mm-add-palette .mm-add-events  { background: var(--skye-light);      border-color: var(--skye);      color: #246300; }
.mm-add-palette .mm-add-button  { background: var(--rose-light);      border-color: var(--sienna);    color: var(--sienna); }
.mm-add-palette .mm-add-columns { background: var(--pink-light);      border-color: var(--ruby);      color: #b2416c; }
.mm-add-palette .mm-add-divider,
.mm-add-palette .mm-add-spacer  { background: var(--white);           border-color: var(--navy-20);   color: var(--navy-50); }
.mm-add-palette .mm-add:hover:not(:disabled) { filter: brightness(0.96); }

/* What the campaign is made of. A divider above it, because it is a summary of the thing rather
   than another control. */
.mm-used { border-top: 1px solid var(--border); margin-top: 14px; padding-top: 10px; }
.mm-used-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.mm-used-head .eyebrow { margin: 0; }
.mm-used-total { font-size: calc(12px + var(--font-boost)); color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* auto-fill, so it wraps to as many rows as it needs — two on a laptop, one on a phone — without
   anyone deciding in advance how many there should be. */
.mm-used-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 6px; }
.mm-used-chip {
  display: flex; align-items: baseline; gap: 7px; padding: 5px 10px;
  border: 1px solid var(--border); border-left-width: 3px; border-radius: 6px;
  background: var(--white); font-size: calc(13px + var(--font-boost));
}
.mm-used-chip strong { font-size: calc(15px + var(--font-boost)); font-variant-numeric: tabular-nums; min-width: 1.4ch; }
.mm-used-heading { background: var(--saffron-light);   border-left-color: var(--gold); }
.mm-used-text    { background: var(--grey-soft);       border-left-color: var(--navy-50); }
.mm-used-image   { background: var(--info-blue-light); border-left-color: var(--info-blue); }
.mm-used-card    { background: var(--lilac-light);     border-left-color: var(--violet); }
.mm-used-events  { background: var(--skye-light);      border-left-color: var(--skye); }
.mm-used-button  { background: var(--rose-light);      border-left-color: var(--sienna); }
.mm-used-columns { background: var(--pink-light);      border-left-color: var(--ruby); }

/* Admin: the two fixed bases a campaign type can sit under. Presented as headings rather than as
   an editable field, because moving a type between them would change the unsubscribe rule for
   everything sent under it, past and future. */
.adm-basis { border: 1px solid var(--border); border-radius: 8px; padding: 14px; margin-top: 14px; }
.adm-basis-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 8px; }
.adm-basis-head .hint { margin-top: 2px; }
.adm-basis table { margin-top: 6px; }
.adm-basis .mm-add { padding: 4px 9px; font-size: calc(12px + var(--font-boost)); margin-right: 4px; }

/* The emoji picker ------------------------------------------------------------------------------
   Appended to <body> and positioned against its button, so it is never clipped by a card's
   overflow or trapped inside the editor's scrolling column. */
.emoji-pop {
  position: fixed; z-index: 2000; width: 340px;
  background: var(--surface); border: 1px solid var(--navy-20); border-radius: 10px;
  box-shadow: 0 12px 32px rgba(27, 32, 52, .18); padding: 10px;
  display: flex; flex-direction: column; gap: 8px;
}
.emoji-search { width: 100% !important; padding: 6px 10px; font-size: calc(13px + var(--font-boost)); }
.emoji-tabs { display: flex; gap: 2px; flex-wrap: wrap; }
.emoji-tab {
  border: 1px solid transparent; background: none; border-radius: 6px; cursor: pointer;
  font-size: 17px; line-height: 1; padding: 5px 6px;
}
.emoji-tab:hover { background: var(--grey-soft); }
.emoji-tab.on { background: var(--saffron-light); border-color: var(--gold); }
.emoji-grid { overflow-y: auto; max-height: 260px; }
.emoji-heading {
  font-size: calc(11px + var(--font-boost)); text-transform: uppercase; letter-spacing: .5px;
  color: var(--text-muted); margin: 6px 0 4px; position: sticky; top: 0; background: var(--surface);
}
/* A fixed eight columns rather than auto-fill: emoji differ in advance width, and a grid that
   reflows as you scroll between groups is unpleasant to aim at. */
.emoji-row { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; }
.emoji-cell {
  border: 0; background: none; cursor: pointer; border-radius: 6px;
  font-size: 21px; line-height: 1; padding: 5px 0;
}
.emoji-cell:hover { background: var(--saffron-light); }
.mm-emoji-btn { font-size: 15px; }
/* A field with its own emoji button beside it. */
.mm-input-emoji { display: flex; gap: 6px; align-items: center; }
.mm-input-emoji input { flex: 1 1 auto; }
.mm-input-emoji .mm-icon { flex: none; }
.mm-subject-emoji { padding: 6px 10px; }

/* A card's buttons, one row each, with the remove control at the end of the row. */
.mm-btn-edit { display: flex; gap: 10px; align-items: flex-end; margin-bottom: 8px; }
.mm-btn-edit > div { flex: 1 1 0; min-width: 0; }
.mm-btn-edit > .mm-icon { flex: none; margin-bottom: 1px; }

/* Text pinned to a fixed size, which will not follow the campaign's base size. Amber rather than
   red: it is a choice someone made, and sometimes the right one — a pull quote, a line of fine
   print — so this points it out rather than telling anyone off. */
.mm-pinned:not(:empty) {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 8px; padding: 8px 11px; border-radius: 6px;
  background: var(--saffron-light); border: 1px solid var(--gold);
  font-size: calc(12px + var(--font-boost)); color: var(--gold-deep);
}
.mm-pinned .mm-add { margin-left: auto; }

/* Named text styles, shown in the editor as they will compile. The email gets real inline pixel
   sizes from shared/compile.ts; these are so the writing surface does not lie about them. */
.mm-rte .tiptap .mm-lead  { font-size: 1.15em; line-height: 1.5; }
.mm-rte .tiptap .mm-small { font-size: 0.82em; line-height: 1.5; color: #6b7080; }
/* The house style for a link: bold, underlined, pale sky behind it. Matches LINK_STYLE in
   shared/compile.ts, so the editor shows what the family will see. */
.mm-rte .tiptap a {
  font-weight: 700; text-decoration: underline;
  background: #e3eefa; padding: 1px 3px; border-radius: 3px;
}
.mm-style { width: auto !important; min-width: 124px; }

/* The "preview as this recipient" frame on the review screen. An iframe, not injected HTML: the
   app's stylesheet must not reach the email being checked, and the email's own @font-face and
   media query must not reach the app. */
.mm-sample-wrap {
  margin-top: 10px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--paper); overflow: hidden;
}
.mm-sample-frame { width: 100%; height: 520px; border: 0; display: block; background: var(--paper); }

/* Every recipient, one line each and numbered.
   Deliberately NOT a comma-separated run: that is what a To: field looks like, and this list is
   read by someone deciding whether it is safe to send to hundreds of families. The format should
   not imply the one thing the app exists to prevent. */
.address-list {
  max-height: 260px; overflow: auto; margin: 8px 0 0; padding: 10px 10px 10px 38px;
  background: var(--grey-soft); border-radius: 6px;
  font-size: calc(12px + var(--font-boost)); color: var(--text);
}
.address-list li { padding: 2px 0; }
.address-list li::marker { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.addr-email { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.addr-name { color: var(--text-muted); margin-left: 8px; }

/* Thumbs up or down against the last test. Deliberately large and blunt: this is the last thing
   read before a newsletter reaches hundreds of families, and "it changed" in grey body text was
   too easy to read past. */
.mm-verdict {
  display: flex; align-items: flex-start; gap: 12px;
  margin: 10px 0; padding: 12px 14px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface);
}
.mm-verdict.good { background: var(--skye-light); border-color: var(--skye); }
.mm-verdict.bad  { background: var(--saffron-light); border-color: var(--gold-deep); }
.mm-verdict-mark { font-size: 24px; line-height: 1.1; }

/* ---- The About view (#/about) ---------------------------------------------------------------
   Ported from the standalone about.html page. Everything is scoped under .about-view for one
   specific reason: brand.css sets a global `tr:hover td { background }` and uppercases every
   `th`, and those reach into any table rendered in the app. The email preview learned this the
   hard way and ended up in an iframe; a page of prose does not need an iframe, it needs the
   rules written so they win. Body copy follows the masthead's A-/A/A+ control via --font-boost,
   with a 0px fallback so the same markup still renders correctly outside the app shell. */
.about-view { max-width: 1080px; }
.about-view .aa-head { padding: 4px 0 26px; border-bottom: 1px solid var(--navy-10); }
.about-view .aa-head h1 { font-size: calc(34px + var(--font-boost, 0px)); line-height: 1.12; margin: 8px 0 14px; color: var(--navy); font-weight: 900; }
.about-view .aa-head h1 strong { color: var(--gold-deep); font-weight: 900; }
.about-view .aa-standfirst { font-size: calc(16px + var(--font-boost, 0px)); line-height: 1.6; color: var(--navy-70); max-width: 68ch; margin: 0; }

.about-view .aa-tabs { display: flex; gap: 6px; margin: 22px 0 0; flex-wrap: wrap; }
.about-view .aa-tab {
  appearance: none; border: 1px solid var(--navy-20); background: var(--white);
  color: var(--navy-70); font: inherit; font-weight: 700; font-size: calc(14px + var(--font-boost, 0px));
  padding: 9px 18px; border-radius: 999px; cursor: pointer; transition: all .14s ease;
}
.about-view .aa-tab:hover { border-color: var(--navy-50); color: var(--navy); }
.about-view .aa-tab[aria-selected="true"] { background: var(--navy); border-color: var(--navy); color: var(--white); }

.about-view .aa-panel[hidden] { display: none; }
.about-view .aa-section { padding: 34px 0; border-bottom: 1px solid var(--navy-10); }
.about-view .aa-section:last-child { border-bottom: 0; }
.about-view .aa-n { font-size: 12px; font-weight: 900; letter-spacing: .14em; color: var(--gold-deep); }
.about-view .aa-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--navy-50); margin-left: 10px; }
.about-view .aa-section h2 { font-size: calc(24px + var(--font-boost, 0px)); line-height: 1.2; margin: 10px 0 18px; color: var(--navy); font-weight: 900; }
.about-view .aa-subhead { font-size: calc(15px + var(--font-boost, 0px)); font-weight: 900; color: var(--navy); margin: 30px 0 12px; }
.about-view .aa-section p { font-size: calc(15px + var(--font-boost, 0px)); line-height: 1.72; color: var(--navy-70); margin: 0 0 15px; max-width: 74ch; }
.about-view .aa-section p.lede { font-size: calc(16.5px + var(--font-boost, 0px)); color: var(--navy-90); }
.about-view .aa-section strong { color: var(--navy); font-weight: 700; }
.about-view .aa-section ul { margin: 0 0 16px; padding-left: 20px; max-width: 74ch; }
.about-view .aa-section li { font-size: calc(15px + var(--font-boost, 0px)); line-height: 1.72; color: var(--navy-70); margin-bottom: 8px; }

.about-view .aa-table-wrap { overflow-x: auto; margin: 6px 0 10px; border: 1px solid var(--navy-10); border-radius: 10px; }
/* A four- or five-column table has nowhere to go on a phone: every cell wraps to three words
   and the thing becomes unreadable. Give those a floor and let the wrapper scroll sideways. */
.about-view .aa-table-wrap.wide table { min-width: 660px; }
.about-view .aa-scroll-hint { display: none; font-size: 12px; color: var(--navy-50); margin: 6px 0 0; font-style: italic; }
@media (max-width: 900px) {
  .about-view .aa-table-wrap.wide + .aa-scroll-hint { display: block; }
}
.about-view table { width: 100%; border-collapse: collapse; background: var(--white); }
.about-view th {
  text-align: left; padding: 11px 14px; background: var(--grey-soft); color: var(--navy);
  font-weight: 900; font-size: calc(12px + var(--font-boost, 0px)); letter-spacing: .03em;
  border-bottom: 1px solid var(--navy-10); white-space: nowrap;
  /* brand.css uppercases every th in the app. These are sentence-case column headings. */
  text-transform: none;
}
.about-view td {
  padding: 11px 14px; border-bottom: 1px solid var(--navy-10); color: var(--navy-70);
  font-size: calc(13.5px + var(--font-boost, 0px)); line-height: 1.55; vertical-align: top;
}
.about-view tbody tr:last-child td { border-bottom: 0; }
/* brand.css tints every row on hover. That is right for a list you click and wrong for a
   reference table you read, and it is the same specificity as the rule above, so it has to be
   turned off by name rather than out-weighted. */
.about-view tbody tr:hover td { background: var(--white); }
.about-view td:first-child { color: var(--navy); font-weight: 700; }
.about-view td.yes { color: #2c6b3f; font-weight: 700; }
.about-view td.no { color: var(--navy-50); }
.about-view .aa-table-caption,
.about-view .aa-sources { font-size: calc(12.5px + var(--font-boost, 0px)); line-height: 1.62; color: var(--navy-50); margin: 10px 0 0; max-width: 78ch; }
.about-view .aa-sources { padding-top: 10px; border-top: 1px dashed var(--navy-20); margin-top: 16px; }
.about-view .aa-sources a { color: var(--info-blue); }

.about-view .aa-figs { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 4px 0 22px; }
.about-view .aa-fig { background: var(--grey-soft); border: 1px solid var(--navy-10); border-radius: 10px; padding: 16px 16px 14px; }
.about-view .aa-fig b { display: block; font-size: calc(26px + var(--font-boost, 0px)); line-height: 1; color: var(--navy); font-weight: 900; }
.about-view .aa-fig span { display: block; font-size: calc(12px + var(--font-boost, 0px)); color: var(--navy-50); margin-top: 7px; line-height: 1.4; }

.about-view .aa-note { border-left: 3px solid var(--gold); background: var(--saffron-light); padding: 15px 18px; border-radius: 0 8px 8px 0; margin: 20px 0; }
.about-view .aa-note p:last-child { margin-bottom: 0; }
.about-view .aa-note.rule { border-left-color: var(--scarlett); background: var(--rose-light); }
.about-view .aa-note.good { border-left-color: var(--skye); background: var(--skye-light); }

@media (max-width: 620px) {
  .about-view .aa-head h1 { font-size: calc(27px + var(--font-boost, 0px)); }
  .about-view .aa-section h2 { font-size: calc(21px + var(--font-boost, 0px)); }
  .about-view .aa-fig b { font-size: calc(22px + var(--font-boost, 0px)); }
}

/* ---- The public About page (/about) ---------------------------------------------------------
   Same .about-view content as the in-app #/about route, wrapped for a standalone document.
   .landing is a column flex container, so this needs an explicit width: left at auto its
   hypothetical cross size is its MAX-content width, the flex line grows to match, and one
   660px-wide table drags the page header off the right edge on a phone. */
.about-standalone {
  max-width: 1080px; width: 100%; min-width: 0; box-sizing: border-box;
  margin: 0 auto; padding: 34px 22px 70px; flex: 1;
}
.about-standalone .about-view { max-width: none; }
.about-standalone .aa-head { padding-top: 0; }
.aa-enter { margin: 34px 0 0; }
.aa-enter a { font-size: 15px; font-weight: 700; color: var(--info-blue); }
@media (max-width: 620px) { .about-standalone { padding: 24px 16px 50px; } }

/* The way in to the About page from the landing page: a quiet pill at the top of the footer,
   not a second call to action competing with "Enter Monkey Mail". It sits INSIDE .landing-footer
   rather than above it — as its own band it left a pale strip of page background stranded
   between the dark hero and the navy footer, which read as a mistake. */
.landing-about-link { padding: 0 0 20px; }
.landing-about-link a {
  display: inline-block; font-size: 13px; font-weight: 700;
  color: rgba(255, 255, 255, 0.82); border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 999px; padding: 8px 18px; text-decoration: none; transition: all .14s ease;
}
.landing-about-link a:hover {
  color: var(--navy); background: var(--gold); border-color: var(--gold);
}

/* Per-block spacing control, in the block's own header beside move and delete. Quiet by default:
   it is an adjustment, not an action, and it should not compete with ↑ ↓ ✕. */
.mm-space-ctl {
  display: inline-flex; align-items: center; gap: 2px;
  margin-right: 6px; padding-right: 6px; border-right: 1px solid var(--navy-10);
}
.mm-space-n {
  font-size: 11px; font-weight: 700; color: var(--navy-50);
  min-width: 34px; text-align: center; font-variant-numeric: tabular-nums;
}
.mm-space-ctl .mm-icon { padding: 2px 6px; line-height: 1; }

/* ---- Campaigns list, in three sections ------------------------------------------------------
   Operational and marketing are different obligations, not just different labels — one carries
   an unsubscribe link and the other must not. The tint makes that visible while scanning rather
   than only when you stop and read the badge. Saffron for marketing, pink for operational:
   scarlett was tried and is far too strong for a whole row, and reads as an error state. */
.mm-camp-section { margin-bottom: 16px; }
.mm-camp-head { padding: 2px 2px 10px; }
.mm-camp-head h3 { margin: 0 0 2px; font-size: 16px; display: flex; align-items: center; gap: 8px; }
.mm-camp-n {
  font-size: 12px; font-weight: 700; color: var(--navy-50);
  background: var(--navy-10); border-radius: 999px; padding: 1px 8px;
}
.mm-camp-head .hint { margin: 0; }

.mm-group-row td {
  padding: 10px 10px 4px; border-bottom: 0; background: transparent;
}
.mm-group-row .eyebrow { font-size: 10px; }
.mm-group-n { margin-left: 8px; font-size: 11px; }
.mm-group-operational .eyebrow { color: var(--sienna); }
.mm-group-marketing .eyebrow { color: var(--gold-deep); }

/* Row tints, in one place so they can be nudged without hunting.
   Lighter than the brand's --pink-light (#fceef3) and --saffron-light (#fdf3d9), which are
   pitched for a small badge and read as solid colour once they cover a whole table row. Across a
   row the job is only to say "this group is not that group" while the text stays the thing you
   actually look at, so the tint sits just above the paper rather than beside it. */
:root {
  --row-ops: #fdf4f7;     /* operational — pastel pink */
  --row-ops-hover: #fbe9f0;
  --row-mkt: #fefaef;     /* marketing — pastel saffron */
  --row-mkt-hover: #fdf3d9;
}
.mm-row-ops td { background: var(--row-ops); }
.mm-row-mkt td { background: var(--row-mkt); }
/* The tint IS the hover affordance's baseline, so hover has to move away from it rather than
   toward it — otherwise hovering a tinted row looks like un-selecting it. */
.mm-row-ops:hover td { background: var(--row-ops-hover); }
.mm-row-mkt:hover td { background: var(--row-mkt-hover); }
.mm-when { white-space: nowrap; font-size: 12px; }

/* The way back from "this one also goes to families". Sits above the year groups rather than
   below them: it is an escape hatch, and an escape hatch under 16 year-group cards is one the
   person who needs it has already scrolled past. */
.mm-back-to-staff { margin: 0 0 12px; }

/* "You are running an old version" bar. Fixed to the bottom rather than the top: the top is
   where the masthead and the campaign's own status live, and this must not push the page down
   and move what someone is reading. Asks rather than reloads — reloading out from under someone
   mid-campaign would be worse than the staleness it fixes. */
.mm-stale-bar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px; z-index: 60;
  display: flex; align-items: center; gap: 10px;
  background: var(--navy); color: var(--paper);
  padding: 10px 14px; border-radius: 999px;
  box-shadow: 0 6px 24px rgba(27, 32, 52, 0.28);
  font-size: 14px; max-width: calc(100vw - 32px);
}
.mm-stale-bar .btn { padding: 4px 12px; font-size: 13px; }
.mm-stale-bar .muted { color: var(--navy-20); font-size: 13px; }
@media (max-width: 620px) {
  .mm-stale-bar { bottom: 10px; font-size: 13px; }
  .mm-stale-bar .muted { display: none; }
}


/* Compact numeric tables in the send report.
   Numbers right-aligned with tabular figures so a column compares down its length, which is the
   entire reason this is a table and not a chart. */
.rep-tight { width: 100%; border-collapse: collapse; margin-top: 6px; }
.rep-tight th, .rep-tight td { padding: 5px 8px; font-size: calc(13px + var(--font-boost)); }
.rep-tight th { font-size: calc(11px + var(--font-boost)); text-transform: uppercase;
                letter-spacing: .07em; color: var(--navy-50); font-weight: 700; text-align: left;
                border-bottom: 1px solid var(--navy-10); }
.rep-tight td { border-bottom: 1px solid var(--grey-soft, #f0ede5); }
.rep-tight .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rep-tight tfoot td { border-bottom: 0; border-top: 2px solid var(--navy-10); font-weight: 700; }
/* A row with a bounce is findable without being alarming — the school should notice it, not be
   startled by it. */
.rep-tight .row-flag td { background: #fdf6ea; }


/* The investigation log. A sub-item sits slightly in from its parent, so the pair reads as one
   thing with a detail view rather than as two unrelated pages. */
.nav a.sub { padding-left: 34px; font-size: calc(13px + var(--font-boost, 0px)); }
.log-text { white-space: pre-wrap; font-family: inherit; font-size: 13.5px; line-height: 1.6;
            background: var(--paper, #faf7f1); border: 1px solid var(--navy-10); border-radius: 6px;
            padding: 12px; margin: 8px 0 0; max-height: 360px; overflow-y: auto; }
/* What went and what arrived, coloured the way a diff is read. Not red and green alone — the
   marks carry it too, for anyone who cannot separate the two colours. */
.log-removed { font-size: 13px; padding: 3px 8px; border-left: 3px solid var(--scarlett);
               background: #fdecec; margin-bottom: 3px; border-radius: 3px; }
.log-removed::before { content: "− "; font-weight: 700; }
.log-added { font-size: 13px; padding: 3px 8px; border-left: 3px solid var(--skye);
             background: #eef6fd; margin-bottom: 3px; border-radius: 3px; }
.log-added::before { content: "+ "; font-weight: 700; }

/* Statistics dashboard ------------------------------------------------------------------------
   Period and audience first, then the summary, then one tile per recent campaign. Tiles are
   numbers in a column, number first, because the question on each is "how many" and the eye
   should land on the figure. No bars anywhere: a fill inside a track reads as progress, which is
   how a send report once looked "partly delivered" with every number right. */
.mm-dash-controls { display: flex; flex-wrap: wrap; gap: 10px 24px; align-items: center; margin: 0 0 14px; }
.mm-dash-custom { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; margin: -4px 0 14px; }
.mm-dash-custom label { display: flex; align-items: center; gap: 6px; margin: 0; font-weight: 400; }
.mm-dash-custom input { width: auto; }
/* Five across on a laptop (1440) — the recent five are one row, not four and an orphan. auto-fit
   rather than auto-fill, so on a large monitor the five stretch instead of leaving empty tracks. */
.mm-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: 14px; margin-top: 6px; }
.mm-tile {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
  background: var(--paper); border: 1px solid var(--border); border-top: 3px solid var(--gold);
  border-radius: 8px; padding: 12px 14px 10px; color: var(--text); text-decoration: none;
}
.mm-tile:hover { border-color: var(--navy-20); border-top-color: var(--gold-deep); box-shadow: 0 2px 10px rgba(27, 32, 52, .08); }
.mm-tile-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; min-height: 3.4em; }
.mm-tile-head strong {
  color: var(--navy); font-size: calc(14px + var(--font-boost)); line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mm-tile-head span { color: var(--text-muted); font-size: calc(12px + var(--font-boost)); }
.mm-tile-row { display: flex; align-items: baseline; gap: 10px; padding: 3px 0; border-top: 1px solid var(--border); }
.mm-tile-row .n {
  flex: 0 0 3.6em; text-align: right; font-weight: 700; color: var(--navy);
  font-size: calc(16px + var(--font-boost)); font-variant-numeric: tabular-nums;
}
.mm-tile-row .l { font-size: calc(12.5px + var(--font-boost)); min-width: 0; }
.mm-tile-row.warn .n { color: var(--scarlett); }
.mm-tile-sep {
  margin: 8px 0 0; padding-top: 6px; border-top: 1px solid var(--border);
  font-size: calc(11px + var(--font-boost)); text-transform: uppercase; letter-spacing: .6px; color: var(--text-muted);
}
.mm-tile-foot { margin-top: 4px; font-size: calc(11px + var(--font-boost)); color: var(--text-muted); }
.mm-detail-range { display: flex; flex-wrap: wrap; gap: 10px 20px; align-items: center; margin: 28px 0 14px; }
.mm-detail-range h2 { margin: 0; font-size: calc(20px + var(--font-boost)); color: var(--navy); }

/* Past campaigns: three tiles, never stretched across a large monitor into three wide bands. */
.mm-tiles-3 { grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 290px)); }

/* Records ----------------------------------------------------------------------------------------
   Each card says what its figure is, because a record whose basis is off the page is a boast. */
.mm-records { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); gap: 14px; margin-top: 6px; }
.mm-record {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  background: var(--paper); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px;
  color: var(--text); text-decoration: none;
}
a.mm-record:hover { border-color: var(--navy-20); box-shadow: 0 2px 10px rgba(27, 32, 52, .08); }
.mm-record-head { display: flex; align-items: center; gap: 8px; font-weight: 700; color: var(--navy); font-size: calc(14px + var(--font-boost)); }
.mm-record-icon { font-size: calc(16px + var(--font-boost)); }
.mm-record-basis { margin: 0 0 4px; font-size: calc(11.5px + var(--font-boost)); color: var(--text-muted); line-height: 1.4; }
.mm-podium { list-style: none; margin: 0; padding: 0; }
.mm-podium li { display: flex; gap: 10px; padding: 7px 0; border-top: 1px solid var(--border); }
.mm-podium .place {
  flex: 0 0 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  font-size: calc(11px + var(--font-boost)); font-weight: 700; color: var(--navy); background: var(--navy-10);
}
.mm-podium li.first .place { background: var(--gold); color: var(--navy); }
.mm-podium-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; color: var(--text); text-decoration: none; }
.mm-podium-body:hover .mm-podium-name { text-decoration: underline; }
.mm-podium-fig { font-weight: 700; color: var(--navy); font-size: calc(15px + var(--font-boost)); overflow-wrap: anywhere; }
.mm-podium-name { font-size: calc(12.5px + var(--font-boost)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mm-podium-sub { font-size: calc(11px + var(--font-boost)); color: var(--text-muted); }
.mm-record-win { border-top: 3px solid var(--gold); }
.mm-record-win .mm-podium-fig { font-size: calc(18px + var(--font-boost)); margin-top: 2px; }
.mm-strip-title { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 16px; }
.mm-strip-more { font-size: calc(13px + var(--font-boost)); font-weight: 400; }

/* What changed since the last test. Sentences first; the marked paragraphs are there to find the
   place, not to be decoded. Insertions and deletions are shaded as well as coloured, so the
   difference survives colour blindness and a grey laptop screen. */
.mm-diff-summary { margin: 6px 0 4px; padding-left: 18px; }
.mm-diff-summary li { margin: 2px 0; font-size: calc(13.5px + var(--font-boost)); }
.mm-diff { margin: 4px 0 6px; }
.mm-diff summary { cursor: pointer; font-size: calc(12.5px + var(--font-boost)); color: var(--text-muted); }
.mm-diff p { margin: 6px 0; font-size: calc(13px + var(--font-boost)); line-height: 1.6; overflow-wrap: anywhere; }
.mm-diff ins { text-decoration: none; background: var(--skye-light); color: #1f5a00; font-weight: 700; padding: 0 2px; border-radius: 2px; }
.mm-diff del { text-decoration: line-through; background: var(--rose-light); color: var(--scarlett); padding: 0 2px; border-radius: 2px; }

/* The announcement block, in the editor: navy, the colour of its bar. */
.mm-block-announcement .mm-block-head { background: var(--navy-10); border-left-color: var(--navy); }
.mm-block-announcement .mm-block-type { color: var(--navy); }
.mm-add-palette .mm-add-announcement { background: var(--navy-10); border-color: var(--navy); color: var(--navy); }
.mm-ann-icons { display: flex; flex-wrap: wrap; gap: 6px; }
.mm-ann-icon {
  width: 46px; height: 46px; display: grid; place-items: center; padding: 0; cursor: pointer;
  background: var(--paper); border: 1px solid var(--border); border-radius: 8px;
}
.mm-ann-icon.on { border: 2px solid var(--navy); background: var(--white); }
.mm-ann-icon:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.mm-ann-icon-none { font-size: calc(11px + var(--font-boost)); color: var(--text-muted); }

/* The KEYSTONE app launcher, between the text-size control and the signed-in
   user. The shared stylesheet already draws it for a navy bar -- WA Grey dots
   on a white-10% face -- so this only matches the border and corner of the
   Sign out button beside it, and takes the same gold hover.
   `.masthead button.appbar__launcher` is (0,2,1), so it wins over the shared
   sheet's (0,1,1) whichever of the two loads last. */
.masthead button.appbar__launcher {
  border: 1px solid var(--navy-70);
  border-radius: 6px;
  flex-shrink: 0;
}
.masthead button.appbar__launcher:hover,
.masthead button.appbar__launcher:focus-visible { border-color: var(--gold); }
.masthead button.appbar__launcher:hover i,
.masthead button.appbar__launcher:focus-visible i { background: var(--white); }

/* The sidebar's launcher row, shown only where the masthead's is hidden.
   Styled as a sidebar link rather than a waffle: at this size a 38px grid of
   dots in a list of words reads as an error. */
.sidebar-launcher { display: none; }

@media (max-width: 640px) {
  .masthead button.appbar__launcher { display: none; }

  .sidebar-launcher {
    width: 100%;
    display: block;
    margin-top: 10px;
    padding: 8px 10px;
    border: 0;
    border-top: 1px solid var(--navy-50);
    border-radius: 6px;
    background: transparent;
    color: var(--navy-20);
    font: inherit;
    font-size: calc(13px + var(--font-boost));
    text-align: left;
    cursor: pointer;
    transition: background-color 140ms var(--ease), color 140ms var(--ease);
  }
  .sidebar-launcher:hover,
  .sidebar-launcher:focus-visible { background: var(--navy); color: var(--white); }
}

/* Per-person limits in Admin: who may send what, and to which named groups. */
.limit-list { display: flex; flex-direction: column; gap: 14px; margin-top: 12px; }
.limit-row { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; }
.limit-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.limit-who .muted { font-size: 0.9em; }
.picks { display: flex; flex-wrap: wrap; gap: 8px 14px; }
.picks .pick { margin: 0; }
/* The tick and its words sit together, with a gap — otherwise the box is flush against
   the first letter and the label reads as part of the control. */
.limit-row .pick, .picks .pick { display: inline-flex; align-items: center; gap: 7px; }
.limit-row > .pick { margin: 2px 0; }
.limit-row label:not(.pick) { display: block; margin-bottom: 4px; font-weight: 600; }
.limit-detail[hidden] { display: none; }
/* Whose campaign this is, in a list that spans more than one workspace. */
.ws-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 999px;
  background: var(--wa-grey, #E6DDCB); color: var(--wa-navy, #1B2034);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; vertical-align: middle; }

/* "View as": the band saying whose view this is. Deliberately the loudest thing on the page —
   the risk is not seeing too much (it is read-only) but forgetting it is on. */
.view-as-banner { position: sticky; top: 0; z-index: 9000; display: flex; flex-wrap: wrap;
  gap: 10px 16px; align-items: center; justify-content: center;
  background: #6E4E10; color: #fff; padding: 10px 16px; font-size: 0.95rem; text-align: center; }
.view-as-banner strong { font-weight: 700; }
.view-as-banner .btn { background: #fff; color: #1B2034; border-color: #fff; min-height: 36px; padding: 4px 12px; }
