/* The shared shell: header, drawer, mobile nav, and the one radius everything
   uses. Loaded after app.css so it wins where they overlap. */

:root {
  /* THE THREE CORNERS, AND WHICH ONE A THING GETS.
   *
   *   --r-sm  10px   EVERY CONTROL: text inputs, textareas, selects, and
   *                  buttons. One radius, whatever the control does.
   *   --r     14px   boxes — cards, panels, sheets, dialogs, toasts.
   *   --r-lg  18px   the few things that sit outside a card and need to
   *                  step up from it.
   *
   * `--r` used to be commented "the single radius for boxes, inputs and
   * sheets", which is not what the app was ever built to and not what it
   * does: `.btn` and `.sec-input` have both been 10px since they were
   * written. The comment was describing an intention nobody implemented,
   * and eleven other rules had drifted to 5, 7, 8, 9, 12 and 999 around it.
   *
   * So the standard is what the two commonest controls already were, and the
   * audit moved the drift onto it rather than moving them off it. A CONTROL
   * IS NOT A BOX — that is the line, and it is why a card at 14 beside an
   * input at 10 is correct and two inputs at 9 and 10 is not.
   *
   * TWO THINGS ARE DELIBERATELY NOT ON IT, and both say so where they are
   * written: `.chip` is 999px because a chip is a lozenge by kind rather
   * than a button that drifted, and the form builder and giveaway pages
   * carry a radius the OWNER sets per form — see `--sf-control-radius` and
   * `--gw-btn-round`, which tests/test_control_size_and_corner.py polices. */
  --r-sm: 10px;
  --r:    14px;
  --r-lg: 18px;
  --bar-h: 58px;
}

/* ---------- edge to edge ----------
 * The app is installed as `display: fullscreen`, so Android hands over the
 * whole panel including the status bar and the cutout. Nothing may assume a
 * system bar has reserved space for it: every edge-touching element pads
 * itself back out with env(safe-area-inset-*), and the page background paints
 * all the way to the glass so there is no black band anywhere. */
html {
  background: var(--page-bg, var(--ivory));
  /* Belt and braces for the Android navigation area, which is painted from
     the root element's background rather than the body's. */
  min-height: 100%;
}
body {
  min-height: 100dvh;
  background: transparent;
  padding-bottom: env(safe-area-inset-bottom);
}

/* ---------- header ---------- */
.appbar {
  position: sticky; top: 0; z-index: 40;
  background: rgba(251,247,239,.96);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  padding-top: env(safe-area-inset-top);
  transition: transform .2s ease;
}
.appbar.shrunk { transform: translateY(calc(-1 * var(--bar-h))); }
.appbar.shrunk .appbar-slot { box-shadow: 0 1px 0 var(--line); }

.appbar-top {
  height: var(--bar-h);
  display: flex; align-items: center; gap: 10px;
  padding: 0 max(12px, env(safe-area-inset-left)) 0 max(12px, env(safe-area-inset-right));
}
.appbar-logo { flex: 0 0 auto; line-height: 0; }
.appbar-logo img { width: 34px; height: 34px; border-radius: var(--r-sm); display: block; }
.appbar-title { flex: 1; min-width: 0; }
/* ONE HEADING SHAPE, EVERYWHERE.
 *
 * Main headline bold, the rest regular. The whole string used to be 700, so
 * on "Posting / Materials" the section and the page shouted equally and
 * neither was the headline — and pages with no parent looked like a different
 * design from pages with one. The weight is on the box rather than on the
 * bold half so a page with no parent needs no second rule. */
