/* AstroBoy recording operations dashboard.
 *
 * Design system, not decoration. Every colour, radius, shadow and step of the
 * type scale is a token below, so a new surface inherits the product rather
 * than inventing its own. The reference for the product family is the Doramon
 * Media tab: 14px base, a narrow centred reading column, dense but legible
 * rows, day grouping, and status colour that means the same thing everywhere.
 * AstroBoy is a separate product with its own data and its own navigation; it
 * only shares the visual language.
 *
 * Mobile first. One column, 44px touch targets, no modal navigation. */

:root {
  /* surface */
  --bg: #ffffff;
  --surface: #f7f8fa;
  --surface-2: #eceef2;
  --line: #dfe3eb;
  --line-strong: #c9cfda;

  /* type */
  --text: #1f2328;
  --text-2: #656d76;
  /* Measured on every surface it lands on, not eyeballed. The original
     #8b939e was 3.1:1 against the card and failed AA for the 11px secondary
     text it carries. #636b75 clears 4.5:1 on --bg, --surface AND --surface-2,
     which is what the quiet chips and hints actually sit on. */
  --text-3: #636b75;

  /* semantic */
  --accent: #0969da;
  --accent-bg: #ddf4ff;
  --accent-line: #adddff;
  --green: #1a7f37;
  --green-bg: #dafbe1;
  --green-line: #aeeeb9;
  --red: #cf222e;
  --red-bg: #ffebe9;
  --red-line: #ffc9c6;
  --yellow: #9a6700;
  --yellow-bg: #fff8c5;
  --yellow-line: #f2e08a;
  --purple: #8250df;
  --purple-bg: #fbefff;
  --purple-line: #e5cdf7;

  /* shape */
  --r-sm: 6px;
  --r: 8px;
  --r-lg: 12px;
  --tap: 44px;
  --col: 900px;
  --shadow-1: 0 1px 2px rgba(31, 35, 40, .05);
  --shadow-2: 0 1px 3px rgba(31, 35, 40, .08), 0 8px 24px rgba(31, 35, 40, .06);

  /* type scale */
  --t-11: 11px;
  --t-12: 12px;
  --t-13: 13px;
  --t-14: 14px;
  --t-16: 16px;
  --t-20: 20px;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

/* White theme, always. Frank asked for the white theme explicitly, so the
   surface does NOT follow the operating system dark preference: a dashboard
   that is white on one device and dark on another is two products. The dark
   token set is kept below, commented, so switching back is a one block edit
   rather than a rediscovery of every value.

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0d1117;
    --surface: #161b22;
    --surface-2: #1c2129;
    --line: #2a313c;
    --line-strong: #3d444d;
    --text: #e6edf3;
    --text-2: #9198a1;
    --text-3: #8b949e;
    --accent: #4493f8;
    --accent-bg: #0d2d5e;
    --accent-line: #1f4f96;
    --green: #3fb950;
    --green-bg: #10281a;
    --green-line: #1f5c31;
    --red: #f85149;
    --red-bg: #35191b;
    --red-line: #6b2b2b;
    --yellow: #d29922;
    --yellow-bg: #2e2413;
    --yellow-line: #5c4818;
    --purple: #ab7df8;
    --purple-bg: #23193a;
    --purple-line: #453163;
    --shadow-1: 0 1px 2px rgba(0, 0, 0, .5);
    --shadow-2: 0 1px 3px rgba(0, 0, 0, .6), 0 8px 24px rgba(0, 0, 0, .4);
  }
}
*/

* { box-sizing: border-box; }

/* .topbar .back sets display:inline-flex, which OVERRIDES the UA stylesheet's
   [hidden] { display: none }. Without this the Back control stays visible on
   the attention screen, where there is nothing to go back to. Caught in
   production browser QA. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: var(--t-14)/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

a { color: var(--accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ---- top bar ---------------------------------------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 12px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.topbar .brand {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-13);
  font-weight: 600;
  letter-spacing: -.005em;
}

.topbar .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 3px var(--green-bg);
}
.topbar .dot.down {
  background: var(--red);
  box-shadow: 0 0 0 3px var(--red-bg);
}

.topbar .back {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-height: 36px;
  padding: 0 10px 0 6px;
  margin-left: -4px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--accent);
  font-size: var(--t-13);
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}
.topbar .back:hover {
  background: var(--accent-bg);
  border-color: var(--accent-line);
}

.topbar .icon {
  margin-left: auto;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--text-2);
  font-size: 15px;
  cursor: pointer;
}
.topbar .icon:hover { color: var(--text); border-color: var(--line-strong); }

/* ---- layout ----------------------------------------------------------- */

main {
  max-width: var(--col);
  margin: 0 auto;
  padding: 14px 12px 56px;
}

@media (min-width: 700px) {
  main { padding: 18px 16px 72px; }
}

/* Product header: title, one line saying what this screen is for, and the
   primary refresh affordance. Without it the page opened on an anonymous grey
   field with a small label floating in it. */
.pagehead {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
/* The title block shrinks and the action stays anchored top right. Wrapping
   instead pushed Refresh onto its own line on a phone, which read as a stray
   button rather than as part of the header. */
.pagehead > div { min-width: 0; }
.pagehead > .btn { flex: 0 0 auto; }
.pagehead h1 {
  margin: 0;
  font-size: var(--t-16);
  font-weight: 600;
  letter-spacing: -.01em;
}
.pagehead .sub {
  margin-top: 2px;
  font-size: var(--t-12);
  color: var(--text-2);
}

/* Section kicker, the same device the reference uses to separate zones. */
.kicker {
  margin: 22px 0 8px;
  font-size: var(--t-11);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--purple);
}
.kicker .n {
  margin-left: 6px;
  color: var(--text-3);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}

/* ---- buttons ---------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--text);
  font: 600 var(--t-12)/1 inherit;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.btn:hover { background: var(--surface-2); border-color: var(--line-strong); }
.btn:active { transform: translateY(1px); }

.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn.primary:hover { filter: brightness(1.08); background: var(--accent); }

.btn.listen {
  background: var(--purple-bg);
  border-color: var(--purple-line);
  color: var(--purple);
}
.btn.listen:hover { background: var(--purple-bg); border-color: var(--purple); }

.btn.wide {
  width: 100%;
  min-height: var(--tap);
  font-size: var(--t-13);
}

.btn.quiet {
  background: transparent;
  border-color: transparent;
  color: var(--text-2);
  font-weight: 500;
}
.btn.quiet:hover { background: var(--surface-2); color: var(--text); }

/* ---- health panel ------------------------------------------------------ */

.health {
  border: 1px solid var(--green-line);
  background: var(--green-bg);
  border-radius: var(--r);
  padding: 12px 14px;
}
.health[data-state="attention"] {
  border-color: var(--red-line);
  background: var(--red-bg);
}
.health[data-state="warn"] {
  border-color: var(--yellow-line);
  background: var(--yellow-bg);
}

.health .row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.health .state {
  font-size: var(--t-13);
  font-weight: 700;
  color: var(--green);
}
.health[data-state="attention"] .state { color: var(--red); }
.health[data-state="warn"] .state { color: var(--yellow); }

.health .runmeta {
  font-size: var(--t-11);
  color: var(--text-2);
  font-family: var(--mono);
}
.health .note {
  margin-top: 6px;
  font-size: var(--t-12);
  color: var(--text-2);
}

/* Stat tiles. Health, not a list of routine runs, so the attention screen can
   carry weight without violating attention-first. */
.stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}
@media (min-width: 620px) {
  .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.stat {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  padding: 8px 10px;
}
.stat .v {
  font-size: var(--t-16);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.stat .k {
  margin-top: 1px;
  font-size: var(--t-11);
  color: var(--text-2);
}
.stat[data-tone="red"] .v { color: var(--red); }
.stat[data-tone="green"] .v { color: var(--green); }

/* ---- cards ------------------------------------------------------------- */

.card {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  box-shadow: var(--shadow-1);
  padding: 12px 14px;
  margin-bottom: 10px;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
button.card:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-2);
}
button.card:active { transform: translateY(1px); }

.card.flat { box-shadow: none; cursor: default; }
.card.sev-high { border-left: 3px solid var(--red); }
.card.sev-medium { border-left: 3px solid var(--yellow); }

.card .title {
  font-size: var(--t-14);
  font-weight: 600;
  line-height: 1.35;
}

/* ---- bilingual title --------------------------------------------------- */

/* Titles are the one exception to this product's Chinese-first rule: English
   line, then Chinese line, on every surface. Two BLOCK spans rather than one
   string, so the order is structural and a long English title cannot wrap the
   Chinese up onto a shared line. Both lines keep the full text colour: the
   Chinese line is the one George reads, so it is a second line of the same
   title, not a dimmed subtitle. */
.title-en,
.title-zh {
  display: block;
  overflow-wrap: anywhere;
}
.title-zh { margin-top: 1px; }

.rec .title .title-zh,
.card .title .title-zh { font-size: var(--t-12); }

.card .meta {
  margin-top: 3px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: var(--t-11);
  color: var(--text-2);
}
.card .meta .bad { color: var(--red); font-weight: 600; }

/* ---- chips ------------------------------------------------------------- */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 8px;
}

.chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-2);
  font-size: var(--t-11);
  font-weight: 500;
  line-height: 1.6;
  white-space: nowrap;
}

/* Bookkeeping labels from the migration are real data but they are not what
   anyone scans for, so they read as quiet metadata rather than as status. */
.chip.book {
  background: transparent;
  border-style: dashed;
  color: var(--text-3);
}

.chip.lang {
  background: var(--purple-bg);
  border-color: var(--purple-line);
  color: var(--purple);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
}

.chip.status { font-weight: 700; }
.chip.status[data-s="completed"] { background: var(--green-bg); border-color: var(--green-line); color: var(--green); }
.chip.status[data-s="failed"],
.chip.status[data-s="blocked"] { background: var(--red-bg); border-color: var(--red-line); color: var(--red); }
.chip.status[data-s="queued"],
.chip.status[data-s="downloading"],
.chip.status[data-s="transcribing"],
.chip.status[data-s="interpreting"],
.chip.status[data-s="clipping"],
.chip.status[data-s="publishing"] { background: var(--accent-bg); border-color: var(--accent-line); color: var(--accent); }

/* A backfilled run whose transcript did not survive the migration is
   `completed` in the record and stays labelled `completed`, because inventing
   a status the data does not hold would be its own dishonesty. What it loses
   is the green. Six of these wore the identical achievement pill as a fully
   summarised recording with clips and a Listen button, and the day they sat on
   read as a wall of green cards that were in fact empty. `data-legacy` wins
   over the status colour above by being later and more specific. */
