/* ═══════════════════════════════════════════════════════════════════════
   NOTHING HERE YET
   The single home for the two things the app was spelling fourteen ways.

   They are not the same thing, and conflating them is how we ended up with
   fourteen:

     .empty        A place that has nothing in it yet and needs to say what
                   to do about that. A column: art, what is missing, why it
                   matters, and at most one filled call to action.

     .placeholder  A row's worth of muted text where rows would be. Not an
                   empty state — no art, no call to action, no heading. It
                   is a status line inside a panel that already has a title,
                   and giving it a picture and a button would be shouting.

   Do not style an empty state anywhere else. Like .menu and the modal size
   scale, this exists so a fix to one lands on all of them;
   scripts/checks/empty-state-check.js fails the build on a new dialect.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── THE EMPTY STATE ── */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 4px;
  padding: 56px 20px;
  color: var(--t3);
}

/* Three densities, chosen by what the empty thing sits in — the same idea as
   the modal size scale, for the same reason. */
.empty.sm { padding: 26px 14px; gap: 3px; }          /* a widget, a column */
.empty.lg { padding: 72px 24px; }                     /* a whole page */

/* The box a dot illustration is drawn into. js/illustrations.js puts a canvas
   in it, sized to this box, and paints the scene named by data-scene.

   No drop-shadow: a shadow cast by a field of dots reads as dirt, and there
   is no solid shape here to cast one anyway — the paper is transparent, so
   the dots sit on the card itself. */
.empty-illus {
  display: block;
  margin: 0 auto 14px;
  user-select: none;
  filter: none;
}
.empty-illus > canvas { display: block; width: 100%; height: 100%; }

/* An empty state's picture is the artwork as drawn — a plain <img>, no script
   involved. The dithered screen is still there for the dashboard banner,
   which is why .empty-illus can be either a box holding a canvas or the
   image itself. */
img.empty-illus { object-fit: contain; }

/* The undithered variant draws the SVG inline instead, so it can read the
   page's custom properties — an <img> cannot, and a flat hex would not
   invert in dark mode. */
.empty-illus > svg { display: block; width: 100%; height: 100%; }

/* 112, not 72. A halftone is a count of dots, and below about 110px there are
   not enough of them left to describe anything. */
.empty-illus.sm { width: 112px; height: 112px; margin-bottom: 10px; }
.empty-illus.md { width: 180px; height: 180px; }
.empty-illus.lg { width: 240px; height: 240px; }

/* ═══════════════════════════════════════════════════════════════════════
   THE GHOST

   What stands above the words is not a picture of someone at a desk. It is a
   faint drawing of THIS panel with something in it: the pipeline as four
   stages narrowing, the breakdown as a ring, the deadlines as three rows.

   Stock art decorates the gap. A ghost shows what will fill it, which is the
   only thing an empty state is for. It is also made of the app's own tokens,
   so it cannot drift away from the widget it is promising — and it costs no
   download at all.

   It is furniture, not content: aria-hidden at every call site, no pointer
   events, nothing to read or reach.

   Exactly one element in each carries the accent, in the same role the one
   orange prop played in the illustrations.
   ═══════════════════════════════════════════════════════════════════════ */
.empty-ghost {
  --gh: var(--bg4);
  position: relative;
  width: 100%;
  max-width: 190px;
  margin: 0 auto 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  pointer-events: none;
  user-select: none;
}
.empty-ghost b { display: block; }
.empty.sm .empty-ghost { max-width: 158px; gap: 6px; margin-bottom: 12px; }
.empty-ghost > i { display: block; background: var(--gh); border-radius: var(--r-2, 6px); }

/* The pipeline: four stages, each narrower than the one above, the last one
   — the ones that made it all the way out — in the accent. */
.gh-funnel > i { height: 11px; }
.gh-funnel > i:nth-child(1) { width: 100%; }
.gh-funnel > i:nth-child(2) { width: 74%; }
.gh-funnel > i:nth-child(3) { width: 50%; }
.gh-funnel > i:nth-child(4) { width: 28%; background: var(--accent); opacity: .55; }