.appbar-name { font-weight: 400; font-size: 16px; line-height: 1.15; }
.appbar-page { font-weight: 700; }
.appbar-parent { color: var(--ink-soft); }
.appbar-counters { font-size: 12.5px; color: var(--ink-soft); line-height: 1.2;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appbar-more {
  flex: 0 0 auto; border: 1px solid var(--line); background: var(--card);
  color: var(--ink-soft); border-radius: 999px; width: 30px; height: 30px;
  font-size: 13px; cursor: pointer; line-height: 1;
}
.appbar-burger {
  flex: 0 0 auto; width: 38px; height: 38px; border: none; background: none;
  cursor: pointer; display: flex; flex-direction: column; justify-content: center;
  gap: 4px; padding: 0 7px;
}
.appbar-burger span { display: block; height: 2px; background: var(--ink); border-radius: 2px; }
.appbar-extra { padding: 0 14px 10px; font-size: 13px; color: var(--ink-soft); }
.appbar-slot { padding: 0 10px 8px; }
.appbar-slot:empty { display: none; }

/* ---------- mobile nav ---------- */
.mnav { position: fixed; inset: 0; z-index: 70; background: rgba(43,38,34,.45); }
.mnav-panel {
  width: min(280px, 82vw); height: 100%; background: var(--card);
  border-right: 1px solid var(--line); padding: max(14px, env(safe-area-inset-top)) 10px 14px;
  display: flex; flex-direction: column; gap: 2px;
  animation: slidein .18s ease;
}
@keyframes slidein { from { transform: translateX(-100%); } }
.mnav-head { display: flex; align-items: center; gap: 10px; padding: 4px 8px 14px; }
.mnav-head img { width: 30px; height: 30px; border-radius: 8px; }
.mnav-close { margin-left: auto; border: none; background: none; font-size: 18px;
              color: var(--ink-soft); cursor: pointer; padding: 4px 8px; }
.mnav-item {
  display: flex; align-items: center; gap: 13px; padding: 13px 10px;
  border-radius: var(--r-sm); color: var(--ink-soft); text-decoration: none;
  font-size: 15.5px; border: none; background: none; width: 100%; cursor: pointer;
  font-family: inherit; text-align: left;
}
.mnav-item.active { background: #F1F6F2; color: var(--ink); font-weight: 600; }
.mnav form { margin: 0; }

/* ---------- avatars ---------- */
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%; flex: 0 0 auto;
  color: #fff; font-weight: 700; font-size: 14px; letter-spacing: .3px;
}

/* ---------- links ---------- */
.iglink { color: var(--ig); text-decoration: none; font-weight: 600; }
.iglink:hover { text-decoration: underline; }
.postctx.clickable { cursor: pointer; text-decoration: none; color: inherit; display: flex; }
.postctx.clickable:hover { background: #F5EFE3; }

/* Brand mark, not a nav item — the house icon below it is the only Home. */
.drawer-logo-link { display: block; line-height: 0; }

/* ---------- one radius everywhere ---------- */
.card, .person-card, .sec-policy, .sec-qr, .sec-secret, .postctx, .person,
.answered-reply, .p-field input, .p-field textarea, .sec-input, .draft,
.namefield, .bulkbar, .toast, .login input, .drawer-item, .mnav-item,
.stat-card, .home-card, .log-entry { border-radius: var(--r); }
/* All four corners, on every screen size. */
.sheet-inner { border-radius: var(--r); overflow: hidden; }
.btn, .chip { border-radius: var(--r-sm); }
/* THE PILLS, RESTATED HERE BECAUSE THIS FILE IS WHAT TOOK THEM AWAY.
 *
 * `.chip` has always been on this line. `.btn-quiet` and `.btn-orange` ask
 * app.css for a 999px pill and had not been getting one ANYWHERE in the app
 * since the block above was written: same specificity — one class each — and
 * shell.css loads after app.css, so the last one wins and every quiet button
 * in the application quietly became a 10px rectangle.
 *
 * Nobody reported it because nothing looked broken; it only showed up when a
 * rule restated the pill on two buttons of four and made those two the odd
 * ones out. That is the wrong end to fix it from — the four buttons were not
 * mismatched, the whole app was.
 *
 * BOTH, NOT JUST THE ONE THAT WAS ASKED ABOUT. app.css gives `.btn-orange`
 * the pill explicitly "because that is the shape the row already has": it
 * stands beside `.btn-quiet` — "+ New Special Form" next to "+ New form" —
 * and restoring one without the other would ship exactly the mismatch this
 * is fixing, with the two buttons swapped.
 *
 * `.btn-approve` is NOT here and that is not an omission: app.css never asks
 * for a pill on it. The green primary is a rectangle, and the outline pill
 * beside it is what makes the green one read as the primary. */
.chip, .btn-quiet, .btn-orange { border-radius: 999px; }

/* ---------- collapsible filter bars ---------- */
.filterrow { display: flex; gap: var(--gap, 15px); align-items: center;
             padding-bottom: 2px; }
.filter-toggle { font-weight: 600; }
.filter-toggle .ft-icon { margin-right: 3px; }
.filterdrop {
  max-height: 0; overflow: hidden;
  transition: max-height .2s ease;
  border-top: 1px dashed var(--line);
  margin-top: 8px;
}
.filterdrop nav.filters:first-child { margin-top: 8px; }

/* ---------- desktop / tablet drawer ---------- */
/* 700px so a tablet in portrait gets the desktop layout, not the phone one. */
@media (min-width: 700px) {
  .drawer { width: 64px; }
  .drawer:hover { width: 216px; }
  body { padding-left: 64px; }
  /* Taskbar-sized icons — the previous 15px read as decoration.
     19px rather than 21px, and 8px of padding rather than 11px, because the
     rail has to FIT. Thirteen sections plus Download log and Sign out came to
     853px at 54px a row, and a 1366x768 laptop has 768 of them — so the last
     rows were simply below the bottom of the screen, present in the page and
     never seen. Scrolling (see `.drawer-inner` in app.css) stops them being
     lost outright, but "you can scroll a 64px rail" is not an answer to "the
     Settings icon has gone": it has to be THERE. At ~44px a row the whole
     list fits a 700px window with room over, and the icons are still
     taskbar-sized rather than decoration. */
  .di-icon { width: 30px; font-size: 19px; }
  .drawer-item { padding: 8px; gap: 13px; }
  .appbar-burger { display: none; }
  .appbar-logo img { width: 38px; height: 38px; }
}
@media (min-width: 1400px) {
  .drawer { width: 216px; }
  body { padding-left: 216px; }
}


/* ---------- blog comments (Hyvor) as the third platform ---------- */
.card.platform-hyvor { border-left-color: var(--ht); }
.person-card.pf-hyvor { border-left-color: var(--ht); }
.badge.hyvor, .pbadge.hyvor { background: var(--ht); }
.p-fact.pf-hyvor { background: #F1EBF9; color: var(--ht); }
.log-entry.platform-hyvor { border-left-color: var(--ht); }

/* ---------- settings ---------- */
/* Section navigation, not options — so it is never folded into "Filters". */
.setnav { display: flex; gap: var(--gap, 15px); overflow-x: auto;
          padding-bottom: 2px; }
.setnav::-webkit-scrollbar { display: none; }
/* One spacing value, shared with Forms, Owed & Queues and the Agenda. */
/* A row of actions. The gap is the full token, not the small one: these are
   buttons that do something, and buttons that do something should not touch
   each other. */
.set-nav {
  display: flex; gap: var(--space); flex-wrap: wrap; align-items: center;
  margin-top: var(--space);
}
.set-nav:first-child { margin-top: 0; }
.set-steps { font-size: 13.5px; line-height: 1.5; margin: 12px 0 0; padding-left: 20px; }
.set-steps li { margin-bottom: 6px; }
.set-card + .set-card { margin-top: 12px; }
.set-group { margin-bottom: 6px; }
.set-group h3 {
  font-size: 11px; letter-spacing: .5px; text-transform: uppercase;
  color: var(--ink-soft); margin: 0 0 8px;
}
.set-card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow); padding: 14px 16px;
}
.set-note { font-size: 13.5px; color: var(--ink-soft); margin: 0 0 12px; }

/* The timezone dropdown. It has to hold "America/Los Angeles" without
   squeezing its label off the row, and it has to stay a tappable height on a
   phone — a native select is the right control here precisely because the
   phone turns it into a full-height wheel. */
.tz-select {
  max-width: 58%; min-height: 34px; font: inherit; font-size: 13.5px;
  padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card); color: var(--ink);
}
.tz-now { font-variant-numeric: tabular-nums; }
@media (max-width: 520px) {
  /* Narrow enough that the label and the control stop sharing a line
     comfortably, so they stack and the select takes the full width. */
  .set-card .sec-item:has(> .tz-select) { flex-wrap: wrap; }
  .tz-select { max-width: 100%; flex: 1 1 100%; margin-top: 6px; }
}