.chip.status[data-legacy="1"] {
  background: var(--surface-2);
  border-color: var(--line-strong);
  color: var(--text-2);
  font-weight: 600;
}

.chip.warn {
  background: var(--yellow-bg);
  border-color: var(--yellow-line);
  color: var(--yellow);
  font-weight: 600;
}

/* ---- attention reasons ------------------------------------------------- */

.reasons { list-style: none; margin: 9px 0 0; padding: 0; }

.reason {
  padding: 8px 10px;
  border-radius: var(--r-sm);
  font-size: var(--t-12);
  line-height: 1.5;
  margin-bottom: 6px;
}
.reason.high { background: var(--red-bg); color: var(--red); }
.reason.medium { background: var(--yellow-bg); color: var(--yellow); }
.reason b { text-transform: capitalize; }

/* ---- empty / loading --------------------------------------------------- */

.allclear {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  padding: 34px 20px;
  text-align: center;
}
.allclear .mark {
  width: 40px;
  height: 40px;
  margin: 0 auto 10px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--green-bg);
  color: var(--green);
  font-size: 19px;
}
.allclear h3 { margin: 0; font-size: var(--t-14); font-weight: 600; }
.allclear p {
  margin: 6px auto 0;
  max-width: 44ch;
  font-size: var(--t-12);
  color: var(--text-2);
}

.loading, .empty-note {
  padding: 22px 4px;
  color: var(--text-2);
  font-size: var(--t-12);
}

/* Skeleton rows, so a slow load looks like the product loading rather than
   like an empty product. */
.skeleton {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  padding: 12px 14px;
  margin-bottom: 10px;
}
.skeleton i {
  display: block;
  height: 10px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface) 50%, var(--surface-2) 75%);
  background-size: 300% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
  margin-bottom: 7px;
}
.skeleton i:nth-child(1) { width: 58%; height: 13px; }
.skeleton i:nth-child(2) { width: 34%; }
.skeleton i:nth-child(3) { width: 76%; margin-bottom: 0; }
@keyframes shimmer {
  0% { background-position: 100% 0; }
  100% { background-position: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton i { animation: none; }
  * { transition: none !important; }
}

.gate {
  max-width: 34rem;
  margin: 44px auto;
  text-align: center;
}
.gate h1 { font-size: var(--t-16); margin: 0 0 6px; }
.gate p { font-size: var(--t-13); color: var(--text-2); margin: 0; }

/* ---- filters ----------------------------------------------------------- */

.filters {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  padding: 10px 12px 12px;
  margin-bottom: 14px;
}
.filters .head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.filters .head .lbl {
  font-size: var(--t-11);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-2);
}
.filters .head .hint { font-size: var(--t-11); color: var(--text-3); }
.filters .head .clear { margin-left: auto; }