/* The breakdown: one ring, one slice of it ours. A conic gradient masked to
   a ring rather than four rotated boxes, so it is the same shape the real
   chart draws. */
.gh-donut {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: conic-gradient(var(--accent) 0 34%, var(--gh) 0 62%, var(--b2) 0 82%, var(--gh) 0);
  -webkit-mask: radial-gradient(circle, transparent 53%, #000 54%);
  mask: radial-gradient(circle, transparent 53%, #000 54%);
  opacity: .8;
}
.empty.sm .gh-donut { width: 80px; height: 80px; }

/* A list of things that happened, or things that are due: a chip and a line,
   three times, the first chip ours. */
.gh-rows { gap: 10px; }
.gh-rows > i {
  width: 100%;
  height: 17px;
  display: flex;
  align-items: center;
  gap: 9px;
  background: none;
}
.gh-rows > i::before {
  content: '';
  width: 17px;
  height: 17px;
  flex: none;
  border-radius: var(--r-2, 6px);
  background: var(--gh);
}
.gh-rows > i::after { content: ''; height: 7px; border-radius: 99px; background: var(--gh); }
.gh-rows > i:nth-child(1)::after { width: 72%; }
.gh-rows > i:nth-child(2)::after { width: 50%; }
.gh-rows > i:nth-child(3)::after { width: 63%; }
.gh-rows > i:first-child::before { background: var(--accent); opacity: .55; }

/* Cards in a list — a prompt, a video. The outline is the card's own border
   token, so these are the real card at a whisper. */
.gh-cards > i,
.gh-tiles > i {
  width: 100%;
  border: 1px solid var(--b1);
  border-radius: var(--r-3, 8px);
  background: none;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 9px;
}
.gh-cards > i { height: 30px; }
.gh-tiles > i { height: 34px; }
.gh-tiles > i::before {
  content: '';
  width: 32px;
  height: 18px;
  flex: none;
  border-radius: 4px;
  background: var(--gh);
}
.gh-cards > i::after,
.gh-tiles > i::after { content: ''; height: 7px; border-radius: 99px; background: var(--gh); }
.gh-cards > i:nth-child(1)::after, .gh-tiles > i:nth-child(1)::after { width: 64%; }
.gh-cards > i:nth-child(2)::after, .gh-tiles > i:nth-child(2)::after { width: 46%; }
.gh-cards > i:nth-child(3)::after, .gh-tiles > i:nth-child(3)::after { width: 55%; }
.gh-cards > i:first-child::after { background: var(--accent); opacity: .55; }
.gh-tiles > i:first-child::before { background: var(--accent); opacity: .5; }

/* ── ONE PER SURFACE, NOT FIVE REUSED ───────────────────────────────────
   A ghost is only worth drawing if it is a ghost of the thing you are
   looking at. Five shapes spread over thirty panels would be the same
   mistake as one illustration spread over thirty panels, one level quieter.
   So the task board gets columns, the chat gets bubbles, the changelog gets
   a rail of dots, and the file drawer gets a grid of thumbnails. */

/* The task board: three columns, uneven, the way a real one is. */
.gh-board { flex-direction: row; align-items: flex-start; gap: 8px; }
.gh-board > i { flex: 1; display: flex; flex-direction: column; gap: 5px; background: none; }
.gh-board > i > b { height: 15px; border-radius: 4px; background: var(--gh); }
.gh-board > i:nth-child(2) > b:first-child { background: var(--accent); opacity: .55; }

/* A conversation: bubbles leaning to the side that said them. */
.gh-chat { align-items: stretch; gap: 8px; }
.gh-chat > i { height: 17px; border-radius: 10px; }
.gh-chat > i:nth-child(1) { width: 64%; margin-right: auto; }
.gh-chat > i:nth-child(2) { width: 46%; margin-left: auto; background: var(--accent); opacity: .5; }
.gh-chat > i:nth-child(3) { width: 54%; margin-right: auto; }

/* A leaderboard: ranked rows, each bar shorter than the one above. The one
   at the top is the thing you came to see, so it is the one in the accent. */
.gh-rank { align-items: stretch; gap: 9px; }
.gh-rank > i { height: 13px; display: flex; align-items: center; gap: 8px; background: none; }
.gh-rank > i::before {
  content: '';
  width: 13px; height: 13px; flex: none;
  border-radius: 4px; background: var(--gh);
}
.gh-rank > i::after { content: ''; height: 9px; border-radius: 99px; background: var(--gh); }
.gh-rank > i:nth-child(1)::after { width: 88%; background: var(--accent); opacity: .55; }
.gh-rank > i:nth-child(2)::after { width: 66%; }
.gh-rank > i:nth-child(3)::after { width: 48%; }
.gh-rank > i:nth-child(4)::after { width: 34%; }

/* A changelog: one rail, a dot for every entry. The rail is drawn behind the
   dots and stops short of both ends so it reads as a thread, not a border. */
.gh-trail { align-items: stretch; gap: 13px; }
.gh-trail::before {
  content: '';
  position: absolute;
  left: 5px; top: 8px; bottom: 8px;
  width: 2px;
  background: var(--gh);
}
.gh-trail > i { height: 12px; display: flex; align-items: center; gap: 11px; background: none; }
.gh-trail > i::before {
  content: '';
  width: 12px; height: 12px; flex: none;
  border-radius: 50%;
  background: var(--gh);
  box-shadow: 0 0 0 3px var(--bg2);
}
.gh-trail > i::after { content: ''; height: 8px; border-radius: 99px; background: var(--gh); }
.gh-trail > i:nth-child(1)::before { background: var(--accent); opacity: .6; }
.gh-trail > i:nth-child(1)::after { width: 74%; }
.gh-trail > i:nth-child(2)::after { width: 56%; }
.gh-trail > i:nth-child(3)::after { width: 66%; }

/* Invitations waiting: a face, an address, and the button you would press. */
.gh-invite { align-items: stretch; gap: 8px; }
.gh-invite > i {
  height: 30px;
  display: flex; align-items: center; gap: 9px;
  padding: 0 9px;
  border: 1px solid var(--b1);
  border-radius: var(--r-3, 8px);
  background: none;
}
.gh-invite > i::before {
  content: '';
  width: 16px; height: 16px; flex: none;
  border-radius: 50%; background: var(--gh);
}
.gh-invite > i::after { content: ''; height: 8px; border-radius: 99px; background: var(--gh); }
.gh-invite > i:nth-child(1)::after { width: 46%; }
.gh-invite > i:nth-child(2)::after { width: 36%; }
.gh-invite > i > b {
  margin-left: auto;
  width: 34px; height: 13px;
  border-radius: 99px;
  background: var(--gh);
}
.gh-invite > i:nth-child(1) > b { background: var(--accent); opacity: .55; }

/* The channel directory: a table, because that is what it is — rows inside
   one border, divided rather than floating apart. */
.gh-channels {
  align-items: stretch;
  gap: 0;
  border: 1px solid var(--b1);
  border-radius: var(--r-3, 8px);
  overflow: hidden;
}
.gh-channels > i {
  height: 27px;
  display: flex; align-items: center; gap: 9px;
  padding: 0 10px;
  background: none;
  border-radius: 0;
}
.gh-channels > i + i { border-top: 1px solid var(--b1); }
.gh-channels > i::before {
  content: '';
  width: 15px; height: 15px; flex: none;
  border-radius: 4px; background: var(--gh);
}
.gh-channels > i::after { content: ''; height: 8px; border-radius: 99px; background: var(--gh); }
.gh-channels > i:nth-child(1)::before { background: var(--accent); opacity: .55; }
.gh-channels > i:nth-child(1)::after { width: 52%; }
.gh-channels > i:nth-child(2)::after { width: 38%; }
.gh-channels > i:nth-child(3)::after { width: 46%; }

/* What has been shared into a channel: a contact sheet, not a list. */
.gh-files {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 7px;
  align-items: stretch;
}
.gh-files > i { aspect-ratio: 1; border-radius: var(--r-2, 6px); }
.gh-files > i:nth-child(2) { background: var(--accent); opacity: .5; }

/* Nothing to preview: the frame the preview would fill, and the triangle you
   would press. The one ghost that is a single shape — a thumbnail has no
   parts to draw. */
.gh-frame {
  max-width: 172px;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--b1);
  border-radius: var(--r-3, 8px);
  background: none;
}
.gh-frame::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-42%, -50%);
  border-style: solid;
  border-width: 11px 0 11px 18px;
  border-color: transparent transparent transparent var(--accent);
  opacity: .55;
}