/* Rehearsal rows in the entrants list. They are listed and they are marked;
   none of the counts on the page include them. */
.tag-test {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 6px;
  background: #FFF0C2; border: 1px solid #E7CB7A; color: #8A5A00;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; vertical-align: 1px;
}
.sec-item.is-test { background: #FFFCF2; }
.theme-picker { display: flex; gap: var(--gap, 15px); flex-wrap: wrap; }
.theme-opt {
  flex: 1 1 190px; text-align: left; cursor: pointer; padding: 0; overflow: hidden;
  border: 2px solid var(--line); border-radius: var(--r); background: none;
  font: inherit; color: inherit;
}
.theme-opt.on { border-color: var(--go); }
.theme-swatch { height: 66px; display: block; position: relative; }
.theme-swatch span {
  position: absolute; bottom: 8px; left: 10px; width: 34px; height: 12px;
  border-radius: 6px;
}
.theme-meta { padding: 9px 11px; background: var(--card); }
.theme-meta b { display: block; font-size: 14.5px; }
.theme-meta small { color: var(--ink-soft); font-size: 12px; line-height: 1.35; display: block; }

.color-row {
  display: flex; align-items: center; gap: var(--gap, 15px);
  padding: var(--gap, 15px) 0; border-bottom: 1px solid var(--line);
}
.color-row:last-child { border-bottom: none; }
.color-dot {
  width: 30px; height: 30px; border-radius: 8px; flex: 0 0 auto;
  border: 1px solid rgba(0,0,0,.14);
}
.color-name { flex: 1; min-width: 0; font-size: 14px; }
.color-name small { display: block; color: var(--ink-soft); font-size: 11.5px; }
.color-hex {
  width: 108px; flex: 0 0 auto; font: inherit; font-size: 13.5px;
  font-variant-numeric: tabular-nums; text-transform: lowercase;
  padding: 7px 9px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card); color: var(--ink);
}
.color-hex.bad { border-color: var(--danger); }
.color-pick { width: 34px; height: 34px; flex: 0 0 auto; padding: 0;
              border: 1px solid var(--line); border-radius: var(--r-sm);
              background: none; cursor: pointer; }