.filters input[type="search"] {
  width: 100%;
  min-height: var(--tap);
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--text);
  font: var(--t-13)/1 inherit;
  font-family: inherit;
}
.filters input::placeholder { color: var(--text-3); }

.controlgroup {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 8px;
}
@media (min-width: 620px) {
  .controlgroup { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.field { position: relative; }
.field > span {
  position: absolute;
  top: 5px;
  left: 10px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-3);
  pointer-events: none;
}
.field select,
.field input[type="date"] {
  width: 100%;
  min-height: var(--tap);
  padding: 14px 10px 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--text);
  font: var(--t-13)/1.2 inherit;
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
}
.field select {
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 15px) 21px, calc(100% - 10px) 21px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  color: var(--text);
}
/* Selected state has to be visible, not implied by the option text alone. */
.field[data-active="1"] select,
.field[data-active="1"] input[type="date"] {
  border-color: var(--accent);
  background-color: var(--accent-bg);
  font-weight: 600;
}
.field[data-active="1"] > span { color: var(--accent); }

.tagrow {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 10px;
}
.tag {
  min-height: 28px;
  padding: 0 9px;
  border: 1px solid var(--accent-line);
  border-radius: 999px;
  background: var(--accent-bg);
  color: var(--accent);
  font: 600 var(--t-11)/1 inherit;
  font-family: inherit;
  cursor: pointer;
}
.tag:hover { filter: brightness(.97); }
.tag[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.tag.book {
  border-color: var(--line);
  background: var(--surface-2);
  color: var(--text-2);
}
.tag.book[aria-pressed="true"] {
  background: var(--text-2);
  border-color: var(--text-2);
  color: var(--bg);
}

.filters details.morelabels { margin-top: 8px; }
.filters details.morelabels > summary {
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  font-size: var(--t-11);
  color: var(--text-2);
  cursor: pointer;
}
.filters details.morelabels > summary::-webkit-details-marker { display: none; }

.filter-count {
  margin: 0 2px 10px;
  font-size: var(--t-11);
  color: var(--text-2);
}

/* ---- older history, expanded in place ---------------------------------- */

/* No frame and no indent. A left hairline was tried first and it pushed the
   filter panel and every day group 14px out of line with the control that
   opened them, which is a visible defect bought in exchange for decoration the
   kicker and the button already provide. Alignment is the affordance. */
.older { margin: 10px 0 4px; }
.older .filters { margin-bottom: 12px; }

/* ---- day groups -------------------------------------------------------- */

.daygroup {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  overflow: hidden;
  margin-bottom: 12px;
}

.daygroup > .dayhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.daygroup > .dayhead .d {
  font-size: var(--t-12);
  font-weight: 700;
  letter-spacing: -.005em;
}
.daygroup > .dayhead .c {
  font-size: var(--t-11);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.rec {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-top: 1px solid var(--line);
  background: var(--bg);
  color: inherit;
  font: inherit;
  padding: 11px 14px;
  cursor: pointer;
  transition: background .1s ease;
}
.daygroup .rec:first-of-type { border-top: 0; }
.rec:hover { background: var(--surface); }
.rec[data-sev="high"] { box-shadow: inset 3px 0 0 var(--red); }
.rec[data-sev="medium"] { box-shadow: inset 3px 0 0 var(--yellow); }

.rec .when {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 2px;
}
.rec .when .t {
  font-family: var(--mono);
  font-size: var(--t-11);
  font-weight: 600;
  color: var(--purple);
  background: var(--purple-bg);
  border-radius: 4px;
  padding: 1px 5px;
}
.rec .when .t.est { color: var(--text-2); background: var(--surface-2); }
.rec .when .len { font-size: var(--t-11); color: var(--text-2); }

.rec .title {
  display: block;
  font-size: var(--t-13);
  font-weight: 600;
  line-height: 1.4;
  color: var(--text);
  text-decoration: none;
}
.rec .title:hover { color: var(--accent); text-decoration: underline; }
.rec .file {
  margin-top: 1px;
  font-size: var(--t-11);
  color: var(--text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- point-form bilingual summary --------------------------------------- */

/* Chinese leads and stays open; English sits behind the same disclosure token
   the detail page uses, so both surfaces behave identically. A real <ul>, so a
   screen reader announces the list and its length. */
.points {
  margin: 8px 0 0;
  padding-left: 18px;
  list-style: disc;
}

.points li {
  margin: 3px 0;
  font-size: var(--t-14);
  line-height: 1.6;
  color: var(--text);
}

.points.summary-en li { color: var(--text-2); }

.rec .rowsummary { margin-top: 8px; }
.rec .rowsummary .points li { font-size: var(--t-13); line-height: 1.55; }

/* The prose overview is kept but demoted: point form is what gets scanned,
   the paragraph is the longer record for exports and the Doc. */
.summary-zh-prose,
.summary-en-prose {
  margin: 8px 0 0;
  font-size: var(--t-13);
  line-height: 1.65;
  color: var(--text-2);
}

/* The migration's own sentence about why this row is thin. Quiet on purpose:
   it explains, it does not alarm, and these rows are deliberately kept off the
   attention screen. */
.rec .legacynote {
  margin-top: 6px;
  padding-left: 9px;
  border-left: 2px solid var(--line-strong);
  font-size: var(--t-12);
  line-height: 1.5;
  color: var(--text-2);
}

.rec .actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 9px;
}
.rec .actions .btn { min-height: 30px; padding: 0 10px; }

@media (min-width: 700px) {
  .rec .actions .btn { min-height: 28px; }
}

/* ---- transcript, opened in place ---------------------------------------- */

/* A transcript that only exists on the host still deserves to be readable.
   Scrollable rather than unbounded: a facility walkthrough runs to hundreds of
   segments and would otherwise push every other row off the page. */
.transcript {
  margin-top: 10px;
  max-height: 340px;
  overflow-y: auto;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
}

.transcript .tmeta {
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
  font-size: var(--t-11);
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: .03em;
}

.transcript .tseg {
  display: flex;
  gap: 10px;
  padding: 4px 0;
}

.transcript .tt {
  flex: none;
  width: 46px;
  font-variant-numeric: tabular-nums;
  font-size: var(--t-11);
  color: var(--text-3);
  padding-top: 2px;
}

.transcript .tline {
  margin: 0;
  font-size: var(--t-14);
  line-height: 1.65;
  color: var(--text);
  /* Chinese needs the extra leading above, and must be allowed to wrap
     anywhere since it carries no spaces. */
  overflow-wrap: anywhere;
}

/* ---- detail ------------------------------------------------------------ */

.detail h1 {
  margin: 0 0 2px;
  font-size: var(--t-20);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -.015em;
}
/* The Chinese line of a heading steps down one size so the pair reads as one
   title rather than two competing headings, and still outranks everything
   below it. */
.detail h1 .title-zh {
  font-size: var(--t-16);
  margin-top: 2px;
  letter-spacing: 0;
}
.detail .filename {
  font-size: var(--t-12);
  color: var(--text-2);
  font-family: var(--mono);
  word-break: break-all;
}

.panel {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  box-shadow: var(--shadow-1);
  padding: 12px 14px;
  margin-top: 10px;
}
.panel h3 {
  margin: 0 0 8px;
  font-size: var(--t-11);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-2);
}

.kv {
  display: grid;
  grid-template-columns: minmax(9rem, auto) 1fr;
  gap: 5px 14px;
  margin: 0;
  font-size: var(--t-12);
}
.kv dt { color: var(--text-2); }
.kv dd { margin: 0; }

.timeline { list-style: none; margin: 0; padding: 0 0 0 15px; }
.timeline li {
  position: relative;
  padding: 0 0 11px 12px;
  border-left: 2px solid var(--line);
  font-size: var(--t-12);
}
.timeline li:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline li::before {
  content: "";
  position: absolute;
  left: -6px;
  top: 3px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg);
}
.timeline li.bad::before { background: var(--red); }
.timeline li .when { color: var(--text-3); font-size: var(--t-11); }

.plain { list-style: none; margin: 0; padding: 0; font-size: var(--t-12); }
.plain li { padding: 4px 0 4px 14px; position: relative; line-height: 1.5; }
.plain li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 11px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--text-3);
}
.plain.none li { color: var(--text-3); }
.plain.none li::before { display: none; }