/* The first run: the field you are being asked to paste into, with the caret
   already blinking in it. */
.gh-link { align-items: stretch; }
.gh-link > i {
  height: 36px;
  display: flex; align-items: center; gap: 8px;
  padding: 0 11px;
  border: 1px solid var(--b2);
  border-radius: var(--r-3, 8px);
  background: none;
}
.gh-link > i::before { content: ''; width: 44%; height: 8px; border-radius: 99px; background: var(--gh); }
.gh-link > i::after {
  content: '';
  width: 2px; height: 15px;
  border-radius: 1px;
  background: var(--accent);
  opacity: .7;
}

/* The archive: a drawer, with what went into it stacked above the lip. The
   directory and its archive are two different states of one screen, so they
   get two different ghosts — reusing one would say they were the same. */
.gh-archive { align-items: center; gap: 6px; }
.gh-archive > i:nth-child(1) { width: 50%; height: 9px; border-radius: 3px; opacity: .5; }
.gh-archive > i:nth-child(2) { width: 68%; height: 9px; border-radius: 3px; }
.gh-archive > i:nth-child(3) {
  position: relative;
  width: 100%;
  height: 36px;
  margin-top: 3px;
  border: 1px solid var(--b2);
  border-radius: var(--r-3, 8px);
  background: none;
}
.gh-archive > i:nth-child(3)::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 11px;
  height: 1px;
  background: var(--b2);
}
.gh-archive > i:nth-child(3)::after {
  content: '';
  position: absolute;
  left: 50%; top: 19px;
  transform: translateX(-50%);
  width: 36px; height: 7px;
  border-radius: 99px;
  background: var(--accent);
  opacity: .5;
}