.color-reset { border: none; background: none; color: var(--ink-soft);
               cursor: pointer; font-size: 15px; padding: 4px 6px; }

/* ---------- donut charts ---------- */
.donuts { display: flex; flex-wrap: wrap; gap: 14px; }
.donut { flex: 1 1 190px; min-width: 170px; text-align: center; }
.donut svg { width: 100%; max-width: 190px; height: auto; display: block; margin: 0 auto; }
.donut-title { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.donut-centre { font-size: 15px; font-weight: 700; }
.donut-legend {
  display: flex; flex-direction: column; gap: 3px; margin-top: 8px;
  font-size: 12.5px; text-align: left;
}
.donut-legend div { display: flex; align-items: center; gap: 7px; }
.donut-legend i {
  width: 11px; height: 11px; border-radius: 3px; flex: 0 0 auto; display: block;
}
.donut-legend .lv { margin-left: auto; color: var(--ink-soft);
                    font-variant-numeric: tabular-nums; }

/* The page's own filter bar sits under the app bar and no longer carries the
   counters or the title — the header owns those now. */
.topbar { position: static; background: none; backdrop-filter: none;
          border-bottom: 1px solid var(--line); padding-top: 6px; }
.topbar > .counters, .topbar > .split { display: none; }
.appbar + .topbar { margin-top: 0; }
.appbar.shrunk + .topbar { position: sticky; top: 0; z-index: 30;
                           background: rgba(251,247,239,.96); backdrop-filter: blur(10px); }

/* ---------- giveaway admin ---------- */
.gw-nums { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.gw-check { display: flex; align-items: center; gap: 9px; font-size: 14.5px;
            margin: 10px 0; }
.gw-check input { width: 19px; height: 19px; accent-color: var(--go); }
.gw-preview { margin-top: 12px; }
.gw-winner-row { font-size: 15px; font-weight: 600; padding: 5px 0;
                 border-bottom: 1px solid var(--line); }
.gw-winner-row:last-child { border-bottom: none; }

/* The tabs a page shows when it belongs to a parent — Posting's Agenda and
   Owed & Queues, Settings' three. The rail shows the parent once; these are
   how you move between its pages without going back out to the rail, which on
   a phone is two taps and a scroll. */
/* ---------- the one tab row, in the one header ----------
 *
 * EXCEL-STYLE, and the word is doing real work: the tabs sit ON a rule and the
 * one you are on is JOINED to the page under it — same background, no bottom
 * edge, so the tab and the content it opens are visibly one surface. Pills
 * cannot say that. A row of pills says "here are six filters"; a row of folder
 * tabs says "here are six pages and you are in this one", which is what a
 * section's sub-nav is.
 *
 * ONE SOURCE, WHICH IS THIS FILE. Every screen with sub-navigation draws it
 * here — the shell builds the row from NAV for pages inside a parent, and a
 * page with tabs of its own hands them over with `shell.setTabs`. Nothing
 * draws a second strip in its own body any more; that is what made Reviews
 * Settings carry two rows and a repeated title.
 */
.subtabs {
  display: flex; gap: 2px; padding: 0; overflow-x: auto;
  scrollbar-width: none; align-items: flex-end;
  border-bottom: 1px solid var(--line);
}
.subtabs::-webkit-scrollbar { display: none; }

.subtab {
  flex: 0 0 auto; font: inherit; font-size: 13.5px; text-decoration: none;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: 8px 8px 0 0;
  padding: 8px 15px 9px;
  cursor: pointer;
  /* The row's own rule runs under every tab; the active one covers its inch
     of it. `margin-bottom: -1px` is what does the covering. */
  margin-bottom: -1px;
  white-space: nowrap;
  display: inline-flex; align-items: center; gap: 7px;
}

.subtab:hover { color: var(--ink); background: var(--ivory); }

.subtab-on {
  background: var(--card);
  border-color: var(--line);
  border-bottom: 1px solid var(--card);
  color: var(--ink);
  font-weight: 650;
}

/* A count riding inside a tab — what that source has waiting. */
.subtab-count {
  display: inline-grid; place-items: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; background: var(--line); color: var(--ink);
  font-size: 11px; font-weight: 700; line-height: 1;
}
.subtab-count.is-loud { background: var(--danger); color: #fff; }

/* NON-CATEGORY ACTIONS ARE PILLS, and that is the other half of the same
 * rule. A tab takes you somewhere; a button does something. Refresh, Show
 * More and the auto-post pull-out are not places, so they are not drawn as
 * folder tabs — they sit at the end of the row as pills, which is the shape
 * the rest of this app already uses for "do a thing". */
.subtabs-action,
.subtabs > [data-appbar-action] {
  flex: 0 0 auto;
  margin-bottom: 5px;
  font: inherit; font-size: 13px;
  color: var(--ink-soft);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 13px;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
  text-decoration: none;
}
.subtabs-action:hover,
.subtabs > [data-appbar-action]:hover { border-color: var(--ink-soft);
                                        color: var(--ink); }
.subtabs-action.is-on { border-color: var(--ink); color: var(--ink);
                        font-weight: 650; }
/* The first pill takes whatever room is left, so every action is right-aligned
   and the tabs stay left-aligned however many of each there are. */
.subtabs-spacer { flex: 1 1 auto; min-width: var(--space-sm); }
.subtabs-with-action > [data-appbar-action] { margin-left: auto; }

/* ---------- auto-post, in the Inbox tab row ---------- */
/*
 * A pull-out between Comments and Delivery. The button carries a red count of
 * how many networks reply without Tom seeing them first, so that number is on
 * screen everywhere in this section rather than behind a settings page.
 */
.ap-wrap { position: relative; flex: 0 0 auto; }
.ap-btn {
  display: inline-flex; align-items: center; gap: 7px;
  font: inherit; font-size: 13.5px; cursor: pointer;
}
.ap-btn.ap-on { border-color: var(--danger); color: var(--danger);
                font-weight: 650; }
.ap-badge {
  display: inline-grid; place-items: center;
  min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: 999px; background: var(--danger); color: #fff;
  font-size: 11.5px; font-weight: 700; line-height: 1;
}

/* Fixed, and placed by hand — see apPlace. The tab row scrolls sideways, and
   a scroll container clips an absolutely positioned child whatever its
   z-index, which left the whole pull-out invisible under a one-pixel sliver. */
.ap-panel {
  position: fixed; z-index: 60;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 14px; display: flex; flex-direction: column; gap: 12px;
  /* `apPlace` sets `width` to the number it then clamps `left` against. With
     the default box model that number was the CONTENT width and the real box
     was 30px wider — two 14px paddings and two 1px borders — so the clamp was
     wrong by exactly that much. It never showed while the button sat in the
     middle of the tab row and there was slack to the right of it; v8 put the
     button at the far right and the panel went off the edge of the screen. */
  box-sizing: border-box;
  /* The height is set by `apPlace` from the room actually below the button.
     Without the scroll the Confirm and the kill switch fell off the bottom of
     a short window the moment the backlog box appeared. */
  overflow-y: auto;
  overscroll-behavior: contain;
}
.ap-lead { margin: 0; font-size: 12.5px; color: var(--ink-soft);
           line-height: 1.45; }
.ap-list { display: flex; flex-direction: column; gap: 2px; }
.ap-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 6px; border-radius: 8px; cursor: pointer; font-size: 14.5px;
}
.ap-row:hover { background: var(--ivory); }
.ap-row input { width: 18px; height: 18px; flex: 0 0 auto; }
.ap-name { flex: 1 1 auto; }
/* Ticked but waiting on the platform, not broken and not being attempted. */
.ap-hold {
  flex: 0 0 auto; font-size: 11px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
}
.ap-why { flex: 1 0 100%; font-size: 11.5px; color: var(--ink-soft);
          padding: 0 6px 4px 34px; line-height: 1.4; }
.ap-dead { margin: 0; font-size: 12.5px; color: var(--danger);
           font-weight: 600; }

/* The backlog drain: how far through it is, and the one button that stops it.
   Boxed off from the network list above because it is a different question —
   that one is "may it", this one is "how is it getting on". */
.ap-drain {
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--ivory);
}
.ap-drain-head { font-size: 13px; font-weight: 600; }
.ap-drain-bar {
  height: 6px; border-radius: 999px; background: var(--line);
  overflow: hidden;
}
.ap-drain-fill { height: 100%; background: var(--ink-soft); border-radius: 999px; }
.ap-drain-says { font-size: 11.5px; color: var(--ink-soft); line-height: 1.4; }
/* 44px, like every other target in this app that a thumb has to find. */
.ap-drain-btn { width: 100%; justify-content: center; min-height: 44px; }

.ap-foot { display: flex; flex-direction: column; gap: 8px; }
.ap-foot .btn { width: 100%; justify-content: center; }
.ap-kill { color: var(--danger); border-color: var(--danger); }

/* Delivery has no toast stack of its own; a save this consequential is not
   allowed to be silent there. */
.ap-toast {
  position: fixed; left: 12px; right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom));
  z-index: 80; background: var(--ink); color: var(--ivory);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 11px 13px; font-size: 14px;
}
.ap-toast.is-bad { background: var(--danger); }

