/* THE PAGE IS NEUTRAL GREY, NOT BLUE-GREY.
   It used to be #F4F6F9, which is a blue-tinted white, and the tint was doing
   more than it looked: everything on the page sits on it, so a whole interface
   read faintly cold. Craig asked for the neutral version after looking at one
   beside it, and he was right — the navy, the gold and the serif do the
   character, and they do it better against a background that is not already
   pulling in one of those directions.
   Changed HERE and nowhere else. Every surface in this file is one of these
   three tokens, which is what makes a decision like this three lines. */
:root {
  --ground: #F7F7F8;
  --surface: #FFFFFF;
  --surface-2: #F0F0F2;
  /* NEAR-BLACK, NOT NAVY. The ink used to be #14213A, a navy that tinted every
     word on every page. Craig chose the modern reading, and on a neutral
     background the navy was the other half of the coldness the ground change
     started on. The navy itself is not gone — it is still --navy, and it still
     does the buttons, the links and the rules, which is where a brand colour
     belongs. It just stopped being the colour of ordinary text. */
  --ink: #16171D;
  --ink-2: #3E4049;
  /* Set by the WORST surface it lands on, not the page. At #6E707A it cleared
     4.5:1 against the page and fell to 4.33 on --surface-2, which is where
     half the small grey labels in this application actually sit. Two points
     darker and both pass. */
  --muted: #696B75;
  --line: #E4E4E8;
  --line-soft: #EFEFF2;
  --navy: #1E3A5F;
  --navy-soft: #E3EAF3;
  /* Halfway between --navy and --navy-soft. Its own token rather than a
     color-mix() written out wherever it is wanted, so "medium navy" means one
     colour across the app instead of whatever each author eyeballed. */
  --navy-line: #90A9C6;
  --on-accent: #FFFFFF;
  --gold: #A67C1F;
  /* The same gold taken down six points of lightness. The brand gold is right
     for a 2px rule or a bar, but at label sizes it lands at 3.5:1 on the page
     background — under the 4.5:1 that text this small needs. Same hue, same
     saturation, legible. Used for every small gold label; the brand gold still
     does the graphic work. */
  --gold-ink: #8E6A1B;
  --gold-soft: #F2E9D2;
  --good: #2E6F52;
  --good-soft: #DFEDE5;
  --warn: #B05E12;
  --warn-soft: #F8E8D6;
  --crit: #9E3227;
  --crit-soft: #F6E0DD;
  --radius: 14px;
  /* Depth, in three steps.
     Two layers each on purpose: a tight, nearly-opaque one that draws the
     edge, and a wide, faint one that is the light. One layer alone reads as a
     grey smudge — it is the pair that reads as a card sitting slightly off the
     page. Tinted with the ink colour rather than black, because a neutral grey
     shadow on a blue-grey page looks dirty.

     Tuned by eye against the mockup and then lifted once more when the first
     pass read as too polite on a real screen. If they are ever changed again,
     change them HERE and nowhere else — every shadow in this file is one of
     these three, which is the whole reason the app can be made flatter or
     deeper in three lines. */
     The tint came off these with the background. A navy-tinted shadow on a
     blue-grey page reads as light; the same shadow on a neutral page reads as
     the grey smudge this comment warns about two paragraphs up. They are
     tinted with a near-black now, which is what the page is neutral against. */
  --shadow-sm: 0 1px 2px rgba(17,18,24,.04), 0 2px 6px -2px rgba(17,18,24,.06);
  --shadow: 0 1px 2px rgba(17,18,24,.05), 0 6px 20px -8px rgba(17,18,24,.14);
  --shadow-lg: 0 2px 4px rgba(17,18,24,.06), 0 16px 36px -14px rgba(17,18,24,.22);
  /* THE HEADINGS ARE SANS NOW, and Spectral has left the building entirely —
     it was the only serif in the file, so dropping it here also drops three
     font files from the one Google Fonts request every page makes.
     --display is kept as its own token rather than being collapsed into
     --sans, because "the face headings are set in" is still a decision worth
     having one name for, and thirty rules in this file ask for it. */
  --display: "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif;
  --sans: "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif;
}
@media (prefers-color-scheme: dark) {
  :root {
    /* Neutralised with the light theme, and for the same reason: the blue cast
       was doing the work of a decision nobody had made. The navy accents below
       are untouched — they are the brand, and on a neutral dark page they read
       more like it, not less. */
    --ground: #121317; --surface: #1B1C22; --surface-2: #23242B;
    --ink: #E9E9ED; --ink-2: #C2C3CB; --muted: #8B8D97;
    --line: #2C2D35; --line-soft: #26272E;
    --navy: #8FB4DE; --navy-soft: #1D2E44; --navy-line: #43618A; --on-accent: #0E1622;
    --gold: #D6AC55; --gold-ink: #D6AC55; --gold-soft: #322A17;
    --good: #6FBF95; --good-soft: #14301F;
    --warn: #E0A05C; --warn-soft: #33230F;
    --crit: #E38578; --crit-soft: #3A1D18;
    /* Black, and stronger. A shadow tinted with the ink colour is invisible on
       a near-black page, so dark mode gets real black and leans harder on the
       borders — which is why the borders are never dropped when a shadow is
       added. */
    --shadow-sm: 0 1px 3px rgba(0,0,0,.5), 0 4px 12px -3px rgba(0,0,0,.55);
    --shadow: 0 2px 4px rgba(0,0,0,.45), 0 12px 30px -14px rgba(0,0,0,.8);
    --shadow-lg: 0 3px 8px rgba(0,0,0,.55), 0 22px 44px -16px rgba(0,0,0,.9);
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--ground); color: var(--ink);
  font-family: var(--sans); font-size: 15px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/* ---- page width ----------------------------------------------------------
   The board grows in steps rather than filling the screen edge to edge. Text
   that runs the full width of a 32" monitor is genuinely harder to read, so
   the extra room goes into more cards per row instead of longer lines. Reading
   pages (a desk, a task, sign-in) stay narrow on purpose. */
.wrap { max-width: 1080px; margin: 0 auto; padding: 0 18px; padding-block: 20px 70px; }
.wrap.narrow { max-width: 680px; }
@media (min-width: 1400px) { .wrap:not(.narrow) { max-width: 1280px; } }
@media (min-width: 1680px) { .wrap:not(.narrow) { max-width: 1500px; } }
@media (min-width: 1980px) { .wrap:not(.narrow) { max-width: 1720px; } }

/* Tighter than it was. -.01em was tuned against Spectral, whose letterfit is
   already loose; a grotesque at the same tracking reads slack at heading
   sizes. This is the one adjustment the switch actually needs — everything
   else follows from --display. */
h1, h2, h3 { font-family: var(--display); font-weight: 600; margin: 0; letter-spacing: -.021em; }
h1 { font-size: 28px; line-height: 1.15; }
h2 { font-size: 19px; }
h3 { font-size: 16.5px; }
p { margin: 0; }
a { color: var(--navy); }

.eyebrow { display: block; font-size: 10.5px; font-weight: 600; letter-spacing: .14em;
           text-transform: uppercase; color: var(--gold-ink); }

/* ---- top bar ---- */
/* THE BAR STAYS PUT (22 Sept, Craig).
   ------------------------------------------------------------------------
   Sticky rather than fixed: it keeps its place in the page, so nothing has to
   be pushed down by hand and a page with no bar (a parent's form, the sign-in)
   is unaffected. The glass behind it and the soft edge under it are drawn by
   two pseudo-elements that bleed past the page's own width, because the bar
   itself is only as wide as the text column and the page scrolls past either
   side of it. Content slides under, dissolving into the ground as it goes.

   Once the page has moved, the bar draws itself in a little — see .scrolled
   below — which is what makes it feel like a bar rather than a heading. */
/* position and z-index are not decoration here. Naming the bar for the page
   cross-fade (view-transition-name, below) makes it its own stacking context,
   and without a z-index of its own that context sits UNDER the cards further
   down the page — which put the Sites menu behind them. */
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 14px;
          flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--line); margin-bottom: 24px;
          position: sticky; top: 0; z-index: 100;
          padding-top: 14px; margin-bottom: 20px;
          transition: padding .2s ease; }

/* The glass. It reaches beyond the page's column on both sides and up over the
   top so nothing shows above it. */
.topbar::before {
  content: ""; position: absolute; z-index: -1;
  top: -40px; bottom: 0; left: -50vw; right: -50vw;
  background: var(--ground);
}
/* SOLID, not frosted glass, and that is a decision rather than an omission.
   A see-through bar looked handsome on a still screenshot and unreadable in
   use: the names and numbers sliding underneath kept showing through the
   church's own logo. The softness lives in the edge below it instead, where
   the page dissolves as it goes under. */
/* And the soft edge under it: the page fades out as it slides beneath. */
.topbar::after {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  top: 100%; height: 28px; left: -50vw; right: -50vw;
  background: linear-gradient(to bottom, var(--ground), color-mix(in srgb, var(--ground) 40%, transparent), transparent);
}
/* Drawn in, once the page has moved. */
.scrolled .topbar { padding-top: 8px; padding-bottom: 10px; }
.scrolled .lockup.topbrand { height: 40px; }
.lockup.topbrand { transition: height .2s ease; }
/* A link to a part of a page must not land under the bar. */
html { scroll-padding-top: 110px; }
@media (max-width: 700px) { html { scroll-padding-top: 90px; } }
@media (prefers-reduced-motion: reduce) { .topbar, .lockup.topbrand { transition: none; } }
.brand strong { font-family: var(--display); font-size: 18px; font-weight: 600; display: block; }
.navlinks { display: flex; gap: 4px; flex-wrap: wrap; }
.navlinks a { font-size: 13.5px; font-weight: 600; text-decoration: none; color: var(--ink-2);
              padding: 5px 11px; border-radius: 999px; border: 1px solid transparent; }
.navlinks a:hover { border-color: var(--line); }
.navlinks a.on { background: var(--navy-soft); color: var(--navy); }
.navlinks a.quiet { color: var(--muted); font-weight: 500; }

/* ---- grouped nav (Sites, Calendar, People, the gear) ----
   NO JAVASCRIPT. The menus open on :hover and :focus-within, which means they
   also open for a keyboard, and when they cannot open — a touch screen has no
   hover — the parent is a real link to a real page and tapping it goes there.
   That is the whole reason every group has a landing page behind it.

   Written this way on purpose rather than with a click handler: the bar has to
   survive scripting being off, the same promise the hamburger keeps by staying
   hidden until app.js reveals it. */
.navgroup { position: relative; display: inline-flex; }
/* The phone expander, invisible on a laptop. Hover and focus-within do the
   work up here and always have; see nav_expander() for what these are. */
.navcheck, .navexp { display: none; }
.navgroup > .navtop { display: inline-flex; align-items: center; gap: 5px; }
.navgroup.on > .navtop { background: var(--navy-soft); color: var(--navy); }
.caret { width: 0; height: 0; margin-left: 1px;
         border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
         border-top: 4px solid currentColor; opacity: .55; }

.navmenu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
           min-width: 268px; padding: 6px; background: var(--surface);
           border: 1px solid var(--line); border-radius: var(--radius);
           box-shadow: 0 12px 28px rgba(20, 33, 58, .16);
           opacity: 0; visibility: hidden; transform: translateY(-4px);
           transition: opacity .12s ease, transform .12s ease, visibility .12s; }
.navmenu.right { left: auto; right: 0; }
.navgroup:hover > .navmenu,
.navgroup:focus-within > .navmenu { opacity: 1; visibility: visible; transform: none; }

/* A hover bridge. Without it the 6px gap between the parent and the menu is a
   strip of dead page: the pointer crosses it, :hover drops, and the menu shuts
   under the cursor on its way to the first item. */
.navgroup > .navmenu::before { content: ""; position: absolute; left: 0; right: 0;
                               top: -8px; height: 8px; }

.navlinks .navmenu a { display: block; padding: 8px 11px; border-radius: 8px;
                       border: 1px solid transparent; position: relative; }
.navlinks .navmenu a b { display: block; font-size: 13.5px; color: var(--ink); font-weight: 600; }
.navlinks .navmenu a span { display: block; font-size: 11.5px; color: var(--muted);
                            font-weight: 500; margin-top: 1px; }
.navlinks .navmenu a:hover { background: var(--surface-2); border-color: transparent; }
.navlinks .navmenu a.on { background: var(--navy-soft); }
.navlinks .navmenu a .badge { position: absolute; top: 9px; right: 10px; }

/* The two drawn icons. The word beside each is always in the markup — a
   screen reader needs it, and on a phone it is shown as well, because an icon
   on its own in a vertical list of words looks like something failed to load. */
.navlinks a.iconlink, .navgroup > .navtop.iconlink {
  display: inline-flex; align-items: center; justify-content: center; padding: 6px 10px; }
.navsym { display: block; flex: 0 0 auto; }
.navsymword { display: none; }

/* ---- page head ---- */
.pagehead { margin-bottom: 6px; }
.pagehead h1 { margin-top: 3px; }
.pagehead .sub { color: var(--muted); font-size: 13.5px; margin-top: 5px; }
.rule { height: 2px; width: 44px; background: var(--gold); border-radius: 2px; margin-top: 14px; }

/* ---- flash ---- */
.flash { padding: 10px 14px; border-radius: var(--radius); font-size: 14px; margin-bottom: 18px;
         border: 1px solid var(--good); background: var(--good-soft); color: var(--good); }
.flash.err { border-color: var(--crit); background: var(--crit-soft); color: var(--crit); }
.flash.info { border-color: var(--gold); background: var(--gold-soft); color: var(--gold); }

/* ---- stats ---- */
/* Four tiles with air between them rather than one block ruled into quarters.
   The hairline grid was correct and flat; separating them is what lets each
   number sit on its own card and lift off the page. */
.pulse { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 22px; }
.pulse.three { grid-template-columns: repeat(3, 1fr); }
.pulse > div { background: var(--surface); padding: 14px 16px; border: 1px solid var(--line);
               border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.pulse .n { font-family: var(--display); font-size: 24px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.pulse .k { font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); margin-top: 3px; }
.pulse .crit .n { color: var(--crit); }
.pulse .warn .n { color: var(--warn); }
.pulse .gold .n { color: var(--gold); }
@media (max-width: 560px) {
  .pulse { grid-template-columns: 1fr 1fr; }
  .pulse.three { grid-template-columns: repeat(3, 1fr); }
  .pulse.three .n { font-size: 21px; }
  .pulse.three .k { font-size: 9.5px; letter-spacing: .04em; }
}

/* ---- sections ---- */
.sec { margin-top: 34px; }

/* Runway and EventSites ride side by side once there's room for both to stay
   readable. Below the breakpoint .topgrid is an ordinary block and its two
   sections stack, which is also what happens when only one of them renders. */
@media (min-width: 1280px) {
  .topgrid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
             gap: 0 30px; align-items: start; }
}

/* Projects on the left, whatever needs a decision on the right. */
.deskcols { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
            gap: 28px; align-items: start; }
@media (max-width: 900px) { .deskcols { grid-template-columns: 1fr; gap: 30px; } }

/* Past a certain width a single stack of project cards is mostly empty space
   to the right of each task. Two across halves the scrolling instead.

   TWO REAL STACKS, and the two wrong ways to get there are both worth keeping
   written down, because each was shipped and each was sent back.
   ---------------------------------------------------------------------------
   A GRID lays out in ROWS: every card in a row occupies a row as tall as the
   tallest card in it. A short project beside a long one left a hole the height
   of the difference — Harvest in the Hills ended after three lines and nothing
   appeared beneath it until the Holiday Market card, twenty tasks later,
   finally ended the row. `align-items: start` does not fix it; the card stops
   stretching but the row still waits. `grid-template-rows: masonry` would, and
   is not shipped widely enough to rely on.

   MULTI-COLUMN packs with no holes, but it fills column one top to bottom
   before it starts column two, so the order stops being left-right-left-right
   and the two columns come out visibly lopsided — one card alone beside four.
   It also fragments: a card taller than the column is broken across the gutter
   whatever `break-inside` says, and each piece paints its own box-shadow, which
   is where the stray shadow edge above a card with nothing over it came from.

   So: two independent flex stacks, and board.php deals the cards into them
   alternately — odds left, evens right. Reading order is the grid's, packing is
   the column's, and nothing can fragment because nothing flows.

   Narrow screens must still read 1, 2, 3, 4 straight down, and the DOM is no
   longer in that order. `display: contents` dissolves the two wrappers so every
   card becomes a direct flex item of .projlist again, and the `order` board.php
   writes on each card puts them back in sequence. */
.projlist { display: flex; flex-direction: column; gap: 13px; }
.projcol  { display: contents; }

/* Inside .projlist the gap does the spacing. The sibling margin would be added
   on top of it — and on a narrow screen it would not even land on the right
   cards, since under display: contents the DOM siblings are 1-3-5, not 1-2-3. */
.projlist .card + .card { margin-top: 0; }

@media (min-width: 1680px) {
  .projlist { flex-direction: row; align-items: flex-start; }
  .projcol  { display: flex; flex-direction: column; gap: 13px;
              flex: 1 1 0; min-width: 0; }
}
.sechead { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap;
           border-bottom: 1px solid var(--line); padding-bottom: 8px; margin-bottom: 16px; }
.sechead .hint { font-size: 12.5px; color: var(--muted); }

/* ---- cards & task rows ---- */
/* A CARD IS LIFTED OFF THE PAGE, NOT DRAWN ON IT.
   The border is gone and a soft shadow does the separating instead. That is
   the single change that carries most of the look Craig pointed at — a
   1px grey outline round every card is what makes an interface read as a
   form, and its absence is what makes the same layout read as a surface.
   The shadow is still one of the three tokens, so "flatter" and "deeper" are
   still one line each.
   DARK MODE KEEPS ITS BORDER. A shadow on a near-black page is invisible, so
   there the border IS the edge — see the note on the dark shadows above. */
.card { background: var(--surface); border: 1px solid transparent;
        border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); }
@media (prefers-color-scheme: dark) { .card { border-color: var(--line); } }
.card + .card { margin-top: 13px; }
.card > header { padding: 12px 16px 10px; border-bottom: 1px solid var(--line-soft);
                 display: flex; gap: 4px 12px; align-items: baseline; justify-content: space-between; flex-wrap: wrap; }
.card > header .lead { font-size: 12.5px; color: var(--muted); }
/* .pad is used two ways: as a modifier on the card itself
   (class="card pad") and on a wrapper inside one. Both need to match — a
   descendant-only selector silently skips the first, which is how forms end
   up with their labels and buttons jammed against the border. */
.card.pad, .card .pad { padding: 16px 18px; }

/* The last field in a padded form sits right above the button; give the
   button a little room of its own rather than relying on that margin. */
.card.pad > .btn, .card .pad > .btn { margin-top: 3px; }

ul.tasks { list-style: none; margin: 0; padding: 0; }
li.taskrow { display: flex; gap: 11px; align-items: flex-start; padding: 11px 16px;
             border-bottom: 1px solid var(--line-soft); }
li.taskrow:last-child { border-bottom: 0; }
li.taskrow.done .t { text-decoration: line-through; color: var(--muted); }

.tickform { margin: 0; flex: none; }
.tick { width: 20px; height: 20px; margin-top: 1px; border-radius: 5px; cursor: pointer; padding: 0;
        border: 1.5px solid var(--line); background: var(--surface); position: relative; display: block; }
.tick:hover { border-color: var(--navy); }
.tick[data-on="1"] { background: var(--good); border-color: var(--good); }
.tick[data-on="1"]::after { content: ""; position: absolute; left: 5.5px; top: 2px; width: 5px; height: 10px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(42deg); }

.taskbody { flex: 1; min-width: 0; }
.t { font-size: 14.5px; line-height: 1.4; overflow-wrap: break-word; }
.meta { display: flex; flex-wrap: wrap; gap: 6px 9px; align-items: center; margin-top: 5px;
        font-size: 12px; color: var(--muted); }
.meta .who { color: var(--ink-2); font-weight: 600; }
.due { font-variant-numeric: tabular-nums; }
.due.over { color: var(--crit); font-weight: 600; }
.due.soon { color: var(--warn); font-weight: 600; }
.notes { font-size: 13px; color: var(--ink-2); margin-top: 6px; padding-left: 10px; border-left: 2px solid var(--line); }

.pill { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
        padding: 2px 7px; border-radius: 4px; background: var(--surface-2); color: var(--ink-2); }
.pill.doing { background: var(--navy-soft); color: var(--navy); }
.pill.blocked { background: var(--crit-soft); color: var(--crit); }
.pill.done { background: var(--good-soft); color: var(--good); }

.inlineform { display: inline; margin: 0; }
.mini { font: inherit; font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 5px; cursor: pointer;
        border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); text-decoration: none; display: inline-block; }
.mini:hover { border-color: var(--navy); color: var(--navy); }

/* ---- forms ---- */
input, select, textarea {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 7px; padding: 8px 10px; min-width: 0; max-width: 100%;
}
textarea { resize: vertical; min-height: 70px; width: 100%; }
label { display: block; font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
        color: var(--muted); margin-bottom: 4px; }
.field { margin-bottom: 13px; }
.field input, .field select, .field textarea { width: 100%; }
.row { display: flex; gap: 9px; flex-wrap: wrap; }
.row > * { flex: 1 1 150px; }

.btn { font: inherit; font-size: 13.5px; font-weight: 600; padding: 8px 16px; border-radius: 7px; cursor: pointer;
       border: 1px solid var(--navy); background: var(--navy); color: var(--on-accent); text-decoration: none; display: inline-block; }
.btn.ghost { background: var(--surface); color: var(--ink-2); border-color: var(--line); }
.btn.ghost:hover { border-color: var(--navy); color: var(--navy); }
.btn.danger { background: var(--surface); color: var(--crit); border-color: var(--crit); }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.addrow { display: flex; gap: 8px; padding: 10px 16px; background: var(--surface-2); flex-wrap: wrap; margin: 0; }
.addrow input, .addrow select { flex: 1 1 140px; font-size: 13.5px; padding: 6px 9px; }
.addrow .grow { flex: 3 1 200px; }

/* ---- team grid ---- */
.team { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 10px; }
.mate { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
        padding: 12px 14px; display: flex; flex-direction: column; gap: 7px; }