/* Shipped: the same tiles as a board column, each with its work ticked off. */
.gh-done { align-items: stretch; gap: 8px; }
.gh-done > i {
  height: 30px;
  display: flex; align-items: center; gap: 10px;
  background: none;
}
.gh-done > i::before {
  content: '';
  width: 20px; height: 20px; flex: none;
  border-radius: 50%; background: var(--gh);
}
.gh-done > i::after { content: ''; height: 8px; border-radius: 99px; background: var(--gh); }
.gh-done > i:nth-child(1)::after { width: 70%; }
.gh-done > i:nth-child(2)::after { width: 52%; }
.gh-done > i:nth-child(3)::after { width: 61%; }
.gh-done > i:nth-child(1)::before { background: var(--accent); opacity: .6; }
/* The tick, drawn as two sides of a square turned on its corner. */
.gh-done > i > b {
  position: absolute;
  left: 6px;
  top: 50%;
  margin-top: -4px;
  width: 8px; height: 4px;
  border-left: 2px solid var(--bg2);
  border-bottom: 2px solid var(--bg2);
  transform: rotate(-45deg);
}
.gh-done > i { position: relative; }

/* Some empty states have no illustration, only a glyph. One circle, one
   size, rather than the 46px one in the dashboard and the 54px one in chat. */
/* 54px holding a 25px glyph — the figures branddesign.md §8 already gave for
   an empty state. The two dialects this replaced had invented 46px and 54px
   independently, which is how a doc ends up describing neither. */
.empty-glyph {
  width: 54px;
  height: 54px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 12px;
  border-radius: 50%;
  background: var(--bg3);
  color: var(--t3);
  font-size: 24px;
}
.empty-glyph svg { width: 25px; height: 25px; }
.empty.sm .empty-glyph { width: 44px; height: 44px; margin-bottom: 9px; font-size: 20px; }
.empty.sm .empty-glyph svg { width: 20px; height: 20px; }