@media (min-width: 720px) {
  .ap-toast { left: auto; right: 24px; width: min(420px, calc(100vw - 48px)); }
}


/* ---------- the reviewer's read-only bar ----------
 *
 * Meta's App Review account can read the tool and nothing else, and the
 * submission notes say so. A claim like that has to be visible on the screen
 * the reviewer is looking at, not only in the notes they read a week earlier —
 * so this rides above everything, on every page, for the whole session.
 *
 * The colours are hard-coded rather than themed on purpose. Every other colour
 * in the app can be overridden to any hex from Appearance, and a compliance
 * notice that can be made invisible by a colour picker is not a notice. Amber
 * on cream reads as a state, not as chrome, in both Classic and Branded.
 */
:root { --rv-h: 34px; }

.reviewer-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 95;
  height: var(--rv-h);
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 0 12px;
  background: #F7E7D2; color: #6A3F0B;
  border-bottom: 1px solid #D8B583;
  font-size: 12.5px; letter-spacing: .6px; text-transform: uppercase;
  white-space: nowrap; overflow: hidden;
}
.reviewer-bar b { font-weight: 800; letter-spacing: 1.1px; }
.rv-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #B4700F; flex: 0 0 auto;
}
.rv-sep { opacity: .55; }
/* The sentence that says what "read-only" actually means. Sentence case and
   un-tracked, because it is prose rather than a label. */