.mate.stale { border-left: 3px solid var(--gold); }
.mate .nm { font-family: var(--display); font-size: 15.5px; font-weight: 600; }
.mate .rl { font-size: 12px; color: var(--muted); margin-top: -4px; }
.mate .nums { display: flex; gap: 12px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mate .nums b { font-family: var(--display); font-size: 15px; font-weight: 700; }
.mate .nums .od b { color: var(--crit); }
.mate .warnline { font-size: 11.5px; color: var(--gold); font-weight: 600; }

/* ---- events ---- */
.runway { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 11px; }
.ev { background: var(--surface); border: 1px solid var(--line); border-top: 3px solid var(--navy);
      border-radius: var(--radius); padding: 13px 14px; display: flex; flex-direction: column; gap: 8px; }
.ev.soon { border-top-color: var(--warn); }
.ev.now { border-top-color: var(--crit); }
.ev .name { font-family: var(--display); font-size: 16px; font-weight: 600; line-height: 1.25; }
.ev .days { font-family: var(--display); font-size: 26px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.ev .days span { font-family: var(--sans); font-size: 11px; font-weight: 600; letter-spacing: .06em;
                 text-transform: uppercase; color: var(--muted); margin-left: 6px; }
.ev.soon .days { color: var(--warn); }
.ev.now .days { color: var(--crit); }
.ev .when, .ev .tally { font-size: 12px; color: var(--muted); }
.bar { height: 5px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.bar i { display: block; height: 100%; background: var(--good); }

/* ---- messages ---- */
.msg { padding: 13px 16px; border-bottom: 1px solid var(--line-soft); }
.msg:last-child { border-bottom: 0; }
.msg .from { font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--gold); font-weight: 700; margin-bottom: 4px; }
.msg .q { font-size: 14.5px; line-height: 1.42; }
.msg .ctx { font-size: 12.5px; color: var(--muted); margin-top: 4px; font-style: italic; }
.msg.answered { background: var(--good-soft); }
.msg .a { font-size: 13.5px; margin-top: 6px; padding-left: 10px; border-left: 2px solid var(--good); color: var(--ink-2); }
/* Who replied, tucked under the reply and aligned with it. */
.msg .byline { font-size: 12px; color: var(--muted); margin-top: 4px; padding-left: 12px; }
.msg .byline b { color: var(--ink-2); font-weight: 600; }
.msg form { margin-top: 9px; }
.msg .acts { display: flex; gap: 7px; margin-top: 8px; align-items: center; flex-wrap: wrap; }
/* Where the answer is about to go, next to the button that sends it. Wraps
   under the button on a phone rather than squeezing an address to three
   characters and an ellipsis. */
.msg .acts .hint { font-size: 12.5px; color: var(--muted); }

.empty { padding: 22px 16px; color: var(--muted); font-size: 13.5px; text-align: center; }
.note { font-size: 12.5px; color: var(--muted); margin-top: 10px; line-height: 1.5; }

/* ---- login ---- */
.login { max-width: 380px; margin: 8vh auto 0; }
.login h1 { margin-bottom: 6px; }
.login .card { padding: 22px; margin-top: 22px; }

/* ---- table ---- */
.scroller { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
table { border-collapse: collapse; width: 100%; min-width: 560px; font-size: 14px; }
th, td { text-align: left; padding: 10px 13px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
thead th { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted);
           background: var(--surface-2); border-bottom: 1px solid var(--line); }
tbody tr:last-child td { border-bottom: 0; }

code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .88em;
       background: var(--surface-2); padding: 2px 6px; border-radius: 4px; word-break: break-all; }

@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

/* ---- notes and the completion archive ---- */
details.noteadd { margin-top: 7px; }
details.noteadd > summary { font-size: 11.5px; font-weight: 600; color: var(--muted); cursor: pointer;
  list-style: none; display: inline-block; padding: 1px 8px; border: 1px solid var(--line);
  border-radius: 5px; background: var(--surface); }
details.noteadd > summary::-webkit-details-marker { display: none; }
details.noteadd > summary:hover { color: var(--navy); border-color: var(--navy); }
details.noteadd[open] > summary { color: var(--navy); border-color: var(--navy); }
details.noteadd form { margin-top: 7px; display: grid; gap: 6px; justify-items: start; }
details.noteadd textarea { font-size: 13.5px; min-height: 52px; }

details.archive { margin-top: 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; }
details.archive > summary { cursor: pointer; padding: 12px 16px; list-style: none;
  display: flex; align-items: center; gap: 9px; font-family: var(--display); font-size: 15.5px;
  font-weight: 600; color: var(--ink-2); }
details.archive > summary::-webkit-details-marker { display: none; }
details.archive > summary::before { content: "▸"; color: var(--muted); font-family: var(--sans); font-size: 12px; }
details.archive[open] > summary::before { content: "▾"; }
details.archive[open] > summary { border-bottom: 1px solid var(--line-soft); }
details.archive > summary .count { font-family: var(--sans); font-size: 11px; font-weight: 700;
  background: var(--good-soft); color: var(--good); padding: 2px 8px; border-radius: 999px; }
.archmonth { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted);
  font-weight: 600; padding: 11px 16px 5px; background: var(--surface-2); }
ul.archlist { list-style: none; margin: 0; padding: 0; }
ul.archlist li { padding: 8px 16px; border-bottom: 1px solid var(--line-soft); display: flex;
  flex-wrap: wrap; gap: 2px 10px; align-items: baseline; }
ul.archlist li:last-child { border-bottom: 0; }
ul.archlist .ttl { font-size: 13.5px; color: var(--muted); text-decoration: line-through; flex: 1 1 200px; }
ul.archlist .sub { font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* ---- events ---- */
.evbar { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 12px;
  margin-top: 22px; }
.evbar .evstat { background: var(--surface); padding: 14px 16px; border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.evbar .n { font-family: var(--display); font-size: 24px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.evbar .n.bad { color: var(--crit); }
.evbar .k { display: block; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); margin-top: 4px; }

.cd { display: inline-block; font-family: var(--display); font-size: 20px; font-weight: 700;
  line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; }
.cd.now { color: var(--crit); }
.cd.soon { color: var(--warn); }
.cd.past { color: var(--muted); }

.evcols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 30px; align-items: start; margin-top: 34px; }
@media (max-width: 900px) { .evcols { grid-template-columns: 1fr; gap: 34px; } }

details.wsblock { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  margin-bottom: 11px; overflow: hidden; }
details.wsblock > summary { cursor: pointer; padding: 12px 16px; list-style: none; display: flex;
  align-items: center; gap: 9px; font-family: var(--display); font-size: 16px; font-weight: 600; }
details.wsblock > summary::-webkit-details-marker { display: none; }
details.wsblock > summary::before { content: "▸"; color: var(--muted); font-family: var(--sans); font-size: 12px; }
details.wsblock[open] > summary::before { content: "▾"; }
details.wsblock[open] > summary { border-bottom: 1px solid var(--line-soft); }
details.wsblock .donecount { margin-left: auto; font-family: var(--sans); font-size: 11px; font-weight: 600;
  color: var(--muted); background: var(--surface-2); padding: 2px 8px; border-radius: 999px; font-variant-numeric: tabular-nums; }
.wsbody { padding: 15px 16px 4px; }

.savebar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px;
  position: sticky; bottom: 0; background: var(--ground); padding: 10px 0; }
.savebar .hint { font-size: 12.5px; color: var(--muted); }

.readouts { margin: 0; }
.readout { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 4px 16px;
  padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.readout:last-child { border-bottom: 0; }
@media (max-width: 560px) { .readout { grid-template-columns: 1fr; } }
.readout dt { font-size: 11px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
  font-weight: 600; padding-top: 3px; }
.readout dd { margin: 0; font-size: 14.5px; color: var(--ink); overflow-wrap: break-word; }
.readout .tbd { color: var(--muted); font-style: italic; }

.evgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; }
.evcard { background: var(--surface); border: 1px solid var(--line); border-top: 3px solid var(--navy);
  border-radius: var(--radius); padding: 14px 15px; display: flex; flex-direction: column; gap: 9px; }
.evcard.soon { border-top-color: var(--warn); }
.evcard.now { border-top-color: var(--crit); }
.evcard.past { border-top-color: var(--line); }
.evcard > header { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.evcard h3 { font-size: 16px; line-height: 1.25; }
.evcard h3 a { text-decoration: none; color: var(--ink); }
.evcard h3 a:hover { color: var(--navy); }
.evwhen { font-size: 12.5px; color: var(--muted); }
.evmeter { display: grid; gap: 5px; }
.mrow { display: grid; grid-template-columns: 64px minmax(0, 1fr) 34px; gap: 8px; align-items: center;
  font-size: 11px; color: var(--muted); }
.mrow b { font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-2); }
.mrow .bar i.alt { background: var(--gold); }
.evfacts { font-size: 12.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.evfacts .bad { color: var(--crit); font-weight: 600; }
.evfacts .muted { color: var(--muted); }
.evacts { display: flex; gap: 7px; flex-wrap: wrap; margin-top: auto; padding-top: 3px; }

/* ---- repeatable EventSite rows ---- */
table.rowgrid { min-width: 640px; font-size: 13.5px; }
table.rowgrid th { padding: 8px 10px; font-size: 10px; }
table.rowgrid td { padding: 5px 6px; vertical-align: top; }
table.rowgrid input, table.rowgrid textarea { width: 100%; font-size: 13px; padding: 5px 7px; border-radius: 5px; }
table.rowgrid textarea { min-height: 40px; }
table.rowgrid tbody tr:nth-child(odd) td { background: transparent; }
table.rowgrid .tbd { color: var(--muted); }
.wsbody .scroller { border-radius: 8px; }

/* the save bar must read as a bar, not as text floating over a table */
.savebar { border-top: 1px solid var(--line); box-shadow: 0 -6px 14px -10px rgba(20,33,58,.4);
  padding: 12px 0 14px; z-index: 5; }
table.rowgrid { min-width: 760px; }
.savebar.plain { position: static; box-shadow: none; }

/* ===========================================================================
   Phones and small tablets.
   Kept in one block at the end so it's obvious what changes on a small screen
   and nothing above has to be read to understand it. The layout above is
   already fluid — this tightens spacing, enlarges the things a thumb has to
   hit, and stops the few fixed-width grids from squeezing.
   ========================================================================= */
@media (max-width: 700px) {
  .wrap { padding: 0 14px; padding-block: 14px 56px; }

  h1 { font-size: 23px; }
  h2 { font-size: 17.5px; }
  .pagehead .sub { font-size: 13px; }

  /* Brand on its own line, then the nav as a full-width row of real buttons. */
  .topbar { padding-bottom: 11px; margin-bottom: 18px; gap: 10px; }
  .navlinks { width: 100%; gap: 6px; }
  .navlinks a { padding: 7px 13px; font-size: 14px; border-color: var(--line); }
  .navlinks a.quiet { margin-left: auto; border-color: transparent; }

  .sec { margin-top: 26px; }
  .sechead { margin-bottom: 13px; }

  /* One card per row beats two half-legible ones. */
  .runway, .evgrid, .team { grid-template-columns: 1fr; }

  /* Stacked add-forms: every control full width, button last and tappable. */
  .addrow { padding: 12px 14px; gap: 9px; }
  .addrow input, .addrow select, .addrow .grow { flex: 1 1 100%; font-size: 16px; padding: 9px 11px; }
  .addrow .btn, .addrow button { width: 100%; padding: 11px 16px; }

  /* 16px stops iOS zooming the page in when a field is focused. */
  input, select, textarea { font-size: 16px; }

  /* Row actions are 11px chips on a desktop; that's not a tap target. */
  .mini { font-size: 13px; padding: 6px 12px; border-radius: 7px; }
  .evacts { gap: 8px; }
  .taskrow .meta { gap: 7px 10px; }
  .tick { width: 24px; height: 24px; }
  .tick[data-on="1"]::after { left: 7px; top: 3.5px; width: 6px; height: 12px; }
  /* The checkbox stays 24px so the row doesn't balloon, but an invisible
     collar around it gives a thumb the ~44px it actually needs. */
  .tick::before { content: ""; position: absolute; inset: -10px; }

  .btn { padding: 10px 18px; }
  .card.pad, .card .pad { padding: 14px 15px; }
  li.taskrow { padding: 12px 14px; }

  /* The sticky save bar eats a phone screen; let it scroll with the form. */
  .savebar { position: static; box-shadow: none; }
  .savebar .btn { width: 100%; }
  .savebar .hint { width: 100%; text-align: center; }

  /* Wide data tables keep their own horizontal scroll rather than shrinking. */
  .scroller { -webkit-overflow-scrolling: touch; }
}

@media (max-width: 430px) {
  .pulse .n { font-size: 21px; }
  .pulse > div { padding: 10px 11px; }
  .evbar { grid-template-columns: 1fr 1fr; }
}

/* A date field has a fixed natural size; letting it stretch to fill a wrapped
   row makes the add-a-task forms look broken in narrow columns. */
.addrow input[type="date"] { flex: 0 1 170px; }
@media (max-width: 700px) { .addrow input[type="date"] { flex: 1 1 100%; } }

/* ---- the public request form ----------------------------------------------
   The honeypot. Off-screen rather than display:none, because some bots skip
   anything explicitly hidden. Real people never reach it: it is out of the
   flow, untabbable and marked aria-hidden. */
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

input[type="file"] { width: 100%; font-size: 14px; padding: 9px 10px; background: var(--surface-2);
                     border: 1px dashed var(--line); border-radius: 7px; cursor: pointer; }

/* ---- maintenance ---- */
/* align-items:start, or every card in a row stretches to match the tallest —
   one request with a photo leaves its neighbours with a foot of white space. */
.reqgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
           gap: 12px; align-items: start; }
.req { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
       padding: 14px 16px; border-left: 3px solid var(--line); }
.req.urgent { border-left-color: var(--crit); }
.req.soon   { border-left-color: var(--warn); }
.req.fresh  { box-shadow: var(--shadow); }
.req h3 { font-size: 15.5px; line-height: 1.35; }
.req .ref { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11.5px;
            letter-spacing: .04em; color: var(--muted); }
.req .who { font-size: 13px; color: var(--ink-2); margin-top: 7px; }
.req .who b { font-weight: 600; }
.req .said { font-size: 13.5px; color: var(--ink-2); margin-top: 9px; padding-left: 10px;
             border-left: 2px solid var(--line-soft); white-space: pre-wrap; }
.req .reqacts { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 12px; align-items: center; }
.req .thumb { display: block; margin-top: 10px; border-radius: 8px; border: 1px solid var(--line);
              max-height: 190px; width: auto; }
.assignbox { margin-top: 11px; padding-top: 11px; border-top: 1px solid var(--line-soft); }
.assignbox .people { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-bottom: 9px; }
.assignbox label.pick { display: inline-flex; align-items: center; gap: 5px; font-size: 13px;
                        text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--ink-2);
                        margin: 0; cursor: pointer; }
.assignbox label.pick input { width: auto; margin: 0; }
/* Navy for a count, red only for something late. A menu where unread
   messages, rooms and new requests all glow red teaches people that red means
   "there is a number here" — and then the overdue badge means nothing. */
.badge { display: inline-block; min-width: 17px; padding: 0 5px; border-radius: 999px;
         background: var(--navy); color: var(--on-accent); font-size: 10.5px; font-weight: 700;
         /* normal, because in the menus a badge is an <i> — and a slanted digit
            in a round bubble reads as off-centre. */
         font-style: normal; line-height: 17px; text-align: center; margin-left: 5px; }