/* A bare glyph with no circle — the "no results" question mark. */
.empty-mark {
  font-size: 28px;
  line-height: 1;
  margin-bottom: 10px;
  opacity: .25;
}
.empty.lg .empty-mark { font-size: 40px; }

/* The line that says what is missing. One line, in the app's voice: name the
   thing, not the state of the database. */
.empty-t {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--tx);
}
.empty.sm .empty-t { font-size: 13.5px; }
.empty.lg .empty-t { font-size: 17px; }

/* Why it matters, or what to do. Capped so it never runs to a full-width
   paragraph that nobody reads. */
.empty-d {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--t3);
  max-width: 38ch;
  margin: 2px auto 0;
}
.empty.lg .empty-d { font-size: 13px; }

/* A quieter afterthought under the description — a tip, not the point. */
.empty-hint { font-size: 12px; color: var(--t4); }

.empty code {
  font-family: var(--ff-mono, ui-monospace, monospace);
  font-size: 12px;
  background: var(--bg3);
  padding: 2px 6px;
  border-radius: var(--r-1, 5px);
}

/* At most one filled action. A second belongs here as a ghost, never as a
   second filled button competing with the first. */
.empty-act {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}
.empty.sm .empty-act { margin-top: 10px; }

/* A button written straight into the block, with no .empty-act around it,
   gets the same air. Most call sites do exactly that, so without this one
   section's call to action sat tight under its text while the next one's
   had room above it. */
.empty > .empty-cta { margin-top: 14px; }
.empty.sm > .empty-cta { margin-top: 10px; }
.empty-act > .empty-cta { margin-top: 0; }

/* TWO WEIGHTS, DECIDED BY WHERE THE BUTTON STANDS.
   One button style everywhere was wrong in both directions. As a solid
   block it made five empty dashboard widgets five of the loudest things on
   the page; as a quiet surface button it made the one action on an empty
   page — "Start exploring", "Create a channel", "Add your first task" — look
   optional, when it is the only thing that page is for.

   So the weight follows the context, which the markup already states:

     .empty        a page or a panel with nothing in it. The button is the
                   whole point of the screen, so it is PROMINENT: ink.
     .empty.sm     a widget among other widgets. Its button is one of
                   several on the page, so it is QUIET: a surface button.
     .prominent    a widget whose action IS the point of the page — the
                   dashboard's Pipeline, where "New Video" is how anything
                   gets onto the board — opts back into ink.

   Neither weight is ever accent-filled: branddesign.md §3, "Orange buttons
   everywhere = cheap". The accent is the plus and the focus ring. */
.empty-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 18px 0 16px;
  border: 1px solid transparent;
  border-radius: var(--r-4, 10px);
  background: var(--fill, var(--tx));
  color: var(--fill-ink);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  /* One curve for everything, the standard ease-out: fast to respond,
     gentle to settle. Nothing on this button moves position on hover — a
     call to action that jumps as you approach it reads as a toy. */
  transition:
    opacity .16s cubic-bezier(.2, 0, 0, 1),
    background-color .16s cubic-bezier(.2, 0, 0, 1),
    border-color .16s cubic-bezier(.2, 0, 0, 1),
    color .16s cubic-bezier(.2, 0, 0, 1),
    transform .08s cubic-bezier(.2, 0, 0, 1);
}
/* Ink has no lighter token to step to, and the ink is black in one theme
   and near-white in the other — a slight fade is the hover that reads the
   same way in both. */
.empty-cta:hover { opacity: .88; }
/* The press is the only motion: a 2% give under the finger, released as
   soon as it lifts. Felt rather than seen. */
.empty-cta:active { opacity: 1; transform: scale(.98); }
.empty-cta:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-line); }