.rv-note {
  text-transform: none; letter-spacing: 0; font-size: 12px; opacity: .82;
  margin-left: 6px; overflow: hidden; text-overflow: ellipsis;
}

/* The bar is fixed, so the page and the two fixed navigations all start below
   it rather than under it. */
body.reviewer { padding-top: var(--rv-h); }
body.reviewer .appbar { top: var(--rv-h); }
body.reviewer .drawer { top: var(--rv-h); }
body.reviewer .mnav { top: var(--rv-h); }

/* A phone has no room for the explanation, and the two words are the part that
   matters. The long form stays on desktop. */
@media (max-width: 699px) {
  .rv-note { display: none; }
}

/* ---------- the reviewer's disabled controls ----------
 *
 * Meta is reviewing comment moderation, so the reply workflow has to be ON the
 * page — an inbox with the drafting and the Approve button stripped out is a
 * list of comments, and a reviewer who has only seen a list of comments has
 * not seen the thing they are being asked to approve. So the controls stay
 * where they are and stop working.
 *
 * The app already greys a disabled button with `.btn[disabled]`, and this
 * keeps that look rather than inventing a second one. What it adds is the
 * cursor: `.btn[disabled]` sets `pointer-events: none`, which means the
 * pointer never lands on the element and `cursor` is never consulted — so the
 * reviewer gets an ordinary arrow over a dead button and no signal at all
 * until they click. Pointer events go back on and the cursor says
 * not-allowed. The `disabled` attribute is what actually stops the click;
 * nothing here is load-bearing for that.
 */