.badge.late { background: var(--crit); color: #fff; }
@media (prefers-color-scheme: dark) { .badge.late { color: #1A0C0A; } }

/* ---- the clock ------------------------------------------------------------
   Tabular figures so the seconds tick without the line jittering sideways —
   a clock that shoves the text beside it back and forth every second is worse
   than no clock. */
.clock { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ===========================================================================
   The mark
   ========================================================================= */
.mark { display: block; color: var(--navy); flex: none; }

/* The lockup in the top bar. It is the whole logo now — mark and wordmark as
   drawn — so there is no stacking to reproduce here. */
.brand { display: flex; align-items: center; gap: 16px; color: var(--navy); }
.lockup.topbrand { display: block; height: 54px; width: auto; }

/* Logo, a breath, a rule, then the name of the thing you are actually in. */
.brandrule { width: 1px; align-self: stretch; margin: 6px 0; background: var(--line); flex: none; }
.brand .product {
  font-family: var(--sans); font-size: 13px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--gold-ink); white-space: nowrap;
}

@media (max-width: 700px) {
  .brand { gap: 12px; }
  .lockup.topbrand { height: 42px; }
  .brand .product { font-size: 11.5px; letter-spacing: .14em; }
}

/* Visible to a screen reader, to nobody else. The logo is a picture; the name
   still needs to be readable by something that cannot see it. */
.sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
      overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ---- the clock panel ------------------------------------------------------
   No dome here. The mark's weight is all along its flat bottom edge, so beside
   a block of type it reads as sinking however it is aligned. The day gets a
   straight bar instead, which sits level with text and says the same thing. */
.clockpanel {
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 15px 20px; margin-top: 22px;
}
.cp-read { min-width: 0; }
.cp-time {
  font-family: var(--display); font-weight: 600; font-size: 38px; line-height: 1;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--ink);
  display: flex; align-items: baseline; gap: 2px;
}
.cp-sec { font-size: 20px; color: var(--muted); font-weight: 400; }
.cp-sec::before { content: ":"; }
.cp-ampm { font-family: var(--sans); font-size: 13px; font-weight: 600; letter-spacing: .08em;
           text-transform: uppercase; color: var(--muted); margin-left: 7px; }
.cp-date { font-size: 14px; color: var(--ink-2); margin-top: 7px; }
.cp-tz { font-size: 10.5px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
         color: var(--gold-ink); margin-top: 3px; }

.cp-day { flex: 1 1 220px; max-width: 380px; margin-left: auto; }
.cp-daylabel { font-size: 12.5px; color: var(--muted); margin-bottom: 7px; }
.cp-track { height: 6px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.cp-track i { display: block; height: 100%; border-radius: 999px;
              background: linear-gradient(90deg, var(--navy), var(--gold));
              transition: width 800ms cubic-bezier(.4,0,.2,1); }
.cp-ends { display: flex; justify-content: space-between; margin-top: 5px;
           font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }

@media (max-width: 620px) {
  .clockpanel { gap: 14px; padding: 13px 15px; }
  .cp-time { font-size: 31px; }
  .cp-sec { font-size: 17px; }
  .cp-day { flex-basis: 100%; max-width: none; margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) { .cp-track i { transition: none; } }

/* The mark again, very quietly, behind the sign-in card. */
.lockup { display: block; color: var(--navy); height: auto; max-width: 100%; }
.lockup.signin { margin: 0 auto 14px; }
.lockup.pub    { margin: 0 0 12px; }
/* The light logo. In dark mode the navy that draws the lockup turns into a
   pale blue, which read as a faded logo rather than a light one — so the
   lockup is drawn in the page's own ink, near-white, and the dome keeps a
   touch of the church's blue. */
@media (prefers-color-scheme: dark) {
  .brand, .lockup { color: var(--ink); }
  .lockup .arc path, .lockup > g:first-of-type path { color: var(--navy); fill: var(--navy); }
}

/* The lockup already says the church's name, so the eyebrow above the page
   title would only say it twice. */
.login .lockup + h1 { margin-top: 4px; }


/* ---- the mark, waking up ---------------------------------------------------
   Only on the sign-in and set-up pages, where nobody is trying to work. Each
   segment carries its own delay, so the light crosses the dome in the order
   the sun would — up from the left, over the top, down to the right — then
   recedes the same way and goes round again.

   It is a loop, not a clock: nothing here is claiming to tell you the time. */
@keyframes markSweep {
  0%,  2%   { opacity: .16; }
  9%,  64%  { opacity: 1; }
  76%, 100% { opacity: .16; }
}
.lockup.live .arc path {
  opacity: .16;
  animation: markSweep 7s ease-in-out infinite;
}
/* The wordmark stays put. Only the dome moves. */
.lockup.live > path,
.lockup.live > g:not(.arc) path { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .lockup.live .arc path { animation: none; opacity: 1; }
}

/* A message in the collapsed list is a record, not something to act on. */
.msg.archived { background: transparent; padding: 11px 16px; }
.msg.archived .q { color: var(--ink-2); }
.msg.archived .a { border-left-color: var(--line); }

/* ---- church calendar ------------------------------------------------------
   The strip on the board and on every desk: the next few things that are not
   the weekly services. Cards rather than a list because it is read at a glance
   on the way past, not studied. */
.calstrip { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 11px; }
.calcard { display: flex; gap: 13px; align-items: flex-start;
           background: var(--surface); border: 1px solid var(--line);
           border-radius: var(--radius); padding: 13px 15px; }
/* The same countdown language the Runway uses, so "soon" means one thing
   across the whole board. */
.calcard.soon { border-color: var(--gold); }
.calcard.now  { border-color: var(--crit); }
.calcard .cdate { flex: 0 0 auto; text-align: center; min-width: 38px;
                  padding-top: 1px; border-right: 1px solid var(--line-soft); padding-right: 12px; }
.calcard .cd { display: block; font-family: var(--display); font-size: 22px; font-weight: 600;
               line-height: 1; color: var(--ink); }
.calcard .cm { display: block; margin-top: 3px; font-size: 10.5px; font-weight: 700;
               letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.calcard.now .cd  { color: var(--crit); }
.calcard.soon .cd { color: var(--gold-ink); }
.calcard .cbody { min-width: 0; }
.calcard .cname { font-weight: 600; line-height: 1.35; color: var(--ink); }
.calcard .cwhen { font-size: 12.5px; color: var(--ink-2); margin-top: 3px; }
.calcard .cwhere { font-size: 12px; color: var(--muted); margin-top: 2px;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The calendar page itself: one card per day. */
.calday > header h3 { font-size: 16px; }
.calrow .taskbody { padding: 11px 16px; }
.calrow.routine .t { color: var(--ink-2); font-weight: 500; }
.calrow .meta .due { color: var(--ink-2); font-weight: 500; }
@media (max-width: 560px) {
  .calcard { padding: 11px 13px; gap: 11px; }
  .calcard .cdate { padding-right: 10px; min-width: 34px; }
}

/* The admin strip on the calendar page: state on the left, the two buttons on
   the right. Stays put once configured — "is this still syncing" is a question
   that arrives months later, not during setup. */
.calsync { display: flex; align-items: center; justify-content: space-between;
           gap: 14px; flex-wrap: wrap; margin-top: 18px; }
.calsync-state { font-size: 14px; color: var(--ink); }
.calsync-state .muted { color: var(--muted); font-weight: 400; }
.calsync-state .warnline { display: block; margin-top: 3px; font-size: 12.5px; color: var(--warn); }
.calsync .reqacts { margin: 0; }
/* Whether the scheduled job is running is a separate fact from how fresh the
   calendar is, and it is the one somebody is actually trying to find out — so
   it gets its own line rather than being tacked onto the end of the first. */
.calsync-state .runline { display: block; margin-top: 7px; padding-top: 7px;
                          border-top: 1px solid var(--line-soft);
                          font-size: 12.5px; color: var(--ink-2); line-height: 1.5; }
.calsync-state .runline b.bad { color: var(--crit); }
.calsync-state .runline .good { color: var(--good); font-weight: 600; }
/* A line meant to be copied into cPanel: it must survive being long without
   pushing the card sideways, and must not be re-wrapped into something that
   does not work when pasted. */
/* ---- scheduled jobs -------------------------------------------------------
   One card per job, with the state carried by a rule down the left rather than
   a fill: the page is read when something is wrong, and four red boxes are
   harder to scan than four cards with one red edge. */
.joblist { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 14px; }
.jobcard { border-left: 3px solid var(--line); }
.jobcard.good { border-left-color: var(--good); }
.jobcard.warn { border-left-color: var(--warn); }
.jobcard.bad  { border-left-color: var(--crit); }
.jobcard > header { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.jobcard .jobwhen { font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
                    text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.jobcard .jobstate { margin: 0 0 8px; font-size: 14px; line-height: 1.5; color: var(--ink); }
.jobcard.good .jobstate { color: var(--good); font-weight: 600; }
.jobcard.warn .jobstate { color: var(--warn); font-weight: 600; }
.jobcard.bad  .jobstate { color: var(--crit); font-weight: 600; }
.jobcard .jobphp { font-size: 11.5px; color: var(--muted); margin-top: 6px; }
.jobcard .jobfix { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft); }

.cronline { display: block; padding: 9px 11px; background: var(--surface-2);
            border: 1px solid var(--line); border-radius: var(--radius);
            font-size: 12px; line-height: 1.5; overflow-x: auto; white-space: pre;
            -webkit-overflow-scrolling: touch; }

/* ---- calendar: month grid beside the list --------------------------------
   The grid is the navigator and the list is the detail. Below 1024px the grid
   goes away entirely rather than shrinking: a month of seven columns on a
   phone gives each day about forty pixels, which is enough for a number and
   nothing else — and the list is what people are reading on a phone anyway. */
/* One column below the breakpoint, two above it. A single-track grid rather
   than display:block, so the two halves can be ORDERED: on a phone the day
   list is what people came for and the meetings follow it, while on a wide
   screen the meetings sit under the grid in the left column, which is where
   Craig wanted them. */
.calcols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; }
.calcols > .calleft { order: 2; }
.calcols > .callist { order: 1; }
/* The GRID disappears on a narrow screen — a month of forty-pixel cells is
   unreadable. The column it sits in does not, because the meetings are in
   there too. */
.calgrid-wrap { display: none; }
/* 1100px, not 1024: .wrap is 1080px wide at that point, and two columns need
   the container to be wide — a media query asks the viewport, which is a
   different question. Splitting a 680px container into two tracks is what put
   the list on top of the grid. */
@media (min-width: 1100px) {
  .calcols { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); align-items: start; }
  .calcols > .calleft { order: 0; }
  .calcols > .callist { order: 0; }
  .calgrid-wrap { display: block; }
}
/* Grid children default to min-width:auto, so anything with a wide minimum —
   a table, a long unbroken title — pushes out of its track instead of
   shrinking. Both of these have to be told they may be narrower than their
   contents want. */
.calcols > .calleft, .calcols > .callist { min-width: 0; }
/* The meetings section under the grid is already inside a column, so it does
   not need the standing-on-its-own top margin a .sec carries. */
.calleft > .sec { margin-top: 26px; }
.calleft > .sec:first-child { margin-top: 0; }
.calgrid { max-width: 100%; }

.calnav { display: flex; align-items: center; justify-content: space-between; gap: 12px 18px;
          flex-wrap: wrap; border-bottom: 1px solid var(--line); padding-bottom: 10px; margin-bottom: 18px; }
.calnav-month { display: flex; align-items: center; gap: 10px; }
.calnav-month h2 { min-width: 0; }
.calnav-tools { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.calnav-tools select { font-size: 13px; padding: 5px 9px; }
.calnav-tools .inlineform { margin: 0; }

.calgrid { width: 100%; border-collapse: separate; border-spacing: 0;
           background: var(--surface); border: 1px solid var(--line);
           border-radius: var(--radius); overflow: hidden; table-layout: fixed; }
.calgrid th { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
              color: var(--muted); padding: 9px 0; background: var(--surface-2);
              border-bottom: 1px solid var(--line); }
.calgrid td { vertical-align: top; height: 96px; padding: 0;
              border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); }
.calgrid tr td:last-child { border-right: 0; }
.calgrid tbody tr:last-child td { border-bottom: 0; }
.calgrid td > a, .calgrid td > span.dn { display: block; height: 100%; padding: 6px 7px;
                                          text-decoration: none; color: inherit; }
.calgrid td > a:hover { background: var(--surface-2); }
.calgrid .dn { display: block; font-size: 12.5px; font-weight: 600; color: var(--ink-2); margin-bottom: 4px; }
.calgrid td.out { background: var(--ground); }
.calgrid td.out .dn { color: var(--muted); font-weight: 400; }
/* Today is a fact about the calendar; the selected day is a fact about what
   you are looking at. They need to be distinguishable when they are the same
   cell, so one is a ring and the other a fill. */
.calgrid td.today .dn { color: var(--crit); font-weight: 700; }
.calgrid td.sel { background: var(--navy-soft); box-shadow: inset 0 0 0 2px var(--navy); }
.calgrid td.sel .dn { color: var(--navy); }
.calgrid .chip { display: block; font-size: 11px; line-height: 1.3; margin-bottom: 2px;
                 padding: 2px 5px; border-radius: 4px; background: var(--gold-soft);
                 color: var(--gold-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calgrid .chip.routine { background: var(--surface-2); color: var(--muted); }
/* Somebody being away is not a booking, and the chip must never be mistaken
   for one — so it is outlined rather than filled, which reads as different at
   a glance and at chip size, where a colour difference alone would not. The
   colour is the one the desk strips already use for absence, so the two pages
   are speaking the same language. The outline also survives the selected
   cell's fill, which a soft background would disappear into. */
.calgrid .chip.away { background: transparent; color: var(--warn);
                      border: 1px dashed var(--warn); padding: 1px 4px; font-weight: 600; }
.calgrid .more { display: block; font-size: 10.5px; color: var(--muted); padding-left: 5px; }

.callist .sechead { margin-bottom: 14px; }

/* Who is away, at the top of a day. Above the events rather than among them:
   it answers a different question, and a row of it in the task list would read
   as a thing happening in the building. */
.calaway { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px;
           padding: 9px 16px; background: var(--warn-soft);
           border-bottom: 1px solid var(--line-soft); }
.calaway .awk { font-size: 10px; font-weight: 700; letter-spacing: .09em;
                text-transform: uppercase; color: var(--warn); }
.calaway .awp { font-size: 12.5px; color: var(--ink-2); }
.calaway .awp b { color: var(--ink); font-weight: 600; }
.calaway .awp b::after { content: " · "; color: var(--muted); font-weight: 400; }

/* ---- event budget --------------------------------------------------------
   Three figures across the top: what was approved, what is spoken for, what is
   left. Remaining is the one people look for, so it carries the colour. */
.budgetsum { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px;
             background: var(--line-soft); border: 1px solid var(--line);
             border-radius: var(--radius); overflow: hidden; margin: 0 0 14px; }
.budgetsum > div { background: var(--surface); padding: 11px 14px; }
.budgetsum .bk { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .09em;
                 text-transform: uppercase; color: var(--muted); }
.budgetsum .bv { display: block; margin-top: 4px; font-family: var(--display);
                 font-size: 21px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
.budgetsum .good .bv { color: var(--good); }
.budgetsum .bad .bv  { color: var(--crit); }
.budgetsum .bad .bk  { color: var(--crit); }
@media (max-width: 560px) { .budgetsum { grid-template-columns: 1fr; } }

input.money { text-align: right; font-variant-numeric: tabular-nums; }
.budgetgrid td.bt, .budgetgrid th.bt { text-align: right; font-variant-numeric: tabular-nums;
                                       white-space: nowrap; color: var(--muted); font-size: 13px; }
/* A line with a real cost against it is done being guessed at. */
.budgetgrid tr.settled td.bt { color: var(--ink); font-weight: 600; }

/* The way on to the church calendar is a link out to Planning Center, not a
   form here — so it is stated plainly rather than tucked in with the nav. */
.calrequest { display: flex; align-items: center; justify-content: space-between; gap: 14px;
              flex-wrap: wrap; margin-top: 18px; padding: 14px 16px;
              background: var(--gold-soft); border: 1px solid var(--gold);
              border-radius: var(--radius); }
.calrequest b { display: block; font-size: 14.5px; color: var(--ink); }
.calrequest span { display: block; margin-top: 3px; font-size: 13px; color: var(--ink-2); max-width: 62ch; }

/* ---- the debrief ---------------------------------------------------------
   Reusing .budgetsum's figure styling for the comparison band: expected
   against actual, approved against spent. Same visual grammar, because they
   are the same kind of fact — a number with a number to answer to. */
.dbnums { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px;
          background: var(--line-soft); border: 1px solid var(--line);
          border-radius: var(--radius); overflow: hidden; margin: 0 0 14px; }
.dbnums > div { background: var(--surface); padding: 11px 14px; }
.dbnums .bk { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .09em;
              text-transform: uppercase; color: var(--muted); }
.dbnums .bv { display: block; margin-top: 4px; font-family: var(--display);
              font-size: 21px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
.dbnums .bd { display: block; margin-top: 2px; font-size: 12px; color: var(--muted);
              font-variant-numeric: tabular-nums; }
.dbnums .good .bv { color: var(--good); }
.dbnums .bad .bv  { color: var(--crit); }
.dbnums .bad .bk  { color: var(--crit); }

/* Two columns on a desktop so six questions read as one short page rather
   than a long scroll — the length of a form is most of what decides whether
   anyone finishes it. */
.dbgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 0 18px; }
.dbnudge { max-width: 78ch; margin: 0 0 14px; }

/* Last year's notes are a record, not an input, so they sit on the gold the
   rest of the app uses for "this came from somewhere else". */
.prior { background: var(--gold-soft); border-color: var(--gold); }
.prior .pnums { margin: 0 0 12px; font-family: var(--display); font-size: 15px;
                font-variant-numeric: tabular-nums; color: var(--ink); }
.prior .readouts { margin: 0; }

/* ---- Team One roles ------------------------------------------------------
   The colour on a role card is the vacancy, not the department. Somebody
   scanning this page is looking for holes; departments are already the
   headings, so spending the card's one strong signal on them again would
   waste it. */
.rolegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.rolecard { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--good);
  border-radius: var(--radius); padding: 13px 15px; display: flex; flex-direction: column; gap: 7px; }
.rolecard.short { border-left-color: var(--warn); }
.rolecard.open  { border-left-color: var(--crit); }
.rolecard > header { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.rolecard h3 { font-size: 15.5px; line-height: 1.25; }
.rolecard h3 a { text-decoration: none; color: var(--ink); }
.rolecard h3 a:hover { color: var(--navy); }
.leadtag { display: inline-block; margin-left: 6px; font-family: var(--sans); font-size: 9.5px;
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase; vertical-align: middle;
  background: var(--gold-soft); color: var(--gold-ink); padding: 2px 6px; border-radius: 999px; }

.rstat { font-size: 11px; font-weight: 700; letter-spacing: .04em; white-space: nowrap;
  color: var(--good); font-variant-numeric: tabular-nums; }
.rstat.short { color: var(--warn); }
.rstat.open  { color: var(--crit); }

.rwho { font-size: 14px; color: var(--ink); }
.rwho.empty { color: var(--muted); font-style: italic; }
.rfacts { font-size: 12.5px; color: var(--muted); }
.rfacts:empty { display: none; }
.rwarn { font-size: 12px; color: var(--warn); margin-top: auto; }

/* "My role", at the top of a desk.

   GREY, NOT NAVY. It was a navy tint with a navy rule and navy labels, and on
   the neutral page that arrived with the new palette it was the one loud thing
   above the fold — a nameplate shouting. Craig asked for the modern grey and
   he is right: it is the raised grey surface this file already has for exactly
   this, so it separates from the page by being a different plane rather than a
   different colour, and it needs no dark-mode rule of its own because both
   sides of --surface-2 are already chosen.

   The navy has not gone anywhere: the name, the reporting line and the pill on
   hover are all links, and links are navy. What stopped being navy is the
   furniture. */
.myrole { background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 16px; margin: 0 0 20px; }
.myrole .eyebrow { color: var(--muted); margin-bottom: 3px; }
.myrole .rl + .rl { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--line); }
/* THE ROW. Name and facts on the left, the way through on the right, and it
   folds the pill underneath when there is no room for both — that is the whole
   reason this is flex-wrap rather than a float or a grid. The left side's
   flex-basis is what decides where it folds: 17rem is roughly the narrowest a
   role name and its department read comfortably, so anything tighter than
   about a 390px phone puts the pill on its own line. space-between rather than
   margin-left:auto so that when it does fold it folds to the LEFT edge, under
   the name, instead of hanging off the right on a line of its own. */
.myrole .rl { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 8px 16px; }
.myrole .rl-t { flex: 1 1 17rem; min-width: 0; }
.myrole h2 { font-size: 18px; line-height: 1.25; margin: 0; }
.myrole h2 a { text-decoration: none; color: var(--ink); }
.myrole h2 a:hover { color: var(--navy); }
/* Department and reporting line share one line and wrap as a pair. Both are
   --muted now; they were the other half of the navy. */
.myrole .rl-m { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 1px 12px; margin-top: 2px; }
.myrole .dept { font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.myrole .upto { font-size: 12.5px; color: var(--muted); }
/* Underlined on hover only. It is the third link in a card eighty pixels tall;
   a permanent rule under it competed with the name for the eye. */
.myrole .upto a { text-decoration: none; }
.myrole .upto a:hover { text-decoration: underline; }
.myrole p { margin: 7px 0 0; font-size: 14px; line-height: 1.6; color: var(--ink-2); max-width: 74ch; }
/* THE PILL. Translucent over the grey rather than another opaque chip on top
   of it: the white is at 60%, so what you see is the card lightened, not a
   second surface. backdrop-filter is the polish and not the effect — where it
   is unsupported the alpha alone still reads correctly, which is why the
   background is rgba and not a blur-dependent transparent.
   Borders and hover in ink alpha rather than --line, because a fixed grey line
   on a translucent chip goes muddy; alpha over whatever is behind stays clean
   in both themes. */
.myrole .rolego { margin: 0; flex: 0 0 auto; display: inline-block;
  font-size: 12px; font-weight: 600; padding: 7px 15px; border-radius: 999px;
  color: var(--ink-2); background: rgba(255, 255, 255, .6);
  border: 1px solid rgba(22, 23, 29, .09); text-decoration: none;
  -webkit-backdrop-filter: blur(6px) saturate(1.3);
          backdrop-filter: blur(6px) saturate(1.3);
  transition: background .14s ease, border-color .14s ease, color .14s ease; }
.myrole .rolego:hover { background: rgba(255, 255, 255, .95);
  border-color: rgba(22, 23, 29, .18); color: var(--ink); }
@media (prefers-color-scheme: dark) {
  /* White at 6% is what "translucent" means on a near-black card; the light
     theme's 60% would be a white chip. */
  .myrole .rolego { background: rgba(255, 255, 255, .06);
    border-color: rgba(255, 255, 255, .13); }
  .myrole .rolego:hover { background: rgba(255, 255, 255, .12);
    border-color: rgba(255, 255, 255, .24); color: var(--ink); }
}
/* A pill is a tap target on a phone, and 12px with 7px of padding is not one.
   This is the same bump .mini gets at this width; it needs restating because
   .myrole .rolego outranks the plain .mini rule that does it. */
@media (max-width: 700px) {
  .myrole .rolego { font-size: 13px; padding: 9px 16px; }
}
@media (prefers-reduced-motion: reduce) { .myrole .rolego { transition: none; } }

/* The Team One role under somebody's typed title on the People page. */
.roleref { font-size: 12.5px; }
.roleref a { text-decoration: none; }
.roleref a:hover { text-decoration: underline; }

.rolecols { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 30px;
  align-items: start; margin-top: 26px; }
@media (max-width: 960px) { .rolecols { grid-template-columns: 1fr; gap: 34px; } }

/* One section of the description, set to be read rather than filled in. */
.roledoc { margin-bottom: 11px; }
.roledoc h3 { font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 7px; }
.roledoc .rbody { font-size: 15px; line-height: 1.6; color: var(--ink); overflow-wrap: break-word; }

/* The huddle box, sitting where it sits on the printed page. */
.huddle { background: var(--gold-soft); border: 1px solid var(--gold); border-radius: var(--radius);
  padding: 13px 16px; margin-bottom: 11px; }
.huddle b { display: block; font-family: var(--display); font-size: 14.5px; color: var(--ink); }
.huddle p { margin-top: 4px; font-size: 14px; line-height: 1.55; color: var(--ink-2); }
.huddle .arr { font-weight: 600; color: var(--ink); }

.holders li { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.chain { font-size: 14.5px; line-height: 1.9; }
.chain .arr { color: var(--muted); margin: 0 3px; }
.chain a { text-decoration: none; }
.chain a:hover { text-decoration: underline; }

/* Team One gaps on the board. Cards, not a list, because a gap should read as
   a thing to pick up rather than a line to scroll past. */
.teamstrip { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; }
.gapcard { display: flex; flex-direction: column; gap: 3px; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--warn);
  border-radius: var(--radius); padding: 11px 13px; }
.gapcard.open { border-left-color: var(--crit); }
.gapcard:hover { border-color: var(--navy); }
.gapcard .gname { font-family: var(--display); font-size: 15px; font-weight: 600; color: var(--ink); line-height: 1.25; }
.gapcard .gwhat { font-size: 12px; font-weight: 600; color: var(--warn); font-variant-numeric: tabular-nums; }
.gapcard.open .gwhat { color: var(--crit); }
.gapcard .gdept { font-size: 11.5px; color: var(--muted); }

/* ---- who's out -----------------------------------------------------------
   Today reads differently from next week, because "is she in right now" is the
   question people actually walk down the corridor to ask. */
.offstrip { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
.offcard { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: var(--radius); padding: 11px 13px; display: flex; flex-direction: column; gap: 2px; }
.offcard.now { border-left-color: var(--warn); background: var(--warn-soft); }
.offcard .oname { font-family: var(--display); font-size: 15px; font-weight: 600; color: var(--ink); }
.offcard .owhen { font-size: 12.5px; font-weight: 600; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.offcard .okind { font-size: 11.5px; color: var(--muted); }

.offrow { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.offacts { display: flex; gap: 6px; margin-left: auto; }
.mini.go { border-color: var(--good); color: var(--good); }

/* ---- the run sheet -------------------------------------------------------
   Built for two situations that turn out to want the same thing: a phone held
   in one hand in a car park, and a sheet of paper on a clipboard. Both want
   big type, short lines, and nothing that is not needed in the next ten
   minutes. */
.runsheet { max-width: 900px; margin: 0 auto; }
.rshead h1 { font-size: 30px; line-height: 1.15; }
.rswhen { font-size: 16px; color: var(--ink-2); margin-top: 5px; }
.rsback { font-size: 13px; margin-top: 8px; }

.rsfacts { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1px;
  background: var(--line-soft); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; margin: 18px 0 26px; }
.rsfacts > div { background: var(--surface); padding: 10px 13px; }
.rsfacts .k { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); }
.rsfacts .v { display: block; margin-top: 3px; font-family: var(--display); font-size: 18px;
  font-weight: 600; color: var(--ink); }

.rsblock { margin-bottom: 30px; }
.rsblock > h2 { font-family: var(--display); font-size: 13px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); padding-bottom: 6px; margin-bottom: 12px;
  border-bottom: 2px solid var(--navy); }
.rsday { margin-bottom: 18px; }
.rsday h3 { font-family: var(--display); font-size: 19px; margin-bottom: 7px; color: var(--ink); }

.rstable { width: 100%; border-collapse: collapse; }
.rstable th, .rstable td { text-align: left; vertical-align: top; padding: 9px 10px 9px 0;
  border-bottom: 1px solid var(--line-soft); }
.rstable th { width: 8.5em; font-weight: 700; font-size: 15.5px; color: var(--ink);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.rstable td { font-size: 15.5px; color: var(--ink); }
.rstable .what { display: block; }
.rstable .who { display: block; margin-top: 2px; font-size: 13.5px; color: var(--muted); }
.rstable.calls td.who { font-size: 15.5px; color: var(--ink); width: 40%; }
.rstable.calls td.tel { white-space: nowrap; font-variant-numeric: tabular-nums; }
.rstable.calls td.tel a { font-weight: 600; }

.rsnote { margin-bottom: 14px; }
.rsnote h3 { font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 3px; }
.rsnote p { font-size: 15px; line-height: 1.55; }
.rsfoot { margin-top: 26px; padding-top: 10px; border-top: 1px solid var(--line-soft);
  font-size: 12px; color: var(--muted); }

@media (max-width: 560px) {
  /* On a phone the time wants its own line above what happens, not 8em of
     column stolen from the thing you are actually reading. */
  .rstable th, .rstable td { display: block; width: auto; border-bottom: 0; padding: 2px 0; }
  .rstable tr { display: block; padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
  .rstable th { font-size: 13px; color: var(--muted); letter-spacing: .04em; }
  .rstable.calls td.who { width: auto; }
  .rshead h1 { font-size: 25px; }
}

/* ---- print ---------------------------------------------------------------
   Anything that only exists to be clicked is furniture on paper. Ink is not
   free and a run sheet is photocopied: no backgrounds, no colour, black text
   on white, and a page break rather than a day split across two sheets. */
@media print {
  .topbar, .noprint, .flash, .clock, .clockpanel, .savebar { display: none !important; }
  body { background: #fff; color: #000; }
  .wrap { max-width: none; padding: 0; }
  .runsheet { max-width: none; }
  a { color: #000; text-decoration: none; }
  .rsblock > h2 { border-bottom: 2px solid #000; color: #000; }
  .rsfacts { border: 1px solid #000; background: none; }
  .rsfacts > div { background: none; border-right: 1px solid #000; }
  .rsfacts .k, .rsnote h3, .rstable .who { color: #333; }
  .rstable th, .rstable td { border-bottom: 1px solid #bbb; }
  .rsday, .rsblock { break-inside: avoid; page-break-inside: avoid; }
  .rsday h3 { break-after: avoid; page-break-after: avoid; }
  .card, .wsblock { border: 1px solid #999; background: none; box-shadow: none; }
}

/* Search hits. The match is marked after escaping, never before. */
.searchbox { margin-top: 20px; }
.hits .ttl { text-decoration: none; color: var(--ink); font-weight: 600; }
.hits .ttl:hover { color: var(--navy); text-decoration: underline; }
mark { background: var(--gold-soft); color: var(--ink); padding: 0 2px; border-radius: 3px; }

/* ---- Setup & Reset -------------------------------------------------------
   The colour is the pressure, not the room. Somebody scanning this page is
   deciding what to worry about, and the room names are already the loudest
   thing on each card. */
.roomfilters { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; margin: 18px 0 4px; }
.roomfilters .mini.on { border-color: var(--navy); color: var(--navy); background: var(--navy-soft); }
.roomfilters .fsep { width: 1px; height: 18px; background: var(--line); margin: 0 3px; }

.roomday { margin-top: 26px; }

.flip { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: var(--radius); padding: 13px 15px; margin-bottom: 10px; }
.flip.soon  { border-left-color: var(--gold); }
.flip.tight { border-left-color: var(--crit); }
.flip.clash { border-left-color: var(--crit); background: var(--crit-soft); }
.flip.done  { opacity: .62; border-left-color: var(--good); }

.flip > header { display: flex; justify-content: space-between; align-items: baseline;
  gap: 14px; flex-wrap: wrap; }
.flip .fwho { display: flex; align-items: baseline; gap: 10px; }
.flip h3 { font-size: 16.5px; line-height: 1.2; }
.fgap { font-size: 11.5px; font-weight: 700; letter-spacing: .04em; white-space: nowrap;
  color: var(--muted); font-variant-numeric: tabular-nums; }
.fgap.tight, .fgap.clash { color: var(--crit); }
.fgap.soon { color: var(--warn); }

.fwhen { font-size: 13px; color: var(--ink-2); display: flex; align-items: baseline;
  gap: 8px; flex-wrap: wrap; margin-left: auto; }
.fwhen b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.ffrom { color: var(--muted); }
.ffrom b { color: var(--ink-2); }
.farrow { color: var(--muted); }

.fclash { margin-top: 8px; font-size: 13px; color: var(--crit); max-width: 70ch; }
.fform { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; margin-top: 11px; }
.fform .grow { flex: 1 1 260px; }
.fform input, .fform select { font-size: 13.5px; padding: 5px 8px; }
.ftick { margin-top: 8px; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.fdone { font-size: 12px; color: var(--good); }
.fnote { margin-top: 8px; font-size: 14px; }
.fowner { margin-top: 6px; font-size: 12.5px; color: var(--muted); }

@media (max-width: 640px) {
  .flip > header { flex-direction: column; gap: 5px; }
  .fwhen { margin-left: 0; }
}

@media print {
  .roomfilters, .fform, .ftick { display: none !important; }
  .flip { break-inside: avoid; page-break-inside: avoid; border: 1px solid #999;
    border-left: 3px solid #000; background: none; }
  .flip.done { opacity: 1; }
  .flip.done h3::after { content: " — done"; font-weight: 400; font-size: 13px; }
}

/* ---- files on an EventSite ----------------------------------------------- */
.filelist { list-style: none; margin: 0; padding: 0; }
.filelist li { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.filelist li:last-child { border-bottom: 0; }
.fkind { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  background: var(--surface-2); color: var(--muted); padding: 2px 7px; border-radius: 999px;
  white-space: nowrap; }
.fname { font-size: 14.5px; font-weight: 600; color: var(--ink); text-decoration: none;
  overflow-wrap: anywhere; }
.fname:hover { color: var(--navy); text-decoration: underline; }
.fmeta { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.forig { font-style: italic; }
.facts { display: flex; gap: 6px; margin-left: auto; }
/* A file that is out on the public link should look different from one that
   is not, at a glance, without having to read the button. */
.facts .mini.on { border-color: var(--gold); color: var(--gold-ink); background: var(--gold-soft); }

/* ---- service coverage ----------------------------------------------------
   A runway, not a list. The interesting thing is where the bar stops being
   full, so the bars are the same width and sit at the same height across the
   row — the eye finds the cliff without reading a single number. */
.servestrip { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; }
.servecard { background: var(--surface); border: 1px solid var(--line); border-top: 3px solid var(--good);
  border-radius: var(--radius); padding: 12px 14px; display: flex; flex-direction: column; gap: 6px; }
.servecard.nearly { border-top-color: var(--good); }
.servecard.thin   { border-top-color: var(--warn); }
.servecard.bare   { border-top-color: var(--crit); }
.servecard .swhen { font-family: var(--display); font-size: 16px; font-weight: 600; color: var(--ink); }
.servecard .stype { font-size: 11.5px; color: var(--muted); }
.servecard .sbar { height: 6px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.servecard .sbar i { display: block; height: 100%; background: var(--good); }
.servecard.thin .sbar i { background: var(--warn); }
.servecard.bare .sbar i { background: var(--crit); }
.servecard .snums { font-size: 12.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.servecard .snums b { color: var(--ink); }
.servecard .sneed { color: var(--crit); font-weight: 600; }
.servecard .sok { color: var(--good); font-weight: 600; }
.servecard .mini { margin-top: auto; align-self: flex-start; }

/* The owner pill. Gold rather than the admin blue — it is a different kind of
   thing, not a bigger one. */
.pill.owner { background: var(--gold-soft); color: var(--gold-ink); }

/* The coordinator's event switcher. Sits above the page head rather than in
   the top bar, because it belongs to the event you are looking at, not to the
   app — and because a nav with four event names in it stops being a nav.
   Scrolls sideways on a phone instead of wrapping into a block. */
.evswitch { display: flex; gap: 6px; margin: 4px 0 -8px; overflow-x: auto;
            scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.evswitch::-webkit-scrollbar { display: none; }
.evswitch a { flex: 0 0 auto; display: flex; align-items: baseline; gap: 7px;
              padding: 7px 13px; border: 1px solid var(--line); border-radius: 999px;
              background: var(--surface); color: var(--ink-2); text-decoration: none;
              font-size: 13px; font-weight: 600; white-space: nowrap; }
.evswitch a span { font-weight: 500; font-size: 11.5px; color: var(--muted);
                   font-variant-numeric: tabular-nums; }
.evswitch a:hover { border-color: var(--navy); color: var(--navy); }
.evswitch a.on { background: var(--navy); border-color: var(--navy); color: #fff; }
.evswitch a.on span { color: rgba(255,255,255,.72); }

/* The access control on the People page. Radios and the event ticks live on
   one line so the relationship reads left to right — what kind of account,
   then which events — and wrap onto a second line rather than squeezing. */
.access { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; margin-top: 10px;
          padding-top: 10px; border-top: 1px solid var(--line-soft); }
.access .alabel { font-size: 11px; font-weight: 700; letter-spacing: .06em;
                  text-transform: uppercase; color: var(--muted); }
.access label { text-transform: none; letter-spacing: 0; font-size: 12.5px; margin: 0;
                display: inline-flex; align-items: center; gap: 5px; }
.access input[type="radio"], .access input[type="checkbox"] { width: auto; margin: 0; }
.access .evpick { display: flex; flex-wrap: wrap; gap: 5px 12px; align-items: center;
                  padding-left: 12px; border-left: 1px solid var(--line-soft); }
.access .evpick label { color: var(--ink-2); }

/* "not on the shared link" — the quiet tag on the money fields and the budget
   panel. Deliberately plain: it is a statement of fact about where the figure
   goes, not a warning, and a red badge on every budget line would read as
   though something were wrong. */
.privtag { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
           background: var(--surface-2); border: 1px solid var(--line);
           font-family: var(--sans); font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
           text-transform: none; color: var(--muted); vertical-align: middle; }

/* The guest page. Warmer and quieter than the team view — no meters, no
   counts, no progress. One column, generous, the kind of page somebody reads
   once on a phone in an airport. */
.guestwelcome { border-top: 3px solid var(--gold); }
.guestwelcome .ghello { font-family: var(--display); font-size: 21px; font-weight: 600;
                        color: var(--ink); margin: 0 0 8px; }
.guestwelcome .gwords { font-size: 15.5px; line-height: 1.7; color: var(--ink-2); margin: 0;
                        max-width: 62ch; }
.gitem + .gitem { margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--line-soft); }
.gitem b { display: block; font-size: 11px; font-weight: 700; letter-spacing: .06em;
           text-transform: uppercase; color: var(--muted); margin-bottom: 4px; }
.gitem p { margin: 0; font-size: 15px; line-height: 1.65; color: var(--ink); max-width: 62ch; }

/* The save state, beside the Save button. It replaces a static "last saved"
   line, so it has to be readable at rest as well as while it is changing —
   quiet grey by default, and only the failure states take a colour. */
.savestate { font-size: 12.5px; color: var(--muted); transition: color .15s ease; }
.savestate.busy { color: var(--muted); }
.savestate.ok   { color: var(--good); font-weight: 600; }
.savestate.bad  { color: var(--crit); font-weight: 600; max-width: 46ch; line-height: 1.45; }

/* `hidden` has to actually hide.
   The attribute's own rule is display:none at the lowest possible specificity,
   so any class that sets a display — .budgetsum is display:grid — beats it, and
   an element the script has just hidden stays on the screen. This is the last
   rule in the file on purpose. */
[hidden] { display: none !important; }

/* --- Trips ---------------------------------------------------------------- */

/* The three numbers on a trip card: who is going, what it costs them, and
   what is still outstanding. The third one is the reason the card exists. */
.tripnums { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px;
            background: var(--line-soft); border: 1px solid var(--line);
            border-radius: var(--radius); overflow: hidden; margin: 11px 0 12px; }
.tripnums > div { background: var(--surface); padding: 9px 11px; text-align: center; }
.tripnums b { display: block; font-family: var(--display); font-size: 19px; font-weight: 600;
              color: var(--ink); font-variant-numeric: tabular-nums; }
.tripnums span { display: block; font-size: 10.5px; color: var(--muted); line-height: 1.3; margin-top: 2px; }
.tripnums .bad b  { color: var(--crit); }
.tripnums .good b { color: var(--good); }

/* The cost panel. One big number, because "what does it cost a student" is the
   question everybody asks and nobody should have to read a table to answer. */
.tripcost .modepick { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.modebtn { font: inherit; font-size: 12.5px; font-weight: 600; padding: 5px 12px; cursor: pointer;
           border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
           color: var(--ink-2); }
.modebtn:hover { border-color: var(--navy); color: var(--navy); }
.modebtn.on { background: var(--navy); border-color: var(--navy); color: var(--on-accent); }

.costbig { text-align: center; padding: 6px 0 14px; border-bottom: 1px solid var(--line-soft); }
.costbig .ck { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .09em;
               text-transform: uppercase; color: var(--muted); }
.costbig .cv { display: block; font-family: var(--display); font-size: 40px; font-weight: 600;
               line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; margin: 3px 0 2px; }
.costbig .cn { display: block; font-size: 12.5px; color: var(--muted); }

.costrows { margin: 12px 0 0; display: grid; gap: 1px; background: var(--line-soft);
            border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.costrows > div { background: var(--surface); padding: 9px 13px; display: flex;
                  justify-content: space-between; align-items: baseline; gap: 12px; }
.costrows dt { font-size: 12.5px; color: var(--ink-2); margin: 0; }
.costrows dd { margin: 0; font-size: 14px; font-weight: 600; color: var(--ink);
               font-variant-numeric: tabular-nums; }
.costrows > div.bad  dd, .costrows > div.bad  dt { color: var(--crit); }
.costrows > div.good dd, .costrows > div.good dt { color: var(--good); }
/* Two different prices on one sheet. Loud enough to be read, quiet enough that
   a trip priced deliberately this way is not nagging anybody. */
.costclash { margin: 14px 0 0; font-size: 13px; line-height: 1.5; }

/* The consent form. Longer than anything else a person outside the church ever
   fills in here, so the required marks are quiet and the agreements are given
   room rather than squeezed into a row of tick boxes. */
.reqmark { font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
           color: var(--crit); margin-left: 6px; }
.agreefield { padding: 11px 13px; background: var(--surface-2); border: 1px solid var(--line);
              border-radius: var(--radius); margin-bottom: 10px; }
.agreefield label { margin: 0 !important; }

/* Payments. One line per payment under a person's name, because a trip paid
   off at fifty dollars a month is five lines and a single total answers none
   of the questions anybody asks about it. */
.payline + .payline { border-top: 1px solid var(--line-soft); }
.payline > summary { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
                     padding: 5px 0; cursor: pointer; list-style: none; }
.payline > summary::-webkit-details-marker { display: none; }
.payline > summary::before { content: "▸"; color: var(--muted); font-size: 10px; }
.payline[open] > summary::before { content: "▾"; }
.payline b { font-variant-numeric: tabular-nums; font-size: 13.5px; min-width: 62px; }
.payline summary > span { font-size: 12px; color: var(--muted); }
.payline .payref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px;
                   color: var(--muted); }
.payline .paysent { font-size: 10.5px; font-weight: 600; color: var(--good);
                    text-transform: uppercase; letter-spacing: .04em; }
.payline .paysent.bad  { color: var(--crit); }
.payline .paysent.warn { color: var(--warn); }
.rcptmini { margin: 8px 0 4px; padding-left: 16px; font-size: 12px; color: var(--muted); }
.rcptmini b { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
              min-width: 0; color: var(--ink-2); }
.payadd { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
.payadd input, .payadd select { font-size: 12.5px; padding: 4px 7px; }
.payadd input[name="amount"] { width: 72px; }
.payadd input[name="reference"] { width: 96px; }
.payadd input[type="date"] { width: 132px; }
.payadd label { display: inline-flex; align-items: center; gap: 4px; margin: 0;
                text-transform: none; letter-spacing: 0; font-size: 12px; color: var(--muted); }
.payadd label input { width: auto; }


/* The receipt log. One row per email that left the building, with the words it
   said folded underneath — the point of it is the copy, not the count. */
.rcpt { border-bottom: 1px solid var(--line-soft); }
.rcpt:last-child { border-bottom: 0; }
.rcpt > summary { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
                  padding: 10px 16px; cursor: pointer; font-size: 13px; }
.rcpt > summary b { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
                    color: var(--ink); }
.rcpt .rc-when { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.rcpt .rc-who  { color: var(--ink-2); }
.rcpt .rc-amt  { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }
.rcpt.failed > summary { background: var(--crit-soft); }
.rcpt .rc-body { padding: 0 16px 14px; }
.rcpt .rc-copy { margin: 0; padding: 12px 14px; background: var(--surface-2);
                 border: 1px solid var(--line); border-radius: var(--radius);
                 font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
                 line-height: 1.6; white-space: pre-wrap; word-break: break-word; color: var(--ink-2); }

/* --- The head count ------------------------------------------------------- */
/* One job: a leader at the door of a bus in the dark. Big targets, a count
   that never leaves the screen, and nothing else on the page. */
.hcbar { /* Under the bar that now stays at the top, not behind it. */
         position: sticky; top: calc(64px + env(safe-area-inset-top, 0px)); z-index: 5;
         display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
         background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
         padding: 12px 16px; margin: 4px 0 12px; box-shadow: var(--shadow); }
.hccount b { font-family: var(--display); font-size: 34px; font-weight: 600; line-height: 1;
             color: var(--ink); font-variant-numeric: tabular-nums; }
.hccount span { display: block; font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.hcbar.allin .hccount b { color: var(--good); }
.hcmeta { margin-right: auto; }
.hcwhat { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.hcwhen { font-size: 11.5px; color: var(--muted); }
.hcnote { max-width: 60ch; }
.hchead { font-size: 12px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
          color: var(--muted); margin: 18px 0 6px; font-family: var(--sans); }
.hchead span { color: var(--line); margin-left: 4px; }
.hclist { display: grid; gap: 6px; }
.hcrow { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
         font: inherit; cursor: pointer; padding: 14px 15px; min-height: 56px;
         background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
         color: var(--ink); }
.hcrow .hctick { flex: 0 0 auto; width: 24px; height: 24px; border-radius: 7px;
                 border: 2px solid var(--line); background: var(--ground); }
.hcrow .hcname { font-size: 16px; font-weight: 600; }
.hcrow .hcgrade { margin-left: auto; font-size: 12px; color: var(--muted); }
.hcrow.on { background: var(--good-soft); border-color: var(--good); }
.hcrow.on .hctick { background: var(--good); border-color: var(--good); position: relative; }
.hcrow.on .hctick::after { content: "✓"; position: absolute; inset: 0; color: #fff;
                           font-size: 15px; line-height: 20px; text-align: center; font-weight: 700; }
.hcrow.on .hcname { color: var(--good); }
@media print {
  .topbar, .hcbar button, .hcnote { display: none !important; }
  .hcrow { break-inside: avoid; }
}

/* The take-a-payment line, under each person rather than in the last column of
   a wide table — reachable without scrolling sideways, which matters when
   somebody is holding a phone with a queue in front of them. */
.payaddrow > td { background: var(--surface-2); padding-top: 8px; padding-bottom: 8px; }
.payadd .payfor { font-size: 12.5px; color: var(--muted); margin-right: 2px; }
.payadd .payfor b { color: var(--ink-2); font-weight: 600; }

/* A stat tile whose label is a link — "no cost set yet" — because a bare dash
   tells somebody what is missing and not what to do about it. */
.evstat .k a { color: inherit; text-decoration: underline; text-underline-offset: 2px;
               text-decoration-color: var(--line); }
.evstat .k a:hover { color: var(--navy); text-decoration-color: var(--navy); }

/* ---- the speaking schedule ----------------------------------------------
   The third calendar layer. It is not a booking and it is not an absence, so
   it looks like neither: a filled chip in the navy the app uses for people
   rather than the gold it uses for things in the building. */
.calgrid .chip.speak { background: var(--navy-soft); color: var(--navy); font-weight: 600; }
.calspeak { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px;
             padding: 9px 16px; background: var(--navy-soft);
             border-bottom: 1px solid var(--line-soft); }
.calspeak .pwk { font-size: 10px; font-weight: 700; letter-spacing: .09em;
                  text-transform: uppercase; color: var(--navy); }
.calspeak .pwp { font-size: 13px; color: var(--ink-2); }
.calspeak .pwp b { color: var(--ink); font-weight: 600; }
.calspeak .psvc, .calspeak .ptitle { margin-left: 7px; color: var(--ink-2); }
.calspeak .psvc::before, .calspeak .ptitle::before { content: "· "; color: var(--muted); }
.calspeak .photel { margin-left: 7px; font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
                     text-transform: uppercase; color: var(--warn); }
.calspeak .mini { margin-left: auto; }

.speaktable td { vertical-align: top; }
/* The edit row under each entry. Folded by default — the table is read far
   more often than it is changed, and a row of form fields under every line
   would make it unreadable. */
.speakedit { background: var(--surface-2); padding: 0; }
.speakedit summary { cursor: pointer; padding: 7px 14px; font-size: 11.5px; font-weight: 700;
                      letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
                      list-style: none; }
.speakedit summary::-webkit-details-marker { display: none; }
.speakedit summary::before { content: "＋ "; }
.speakedit details[open] summary::before { content: "－ "; }
.speakedit summary:hover { color: var(--navy); }
.speakedit .speakbody { padding: 4px 14px 14px; }

.speakform .prow { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end;
                    margin-bottom: 9px; }
.speakform .pf { display: flex; flex-direction: column; gap: 3px; margin: 0;
                  text-transform: none; letter-spacing: 0; flex: 0 1 auto; }
.speakform .pf.grow { flex: 1 1 180px; }
.speakform .pf > span { font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
                         text-transform: uppercase; color: var(--muted); }
.speakform .pf input { margin: 0; }
.speakform .pcheck { display: inline-flex; align-items: center; gap: 7px; margin: 0 0 8px;
                      text-transform: none; letter-spacing: 0; font-size: 13.5px; font-weight: 600; }
.speakform .pcheck input { width: auto; margin: 0; }
.speakform .photelbox { padding: 10px 12px; background: var(--warn-soft);
                         border: 1px solid var(--line); border-radius: var(--radius);
                         margin-bottom: 10px; }
.speakform .photel { margin-bottom: 0; }
.speakform .phnote { flex: 1 1 220px; margin: 0; font-size: 12px; }

/* Run-it-now, and what the job printed. */
.jobrun { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft);
          display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jobrun .note { margin: 0; font-size: 11.5px; }
.jobcard .jobnext { font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.joboutput { margin: 0; padding: 12px 14px; background: var(--surface-2);
             border: 1px solid var(--line); border-radius: var(--radius);
             font-size: 12.5px; line-height: 1.6; white-space: pre-wrap; word-break: break-word;
             max-height: 460px; overflow: auto; }

/* The cron probes. One block each, and the one that has fired says so at a
   glance — the whole panel exists to be read in ten seconds. */
.probe { padding: 12px 14px; border: 1px solid var(--line); border-left: 3px solid var(--line);
         border-radius: var(--radius); margin-bottom: 12px; background: var(--surface); }
.probe.good { border-left-color: var(--good); }
.probe .probehead { margin: 0 0 8px; font-size: 13.5px; display: flex; align-items: baseline;
                    gap: 8px; flex-wrap: wrap; }
.probe .probehead .muted { font-size: 12px; }

/* Which PHP is on the server. A table, because it is four facts about five
   paths and prose would bury the one line that matters. */
.phptable code { font-size: 12px; }
.phptable tr.good td:first-child { border-left: 3px solid var(--good); }
.phptable tr.warn td:first-child { border-left: 3px solid var(--warn); }
.phptable b.good { color: var(--good); }
.phptable b.bad  { color: var(--crit); }

/* Putting the calendar on a phone.
   -----------------------------------------------------------------------
   Set up once and then never looked at again, so it sits at the bottom of
   the Calendar page with a small pointer to it in the header rather than
   taking space from the thing people came for. */
.subphone { margin-left: 10px; font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.feedlead { margin: 0; font-size: 14.5px; }
.feedwhere { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.feedwhere .note { margin: 0 0 8px; }
.feedwhere .note:last-child { margin-bottom: 0; }
.feedwhere b { color: var(--ink); }
.feedtable { min-width: 460px; font-size: 13.5px; }
.feedtable th { font-size: 10px; }

/* The signal that everything is running.
   -----------------------------------------------------------------------
   Every other warning in this app is negative — it appears when something
   breaks and is otherwise silent. That is right, and it leaves a gap: a
   stopped job and a quiet week look identical. Four lights close it. */
.systems { display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
           margin-top: 18px; padding: 11px 15px; border: 1px solid var(--line);
           border-radius: var(--radius); background: var(--surface);
           border-left: 3px solid var(--good); }
.systems.warn { border-left-color: var(--warn); }
.systems.bad  { border-left-color: var(--crit); }
.systems-lights { display: flex; gap: 14px; flex-wrap: wrap; }
.sysdot { display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
          font-weight: 600; letter-spacing: .02em; color: var(--ink-2); white-space: nowrap; }
.sysdot i { width: 8px; height: 8px; border-radius: 50%; background: var(--muted);
            display: inline-block; flex: none; }
.sysdot.good i { background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 18%, transparent); }
.sysdot.warn i { background: var(--warn); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 18%, transparent); }
.sysdot.bad  i { background: var(--crit); box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit) 18%, transparent); }
.systems-say { font-size: 13px; color: var(--ink-2); margin-left: auto; }
.systems-say b { color: var(--ink); }
.systems-say .muted { margin-left: 6px; }
@media (max-width: 640px) {
  .systems-say { margin-left: 0; width: 100%; }
}

/* ---- meetings ------------------------------------------------------------
   The fourth calendar layer, and the first one that is about people agreeing
   with each other rather than about the building. It is drawn in the app's
   green — the colour it already uses for a thing that is settled — and a
   proposal is drawn in outline, because a proposal is not a fact yet. */
.calgrid .chip.meet { background: var(--good-soft); color: var(--good);
                      font-weight: 600; }
.calmeet { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px;
           padding: 9px 16px; background: var(--surface-2);
           border-bottom: 1px solid var(--line-soft); }
.calmeet .mtk { font-size: 10px; font-weight: 700; letter-spacing: .09em;
                text-transform: uppercase; color: var(--good); }
.calmeet .mtp { font-size: 13px; color: var(--ink-2); }
.calmeet .mtp b { color: var(--ink); font-weight: 600; }
.calmeet .mtp b::after { content: " · "; color: var(--muted); font-weight: 400; }
.calmeet .mtwhat, .calmeet .mtwhere { margin-left: 7px; color: var(--ink-2); }
.calmeet .mtwhat::before, .calmeet .mtwhere::before { content: "· "; color: var(--muted); }
.calmeet .mini { margin-left: auto; }

/* A meeting that is not settled yet is not a meeting, and it should not look
   like one sitting next to the diary. Pale tan — the same warn-soft the rest
   of Worksite already uses for "waiting on somebody" — so the difference
   between "Thursday at two" and "we have asked about Thursday at two" is
   visible across the room rather than read off a small grey word.

   Waiting is the DEFAULT here and settled is the exception, which is the right
   way round: a card with no state class on it should look unfinished. */
.meetcard { margin-bottom: 13px; border-left: 3px solid var(--warn);
            background: var(--warn-soft); }
.meetcard.on  { border-left-color: var(--good); background: var(--surface); }
.meetcard.off { border-left-color: var(--line); background: var(--surface); }
.meetcard.past { opacity: .72; }

/* The boxes inside a tan card go white rather than the usual pale blue-grey,
   which on this background reads as a stain rather than a panel. */
.meetcard:not(.on):not(.off) .meetsuggest,
.meetcard:not(.on):not(.off) .meetroom { background: var(--surface); }
.meetwhen { margin: 0 0 10px; font-size: 14px; display: flex; align-items: baseline;
            gap: 10px; flex-wrap: wrap; }
.meetwhen .meetwhere { color: var(--ink-2); }
.mstate { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
          color: var(--muted); }
.mstate.on   { color: var(--good); }
.mstate.off  { color: var(--crit); }
.mstate.priv { color: var(--navy); }

.meetwho { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-wrap: wrap;
           gap: 6px 16px; }
.meetwho .mw { font-size: 13px; color: var(--ink-2); }
.meetwho .mw b { color: var(--ink); font-weight: 600; }
.meetwho .mrole { margin-left: 5px; font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
                  text-transform: uppercase; color: var(--muted); }
.meetwho .msaid { margin-left: 6px; }
.meetwho .msaid::before { content: "· "; color: var(--muted); }
.meetwho .mw.yes .msaid { color: var(--good); }
.meetwho .mw.no  .msaid { color: var(--crit); }
.meetwho .mw.new .msaid { color: var(--warn); }

.meetnotes { margin: 0 0 12px; font-size: 14px; line-height: 1.65; color: var(--ink-2);
             max-width: 68ch; }
.meetsuggest { padding: 11px 13px; margin-bottom: 12px; border: 1px solid var(--line);
               border-left: 3px solid var(--warn); border-radius: var(--radius);
               background: var(--surface-2); }
.meetsuggest p { margin: 0 0 8px; font-size: 13.5px; }
.meetsuggest .note { margin-left: 10px; }
.meetsuggest .inlineform { display: inline-block; }

.meetreply { display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
             padding-top: 11px; border-top: 1px solid var(--line-soft); }
.meetreply .mq { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.meetreply .meetsug { flex: 1 1 100%; }
.meetreply .meetsug summary { cursor: pointer; font-size: 12px; color: var(--muted);
                              list-style: none; }
.meetreply .meetsug summary::-webkit-details-marker { display: none; }
.meetreply .meetsug summary::before { content: "＋ "; }
.meetreply .meetsug[open] summary::before { content: "－ "; }
.meetreply .meetsug summary:hover { color: var(--navy); }
.meetreply .meetsug .addrow { margin-top: 8px; }

/* Every one of these is a DIRECT-child selector, and it has to be.
   `.meetacts details[open] summary` also matches the summary of any details
   NESTED inside the open one — so "Ask somebody else along", which lives in
   there, wore the open panel's minus sign whether it was open or shut, and
   inherited its uppercase besides. Scoping to the one summary this block is
   about leaves anything nested to style itself. */
.meetacts { margin-top: 11px; }
.meetacts > details > summary { cursor: pointer; font-size: 11.5px; font-weight: 700;
                    letter-spacing: .06em;
                    text-transform: uppercase; color: var(--muted); list-style: none; }
.meetacts > details > summary::-webkit-details-marker { display: none; }
.meetacts > details > summary::before { content: "＋ "; }
.meetacts > details[open] > summary::before { content: "－ "; }
/* Not a "+" beside Delete: a plus reads as "add". The opener for a destructive
   action is the same small chevron the archive folders use. */
.meetacts > details > summary.danger::before { content: "▸ "; }
.meetacts > details[open] > summary.danger::before { content: "▾ "; }
.meetacts > details > summary:hover { color: var(--navy); }
.meetedit { padding-top: 10px; }
.meetedit textarea { margin-top: 9px; }

/* The request form. The list of people is the tall part, so it scrolls rather
   than pushing the Send button off the bottom of a phone. */
.meetform .flabel { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
                    text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }
.meetpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
            gap: 4px 12px; max-height: 240px; overflow: auto; padding: 9px 11px;
            border: 1px solid var(--line); border-radius: var(--radius);
            background: var(--surface-2); }
.mpick { display: flex; align-items: baseline; gap: 8px; font-size: 13.5px;
         text-transform: none; letter-spacing: 0; margin: 0; padding: 2px 0; }
.mpick input { width: auto; margin: 0; flex: none; }
.mpick i { font-style: normal; color: var(--muted); font-size: 11.5px; margin-left: 5px; }
.meetguest, .taskrepeat { margin: 12px 0; }
.meetguest summary, .taskrepeat summary { cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--navy);
                     list-style: none; }
.meetguest summary::-webkit-details-marker,
.taskrepeat summary::-webkit-details-marker { display: none; }
.meetguest summary::before, .taskrepeat summary::before { content: "＋ "; }
.meetguest[open] summary::before, .taskrepeat[open] summary::before { content: "－ "; }
.meetguest .addrow { margin-top: 9px; }

.meetask > summary { cursor: pointer; font-size: 12.5px; font-weight: 700; letter-spacing: .04em;
                     color: var(--navy); list-style: none; padding: 6px 0; }
.meetask > summary::-webkit-details-marker { display: none; }
.meetask > summary::before { content: "＋ "; }
.meetask[open] > summary::before { content: "－ "; }

/* What else is on then. Warn-coloured, never red: nothing here is an error and
   nothing here stops the form being sent. */
.meetclash { margin: 4px 0 12px; padding: 10px 13px; border: 1px solid var(--line);
             border-left: 3px solid var(--warn); border-radius: var(--radius);
             background: var(--warn-soft); font-size: 13px; }
.meetclash ul { margin: 7px 0 0; padding-left: 18px; }
.meetclash li { margin-bottom: 3px; }
.meetclash i { font-style: normal; font-weight: 600; color: var(--ink); }
.meetclash .note { display: block; margin-top: 7px; }

/* A room that is not free.
   Red rather than amber, unlike the other clash warning: two people double-
   booked can sort it out between themselves, and two groups walking into the
   same hall cannot. */
.meetroom { margin: 0 0 11px; padding: 10px 13px; border: 1px solid var(--line);
            border-left: 3px solid var(--crit); border-radius: var(--radius);
            background: var(--surface-2); font-size: 13px; }
.meetroom b { color: var(--crit); }
.meetroom ul { margin: 6px 0 0; padding-left: 18px; }
.meetroom li { margin-bottom: 2px; }
.meetroom i { font-style: normal; color: var(--muted); font-size: 11.5px; }
.meetroom i::before { content: "· "; }
.meetroom .note { display: block; margin-top: 7px; }
.meetclash.roomtaken { border-left-color: var(--crit); background: var(--crit-soft); }
.meetclash.roomtaken > b { color: var(--crit); }

/* The navigation on a phone.
   -----------------------------------------------------------------------
   The bar carries up to eleven links for an administrator, which on a wide
   screen is one tidy row and on a 390px screen is four rows of navigation
   above every page in the app. So below 860px it folds behind three lines.

   The button is hidden in the markup and revealed by app.js — with JavaScript
   off there is nothing to open the menu with, so the links stay visible and
   the bar behaves exactly as it always did. Never the other way round: a
   hamburger that does nothing is worse than a long bar.

   .navopen is set on the topbar, not on the links, so the button can be styled
   as pressed at the same time. */
.navbtn { display: none; align-items: center; justify-content: center;
          width: 44px; height: 44px; flex: none; margin-left: auto;
          background: var(--surface); border: 1px solid var(--line);
          border-radius: var(--radius); cursor: pointer; padding: 0;
          -webkit-tap-highlight-color: transparent; }
.navbtn:hover { border-color: var(--navy); }
.navbars { display: block; width: 19px; }
.navbars i { display: block; height: 2px; border-radius: 2px; background: var(--ink);
             transition: transform .18s ease, opacity .15s ease; }
.navbars i + i { margin-top: 4px; }
/* Three lines become a cross while it is open — the same control, so it should
   say what pressing it again does. */
.navopen .navbars i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.navopen .navbars i:nth-child(2) { opacity: 0; }
.navopen .navbars i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
.navbtn[aria-expanded="true"] { background: var(--navy-soft); border-color: var(--navy); }

@media (max-width: 860px) {
  .topbar { flex-wrap: wrap; align-items: center; }
  .topbar .brand { flex: 1 1 auto; min-width: 0; }
  /* Only once app.js has taken the `hidden` off. */
  .navbtn:not([hidden]) { display: inline-flex; }
  /* Folded away, but only when there is a button to bring it back.
     The open rule repeats the :has() rather than reading `.topbar.navopen
     .navlinks`, and it has to: :has() takes the specificity of its argument,
     so the folding rule scores four classes and a plainer open rule scores
     three and loses. The menu then toggles a class that changes nothing,
     which is a bug you can only see in a browser — and did. */
  .topbar:has(.navbtn:not([hidden])) .navlinks { display: none; }
  .topbar.navopen:has(.navbtn:not([hidden])) .navlinks { display: flex; }
  .navlinks { width: 100%; flex-direction: column; align-items: stretch; gap: 2px;
              margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
  .navlinks a { display: flex; align-items: center; gap: 9px; padding: 12px 14px;
                font-size: 15px; border: 1px solid transparent; border-radius: var(--radius); }
  .navlinks a:hover { background: var(--surface-2); border-color: transparent; }
  .navlinks a.on { background: var(--navy-soft); color: var(--navy); border-color: transparent; }
  .navlinks a .badge { margin-left: auto; }
  .navlinks a.quiet { margin-left: 0; margin-top: 6px; border-top: 1px solid var(--line-soft);
                      border-radius: 0; color: var(--muted); }

  /* ON A PHONE THE GROUPS ARE SHUT, AND A TAP OPENS ONE.
     ---------------------------------------------------------------------
     They used to unfold in place the moment the hamburger opened. That was
     right as far as it went — a hover menu inside a menu you had to tap open
     is two taps for one link, and on a touch screen the first of them does not
     reliably work at all — but it made the open bar about two and a half
     screens long, and Craig said so: "collapse the dropdown menus, expand on
     tap, the bar is too long on a phone."

     So each group keeps its parent as a real link and grows a separate caret
     beside it that opens the group. Tap "Sites" and you go to the Sites
     page; tap the caret and the six sites unfold under it. A hidden checkbox
     holds the open state, which means no JavaScript is involved — see
     nav_expander() for why that matters here of all places.

     The row is a flex line: link, caret, and the menu wrapping onto its own
     full-width line underneath. */
  .navgroup { display: flex; flex-wrap: wrap; align-items: center; width: 100%; }
  .navgroup > .navtop { justify-content: flex-start; flex: 1 1 auto; min-width: 0; }
  .navgroup > .navtop > .caret { display: none; }   /* the one ON the link, not the opener */

  /* Visually hidden rather than display:none — a display:none checkbox cannot
     be focused, and then the caret is unreachable by keyboard even though the
     label still works with a mouse. */
  .navcheck { position: absolute; width: 1px; height: 1px; opacity: 0;
              margin: 0; padding: 0; pointer-events: none; }
  .navexp { display: flex; align-items: center; justify-content: center;
            flex: 0 0 auto; width: 44px; height: 44px; cursor: pointer;
            border-radius: var(--radius); color: var(--muted); }
  .navexp:hover { background: var(--surface-2); color: var(--ink); }
  .navexp .caret { display: block; transition: transform .16s ease; }
  /* The caret turns over when the group is open, which is the only thing on
     screen saying which state it is in. */
  .navcheck:checked ~ .navexp .caret { transform: rotate(180deg); }
  /* Keyboard: the checkbox is the focusable thing, so the ring has to be drawn
     on the label it controls or tabbing through the menu looks like nothing is
     selected. */
  .navcheck:focus-visible ~ .navexp { outline: 2px solid var(--navy); outline-offset: -2px; }

  .navmenu, .navmenu.right { display: none; }
  .navcheck:checked ~ .navmenu,
  .navcheck:checked ~ .navmenu.right {
                             display: block;
                             flex: 0 0 100%; order: 9;
                             position: static; opacity: 1; visibility: visible;
                             transform: none; min-width: 0; box-shadow: none;
                             border: 0; padding: 0 0 0 12px; margin: 2px 0 8px;
                             border-left: 2px solid var(--line-soft); border-radius: 0;
                             background: transparent; }
  .navlinks .navmenu a { padding: 9px 12px; }
  .navlinks .navmenu a b { font-size: 14.5px; }
  .navlinks .navmenu a .badge { top: 11px; }
  .navsymword { display: inline; }
  .navsym { margin-right: 5px; }
  /* Search and Settings are centred on a laptop, where they are bare icons at
     the end of a horizontal bar. In the vertical phone menu they are rows like
     any other and have to start where the other rows start, or the list reads
     as two lists. */
  .navlinks a.iconlink, .navgroup > .navtop.iconlink { justify-content: flex-start;
                                                       padding: 12px 14px; }
}

/* :has() is recent enough to want a floor under it. Without it the rule above
   never matches, the links stay visible, and the page is exactly what it was
   before this block existed — which is the right thing to fall back to. */
@supports not selector(:has(*)) {
  @media (max-width: 860px) { .navbtn { display: none !important; } }
}

/* What is in each room.
   A list, not a grid of coloured blocks: the question is "is Thursday
   afternoon free", which a person answers by reading three lines, and an
   hour-by-hour wall chart of fourteen rooms is unreadable on a phone and
   unprintable on paper. */
.roomtabs { display: flex; gap: 6px; margin: 16px 0 2px; }
.roomsched .card { padding: 0; }
.rsday { display: grid; grid-template-columns: 116px 1fr; gap: 0 14px; padding: 11px 16px;
         border-bottom: 1px solid var(--line-soft); }
.rsday:last-child { border-bottom: 0; }
.rsdate { font-size: 12.5px; font-weight: 700; color: var(--ink-2); padding-top: 2px; }
.rslist { list-style: none; margin: 0; padding: 0; }
.rsrow { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
         font-size: 13.5px; padding: 3px 0; }
.rsrow .rswhen { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink);
                 min-width: 118px; }
.rsrow .rswhat { color: var(--ink-2); }
.rsrow.meeting .rswhat { color: var(--navy); font-weight: 600; }
.rsrow.routine .rswhat { color: var(--muted); }
.rsrow .rswho { margin-left: auto; font-size: 11px; letter-spacing: .04em;
                text-transform: uppercase; color: var(--muted); }
@media (max-width: 620px) {
  .rsday { grid-template-columns: 1fr; gap: 6px; }
  .rsrow .rswho { margin-left: 0; width: 100%; }
}

/* Repeating guest rows, and the panel that adds people to a meeting already
   under way. */
.guestrow + .guestrow { margin-top: 8px; }
.meetmore { margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--line-soft); }
.meetmore > summary { cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--navy);
                      list-style: none; }
.meetmore > summary::-webkit-details-marker { display: none; }
.meetmore > summary::before { content: "＋ "; }
.meetmore[open] > summary::before { content: "－ "; }
.meetmore .meetpick { max-height: 190px; }

/* The announcement bar.
   -----------------------------------------------------------------------
   The loudest thing in the app, and the only thing allowed to be. Filled
   rather than outlined, above the flash, and with no dismiss button — it is
   up for hours rather than days, and a notice that can be clicked away is a
   notice that was clicked away by the one person who needed it. */
.announcebar { margin: 0 0 18px; }
.announce {
  border: 1px solid var(--crit); border-left: 5px solid var(--crit);
  border-radius: var(--radius); background: var(--crit-soft);
  padding: 13px 16px 11px;
}
.announce + .announce { margin-top: 10px; }
.announceflag {
  display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--crit); margin-bottom: 5px;
}
.announcebody {
  font-size: 16px; line-height: 1.5; color: var(--ink); font-weight: 600;
  max-width: 78ch;
}
.announcewho {
  margin-top: 9px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--ink-2);
}
.announcewho .inlineform { margin-left: auto; }
@media (max-width: 560px) {
  .announcewho .inlineform { margin-left: 0; width: 100%; }
}

/* Posting one, on the Board. */
.announcepost .field { margin-bottom: 10px; }
.announcepost textarea { min-height: 88px; }
.announceask { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.announceask label { margin: 0; font-size: 12px; font-weight: 600; letter-spacing: .03em;
                     text-transform: none; color: var(--ink-2); }
.announceask select { width: auto; margin: 0; }
.announcelog { font-size: 13px; }
.announcelog .row { display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline;
                    padding: 8px 0; border-top: 1px solid var(--line-soft); }
.announcelog .row:first-child { border-top: 0; }
.announcelog .when { color: var(--muted); font-size: 11.5px; min-width: 120px; }
.announcelog .what { color: var(--ink-2); flex: 1 1 220px; }
.announcelog .tag { font-size: 10px; font-weight: 700; letter-spacing: .06em;
                    text-transform: uppercase; color: var(--muted); }
.announcelog .tag.up { color: var(--crit); }

/* Texting an announcement. Its own block above the send row, because "who
   gets a text" is a different question from "how long does it stay up". */
.announcetext { margin: 12px 0; padding: 11px 13px; border: 1px solid var(--line);
                border-radius: var(--radius); background: var(--surface-2); }
.announcetext .flabel { display: block; font-size: 10.5px; font-weight: 700;
                        letter-spacing: .07em; text-transform: uppercase;
                        color: var(--muted); margin-bottom: 8px; }
.announcetext .pcheck { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 6px;
                        font-size: 13.5px; font-weight: 500; text-transform: none;
                        letter-spacing: 0; color: var(--ink-2); }
.announcetext .pcheck input { width: auto; margin: 3px 0 0; flex: none; }
.announcetext .note { margin: 8px 0 0; }

/* The group headings in the meetings list. Enough to separate four groups
   without four more section headers competing with the page's own. */
.meetgroup { margin: 18px 0 10px; }
.meetgroup:first-of-type { margin-top: 0; }

/* ---- ServeSite -------------------------------------------------------
   The public page, so it is wider-set and larger than anything inside the app:
   it is read once, on a phone, by somebody who was not looking for it, and
   every point of type is worth more here than on a page somebody visits daily. */
.lanetabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 20px 0 4px; }
.lanetabs a { font-size: 12.5px; font-weight: 600; text-decoration: none; color: var(--ink-2);
  padding: 5px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); }
.lanetabs a:hover { border-color: var(--navy); color: var(--navy); }
.lanetabs a.on { background: var(--navy); border-color: var(--navy); color: var(--on-accent); }

.servejob, .servemine { border-left: 3px solid var(--gold); }
.slane { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 5px; }
.servejob h2, .servemine h2 { font-size: 21px; line-height: 1.25; margin: 0; }
.sbody { margin: 9px 0 0; font-size: 15.5px; line-height: 1.65; color: var(--ink-2); max-width: 66ch; }
.sfacts { margin: 11px 0 0; font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
.swho { margin: 9px 0 0; font-size: 13.5px; color: var(--ink-2); }
.swho b { color: var(--warn); }

/* The note from the office. Only ever on a token page or in an email — never
   on the board, which is the whole reason it is a different colour here: if
   this style is ever seen by a stranger, something has gone wrong. */
.snote { margin: 12px 0 0; padding: 10px 13px; border-left: 3px solid var(--navy);
  border-radius: var(--radius); background: var(--surface-2); font-size: 14px;
  line-height: 1.6; color: var(--ink-2); }

.sclaim { margin-top: 14px; }
.sclaim > summary { cursor: pointer; display: inline-block; font-size: 14.5px; font-weight: 700;
  color: var(--navy); list-style: none; padding: 8px 16px; border: 1.5px solid var(--navy);
  border-radius: 999px; }
.sclaim > summary::-webkit-details-marker { display: none; }
.sclaim > summary:hover { background: var(--navy); color: var(--on-accent); }
.sclaim[open] > summary { margin-bottom: 14px; }
.sclaim .field { margin-bottom: 12px; }
.sclaim input { font-size: 16px; }   /* 16px or iOS zooms the whole page on focus */

.stick { margin-top: 14px; }
.servemine.done { border-left-color: var(--good); opacity: .82; }
.sdone { margin: 12px 0 0; font-size: 15px; font-weight: 700; color: var(--good); }

.ssub > summary { cursor: pointer; font-size: 15.5px; font-weight: 700; color: var(--navy);
  list-style: none; }
.ssub > summary::-webkit-details-marker { display: none; }
.ssub > summary::before { content: "＋ "; }
.ssub[open] > summary::before { content: "－ "; }
.ssub input { font-size: 16px; }
.lanepick { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 4px 14px;
  margin-top: 6px; }
.lanepick label { display: flex; align-items: center; gap: 7px; text-transform: none;
  letter-spacing: 0; font-weight: 500; font-size: 14px; color: var(--ink-2); margin: 0; }
.lanepick input { width: auto; margin: 0; }

/* The office's side of the board, on the Ops Board. */
.serverow { display: grid; grid-template-columns: 1fr auto; gap: 4px 14px; align-items: start;
  padding: 11px 0; border-bottom: 1px solid var(--line-soft); }
.serverow:last-child { border-bottom: 0; }
.serverow .st { font-size: 14.5px; font-weight: 600; color: var(--ink); }
.serverow .sm { font-size: 12.5px; color: var(--muted); }
.serverow .sa { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* "tell them now", beside the add-a-task row. Small on purpose: the roundup is
   the default and should look like it. */
.notifynow { display: inline-flex; align-items: center; gap: 6px; margin: 0;
  text-transform: none; letter-spacing: 0; font-size: 12.5px; font-weight: 600;
  color: var(--ink-2); white-space: nowrap; }
.notifynow input { width: auto; margin: 0; }

/* ---- Depth ----------------------------------------------------------------
   Added after the mockup, where the cards sat slightly off the page and the
   real site did not. It is one variable applied in one place per component
   rather than a shadow written out forty times, so the whole app can be made
   flatter or deeper by editing three lines at the top of this file.

   Two rules held to throughout:

   - The border stays. A shadow alone reads beautifully in light mode and
     disappears entirely in dark mode, where a near-black card on a near-black
     page needs a drawn edge. Border for structure, shadow for depth; neither
     doing the other's job.
   - Only things that are actually cards get one. A shadow on every element is
     the same as a shadow on none — nothing looks raised if everything is. Rows
     inside a card, tabs, pills and inline forms stay flat, because they are
     sitting ON the card, not beside it.
   -------------------------------------------------------------------------- */

.card { box-shadow: var(--shadow-sm); }

/* A card whose whole surface is a link lifts under the cursor. Transform
   rather than a bigger shadow alone: the movement is what says "this one does
   something", and 1px is enough to feel without looking like it jumped. */
.card.linkish, .rolecard, .servejob, .req {
  transition: box-shadow .16s ease, transform .16s ease;
}
.card.linkish:hover, .rolecard:hover, .servejob:hover, .req:hover {
  box-shadow: var(--shadow); transform: translateY(-1px);
}
@media (prefers-reduced-motion: reduce) {
  .card.linkish, .rolecard, .servejob, .req { transition: none; }
  .card.linkish:hover, .rolecard:hover, .servejob:hover, .req:hover { transform: none; }
}

/* The things that are meant to be noticed get the next step up, because the
   whole point of a shadow scale is that the levels mean something. */
.systems       { box-shadow: var(--shadow-sm); }
.flash         { box-shadow: var(--shadow-sm); }
.announcebar   { box-shadow: var(--shadow-lg); }   /* above every page, and should look it */
.myrole        { box-shadow: var(--shadow-sm); }
.meetcard      { box-shadow: var(--shadow-sm); }
.rolecard      { box-shadow: var(--shadow-sm); }
.req           { box-shadow: var(--shadow-sm); }
.servejob, .servemine { box-shadow: var(--shadow); }  /* a public page, read once, on a phone */

/* Buttons. A solid button is a raised object; a ghost button is a drawn
   outline and stays flat, which is most of what tells the two apart at a
   glance. Pressing sets it back down. */
.btn { box-shadow: var(--shadow-sm); transition: box-shadow .14s ease, transform .14s ease; }
.btn.ghost { box-shadow: none; }
.btn:hover { box-shadow: var(--shadow); }
.btn.ghost:hover { box-shadow: var(--shadow-sm); }
.btn:active { box-shadow: var(--shadow-sm); transform: translateY(1px); }
@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:active { transform: none; }
}

/* Inputs the other way round: pressed INTO the page rather than sitting on it,
   which is the oldest convention in interface design and still the fastest way
   to say "you type here". Barely there — a deep inset on a white field reads
   as damage. */
input, select, textarea { box-shadow: inset 0 1px 2px rgba(20,33,58,.045); }
@media (prefers-color-scheme: dark) {
  input, select, textarea { box-shadow: inset 0 1px 2px rgba(0,0,0,.3); }
}
input[type="checkbox"], input[type="radio"] { box-shadow: none; }

/* When somebody last signed in, under the status pill on the People page.
   Small and grey: it is context for the pill above it, not a second status. */
.lastin { margin-top: 5px; font-size: 12px; color: var(--muted); }
.lastin .never { color: var(--warn); font-weight: 600; }

/* The sign-in log, folded away at the bottom of the People page. */
.loginrow { display: grid; grid-template-columns: 120px 1fr auto; gap: 2px 14px;
  align-items: baseline; padding: 6px 0; border-bottom: 1px solid var(--line-soft);
  font-size: 13px; }
.loginrow:last-child { border-bottom: 0; }
.loginrow .when { color: var(--muted); font-size: 12px; white-space: nowrap; }
.loginrow .who { color: var(--ink); }
.loginrow .no { color: var(--crit); font-weight: 600; font-size: 11.5px;
  letter-spacing: .04em; text-transform: uppercase; }
.loginrow .yes { color: var(--muted); font-size: 11.5px; }

/* The way in for somebody the password box is not for.
   Under the sign-in card and deliberately not styled as a button: it is the
   other door, not the second-choice one, and a big rival button beside "Sign
   in" makes staff hesitate over which is theirs. */
.publicdoor { max-width: 380px; margin: 26px auto 0; padding: 14px 16px;
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--gold); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); text-align: left; }
.publicdoor a { display: block; font-family: var(--display); font-size: 17px;
  font-weight: 700; color: var(--navy); text-decoration: none; }
.publicdoor a:hover { text-decoration: underline; }
.publicdoor span { display: block; margin-top: 3px; font-size: 13px; line-height: 1.5;
  color: var(--ink-2); }

/* ---- Messages ---------------------------------------------------------------
   Two columns on a desk, one on a phone, and the same rules on the public page
   as the signed-in one — a volunteer reading this in a car park should not get
   a visibly poorer version of it than the COO gets at a desk.

   Deliberately NOT a chat-bubble layout. Bubbles are built for a stream of
   six-word messages; what actually gets written here is three paragraphs about
   a nativity rota, and a paragraph in a bubble with a tail on it is harder to
   read than a paragraph. Left-aligned blocks, a rule between them, and the
   only difference between yours and theirs is which side the name sits and a
   tint. */
/* The side column is CAPPED rather than proportional. On a 1500px page a
   fraction-based sidebar grows to 550px, which is half again as wide as the
   form inside it needs and leaves the conversations — the thing the page is
   for — narrower than they should be. Everything extra goes to the left. */
.talkwrap { display: grid; grid-template-columns: minmax(0, 1fr) minmax(270px, 340px);
            gap: 26px; align-items: start; }
@media (max-width: 1000px) { .talkwrap { grid-template-columns: 1fr; gap: 20px; } }
.talkside { display: flex; flex-direction: column; gap: 13px; }

.talkmsgs { list-style: none; margin: 0; padding: 0; }
.talkmsgs > li { padding: 13px 16px; border-bottom: 1px solid var(--line-soft); }
.talkmsgs > li:last-child { border-bottom: 0; }
.talkmsgs > li.tme { background: var(--navy-soft); }

.talkmsgs .thead { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
                   margin-bottom: 4px; }
.talkmsgs .thead b { font-size: 13.5px; }
.tago { font-size: 12px; color: var(--muted); }
.tvia { font-size: 11px; color: var(--muted); border: 1px solid var(--line);
        border-radius: 999px; padding: 1px 7px; }

/* The one place long text lives, so the one place that needs real line length
   and real spacing between paragraphs. */
.talkmsgs .tbody { font-size: 14.5px; line-height: 1.55; overflow-wrap: anywhere; }

/* The app's own lines — 'Craig added Dave'. Quiet, centred, and unmistakably
   not somebody speaking. */
.talkmsgs > li.tsys { text-align: center; font-size: 12.5px; color: var(--muted);
                      background: none; padding: 9px 16px; }

/* A thread in the list.
   The whole card is still the link, but the card is now a container and the
   link is a child of it — because an archive button had to go on it and a form
   cannot live inside an <a>. The padding therefore moves from the card to the
   link, so that the clickable area is the whole card exactly as before. */
.tthread { position: relative; display: block; padding: 0; color: inherit; }
.tthread-a { display: block; padding: 13px 16px; text-decoration: none; color: inherit;
             /* room for the button, so a long title never runs under it */
             padding-right: 52px; }
.tthread + .tthread { margin-top: 10px; }

/* Archive, on the right of the card, without opening the conversation.
   Sits above the link rather than beside it, so the card keeps one large
   target and the button keeps its own. Quiet until the card is hovered or the
   button itself is focused: it is a per-card control and twenty of them
   shouting down the page would be worse than the trip to open each thread. */
.tarch { position: absolute; top: 9px; right: 9px; margin: 0; z-index: 2; }
.tarch button {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; cursor: pointer;
  border: 1px solid transparent; border-radius: 9px;
  background: transparent; color: var(--muted);
  opacity: .35; transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.tarch svg { width: 18px; height: 18px; }
.tthread:hover .tarch button { opacity: 1; }
.tarch button:hover { background: var(--surface-2); color: var(--ink); border-color: var(--line); }
/* Keyboard: it must become fully visible when tabbed to, or it is a control
   only a mouse can find. */
.tarch button:focus-visible { opacity: 1; outline: 2px solid var(--navy); outline-offset: 1px; }
/* A touch screen has no hover, so it is simply always there. */
@media (hover: none) { .tarch button { opacity: .8; } }
.tline { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tline b { font-size: 15px; }
.tline .tago { margin-left: auto; }
.tprev { margin-top: 3px; font-size: 13.5px; color: var(--ink-2); overflow-wrap: anywhere; }
.tprev .twho { color: var(--muted); }
/* Unread. A left edge rather than a background: it survives dark mode, it
   survives the hover tint, and it lines up down the list so five unread threads
   read as one shape instead of five. */
.tthread.tnew { border-left: 3px solid var(--navy); }
.tthread.tnew .tline b { font-weight: 700; }

.tpeople { list-style: none; margin: 0; padding: 0; font-size: 13.5px; }
.tpeople li { display: flex; align-items: baseline; gap: 8px; padding: 4px 0;
              border-bottom: 1px solid var(--line-soft); }
.tpeople li:last-child { border-bottom: 0; }
.tquiet { font-size: 11.5px; color: var(--muted); margin-left: auto;
          border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px; }
.tpick label.pick i.tquiet { margin-left: 6px; border: 0; padding: 0; font-style: normal; }

/* The who-to-pick list. Capped and scrolled: a church with three hundred people
   in it would otherwise push the message box off the bottom of the screen, and
   the box is the point of the page. */
.tpick { max-height: 260px; overflow-y: auto; border: 1px solid var(--line);
         border-radius: var(--radius); padding: 8px 10px; }
.tpick label.pick { display: flex; align-items: center; gap: 8px; padding: 3px 0;
                    text-transform: none; letter-spacing: 0; font-weight: 500;
                    font-size: 13.5px; color: var(--ink-2); }
.tpick input[type="checkbox"] { width: auto; margin: 0; }

/* On the public page the thread is the whole page, so it gets a card's edges
   of its own rather than sitting loose on the background. */
.pubtalk { background: var(--surface); border: 1px solid var(--line);
           border-radius: var(--radius); overflow: hidden; margin-bottom: 13px;
           box-shadow: var(--shadow); }

/* A project's conversations, on its card. Titles and a button, never the
   messages — the Ops Board is a page anybody with a desk can open and a thread
   belongs to whoever is in it. */
.projtalk { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
            padding: 9px 16px; border-top: 1px solid var(--line-soft);
            background: var(--surface-2, transparent); }
.projtalk a { font-size: 12.5px; font-weight: 600; text-decoration: none;
              border: 1px solid var(--line); border-radius: 999px; padding: 3px 10px; }
.projtalk a:hover { border-color: var(--navy-line); }
.projtalk form { margin-left: auto; }

/* ---- Passkeys and alerts ------------------------------------------------------
   Both are set up once per device and then never thought about again, so they
   sit at the bottom of a desk and are styled to be findable rather than
   noticeable. */
.devcols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
           gap: 13px; align-items: start; }

/* The passkey door under the sign-in form. Hidden in the markup and revealed by
   keys.js only once the browser has admitted it can do this — a button that
   cannot work is worse than no button, because "I tapped it and nothing
   happened" is where a person gives up. */
.pkdoor { margin-top: 14px; text-align: center; }
.pkdoor .pkor { display: block; font-size: 12px; color: var(--muted);
                text-transform: uppercase; letter-spacing: .12em; margin-bottom: 10px; }
.pkdoor .btn { width: 100%; }
.pkdoor .note { margin-top: 9px; }
.pkbad { color: var(--crit); }

/* The invitation to install. A bar rather than a card, because it sits above
   the page's own content and must read as an aside — and dismissible, because
   a bar that returns every morning is a bar nobody sees on the day it matters.
   Hidden in the markup; keys.js shows it only when it has something true to
   say on that device. */
.installbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
              background: var(--navy-soft); border: 1px solid var(--navy-line);
              border-radius: var(--radius); padding: 11px 15px; margin-bottom: 16px;
              box-shadow: var(--shadow-sm); }
.installbar .ib-say { flex: 1 1 260px; min-width: 0; font-size: 13.5px; line-height: 1.5; }
.installbar .ib-say > b { display: block; font-size: 14.5px; }
/* On a phone: two short lines and a small button beside them, never a card
   that fills the top of the screen (Craig, 22 Sept 2026). */
@media (max-width: 700px) {
  .installbar { flex-wrap: nowrap; gap: 10px; padding: 8px 10px 8px 13px; margin-bottom: 12px; }
  .installbar .ib-say { flex: 1 1 auto; font-size: 12.5px; line-height: 1.4; }
  .installbar .ib-say > b { font-size: 13.5px; }
  .installbar .ib-do { flex: none; gap: 6px; }
  .installbar .ib-do .btn { padding: 6px 11px; font-size: 12.5px; }
}
.installbar .ib-say span { color: var(--ink-2); }
.installbar .ib-do { display: flex; align-items: center; gap: 10px; margin-left: auto; }
@media print { .installbar { display: none; } }

/* Who a conversation is with, under its name in the list. Quieter than the
   preview beneath it: the preview is what was said, this is context for it. */
.twith { margin-top: 2px; font-size: 12.5px; color: var(--muted); }

/* Going back, as a control rather than a label.
   The eyebrow style every other page uses is right for "TEAM ONE" above a
   heading — it is a caption. Here it sits above a conversation title and is a
   thing people press dozens of times a day, and as small gold capitals it read
   as decoration and got lost. A pill is unmistakably pressable. */
.backpill { display: inline-flex; align-items: center; gap: 6px;
            font-size: 12.5px; font-weight: 600; text-decoration: none;
            color: var(--navy); background: var(--navy-soft);
            border: 1px solid var(--navy-line); border-radius: 999px;
            padding: 5px 13px; margin-bottom: 10px;
            box-shadow: var(--shadow-sm);
            transition: box-shadow .14s ease, transform .14s ease; }
.backpill:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.backpill:active { box-shadow: var(--shadow-sm); transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .backpill { transition: none; }
  .backpill:hover, .backpill:active { transform: none; }
}

/* A heading inside the who-to-pick list. Team One leads are a different kind of
   person from the staff above them — no account, reached only by email — and a
   flat list of fifty names with no break in it is a list nobody reads to the
   bottom of. */
.tpick .pickhead { font-size: 11px; font-weight: 700; letter-spacing: .1em;
                   text-transform: uppercase; color: var(--muted);
                   margin: 10px 0 4px; padding-top: 8px;
                   border-top: 1px solid var(--line-soft); }
.tpick .pickhead:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* The three ways to add somebody to a Team One role. A fieldset rather than a
   row of radios, because the choice has real consequences — one of them sends
   an email and makes a login — and each option needs a line explaining what it
   actually does. */
.holderdo { border: 1px solid var(--line); border-radius: var(--radius);
            padding: 12px 14px; margin: 4px 0 14px; }
.holderdo legend { font-size: 11px; font-weight: 700; letter-spacing: .1em;
                   text-transform: uppercase; color: var(--muted); padding: 0 6px; }
.holderdo label.pick { display: flex; align-items: flex-start; gap: 9px;
                       padding: 6px 0; text-transform: none; letter-spacing: 0;
                       font-weight: 500; color: var(--ink); }
.holderdo label.pick + label.pick { border-top: 1px solid var(--line-soft); }
.holderdo input[type="radio"] { width: auto; margin: 3px 0 0; flex: none; }
.holderdo label.pick b { display: block; font-size: 14px; }
.holderdo label.pick i { display: block; font-style: normal; font-size: 12.5px;
                         color: var(--muted); line-height: 1.45; margin-top: 1px; }

/* The fifth calendar layer: the signed-in person's own tasks, on the day they
   are due.
 *
 * Gold rather than one of the status colours, because this layer is not a
 * status — it is "this one is yours". Gold is already what Worksite uses for
 * the personal thread (the desk, the roundup), so a gold chip on a Thursday
 * reads as mine without a legend. Overdue switches to crit, which is the one
 * thing about a task a month grid can usefully shout.
 *
 * Outlined, not filled. The filled chips on this grid are things happening in
 * the building; a due date is a reminder about something that is not happening
 * anywhere, and giving it the same weight as a booked room makes a quiet week
 * look busy. */
.calgrid .chip.task { background: transparent; color: var(--gold-ink);
                      border: 1px solid var(--gold-ink); padding: 1px 4px; font-weight: 600; }
.calgrid .chip.task.late { color: var(--crit); border-color: var(--crit); }

.caltask { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px;
           padding: 9px 16px; background: var(--gold-soft);
           border-bottom: 1px solid var(--line-soft); }
.caltask .tkk { font-size: 10px; font-weight: 700; letter-spacing: .09em;
                text-transform: uppercase; color: var(--gold-ink); }
.caltask .tkp { font-size: 13px; color: var(--ink-2); }
.caltask .tkp b { color: var(--ink); font-weight: 600; }
.caltask .tkp.late b { color: var(--crit); }
.caltask .tkwhat { margin-left: 7px; color: var(--ink-2); }
.caltask .tkwhat::before { content: "· "; color: var(--muted); }
.caltask .mini { margin-left: auto; }

/* The To box on a conversation, and the suggestions under it.
 *
 * The panel is absolutely positioned inside .tobox so it floats over whatever
 * is beneath rather than shoving the form down a line every time somebody
 * types — a form that jumps while you are looking at it is a form people stop
 * trusting. z-index above the cards and nothing else; it does not need to beat
 * the header. */
.tobox { position: relative; }
.tosug { position: absolute; left: 0; right: 0; top: calc(100% + 3px); z-index: 30;
         background: var(--surface); border: 1px solid var(--line);
         border-radius: var(--radius); box-shadow: var(--shadow);
         max-height: 268px; overflow-y: auto; padding: 4px; }
.tosugrow { display: flex; align-items: baseline; gap: 8px; width: 100%;
            padding: 7px 9px; border: 0; border-radius: 7px; background: transparent;
            text-align: left; font: inherit; color: var(--ink); cursor: pointer; }
.tosugrow b { font-weight: 600; font-size: 14px; }
.tosugrow i { font-style: normal; font-size: 12px; color: var(--muted);
              margin-left: auto; padding-left: 10px; white-space: nowrap; }
.tosugrow.on, .tosugrow:hover { background: var(--navy-soft); }
.tosugrow.on b, .tosugrow:hover b { color: var(--navy); }

/* The fold-away list of everybody, under the box. Open on the Messages page,
   shut on the desk and the board — see talk_starter(). */
.tolist { margin: -4px 0 14px; }
.tolist > summary { font-size: 13px; color: var(--navy); cursor: pointer;
                    padding: 4px 0; list-style: none; }
.tolist > summary::-webkit-details-marker { display: none; }
.tolist > summary::before { content: "▸ "; color: var(--muted); }
.tolist[open] > summary::before { content: "▾ "; }
.tolist .tpick { margin-top: 8px; }

/* Recent and new messages, on the desk and the Ops Board.
 *
 * Titles and one line of the last message, never a thread — the board is read
 * with people standing behind it. Unread rows carry the navy edge Worksite
 * already uses for "this one wants you", so the panel can be read at a glance
 * from across a desk. */
.talkrecent .trhead { display: flex; align-items: center; gap: 9px;
                      padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
.talkrecent .trhead h3 { margin: 0; font-size: 15px; }
.talkrecent .trbadge { background: var(--navy); color: var(--on-accent);
                       font-size: 11px; font-weight: 700; line-height: 1;
                       padding: 3px 7px; border-radius: 999px; }
.talkrecent .trhead .mini { margin-left: auto; }
.talkrecent .empty { padding: 16px; }
.talkrecent .trrow { display: block; padding: 11px 16px; text-decoration: none;
                     color: inherit; border-bottom: 1px solid var(--line-soft);
                     border-left: 3px solid transparent; }
.talkrecent .trrow:last-child { border-bottom: 0; }
.talkrecent .trrow:hover { background: var(--surface-2); }
.talkrecent .trrow.unread { border-left-color: var(--navy); background: var(--navy-soft); }
.talkrecent .trtop { display: flex; align-items: baseline; gap: 8px; }
.talkrecent .trtop b { font-size: 14px; color: var(--ink); }
.talkrecent .trn { margin-left: auto; background: var(--navy); color: var(--on-accent);
                   font-size: 10.5px; font-weight: 700; line-height: 1;
                   padding: 3px 6px; border-radius: 999px; }
.talkrecent .trwith { font-size: 12px; color: var(--muted); margin-top: 1px; }
.talkrecent .trlast { font-size: 12.5px; color: var(--ink-2); margin-top: 3px;
                      overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.talkrecent .trwho { font-weight: 600; color: var(--ink); }

/* What somebody attached to a reply they sent by email.
 *
 * A row, not a grid of thumbnails. The thread is a conversation and the file is
 * a thing referred to in it — "here is the signed form" — so it belongs on the
 * line under the words, at the size of a line, not as a picture competing with
 * them. Opening it is one tap; talkfile.php decides whether it renders or
 * downloads. */
.tfiles { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.tfile { display: inline-flex; align-items: baseline; gap: 7px;
         padding: 5px 10px; border: 1px solid var(--line);
         border-radius: 999px; background: var(--surface);
         font-size: 12.5px; text-decoration: none; color: var(--ink-2); }
.tfile:hover { border-color: var(--navy-line); background: var(--navy-soft); }
.tfile .tfkind { font-size: 10px; font-weight: 700; letter-spacing: .07em;
                 text-transform: uppercase; color: var(--gold-ink); }
.tfile .tfname { color: var(--ink); font-weight: 600;
                 max-width: 220px; overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; }
.tfile .tfsize { color: var(--muted); font-size: 11.5px; }

/* The archive, at the bottom of Events and Trips.
 *
 * A row per thing put away, with what to do with it on the right. Quiet on
 * purpose — this is a drawer, not a board, and on any ordinary day nothing in
 * it is what somebody came for. */
.arcrow { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
          padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
.arcrow:last-child { border-bottom: 0; }
.arcrow .arwhat { flex: 1 1 220px; min-width: 0; }
.arcrow .arwhat b { font-size: 14.5px; color: var(--ink); }
.arcrow .armeta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.arcrow .aracts { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; }

/* The two folding actions. Closed they are a link; open they are a small form
   sitting under the row it belongs to. */
.arfwd > summary, .ardel > summary { cursor: pointer; list-style: none; }
.arfwd > summary::-webkit-details-marker,
.ardel > summary::-webkit-details-marker { display: none; }
.arfwd[open], .ardel[open] { flex: 1 1 100%; }
.arfwd[open] > summary, .ardel[open] > summary { margin-bottom: 8px; }
.arfwd > form, .ardel > form { background: var(--surface-2);
                               border: 1px solid var(--line-soft); border-radius: var(--radius); }
/* Deleting is the one thing in Worksite that nothing undoes, and it is the one
   place a red border is worth spending. */
.ardel > form { background: var(--crit-soft); border-color: var(--crit); }
.mini.danger { color: var(--crit); border-color: var(--crit); }

/* A trip that is kept whatever happens: people are on it. Not a button, not
   disabled-looking — a statement. */
.arcrow .arkeep { font-size: 11.5px; color: var(--muted); font-style: italic;
                  padding: 5px 2px; white-space: nowrap; }

/* A message that was taken back.
 *
 * A marker, not a hole, and it has to look like an absence rather than like
 * something somebody said — italic and grey, no bubble. The reply underneath
 * saying "yes, agreed" still has something to point at. */
.talkmsgs .tgone { font-style: italic; color: var(--muted); font-size: 13px;
                   padding: 2px 0; }

/* Taking one back. Quiet, on your own messages only, and not something the eye
   lands on while reading. */
.tunsay { margin-top: 6px; opacity: .45; transition: opacity .12s; }
.talkmsgs li:hover .tunsay, .tunsay:focus-within { opacity: 1; }

/* Attaching a file to a message. */
.talkattach input[type="file"] { font-size: 13px; }

/* The document library.
 *
 * A list, not a grid of icons. Somebody comes here knowing roughly what the
 * thing is called, so the name is what has to be scannable and everything else
 * is small grey context under it. */
.libshelves { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }
.libshelves .libn { color: var(--muted); font-size: 11px; margin-left: 5px; }

.librow { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
          padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
.librow:last-child { border-bottom: 0; }
.librow .libwhat { flex: 1 1 260px; min-width: 0; }
.librow .libwhat a { text-decoration: none; }
.librow .libwhat b { font-size: 14.5px; color: var(--ink); }
.librow .libwhat a:hover b { color: var(--navy); }
.librow .libmeta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.librow .libnote { display: block; font-size: 12.5px; color: var(--ink-2); margin-top: 3px; }
.librow .libacts { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; }

/* Retired: off the list but still readable, which has to look like an absence
   rather than like something wrong. */
.librow.retired { background: var(--surface-2); }
.librow.retired .libwhat b { color: var(--muted); font-weight: 500; }
.librow .libold { font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
                  text-transform: uppercase; color: var(--warn);
                  border: 1px solid var(--warn); border-radius: 999px;
                  padding: 1px 6px; margin-left: 7px; }

/* ======================================================================
   ProjectSite, vendors, and the Sites landing page
   ====================================================================== */

/* ---- the stage ladder ---- */
.phasechip { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
             padding: 3px 9px; border-radius: 999px; background: var(--surface-2); color: var(--ink-2); }
.phasechip.ph-approved { background: var(--navy-soft); color: var(--navy); }
.phasechip.ph-bidding  { background: var(--gold-soft); color: var(--gold-ink); }
.phasechip.ph-work     { background: var(--gold-soft); color: var(--gold-ink); }
.phasechip.ph-punch    { background: var(--navy-soft); color: var(--navy); }
.phasechip.ph-done     { background: var(--good-soft); color: var(--good); }

.phaseline { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.phaseopt { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px;
            border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
            font-size: 13px; font-weight: 600; color: var(--ink-2); background: var(--surface); }
.phaseopt:hover { border-color: var(--navy-line); }
.phaseopt.on { background: var(--navy-soft); border-color: var(--navy-line); color: var(--navy); }
/* The radio itself stays in the document rather than display:none, so it is
   still focusable and a keyboard can still move through the ladder. */
.phaseopt input { margin: 0; }

/* ---- the four budget numbers ----
   Committed is the one that catches a church out, so it is the one drawn
   larger. A row of five identical tiles makes the reader work out which
   matters; the layout should already know. */
.bsum { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
.bnum span { display: block; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
             color: var(--muted); }
.bnum b { display: block; font-family: var(--display); font-size: 21px; font-weight: 700;
          line-height: 1.15; margin-top: 3px; font-variant-numeric: tabular-nums; }
.bnum.big b { font-size: 27px; }
.bnum em { display: block; font-size: 11px; color: var(--muted); font-style: normal; margin-top: 2px; }
.bnum b.bad  { color: var(--crit); }
.bnum b.good { color: var(--good); }
@media (max-width: 860px) { .bsum { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 520px) { .bsum { grid-template-columns: 1fr 1fr; } }

/* ---- the budget table ---- */
.btable { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.btable th { text-align: left; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
             color: var(--muted); font-weight: 600; padding: 10px 12px;
             border-bottom: 1px solid var(--line); }
.btable td { padding: 11px 12px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.btable tr:last-child td { border-bottom: 0; }
.btable .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.btable .bcat td { background: var(--surface-2); font-size: 10.5px; font-weight: 700;
                   letter-spacing: .07em; text-transform: uppercase; color: var(--ink-2);
                   padding: 7px 12px; }
.btable .bnote { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.btable .muted { color: var(--muted); }
@media (max-width: 700px) {
  /* Vendor is the column to lose first — it is on the line's own note anyway,
     and the three money columns are the reason anybody opened this. */
  .btable th:nth-child(2), .btable td:nth-child(2) { display: none; }
  .btable th, .btable td { padding: 9px 8px; }
}

/* ---- vendors on a project, and the vendor list ---- */
.vrow, .frow, .lrow, .mrow2 { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
                              padding: 13px 16px; border-bottom: 1px solid var(--line-soft); }
.vrow:last-child, .frow:last-child, .lrow:last-child, .mrow2:last-child { border-bottom: 0; }
.vrow.retired { background: var(--surface-2); }
.vwhat, .fwhat, .lwhat { flex: 1 1 280px; min-width: 0; }
.vwhat b, .fwhat b, .lwhat b { font-size: 14.5px; color: var(--ink); }
.vmeta, .fmeta, .lmeta, .mmeta { display: block; font-size: 12px; color: var(--muted); margin-top: 3px; }
.vscope, .lbody { display: block; font-size: 12.5px; color: var(--ink-2); margin-top: 4px; }
.vacts, .facts { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; }
.fwhat a, .lwhat a { text-decoration: none; }

.vstatus { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
           padding: 2px 8px; border-radius: 999px; margin-left: 6px;
           background: var(--surface-2); color: var(--ink-2); }
.vstatus.st-hired, .vstatus.st-working { background: var(--navy-soft); color: var(--navy); }
.vstatus.st-done { background: var(--good-soft); color: var(--good); }

/* Insurance. Three shades of not-fine rather than a tick or a cross: "we have
   never had one" and "it ran out on Tuesday" want different conversations. */
.vcoi { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; margin-left: 6px; }
.vcoi.bad  { background: var(--crit-soft); color: var(--crit); }
.vcoi.warn { background: var(--warn-soft); color: var(--warn); }
.vcoi.good { background: var(--good-soft); color: var(--good); }

.fshared { display: inline-block; font-size: 11px; font-weight: 600; margin-top: 4px;
           padding: 2px 8px; border-radius: 999px; background: var(--gold-soft); color: var(--gold-ink); }

/* ---- the log ---- */
.lrow { border-left: 3px solid transparent; }
.lrow.k-decision { border-left-color: var(--gold); }
.lrow.k-budget   { border-left-color: var(--navy-line); }
.lrow.k-phase    { border-left-color: var(--line); }
.lamt { display: inline-block; font-size: 13px; font-weight: 700; margin-left: 8px;
        font-variant-numeric: tabular-nums; color: var(--navy); }

/* ---- tasks on a ProjectSite ---- */
.trow { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
        padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
.trow:last-child { border-bottom: 0; }
.trow.done { opacity: .58; }
.twhat { flex: 1 1 260px; min-width: 0; }
.twhat a { text-decoration: none; font-size: 14px; font-weight: 600; color: var(--ink); }
.twhat a:hover { color: var(--navy); }
.tmeta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.tmeta .over { color: var(--crit); font-weight: 600; }
.tmeta .soon { color: var(--warn); font-weight: 600; }
.tserve { font-size: 11.5px; color: var(--gold-ink); font-weight: 600; align-self: center; }

/* A small label beside a name: "child", "Member", "started it".
   It only existed scoped inside .announcelog, so every other use rendered as
   bare text running into the name next to it — which is what it was doing on
   the directory and on a ProjectSite's member list. */
.tag { display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .06em;
       text-transform: uppercase; padding: 2px 8px; border-radius: 999px;
       background: var(--navy-soft); color: var(--navy); margin-left: 7px;
       vertical-align: 1px; }
.tag.quiet { background: var(--surface-2); color: var(--muted); }
.mrow2 .tag { margin-left: 6px; }
.warnbox { border-left: 3px solid var(--warn); background: var(--warn-soft); color: var(--warn);
           font-size: 13.5px; margin-bottom: 14px; }
.check { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px;
         font-weight: 500; color: var(--ink-2); padding-top: 26px; }

/* ---- the Sites landing page ----
   The page behind the dropdown, and the reason the dropdown can be pure CSS.
   Also the better first thing for somebody who has never used Worksite: a menu
   teaches nothing, and a card with a sentence on it teaches what the thing is
   for. */
.opsgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
           gap: 14px; margin-top: 4px; }
.opscard { display: block; text-decoration: none; background: var(--surface);
           border: 1px solid var(--line); border-radius: var(--radius); padding: 18px 20px;
           box-shadow: var(--shadow-sm); position: relative;
           transition: box-shadow .14s ease, transform .14s ease, border-color .14s ease; }
.opscard:hover { box-shadow: var(--shadow); transform: translateY(-2px); border-color: var(--navy-line); }
.opscard .opsname { font-family: var(--display); font-size: 17px; font-weight: 700; color: var(--ink); }
.opscard .opssub { display: block; font-size: 11px; font-weight: 600; letter-spacing: .07em;
                   text-transform: uppercase; color: var(--gold-ink); margin-top: 4px; }
.opscard .opsblurb { display: block; font-size: 13px; color: var(--muted); margin-top: 9px;
                     line-height: 1.5; }
.opscard .badge { position: absolute; top: 16px; right: 16px; }
.opscard .opswhy { display: block; font-size: 11.5px; color: var(--warn); font-weight: 600;
                   margin-top: 8px; }

/* ---- Settings ---- */
.healthstrip { border-left: 3px solid var(--good); font-size: 14px; }
.healthstrip.warn { border-left-color: var(--warn); }
.healthstrip.bad  { border-left-color: var(--crit); }
.healthstrip b { display: block; margin-bottom: 2px; }

.setrow { display: flex; align-items: flex-start; gap: 13px; flex-wrap: wrap;
          padding: 15px 16px; border-bottom: 1px solid var(--line-soft); }
.setrow:last-child { border-bottom: 0; }
.setdot { width: 9px; height: 9px; border-radius: 50%; margin-top: 6px; flex: 0 0 auto;
          background: var(--muted); }
.setdot.good { background: var(--good); }

/* ---- the green lights breathe -------------------------------------------
   Craig: "animate the green status lights — a radiating circle expanding
   outside the green dot."

   GREEN ONLY, and that restraint is the whole design. A green light says
   something is working, and a slow ring going out from it says it is working
   NOW rather than having worked at some point before the page was drawn —
   which on a page of scheduled jobs and connections is exactly the difference
   somebody is looking for. Put the same ring on amber and red and it becomes
   decoration: three things pulsing at once is a fairground, and the eye stops
   reading any of them.

   Drawn on a ::after, so the ring expands OUTSIDE the dot without the dot
   itself moving and without the row reflowing — the pseudo-element is
   absolutely positioned and takes no space. Four seconds, and it spends most
   of that invisible: a two-second cycle would be a heartbeat monitor.

   A global prefers-reduced-motion rule further up turns every animation in
   this file off, so nothing else is needed here for somebody who has asked for
   stillness. */
.setdot.good, .sysdot.good i { position: relative; }
.setdot.good::after, .sysdot.good i::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid var(--good);
  animation: setdot-ring 4s cubic-bezier(.2, .6, .35, 1) infinite;
  pointer-events: none;
}
@keyframes setdot-ring {
  0%       { transform: scale(1);   opacity: .55; }
  /* Out and gone within the first third; the rest of the cycle is the rest
     the eye needs before it stops noticing. */
  35%, 100% { transform: scale(3.2); opacity: 0; }
}
.setdot.warn { background: var(--warn); }
.setdot.bad  { background: var(--crit); }
.setdot.off  { background: var(--line); }
.setwhat { flex: 1 1 320px; min-width: 0; }
.setwhat b { font-size: 14.5px; color: var(--ink); }
.setstate { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
            padding: 2px 8px; border-radius: 999px; margin-left: 8px;
            background: var(--surface-2); color: var(--muted); }
.setstate.good { background: var(--good-soft); color: var(--good); }
.setstate.warn { background: var(--warn-soft); color: var(--warn); }
.setstate.bad  { background: var(--crit-soft); color: var(--crit); }
.setsay   { display: block; font-size: 13px; color: var(--ink-2); margin-top: 5px; }
.setwhat2 { display: block; font-size: 12px; color: var(--muted); margin-top: 3px; }
.setfix   { display: block; font-size: 12.5px; color: var(--warn); margin-top: 6px;
            padding-left: 10px; border-left: 2px solid var(--warn-soft); }
.setacts  { display: flex; align-items: flex-start; gap: 8px; }

/* ---- Directory ---- */
.dirfresh { display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
            font-size: 12.5px; color: var(--muted); margin: 0 0 14px; }
.dirfresh.bad, .dirfresh.never { color: var(--crit); font-weight: 600; }
.dirfresh .setdot { margin-top: 0; }
.dirfresh .inlineform { margin-left: 4px; }

.dirrow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
          padding: 11px 16px; border-bottom: 1px solid var(--line-soft); }
.dirrow:last-child { border-bottom: 0; }
.dirrow.on { background: var(--navy-soft); }
.dirwho { flex: 1 1 260px; min-width: 0; }
.dirwho a { text-decoration: none; }
.dirwho b { font-size: 14.5px; color: var(--ink); }
.dirwho a:hover b { color: var(--navy); }
.dirformal { font-size: 12px; color: var(--muted); margin-left: 7px; }
.dirmeta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
/* The family name is a link now, but a quiet one: it sits under a person's
   name as a label, and a blue underline there would compete with the name
   itself. It announces itself on hover, which is when it matters. */
a.dirmeta { text-decoration: none; }
a.dirmeta:hover, a.dirmeta:focus-visible { color: var(--ink); text-decoration: underline; }
/* The sync breakdown beside the freshness line — administrators only, and
   deliberately the same weight as the rest of that line rather than louder. */
.dircount { color: var(--muted); }
.dirhow { display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
          font-size: 13px; text-align: right; }
.dirhow a { text-decoration: none; color: var(--ink-2); }
.dirhow a:hover { color: var(--navy); text-decoration: underline; }
.dirhow .muted { color: var(--muted); font-size: 12px; }
.empty.bad { color: var(--crit); }
@media (max-width: 560px) {
  .dirhow { align-items: flex-start; text-align: left; }
}

/* ---- pager ---- */
.pager { display: flex; align-items: center; justify-content: space-between; gap: 16px;
         flex-wrap: wrap; margin-top: 16px; }
.pagernav, .pagerper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pagerlabel { font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
              color: var(--muted); font-weight: 600; margin-right: 2px; }
.pagegap { color: var(--muted); font-size: 13px; padding: 0 2px; }
/* A dead Previous on page one stays in place rather than vanishing — a row of
   controls that changes width as you page through is one you have to re-aim at
   every time. */
.mini.off { opacity: .38; pointer-events: none; }
@media (max-width: 560px) {
  .pager { justify-content: flex-start; }
}

/* A form that has stopped saving.
   Somebody else is in the same sheet, so this form's writes would land on top
   of theirs and it has stopped posting. The status line says so — but a person
   entering rows is looking at the rows, not at a caption above them, so the
   form itself has to look different. Not an animation and not red text
   everywhere: a warning edge and a wash, the way a disabled thing looks. */
form.saveblocked {
  border: 2px solid var(--crit);
  border-radius: 12px;
  background: color-mix(in srgb, var(--crit) 6%, transparent);
  padding: 10px;
}
@supports not (background: color-mix(in srgb, red 5%, transparent)) {
  form.saveblocked { background: #fff6f6; }
}

/* A tick box somebody may look at but not press — a task on a ProjectSite
   drawn for a reader. Same square as everywhere else so the list still reads
   as done and not-done, without inviting a press that would be refused. */
.tick.flat { cursor: default; opacity: .7; }

/* ---- the mark at the foot of every page -----------------------------------
   Craig: the logo at the bottom of every page, with a small looping animation,
   inconspicuous.

   INCONSPICUOUS IS THE SPECIFICATION. The same dozen people will see this ten
   thousand times, so the numbers below are deliberately almost nothing: six
   seconds, three percent of scale, a third of a step of opacity. Anything with
   a rhythm quick enough to notice becomes something to resent by Wednesday.

   ONE COLOUR, AND IT IS THE PAGE'S. Everything in footermark.svg is
   currentColor now, so the mark is ink on a light page and light ink on a dark
   one and there is nothing theme-specific in the file. The orange came out:
   at watermark weight a brand colour reads as a faded logo rather than a quiet
   one, which is the opposite of what a watermark is for.

   WHY NOT SCALE. The mark used to breathe — grow three percent, fade in and
   out. That is the right animation for a status light, where the thing being
   said is "this is alive right now". It is the wrong one for a watermark,
   because anything that changes SIZE keeps pulling the eye back to check
   whether the page moved. A sheen changes nothing's position or size: a soft
   band of slightly more ink travels across the shape and is gone, which is
   what a watermark does when the light catches it.

   Built with a mask rather than an overlay so it works on a single-colour
   shape of any form and needs nothing added to the SVG. On a browser with no
   mask support the mark simply renders flat, which is a watermark that does
   not shimmer rather than a broken one.

   The global prefers-reduced-motion rule further up stops it, and the
   mask-position declared below is where it rests when stopped — off the edge,
   so a halted animation leaves the mark even rather than half-lit. */
:root { --mark: var(--muted); }
.pagemark {
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  padding: 34px 16px 26px;
  color: var(--mark);
}
/* The copyright line. Lighter than the mark above it and smaller than anything
   else on the page, because it is the one piece of text in the application
   nobody is ever looking for — it needs to be findable and never noticed.
   No sheen on it: a mask travelling over words makes them briefly harder to
   read, which is a fair price for a shape and not for a sentence. */
.pagemark-c {
  font-size: 11.5px; letter-spacing: .02em;
  color: var(--muted); opacity: .62;
}
.pagemark-in {
  display: block; width: 116px; line-height: 0;
  /* Watermark weight. Low enough that you stop seeing it, high enough that it
     is not a smudge on a phone in daylight. */
  opacity: .34;

  -webkit-mask-image: linear-gradient(100deg,
      rgba(0,0,0,.55) 42%, rgba(0,0,0,1) 50%, rgba(0,0,0,.55) 58%);
          mask-image: linear-gradient(100deg,
      rgba(0,0,0,.55) 42%, rgba(0,0,0,1) 50%, rgba(0,0,0,.55) 58%);
  -webkit-mask-size: 300% 100%;  mask-size: 300% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  /* THE ONLY TWO SAFE RESTING POSITIONS ARE 0% AND 100%, and getting this
     wrong made the mark disappear entirely for eight of every nine seconds.
     With mask-size 300%, a percentage position is measured against
     (element - image) = -200% of the element, so a friendly-looking -60%
     puts the image 120% of the way off to the right and the element is
     masked out completely. At 0% the visible window is the first third of the
     gradient, which is flat; at 100% it is the last third, also flat. Both
     cover the mark evenly, and the band lives outside the window at each. */
  -webkit-mask-position: 0% 0;  mask-position: 0% 0;

  animation: pagemark-sheen 9s ease-in-out infinite;
}
.pagemark-in svg { width: 100%; height: auto; display: block; }
@keyframes pagemark-sheen {
  /* 100% to 0% walks the band left to right across the mark. It is across
     within the first half; the rest of the cycle is flat, which is most of the
     nine seconds and is the whole point. */
  0%        { -webkit-mask-position: 100% 0; mask-position: 100% 0; }
  45%, 100% { -webkit-mask-position: 0% 0;   mask-position: 0% 0; }
}
/* On paper it is ink for nothing. */
@media print { .pagemark { display: none; } }

/* ---- meetings that come round again ---------------------------------------
   The repeat box on the request form, and the "for N times" beside it. Folded
   away by default because most meetings happen once, so this is only ever seen
   by somebody who went looking for it. */
.meetrepeat { margin: 4px 0 12px; }
.meetrepeat > summary { cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
.meetrepeat > summary:hover { color: var(--ink); }
.inlinelabel { display: inline-flex; align-items: center; gap: 7px;
               font-size: 13.5px; color: var(--ink-2); white-space: nowrap; }
/* A past meeting reads as finished rather than as one more thing to do. The
   card already carries .past; this is the folder it now sits in. */
.meetcard.past { opacity: .78; }
.meetcard.past header h3 { font-weight: 600; }
/* "every week · 7 more after this" — the line that stops one card standing in
   for eight meetings without saying so. */
.mstate.rep { background: var(--surface-2); color: var(--ink-2); }

/* The volunteer's own link, shown on the task so the office can read it out
   over the phone. Wraps rather than overflows — it is long, and a link that
   runs off the edge of the card cannot be read aloud. */
.tokline { display: inline-block; font-size: 12.5px; color: var(--muted);
           background: var(--surface-2); border: 1px solid var(--line);
           border-radius: 8px; padding: 6px 9px; overflow-wrap: anywhere;
           max-width: 100%; }

/* Team members needed — the open positions on ServeSite.
   Set apart from the one-off jobs above them by a rule and a heading, because
   they are a different kind of ask: a place on a team rather than an hour of
   moving chairs. */
.sgroup { font-size: 19px; margin: 0 0 8px; padding-top: 22px;
          border-top: 1px solid var(--line); }

/* Team Member Needed, on the Team One card. */
.rposted { font-size: 12.5px; color: var(--good); font-weight: 600; margin: 8px 0 6px; }
.rpost > summary { cursor: pointer; color: var(--ink-2); font-weight: 600; margin-top: 8px; }
.rpost > summary:hover { color: var(--ink); }
.rpost .pad { padding: 12px 0 0; }
/* "oldest 14 days" turns red on its own. The number that matters about people
   waiting to hear back is not how many there are but how long the worst one
   has waited — a count hides exactly the case that matters. */
.sechead .hint.bad { color: var(--crit); font-weight: 700; }

/* A text shown as it will arrive, on the second screen of the all-church send.
   A bubble rather than a box you can type in: the job of that screen is to make
   somebody see the MESSAGE rather than their own draft. */
.smspreview {
  max-width: 24rem; padding: 12px 15px; border-radius: 18px 18px 18px 5px;
  background: var(--navy-soft); color: var(--ink);
  font-size: 15.5px; line-height: 1.45; overflow-wrap: anywhere;
}

/* ---- what has already happened, folded underneath ------------------------
   Two folds, one on the Calendar page and one in the meetings section under
   it, and they are deliberately identical: the same shape means somebody who
   opens one already knows what the other is.

   Craig's point was that past things in amongst coming ones are confusing. The
   fix is not to hide them — "when was that meeting" is a real question and the
   answer has to stay on the page — but to put them below and behind one click,
   with a summary that says how many so nobody has to open it to find out
   whether it is worth opening.

   Quiet, and not a button. A <summary> that looks like a button invites a
   press; this one looks like what it is, a line you can open. */
.calpast, .meetpast { margin-top: 16px; }
.calpast > summary, .meetpast > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  padding: 9px 2px; font-size: 13.5px; font-weight: 600; color: var(--muted);
  border-top: 1px solid var(--line-soft);
}
.calpast > summary::-webkit-details-marker,
.meetpast > summary::-webkit-details-marker { display: none; }
/* The chevron, drawn rather than a character, so it turns when it opens. */
.calpast > summary::before, .meetpast > summary::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-right: 1px;
  border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
  transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform .15s ease;
}
.calpast[open] > summary::before, .meetpast[open] > summary::before {
  transform: rotate(45deg) translate(-2px, -2px);
}
.calpast > summary:hover, .meetpast > summary:hover { color: var(--ink-2); }
.calpast-hint, .meetpast-hint {
  font-weight: 400; font-size: 12.5px; color: var(--muted); opacity: .8;
}
/* Everything inside reads as finished — the same treatment a past meeting card
   already carries, applied to the whole fold so the day cards match. */
.calpast > .calday, .calpast > *:not(summary) { opacity: .82; }
.calpast > *:not(summary):first-of-type { margin-top: 10px; }
.meetpast > *:not(summary) { opacity: .82; }

/* ---- Messages: private, the church, a group ---------------------------------
   Craig: "I want it to be very clear that they are texting or messaging a
   group, not just a private message." Three tabs, each saying in words what it
   is and who can see it, and a group text drawn in amber from the first screen
   to the send button, so it never looks like a thread. */
.msgtabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 22px; }
.msgtab { flex: 1 1 13rem; display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); text-decoration: none; }
.msgtab b { font-size: 14px; display: flex; align-items: center; gap: 6px; }
.msgtab span:not(.badge) { font-size: 12.5px; color: var(--muted); }
.msgtab:hover { border-color: var(--ink-2); }
.msgtab.on { border-color: var(--navy); box-shadow: inset 0 -3px 0 var(--navy); }
.msgtab-group.on { border-color: var(--warn); box-shadow: inset 0 -3px 0 var(--warn); }
.msgtab-texts.on { border-color: var(--gold); box-shadow: inset 0 -3px 0 var(--gold); }

.gtband { margin: -2px 0 14px; padding: 9px 13px; border-radius: 8px;
  background: var(--warn-soft); color: var(--warn); font-size: 13.5px;
  border-left: 4px solid var(--warn); }
.gtband b { letter-spacing: .06em; }
.gtform { border-top: 3px solid var(--warn); }
.gtlists { border: 0; padding: 0; margin: 0; }
.gtlists legend { font-weight: 700; font-size: 13.5px; margin-bottom: 8px; }
.gtlist { padding: 9px 12px; border: 1px solid var(--line); border-radius: 8px; margin-bottom: 7px; }
.gtlist label { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; cursor: pointer;
  text-transform: none; letter-spacing: 0; font-size: 14px; font-weight: 400; color: var(--ink); margin: 0; }
.gtlist .gtn { margin-left: auto; font-size: 12.5px; color: var(--muted); }
.gtlist .gtpar { margin: 7px 0 0 26px; font-size: 13px; color: var(--ink-2); }
.gtto { list-style: none; margin: 0 0 10px; padding: 0; }
.gtto li { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  padding: 7px 0; border-bottom: 1px solid var(--line-soft); }
.gtto li:last-child { border-bottom: 0; }
.gtto em { font-style: normal; font-size: 12px; color: var(--warn); }
.gtto span { margin-left: auto; color: var(--muted); font-size: 13px; }
.btn.gtsend { background: var(--warn); border-color: var(--warn); color: var(--on-accent, #fff); }
.gtwhole { margin-bottom: 12px; font-size: 13.5px; }
.gtrow.failed .gtstate { color: var(--crit); font-weight: 700; }
.gtrow.held .gtstate { color: var(--warn); font-weight: 600; }
.gtrow { display: flex; gap: 10px; align-items: flex-start; justify-content: space-between; }
.gtchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.gttable { width: 100%; border-collapse: collapse; font-size: 13px; }
.gttable th, .gttable td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--line-soft); }
.msg .ctx.gtre { color: var(--warn); font-style: normal; }
@media (max-width: 600px) {
  .msgtab { flex-basis: 100%; }
  .gtadm .addrow { flex-wrap: wrap; }
  .gttable { display: block; overflow-x: auto; }
}
/* The role page's Group texts box: one line per list, tickable by an admin. */
.gtroleboxes { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 8px; }
.gtrolebox { display: flex; gap: 9px; align-items: flex-start; padding: 9px 11px; margin: 0;
  border: 1px solid var(--line); border-radius: 8px; cursor: pointer;
  text-transform: none; letter-spacing: 0; font-size: 14px; font-weight: 400; color: var(--ink); }
.gtrolebox input { width: auto; margin: 3px 0 0; }
.gtrolebox span { display: flex; flex-direction: column; gap: 2px; }
.gtrolebox em { font-style: normal; font-size: 12px; color: var(--muted); }
/* One bubble per list on the check screen, and the header table. */
.gtbubbles { display: grid; gap: 16px; }
.gtbto { font-size: 12.5px; color: var(--muted); margin-bottom: 5px; }
.gtlist .gthd { font-size: 12.5px; color: var(--muted); }
.gthdr td { vertical-align: top; padding: 9px 8px; }
.gthdr input[name^="header"] { width: 100%; min-width: 11rem; }
.gtsign { display: inline-flex; gap: 6px; align-items: center; text-transform: none; letter-spacing: 0;
  font-size: 13.5px; font-weight: 400; color: var(--ink); margin: 0; white-space: nowrap; }
.gtsign input { width: auto; margin: 0; }
.gtex { font-size: 13px; color: var(--ink-2); white-space: normal; min-width: 14rem; }
@media (max-width: 700px) {
  .gthdr, .gthdr tbody, .gthdr tr, .gthdr td { display: block; width: 100%; }
  .gthdr tr:first-child { display: none; }
  .gthdr tr { border-bottom: 1px solid var(--line); padding: 8px 0; }
}
/* The whole church, folded at the top of Group texts: red rather than amber,
   because it is the one text that reaches the congregation. */
details.gtwhole { margin-bottom: 14px; border-top: 3px solid var(--crit); }
details.gtwhole > summary { cursor: pointer; padding: 13px 16px; display: flex; flex-wrap: wrap;
  gap: 6px 12px; align-items: baseline; list-style: none; }
details.gtwhole > summary::-webkit-details-marker { display: none; }
details.gtwhole > summary::before { content: '\25B8'; color: var(--muted); }
details.gtwhole[open] > summary::before { content: '\25BE'; }
details.gtwhole > summary .hint { margin-left: auto; font-size: 12.5px; color: var(--muted); }
details.gtwhole .gtn { font-size: 13px; color: var(--muted); }
.gtwholeband { background: var(--crit-soft); color: var(--crit); border-left-color: var(--crit); }
/* The live counter under a text box. */
.smscount { margin-top: 6px; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.smscount.warn { color: var(--warn); }
.smscount.bad { color: var(--crit); font-weight: 600; }
.smscount .smsnote { font-style: italic; }
.gtwhen { border: 0; padding: 0; margin: 12px 0 0; display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; }
.gtwhen legend { font-weight: 700; font-size: 13.5px; margin-bottom: 6px; width: 100%; }
.gtwhen label { display: inline-flex; gap: 6px; align-items: center; text-transform: none; letter-spacing: 0;
  font-size: 14px; font-weight: 400; color: var(--ink); margin: 0; }
.gtwhen label input { width: auto; margin: 0; }
.gtwhen input[type="datetime-local"] { width: auto; }
.gtwhen .note { width: 100%; margin: 0; }
.gtsched { background: var(--navy-soft); border-left: 3px solid var(--navy); color: var(--ink); }
.gtrep { color: var(--good); font-weight: 600; }
.gtrow.scheduled .gtstate { color: var(--navy); font-weight: 600; }
.gtrow.partial .gtstate { color: var(--crit); font-weight: 700; }
/* One conversation per person on Texts to the church. */
.txthread { margin-bottom: 12px; }
.txthread.waiting { border-left: 3px solid var(--gold); }
.txhead { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; padding: 12px 16px 4px; }
.txhead .tago { font-size: 12.5px; color: var(--muted); }
.txwait { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--gold); }
.txmsgs { list-style: none; margin: 0; padding: 6px 16px 10px; display: flex; flex-direction: column; gap: 8px; }
.txmsgs li { max-width: 34rem; }
.txin { align-self: flex-start; }
.txout { align-self: flex-end; text-align: right; }
.txbub { display: inline-block; text-align: left; padding: 9px 13px; border-radius: 16px; font-size: 14.5px;
  line-height: 1.42; overflow-wrap: anywhere; }
.txin .txbub { background: var(--surface-2); border-bottom-left-radius: 5px; }
.txout .txbub { background: var(--navy-soft); border-bottom-right-radius: 5px; }
.txwhen { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.txre { font-size: 12px; color: var(--warn); margin-bottom: 3px; }
.txsys { align-self: center; font-size: 12px; color: var(--muted); }
.txreply { padding: 4px 16px 6px; border-top: 1px solid var(--line-soft); }
.txreply textarea { margin-top: 10px; }
.txthread.archived { opacity: .85; }
.txreply .acts { display: flex; gap: 7px; margin-top: 8px; align-items: center; flex-wrap: wrap; }
.txreply .acts .hint { font-size: 12.5px; color: var(--muted); }

/* ---- live updates: the corner notices, and the "something new" bar ------- */
.livetray { position: fixed; top: 14px; right: 14px; z-index: 9000; display: flex; flex-direction: column;
  gap: 8px; width: min(22rem, calc(100vw - 28px)); pointer-events: none; }
.livetoast { pointer-events: auto; display: flex; flex-direction: column; gap: 2px; padding: 11px 14px;
  border-radius: 10px; background: var(--surface); color: var(--ink); text-decoration: none;
  border: 1px solid var(--line); border-left: 4px solid var(--navy);
  box-shadow: 0 8px 24px rgba(0,0,0,.14); opacity: 0; transform: translateY(-8px);
  transition: opacity .35s ease, transform .35s ease; }
.livetoast.in { opacity: 1; transform: none; }
.livetoast b { font-size: 13.5px; }
.livetoast span { font-size: 13px; color: var(--ink-2); overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
a.livetoast:hover { border-color: var(--navy); }
.livebar { display: flex; gap: 10px; align-items: center; justify-content: space-between; margin: 0 0 10px;
  padding: 8px 12px; border-radius: 8px; background: var(--navy-soft); color: var(--ink); font-size: 13.5px; }
.livefresh { animation: livefresh 1.6s ease; }
@keyframes livefresh { from { box-shadow: 0 0 0 3px var(--navy-soft); } to { box-shadow: 0 0 0 0 transparent; } }
@media (prefers-reduced-motion: reduce) {
  .livetoast { transition: none; transform: none; }
  .livefresh { animation: none; }
}
/* "Keep me signed in on this device for 2 weeks" on the sign-in form. */
.keepbox { display: flex; gap: 9px; align-items: flex-start; margin: 2px 0 14px; cursor: pointer;
  text-transform: none; letter-spacing: 0; font-size: 14px; font-weight: 500; color: var(--ink); }
.keepbox input { width: auto; margin: 3px 0 0; }
.keepbox small { display: block; font-size: 12.5px; font-weight: 400; color: var(--muted); margin-top: 2px; }

/* ---- spreadsheet import: the row-by-row check ---- */
.csvplan { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.csvplan th { text-align: left; font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
              color: var(--muted); padding: 10px 14px; border-bottom: 1px solid var(--line); }
.csvplan td { padding: 10px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
.csvplan tr:last-child td { border-bottom: 0; }
.csvplan td:first-child { color: var(--muted); font-variant-numeric: tabular-nums; width: 3.5em; }
.csvplan .csv-skip td:last-child { color: var(--crit); }
.csvplan .csv-same td { color: var(--muted); }

/* ---- Settings → This church: the gatherings ---- */
.gathrow { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
.gathrow select { width: auto; min-width: 9.5em; margin: 0; }
.gathrow span { font-size: 13px; color: var(--muted); }

/* ===========================================================================
   Feeling like an app (22 Sept 2026)
   Quick and quiet: nothing here takes longer than a third of a second, and
   anybody whose device asks for less motion gets none of it — the global
   prefers-reduced-motion rule near the top stops every animation and
   transition, and the pieces below are written so that "no animation" is
   simply the finished state.
   ========================================================================= */

/* 1. Moving between pages cross-fades instead of blanking. The bar at the top
   is named so the browser holds it still while the page under it changes.
   Browsers without this (older Safari, Firefox) navigate exactly as before. */
@view-transition { navigation: auto; }
.topbar { view-transition-name: ws-topbar; }   /* see the z-index note on .topbar above */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .18s; }
::view-transition-group(ws-topbar) { animation-duration: .18s; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* 3. The arrival. See the note in head(): only the first page in a tab, or
   the first after half an hour away. */
@keyframes ws-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.arrive .wrap > :not(.topbar):not(script) { animation: ws-rise .34s cubic-bezier(.2, .7, .2, 1) both; }
.arrive .wrap > :not(.topbar):nth-child(2) { animation-delay: .02s; }
.arrive .wrap > :not(.topbar):nth-child(3) { animation-delay: .06s; }
.arrive .wrap > :not(.topbar):nth-child(4) { animation-delay: .10s; }
.arrive .wrap > :not(.topbar):nth-child(5) { animation-delay: .14s; }
.arrive .wrap > :not(.topbar):nth-child(6) { animation-delay: .18s; }
.arrive .wrap > :not(.topbar):nth-child(n+7) { animation-delay: .22s; }

/* 4. Answering a touch. */
.mini, .msgtab, .navlinks a, .tick { transition: transform .08s ease, background-color .12s ease, border-color .12s ease; }
.btn:active { transform: translateY(1px) scale(.985); }
.mini:active, .msgtab:active { transform: scale(.96); }
.tick:active { transform: scale(.9); }

/* The tick draws itself: the box fills, then the mark strokes in. */
@keyframes ws-tickfill { 0% { transform: scale(.85); } 60% { transform: scale(1.08); } 100% { transform: scale(1); } }
@keyframes ws-tickmark { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } }
.tick.ticking { animation: ws-tickfill .26s ease both; }
.tick.ticking::after { animation: ws-tickmark .2s .08s ease both; }
/* …and the task crosses itself out, where it stands. */
li.taskrow .t { transition: color .25s ease; }

/* Sections glide open when somebody taps them, where the browser can (Chrome
   and Edge now; others open instantly as before). Only on a tap: app.js adds
   .glide for the moment of the click, so a section opened by the page itself
   — a folded past, a form reopened after saving — is simply open, at its full
   height, straight away. */
:root { interpolate-size: allow-keywords; }
details.glide::details-content { transition: height .22s ease, content-visibility .22s allow-discrete; height: 0; overflow: clip; }
details.glide[open]::details-content { height: auto; }

/* 5. Bars that appear after the page has drawn slide in, rather than
   shoving everything below them down in one jump. */
@keyframes ws-slidein { from { opacity: 0; max-height: 0; margin-top: 0; margin-bottom: 0; } to { opacity: 1; max-height: 240px; } }
.installbar:not([hidden]) { animation: ws-slidein .3s ease both; overflow: hidden; }

/* A church with no logo yet: its name where the logo goes, in the brand colour. */
.brandtext { display: block; font-family: var(--display); font-weight: 700; letter-spacing: -.01em;
             color: var(--navy); line-height: 1.1; max-width: 16em; }
@media (prefers-color-scheme: dark) { .brandtext { color: var(--ink); } }

/* The demo's frame: the shape of an announcement, in the accent colour rather
   than the alarm red, and a size down — it is a caption, not a warning. */
.announcebar.demobar .announce { border-color: var(--gold); background: var(--gold-soft); padding: 9px 14px 9px; }
.announcebar.demobar .announceflag { color: var(--gold-ink); }
.announcebar.demobar .announcebody { font-size: 14px; font-weight: 500; }