.extlink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 11px;
  margin: 0 6px 6px 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--accent);
  font-size: var(--t-12);
  font-weight: 600;
  text-decoration: none;
}
.extlink:hover { background: var(--accent-bg); border-color: var(--accent-line); }
.extlink::after { content: "\2197"; font-size: 11px; color: var(--text-3); }

.clip {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  padding: 10px 11px;
  margin-bottom: 8px;
}
.clip .t { font-size: var(--t-12); font-weight: 600; }
.clip .meta { font-size: var(--t-11); color: var(--text-2); margin-top: 2px; }
.clip audio { width: 100%; margin-top: 8px; }
.clip .extlink { margin-top: 8px; margin-bottom: 0; }

.caveat {
  margin-top: 8px;
  padding: 7px 9px;
  border-radius: var(--r-sm);
  background: var(--yellow-bg);
  color: var(--yellow);
  font-size: var(--t-11);
  line-height: 1.5;
}

/* ---- bilingual summary -------------------------------------------------- */

.panel.summary .summary-zh {
  margin: 0;
  font-size: var(--t-14);
  line-height: 1.85;
  white-space: pre-wrap;
  color: var(--text);
}
.panel.summary .summary-en-toggle {
  margin-top: 10px;
  border-top: 1px solid var(--line);
  padding-top: 6px;
}