/* QUIET: a widget among widgets. */
.empty.sm .empty-cta {
  height: 32px;
  padding: 0 12px 0 10px;
  gap: 7px;
  border-color: var(--b2);
  border-radius: var(--r-3, 8px);
  background: var(--bg2);
  color: var(--tx);
  font-size: 12.5px;
  font-weight: 500;
}
.empty.sm .empty-cta:hover { opacity: 1; background: var(--bg3); border-color: var(--b3); }
.empty.sm .empty-cta:active { background: var(--bg4); }

/* PROMINENT again, in a widget whose action is the point of the page. */
.empty.sm .empty-cta.prominent {
  height: 34px;
  padding: 0 14px 0 12px;
  border-color: transparent;
  background: var(--fill, var(--tx));
  color: var(--fill-ink);
  font-size: 13px;
  font-weight: 600;
}
.empty.sm .empty-cta.prominent:hover { opacity: .88; background: var(--fill, var(--tx)); border-color: transparent; }
.empty.sm .empty-cta.prominent:active { opacity: 1; }

/* THE MARK.
   Every call to action that makes something carries the same one: a thin
   plus in the accent, the only colour on the button at either weight.

   It used to be a 17px accent disc with the plus knocked out, turning a
   quarter turn on hover — the brightest and the only moving thing in an
   empty state, which made it what the eye went to on a button whose job is
   to wait until it is wanted. It holds still now.

   Five of the thirteen call sites used to put a literal "+ " in front of the
   label and eight did not. The mark belongs to the button, not to the words. */
.empty-cta.add::before {
  content: '';
  width: 12px;
  height: 12px;
  flex: none;
  background:
    linear-gradient(var(--accent), var(--accent)) center / 12px 1.7px no-repeat,
    linear-gradient(var(--accent), var(--accent)) center / 1.7px 12px no-repeat;
}
.empty.sm .empty-cta.add::before {
  width: 11px; height: 11px;
  background-size: 11px 1.5px, 1.5px 11px;
}

/* A GHOST IS A SECOND ACTION, NEVER A LONE ONE.
   Beside an ink primary it is a surface button; beside a quiet one it steps
   back again to plain text, so the pair always reads as first and second. */
.empty-cta.ghost {
  border-color: var(--b2);
  background: var(--bg2);
  color: var(--tx);
}
.empty-cta.ghost:hover { opacity: 1; background: var(--bg3); border-color: var(--b3); }
.empty.sm .empty-cta.ghost { background: transparent; border-color: transparent; color: var(--t2); }
.empty.sm .empty-cta.ghost:hover { background: var(--bg2); border-color: var(--b2); color: var(--tx); }

/* There is no .brand variant. A purple gradient button was a fourth colour on
   a page that is meant to have one, and Slate AI is named well enough by its
   own words and emblem without the button shouting a different brand. */

@media (prefers-reduced-motion: reduce) {
  .empty-cta { transition: none; }
  .empty-cta:active { transform: none; }
}

/* Drawn inside a box of its own, for an empty state that sits among filled
   cards and would otherwise read as a gap in the layout rather than a card
   with nothing in it. */
.empty.boxed {
  border: 1px solid var(--b1);
  border-radius: var(--r-5, 12px);   /* cards and panels — §4 */
}

/* ── THE LIST PLACEHOLDER ── */
.placeholder {
  padding: 18px 12px;
  text-align: center;
  font-size: 13px;
  color: var(--t3);
}
/* Inside something already small — a popover, a sidebar list — where even
   13px centred text is too much furniture. */
.placeholder.quiet {
  padding: 12px 10px;
  font-size: 12px;
  color: var(--t4);
}

/* ── REMOVED FROM EVERYTHING ──────────────────────────
   Shown instead of the app when the signed-in user is a member of no
   workspace. It deliberately does not reuse .empty: an empty state sits
   inside the app and says "nothing here yet", and this says the opposite —
   there is no app to be inside. */
#no-workspace {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--bg);
}
.nw-card {
  max-width: 420px;
  text-align: center;
}
.nw-t {
  font-size: 19px;
  font-weight: 600;
  color: var(--tx);
}
.nw-d {
  margin: 10px 0 20px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--t3);
}
/* The button is .btn.primary — the app already has one primary button and
   it is ink, not accent. */