.is-review-locked,
.btn.is-review-locked[disabled] {
  pointer-events: auto;
  cursor: not-allowed;
}

/* Greyed here rather than leaning on `.btn[disabled]`, because only some of
   these are `.btn`. The reason chips under a draft are `.chip`, the like is
   `.like`, the name box is an `input` — all of them were correctly disabled
   and all of them still looked live, which is the exact thing this change
   exists to stop. The textarea is excluded: it keeps full contrast so the
   draft stays readable. */
button.is-review-locked,
input.is-review-locked {
  opacity: .5;
}

/* The draft is readOnly rather than disabled — a disabled textarea greys its
   text to the point of being hard to read, and the draft is the single most
   important thing on the card for a reviewer to be able to read. So it keeps
   full contrast and takes a muted ground instead. */
textarea.is-review-locked {
  background: var(--page-bg, #FBF7EF);
  cursor: not-allowed;
}

/* The line above the list. An explanation, not a warning: nothing is wrong,
   and it should not shout beside a banner that has already said the same
   thing louder. */
.review-note {
  margin: 10px max(12px, env(safe-area-inset-left)) 0;
  font-size: 13px;
  color: var(--ink-soft);
  font-style: italic;
}


/* ---------- Inbox: Show More ----------
 *
 * A strip under the tab row rather than a pop-up menu. These are places
 * somebody goes, not commands — and a menu that closes when you look away is a
 * menu you open twice. It sits in the flow and pushes the list down, which is
 * what "show more" should look like.
 */
.inbox-more {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  padding: 10px 0 4px;
}

.inbox-more-item {
  flex: 1 1 200px;
  min-width: 0;
  display: block;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  padding: 9px 12px;
}

.inbox-more-item:hover { border-color: var(--ink-soft); }
.inbox-more-item b { display: block; font-size: 13.5px; }
.inbox-more-item span {
  display: block; font-size: 12px; color: var(--ink-soft);
  line-height: 1.4; margin-top: 2px;
}

/* ---------- Inbox: the Emails source ---------- */

.inbox-mail-note {
  margin: 0 0 var(--space-sm);
  font-size: 12.5px;
  color: var(--ink-soft);
  line-height: 1.45;
}

.inbox-mail-subject {
  margin: 6px 0 4px;
  font-size: 15.5px;
  font-weight: 650;
}

.inbox-mail-tags {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px;
}

/* The source rows at the top of the auto-post pull-out, above the networks. */
.ap-sources { border-bottom: 1px solid var(--line); padding-bottom: 8px; }


/* ---------- Delivery: everything, in and out ----------
 *
 * One row shape for four sources. The arrow is the whole of "which way" — a
 * merged list needs that answer to be readable without stopping to read, and a
 * word in each row would be four hundred words on the page.
 */
.dv-ev-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: var(--space-sm) 0;
}
.dv-ev-gap { flex: 0 0 var(--space-sm); }