/* The native ::marker is deliberately removed rather than fought with: a flex
   summary suppresses the marker box in Chrome anyway, which is exactly how this
   control once shipped as a flat grey label with nothing to say it opened. The
   caret below is a real element, so it is measurable, it rotates with [open],
   and it cannot silently vanish the way a marker box can. */
/* Deliberately NOT scoped to `.panel.summary`. The identical control is drawn
   on every recording ROW too, and while this rule lived under the detail panel
   the row copies inherited none of it: they kept the native ::marker AND
   carried the caret, so each row showed two disclosure triangles arguing with
   each other, and the summary measured 21px against a 44px minimum. */
.summary-en-toggle > summary,
.row-contexts > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--t-12);
  font-weight: 600;
  color: var(--text-2);
  transition: color .12s ease;
}
/* The per-event list a multi-event recording shows on its row. Same disclosure
   anatomy as the English toggle; numbered because order is the day's order. */
.row-contexts { margin-top: 6px; }
.row-contexts > summary::-webkit-details-marker { display: none; }
.row-contexts > summary:hover { color: var(--text); }
.row-context-titles { margin: 6px 0 0 4px; padding-left: 18px; }
.row-context-titles li { margin: 2px 0; }
/* The detail panel keeps a tap target at every viewport; the rows get theirs in
   the mobile block, so a desktop list of 17 rows is not padded out by 17
   half-empty 44px strips. */
.panel.summary .summary-en-toggle > summary { min-height: var(--tap); }
.summary-en-toggle > summary::-webkit-details-marker { display: none; }
.summary-en-toggle > summary:hover { color: var(--text); }

/* 技术详情 got the caret element but never got these rules, so it rendered the
   NATIVE triangle as well and every technical disclosure read as a doubled
   marker: a black triangle, then a bordered caret, then the label. Same
   treatment, same tap target. */
.tech-toggle > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--tap);
  font-size: var(--t-12);
  font-weight: 600;
  color: var(--text-2);
  transition: color .12s ease;
}
.tech-toggle > summary::-webkit-details-marker { display: none; }
.tech-toggle > summary:hover { color: var(--text); }

.disclosure-caret {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--surface);
  transition: transform .16s ease, border-color .12s ease;
}
.disclosure-caret::before {
  content: "";
  width: 5px;
  height: 5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg) translate(-1px, -1px);
}
.summary-en-toggle[open] .disclosure-caret,
details.morelabels[open] .disclosure-caret,
.row-contexts[open] .disclosure-caret {
  transform: rotate(90deg);
  border-color: var(--accent);
}
.summary-en-toggle > summary:hover .disclosure-caret,
details.morelabels > summary:hover .disclosure-caret,
.row-contexts > summary:hover .disclosure-caret { border-color: var(--accent); }

