  :root {
    --bg: #fbfaf8; --fg: #1c1b19; --muted: #6b6862; --line: #e2ded7;
    --accent: #00a19b; --err: #b3261e; --card: #fff;
  }
  @media (prefers-color-scheme: dark) {
    :root { --bg:#141412; --fg:#eceae6; --muted:#9b968e; --line:#2e2c28; --card:#1d1c1a; }
  }
  * { box-sizing: border-box; }
  body { margin:0; background:var(--bg); color:var(--fg);
    font:15px/1.55 "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif; }
  .wrap { width:100%; max-width: 46rem; margin: 0 auto; padding: 2.5rem 1.25rem 5rem; }
  /* Sidebar beside content. The menu grows downward, where there is room: a
     horizontal bar runs out of width at six tools and starts hiding the very
     thing it exists to show. */
  .shell { display:grid; grid-template-columns:15rem minmax(0,1fr);
    max-width:64rem; margin:0 auto; }
  /* Signed out there is no sidebar, so there should be no column for one.
     Without this the hidden aside leaves its 15rem track behind and the
     content auto-places into it — a sign-in card squeezed into a gutter. */
  .shell:has(#sidebar[hidden]) { grid-template-columns:minmax(0,1fr); max-width:46rem; }
  #sidebar { padding:1.75rem .75rem 3rem 1.25rem; }
  #sidebar-nav { position:sticky; top:1.75rem; display:flex; flex-direction:column; gap:.1rem; }
  .nav-group { font-size:.66rem; font-weight:700; letter-spacing:.09em;
    text-transform:uppercase; color:var(--muted); margin:1.3rem .6rem .35rem; }
  .nav-group:first-child { margin-top:0; }
  .nav-item { display:flex; align-items:center; gap:.45rem; width:100%;
    text-align:left; padding:.45rem .6rem; border-radius:7px; font-size:.88rem;
    font-weight:600; color:var(--muted); background:none; border:none; margin:0;
    text-decoration:none; }
  .nav-item.is-current { color:var(--fg); background:color-mix(in srgb, var(--accent) 13%, transparent); }
  /* Nothing happens on click, and it should not look as though something will.
     A dead control that looks live is worse than one that says so. */
  .nav-item.pending { opacity:.72; cursor:default; }
  .nav-item .label { flex:1; min-width:0; line-height:1.35; }
  .nav-item .tier, .nav-item .soon { flex:none; align-self:center; }
  /* The tool's own pages. Indented under their tool because moving within a
     tool is a different question from choosing one. */
  .nav-sub { display:block; width:100%; text-align:left; background:none;
    border:none; margin:0; padding:.33rem .6rem .33rem 1.75rem; font-size:.84rem;
    color:var(--muted); cursor:pointer; border-radius:7px; }
  .nav-sub:hover { color:var(--fg); background:var(--card); }
  .soon, .tier { font-size:.6rem; font-weight:700; letter-spacing:.06em;
    text-transform:uppercase; color:var(--muted); border:1px solid var(--line);
    border-radius:999px; padding:.1rem .35rem; white-space:nowrap; }
  .menu-btn { display:none; width:auto; flex:none; font-size:1rem; padding:.35rem .6rem; }
  #scrim { display:none; }
  @media (max-width: 860px) {
    /* The sidebar becomes a drawer. One button beats a row you have to swipe. */
    .shell { grid-template-columns:minmax(0,1fr); }
    #sidebar { position:fixed; z-index:20; top:0; left:0; bottom:0; width:15rem;
      background:var(--card); border-right:1px solid var(--line);
      padding-top:1.25rem; overflow-y:auto; transform:translateX(-100%);
      transition:transform .18s ease; }
    #sidebar.open { transform:translateX(0); }
    #scrim.open { display:block; position:fixed; inset:0; z-index:19;
      background:rgba(0,0,0,.35); }
    .menu-btn { display:block; }
  }
  .card { background:var(--card); border:1px solid var(--line); border-radius:12px;
    padding:1.25rem; margin-bottom:1.25rem; }
  /* The label names the field, the hint explains it. They were the same colour
     and the hint was the larger of the two, so letter-spacing was the only
     thing telling them apart — not enough to scan a form by. The label now
     differs on four counts: darker, heavier, larger, still uppercase.
     The gap above a label is far wider than the one below its hint, so each
     label-input-hint reads as one block instead of the hint floating between
     two fields. */
  label { display:block; font-size:.82rem; font-weight:650; color:var(--fg);
    margin:1.6rem 0 .35rem; text-transform:uppercase; letter-spacing:.07em; }
  input, select, button {
    font:inherit; width:100%; padding:.6rem .7rem; border-radius:8px;
    border:1px solid var(--line); background:var(--bg); color:var(--fg); }
  button { background:var(--accent); color:#fff; border-color:transparent; cursor:pointer;
    font-weight:600; margin-top:1.1rem; }
  button:disabled { opacity:.5; cursor:not-allowed; }
  .msg { padding:.7rem .85rem; border-radius:8px; margin-top:1rem; font-size:.9rem; }
  .msg.err { background:color-mix(in srgb, var(--err) 12%, transparent); color:var(--err); }
  .msg.ok  { background:color-mix(in srgb, var(--accent) 12%, transparent); color:var(--accent); }
  code, .mono { font-family:"IBM Plex Mono", ui-monospace, monospace; font-size:.85em; }
  #qr { display:grid; place-items:center; padding:1.5rem 0 .5rem; }
  #qr svg { width:260px; height:260px; background:#fff; padding:8px; border-radius:8px; }
  .hint { color:var(--muted); font-size:.78rem; margin-top:.35rem; line-height:1.45; }
  .muted-cell { color:var(--muted); }
  .payee-cell { font-family:"IBM Plex Mono", ui-monospace, monospace; font-size:.95em; }
  /* Not display:flex. A flex container stops being a table-cell, so this cell
     laid itself out independently of its row and its bottom border landed a
     few pixels below the others — a broken-looking rule across every row. */
  table.codes .actions { text-align:right; white-space:nowrap; }
  table.codes .actions .link + .link { margin-left:.9rem; }
  .link.armed { color:var(--err); font-weight:700; }
  .target-cell { max-width:16rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    color:var(--muted); font-size:.85em; }
  /* A reference you can open. Looks like a link rather than a button: it leads
     somewhere, it does not submit anything. */
  .open-code { width:auto; margin:0; padding:0; background:none; border:none;
    color:var(--accent); font:inherit; font-weight:600; cursor:pointer;
    text-decoration:underline; text-underline-offset:3px; }
  .open-code:hover { text-decoration-thickness:2px; }
  /* A payee that is not this church's UEN. Coloured like an error because that
     is what it usually is. */
  .odd-payee { color:var(--err); font-family:"IBM Plex Mono", ui-monospace, monospace;
    font-size:.95em; }
  /* Declared here as well as inside the component, because an unknown element
     is inline until its definition arrives — without this the page reflows
     when footer.js lands. */
  yireh-footer { display:block; }
  /* Not sticky. It would hold a strip of every screen for a fact that is not
     load-bearing here: the church is enforced by the session binding (D42), and
     it is repeated in the caption and drawn into the QR image itself. */
  #topbar { background:var(--card); border-bottom:1px solid var(--line); }
  .bar { max-width:46rem; margin:0 auto; padding:.7rem 1.25rem;
    display:flex; align-items:center; gap:1rem; }
  .brand { display:flex; align-items:center; gap:.5rem; font-size:1.05rem;
    font-weight:700; letter-spacing:-.01em; white-space:nowrap; }
  /* The church pushes the brand left and takes the room. Whose data you are
     looking at matters more than whose software it is. */
  .bar .ident { display:flex; flex-direction:column; min-width:0; flex:1;
    padding-left:.9rem; margin-left:.9rem; border-left:1px solid var(--line); }
  /* One line, clipped. A long church name wrapped to three lines on a phone
     and grew the bar into the Sign out button. */
  .bar #bar-church { font-size:.95rem; overflow:hidden; text-overflow:ellipsis;
    white-space:nowrap; }
  .bar #bar-email { font-size:.8rem; color:var(--muted);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .ghost { width:auto; flex:none; margin:0; background:transparent; color:var(--fg);
    border-color:var(--line); font-size:.85rem; padding:.4rem .8rem; }
  .static { margin:.2rem 0 0; font-weight:600; }
  .list-head { display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
               flex-wrap:wrap; row-gap:.25rem; }
  .list-head strong { white-space:nowrap; }
  /* Only the wrapper around two controls. Short links and Name cards put a lone
     button straight in .list-head and need none of this. */
  .list-head > span { display:flex; flex-wrap:wrap; justify-content:flex-end; gap:0 .9rem;
                      margin-left:auto; }
  @media (max-width:560px) { .list-head .link { min-height:40px; display:inline-flex;
                                                align-items:center; } }
  /* The codes table has a minimum width of about 394px. In a 293px card it used to
     push the whole page wider than the phone, form card included. It scrolls on
     its own now. */
  #list-body { overflow-x:auto; }
  .sr { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
  table.codes { width:100%; border-collapse:collapse; margin-top:.9rem; font-size:.88rem; }
  table.codes th { text-align:left; font-weight:500; color:var(--muted); font-size:.72rem;
    text-transform:uppercase; letter-spacing:.06em; padding:0 .6rem .4rem 0;
    border-bottom:1px solid var(--line); }
  table.codes td { padding:.55rem .6rem .55rem 0; border-bottom:1px solid var(--line);
    vertical-align:top; }
  table.codes tr:last-child td { border-bottom:none; }
  table.codes .ref-cell { font-family:"IBM Plex Mono", ui-monospace, monospace; font-weight:600; }
  table.codes .num { font-variant-numeric:tabular-nums; white-space:nowrap; }
  .empty { color:var(--muted); padding:1rem 0; }
  table.codes .desc { font-family:"IBM Plex Sans", ui-sans-serif, sans-serif;
    font-weight:400; color:var(--muted); font-size:.82rem; margin-top:.15rem; }
  table.codes .by { color:var(--muted); font-size:.75rem; margin-top:.15rem;
    font-variant-numeric:normal; }
  .inline { display:flex; gap:.6rem; align-items:center; }
  .inline input[type=color] { width:52px; padding:2px; height:38px; flex:none; cursor:pointer; }
  .inline input[type=text], .inline #fg-hex { font-family:"IBM Plex Mono", ui-monospace, monospace; }
  .link { width:auto; flex:none; background:none; border:none; color:var(--accent);
    margin:0; padding:0 .2rem; font-size:.82rem; font-weight:500; text-decoration:underline; }
  .opt { color:var(--muted); font-weight:400; text-transform:none; letter-spacing:0; }
  #canvas-host canvas { max-width:100%; height:auto; border-radius:8px; }
  #caption { display:grid; grid-template-columns:auto 1fr; gap:.35rem 1rem;
    max-width:22rem; margin:.6rem auto 0; font-size:.9rem; }
  #caption dt { color:var(--muted); }
  #caption dd { margin:0; font-family:"IBM Plex Mono", ui-monospace, monospace; }
  .dl { display:inline-block; width:auto; margin-top:.9rem; padding:.5rem .9rem;
    background:var(--accent); color:#fff; border-radius:8px; text-decoration:none;
    font-weight:600; font-size:.9rem; }
  [hidden] { display:none !important; }

/* A rule the user has broken, said quietly rather than in a red box: the field
   is still editable and they are mid-thought. */
.hint.warn { color:var(--err); font-weight:600; }