.dv-ev-note {
  margin: var(--space-sm) 0 0;
  font-size: 12.5px; color: var(--ink-soft); line-height: 1.45;
}

.dv-ev-list { display: flex; flex-direction: column; }

.dv-ev-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 0;
  border-top: 1px solid var(--line);
}

.dv-ev-way {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  display: inline-grid; place-items: center;
  border-radius: 50%;
  background: var(--line);
  font-size: 12px; line-height: 1;
}
/* Out is the desk talking. Coloured, because "what did we say" is the half of
   this list somebody scans for. */
.dv-ev-row.is-out .dv-ev-way { background: var(--go, #2f6f4f); color: #fff; }

.dv-ev-body { min-width: 0; flex: 1 1 auto; }
.dv-ev-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
}
.dv-ev-head b { font-size: 13.5px; }
.dv-ev-src, .dv-ev-when { font-size: 11.5px; color: var(--ink-soft); }
.dv-ev-when { margin-left: auto; white-space: nowrap; }
.dv-ev-text {
  margin: 2px 0 0; font-size: 13px; line-height: 1.45;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

/* A TAB THAT IS RED, AND WHY IT IS A COLOUR RATHER THAN A BIGGER NUMBER.
 *
 * `.subtab-count` already says how much a source has waiting. What it cannot
 * say is that ONE of those things is a different kind of thing from the rest
 * — a review somebody has reported, against a pile of reviews owed an answer.
 * The pile can wait until Thursday; the report cannot, and in a row somebody
 * glances at on the way past they must not look alike.
 *
 * `--danger` is the app's own rust, the same one a failed send uses. No new
 * colour is invented for this.
 *
 * IT STAYS RED UNTIL THE LAST ONE IS DEALT WITH, rather than until somebody
 * has looked at it — see the note over `open_reports` in
 * app/review_moderation.py. A badge that clears itself on being seen trains
 * a person to stop seeing it.
 */
.subtab-alert {
  color: var(--danger);
  font-weight: 650;
}

.subtab-alert:hover { color: var(--danger); }

/* A DOT, so the tab is marked whether or not it is the one that is open —
   an active tab already carries a background and a border, and a red one
   under all that stops reading as red. */
.subtab-alert::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--danger);
  flex: 0 0 auto;
}

.subtab-alert.subtab-on {
  color: var(--danger);
  border-color: var(--danger);
}

/* And its count goes red with it rather than staying the quiet grey, so the
   two halves of one tab do not disagree. */
.subtab-alert .subtab-count { background: var(--danger); color: #fff; }