/* A word, not only a rotation: the state has to be legible at a glance, and to
   anyone who cannot resolve a 5px chevron. */
.disclosure-state::after {
  content: "展开";
  font-size: var(--t-11);
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--accent);
}
.summary-en-toggle[open] .disclosure-state::after { content: "收起"; }
.tech-toggle .disclosure-state::after { content: "展开"; }
.tech-toggle[open] .disclosure-state::after { content: "收起"; }
.row-contexts[open] .disclosure-state::after { content: "收起"; }

.panel.summary .summary-en {
  margin: 2px 0 0;
  font-size: var(--t-13);
  line-height: 1.7;
  white-space: pre-wrap;
  color: var(--text-2);
}

/* An attention item is a card plus, when the blocker names its own remedy, the
   control that clears it. The action sits outside the card button because an
   anchor cannot legally nest inside one. */
.attn { margin-bottom: 10px; }
.attn > .card { margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.attn > .cardactions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--r) var(--r);
  background: var(--surface);
}
.attn > .card.sev-high + .cardactions { border-left: 3px solid var(--red); }
.attn > .card.sev-medium + .cardactions { border-left: 3px solid var(--yellow); }
.attn > .cardactions .btn { min-height: var(--tap); }

/* ---- touch sizing ------------------------------------------------------ */

/* Desktop keeps the dense 28 to 34px controls the reference uses with a mouse.
   On a phone every one of them has to clear 44px, including the row actions,
   the tag pills, the topbar controls and the title link, which is the primary
   navigation affordance on a row and was 18px tall.

   Found by measuring every interactive element on the live mobile page, not by
   reading the stylesheet: my first pass checked only the filter inputs, which
   already passed, so the suite was green while ten controls per row were too
   small to hit. */
@media (max-width: 699px) {
  .btn,
  .rec .actions .btn,
  .tag,
  .extlink {
    min-height: var(--tap);
    padding-left: 14px;
    padding-right: 14px;
  }

  .topbar .back,
  .topbar .icon {
    min-height: var(--tap);
  }
  .topbar .icon { width: var(--tap); height: var(--tap); }

  .filters details.morelabels > summary { min-height: var(--tap); }

  /* Every row carries this disclosure, so on a phone every row was a 21px
     target. It is the same control as the one on the detail page and it gets
     the same minimum here. */
  .rec .rowsummary .summary-en-toggle > summary { min-height: var(--tap); }

  /* A block link, so padding plus min-height makes the whole strip tappable
     rather than only the glyphs. Column, not row: the title holds two language
     lines and centring them as flex ITEMS would sit English and Chinese side by
     side on the one viewport with no room for it. */
  .rec .title {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    min-height: var(--tap);
    padding: 6px 0;
  }

  .rec .actions { gap: 8px; }
}

/* One event of a multi-event recording, shown as its own list entry. The
   accent edge is the only thing that says "these entries share one audio
   file"; everything else about them reads like any other row. */
.rec.ctxrec { border-left: 3px solid var(--accent-line); padding-left: 12px; }

/* ---- George's standing notice board ----------------------------------- */

/* The reminder board at the top of the home page. Three tones, one per
   quadrant, carried by a left edge and the group label rather than by filled
   backgrounds: the board must read as quiet standing context, not as three
   stacked alerts shouting over the recordings below. */
.panel.notices { margin-bottom: 14px; }
.panel.notices h3 { margin: 0 0 4px; font-size: var(--t-13); }
.notice-group-head {
  margin: 10px 0 4px;
  font-size: var(--t-11);
  font-weight: 700;
  letter-spacing: .04em;
}
.notice-group-head.tone-red { color: var(--red); }
.notice-group-head.tone-yellow { color: var(--yellow); }
.notice-group-head.tone-purple { color: var(--purple); }
.notice {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0 7px 10px;
  border-left: 3px solid var(--line-strong);
}
.notice + .notice { border-top: 1px solid var(--line); }
.notice.tone-red { border-left-color: var(--red-line); }
.notice.tone-yellow { border-left-color: var(--yellow-line); }
.notice.tone-purple { border-left-color: var(--purple-line); }
.notice-text { flex: 1 1 auto; min-width: 0; font-size: var(--t-13); }
.notice-done { flex: 0 0 auto; min-height: 30px; padding: 0 10px; }
.notice-done:disabled { opacity: .5; cursor: default; }
