/* gather — the app layer over the galley core.
 *
 * Cascade order: fonts.css → tokens.css → galley.css → this file. The
 * core owns the reset, the mono meta voice, the job-ticket header, the
 * imprint footer, forms, buttons and notices; everything below is what
 * gather adds on top — the numbered galley entry, the feed head, the
 * reply column, and (dormant) the embed slot Milestone B will light up.
 *
 * Ported 2026-08-04 from torc-art-apps `static/subbin/style.css`, which is
 * the shipped implementation of `tmp/mockups/gather-2-galley.html`. Where
 * the mockup and the working surface disagree, the reasons are written at
 * the selector that carries the difference.
 *
 * Cascade contract: consume tokens, never redefine them.
 */

/* ---- the job ticket ----
 *
 * galley.css draws the ticket itself (the 2px rule over, the hairline
 * under, `torc.art / gather` set mono in the corner). The one thing it has
 * no selector for is the name of the person holding it: a signed-in
 * member's display name, which sits in the ticket beside the links. It is
 * set in the serif rather than the mono voice on purpose — the meta voice
 * is uppercased, and a person's name is not a label. */

.nav-list .who {
  color: var(--muted);
  font-variant: small-caps;
  letter-spacing: 0.1em;
  font-size: var(--step--1);
}

/* ---- the imprint foot ----
 *
 * It names the binaries, and the one you are standing in is set in the
 * second ink. galley.css styles the footer nav but carries no rule for the
 * current page; the mockup does, and it is what makes the line an imprint
 * rather than a list of links. */

.footer-nav a[aria-current] { color: var(--accent); }

/* ---- the galley entry: the numbered-row pattern ----
 *
 * Any list that opts in numbers its entries in the margin with CSS
 * counters — `01`, `02`, mono, hard against the right edge of a 3.4rem
 * gutter. The HTML carries no numeral at all (v1-oracle §4): numbering is
 * positional, resets per rendered list, and is not tied to a row id. That
 * is the whole reason it survives a feed that drops its oldest entries.
 *
 * `decimal-leading-zero` pads to two digits and no further; past 99 it
 * simply counts on, which the hard-50 feed never reaches.
 */

.posts,
.replies ol {
  counter-reset: entry;
  list-style: none;
  padding: 0;
  margin: 0;
}

.post-card,
.reply {
  counter-increment: entry;
  position: relative;
  padding-left: 3.4rem;
}

.post-card::before,
.reply::before {
  content: counter(entry, decimal-leading-zero);
  position: absolute;
  left: 0;
  width: 2.3rem;
  text-align: right;
  font-family: var(--mono);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  color: var(--muted);
}
.post-card::before { top: calc(var(--space-m) + 0.3rem); }
.reply::before { top: calc(var(--space-s) + 0.25rem); }

/* ---- the feed head ---- */

.feed-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-bottom: var(--space-m);
}
.feed-head h1 { margin: 0; }
.feed-deck {
  flex-basis: 100%;
  order: 3;
  color: var(--muted);
  font-style: italic;
  margin: 0;
}
.feed-actions { order: 4; margin: var(--space-xs) 0 0; }

/* The order toggle. Two words and a middot in the mono meta voice; the
 * position you are standing in is set in ink and is not a link, so the
 * line reads as state rather than as a pair of buttons. */
.order {
  font-family: var(--mono);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 var(--space-2xs);
}
.order a { color: var(--muted); text-decoration: none; }
.order a:hover,
.order a:focus-visible { color: var(--accent); }
.order strong { color: var(--ink); font-weight: 400; }

/* ---- posts ---- */

.posts { margin-top: var(--space-2xs); border-top: 1px solid var(--rule); }
.post-card {
  padding-top: var(--space-m);
  padding-bottom: var(--space-m);
  border-bottom: 1px solid var(--line);
}
.post-card:last-child { border-bottom: 1px solid var(--rule); }

/* The slugline: who set it, and where it stands in its seven days. The
 * mockup runs `Mara Voss · tue 09.06` here and hangs a tiny `d.1/7` under
 * the entry number; gather runs the author and `day 03 / 07`, because the
 * lifecycle label is pinned copy (v1-oracle §2) and at eleven characters
 * it will not sit in a 2.3rem gutter. Same two facts, one line instead of
 * two — and the date the mockup shows is a fact gather's feed does not
 * carry, so it is not invented here. */
.post-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-s);
  flex-wrap: wrap;
  margin-bottom: var(--space-2xs);
}
.post-byline { margin: 0; }
.post-link,
.reply-author strong {
  color: var(--ink);
  text-decoration: none;
  font-variant: small-caps;
  letter-spacing: 0.1em;
  font-weight: 400;
  font-size: 1.05em;
}
.post-link:hover,
.post-link:focus-visible { color: var(--accent); }

/* The lifecycle slug and the reply count are the two numeric bits on the
 * page, and they are set in EB Garamond rather than in the mono voice on
 * purpose: this is where the register's old-style figures live, and
 * Inconsolata's figures are lining and monospaced by design. The
 * `oldstyle-nums` declaration is inert against the Fontsource subset
 * shipped here (no `onum` in its GSUB — see fonts/README.md); what
 * renders is EB Garamond's default set, which already sits like text
 * figures. The declaration states the intent and takes effect the day a
 * fuller cut is dropped in. */
.post-meta,
.post-foot {
  font-family: var(--font-serif), serif;
  font-variant-numeric: oldstyle-nums;
  font-feature-settings: "onum" 1;
  font-size: var(--step--1);
  letter-spacing: 0.06em;
  white-space: nowrap;
}
.post-meta {
  margin: 0;
  color: var(--accent);
}
.post-body {
  /* One <p> per entry; the line breaks come from here and never from an
   * inserted <br> (v1-oracle §9). */
  white-space: pre-wrap;
  margin: var(--space-2xs) 0 0;
  hyphens: auto;
}
.post-foot {
  margin: var(--space-s) 0 0;
  white-space: normal;
}
.post-foot a { color: var(--muted); text-decoration: none; }
.post-foot a:hover,
.post-foot a:focus-visible { color: var(--accent); }

.feed-empty {
  margin: var(--space-l) 0;
  padding: var(--space-m) 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  font-style: italic;
  color: var(--muted);
}

/* ---- a post on its own page ---- */

/* No rule over the entry here. On the feed the 2px rule opens the list;
 * on a post's own page the job ticket's own bottom rule is directly above
 * it, and a second line 8px under the first reads as a mistake. */
.post-detail {
  padding-top: var(--space-s);
  padding-bottom: var(--space-m);
  border-bottom: 1px solid var(--line);
}
.post-detail .post-body { font-size: var(--step-0); }
.post-detail .post-tools { margin: var(--space-s) 0 0; }

.replies { margin-top: var(--space-l); }
.replies ol { border-top: 1px solid var(--rule); }
.reply {
  padding-top: var(--space-s);
  padding-bottom: var(--space-s);
  border-bottom: 1px solid var(--line);
}
.reply:last-of-type { border-bottom: 1px solid var(--rule); }
.reply-author { margin: 0; color: var(--ink); }
.reply-body { white-space: pre-wrap; margin: var(--space-3xs) 0 0; }
.reply-tools { margin: var(--space-2xs) 0 0; }

/* ---- the write doors ---- */

/* Compose and reply are the same form in two places, so they are the same
 * object here: a mono label, a hairlined field on card stock, and the
 * button set like the rest of the meta voice (galley.css owns all three).
 * The character count is the browser's `maxlength`, which counts
 * characters where gather counts UTF-8 bytes — deliberately, as in v1
 * (src/content.ts). */
.write-form { margin-top: var(--space-m); }
.write-form textarea { min-height: 7rem; }
.write-form .form-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-s);
  flex-wrap: wrap;
}
.write-form .form-note {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--step--2);
  color: var(--muted);
  letter-spacing: 0.02em;
}

/* The anonymous door, and the two refusals behind it. A visitor who
 * cannot write is not shown a dead form; they are shown one line and the
 * one link that fixes it — and the link is set as a button so the eye
 * lands on the way out rather than on the refusal. */
/* No rule here either: this sits under either the post-detail's hairline
 * or the reply list's closing rule, and both already close the column. */
.signin { margin-top: var(--space-l); }
.gate { max-width: 46ch; }
.gate h1 { margin-top: var(--space-m); }
.gate .lede { font-size: var(--step-0); }
.gate .gate-door { margin: var(--space-m) 0 0; }

/* Delete sits under the thing it removes rather than in the chrome: it is
 * the only destructive control on the surface and it should read as a
 * decision, not a toolbar. galley.css already cuts `button.danger.small`
 * to size; all this needs is the room around it. */
.post-tools,
.reply-tools { line-height: 1; }

/* ---- the embed slot --------------------------------------------------
 *
 * Milestone B tips the plates in: an image per post or reply, dithered
 * into all seven stages at upload, served from R2 by (image, stage) with
 * the stage chosen from the embedding entry's age at render. Written
 * dormant in A5 and lit here with the markup that emits it.
 *
 * The register's embed is an indented block with a hairline of the second
 * ink down its left edge, and the plate itself is a bordered card on
 * `--bg-card` — the mockup's `.embed-image .plate`. `image-rendering:
 * pixelated` keeps the 1-bit blocks square as they scale; the click-
 * through line under the plate is where the full colour hides. */

.embed {
  margin: var(--space-s) 0 0;
  padding: 0.2rem 0 0.2rem var(--space-s);
  border-left: 2px solid var(--accent);
}
.embed-image .plate {
  display: inline-block;
  border: 1px solid var(--ink);
  background: var(--bg-card);
  padding: 0.4rem;
  margin-top: 0.3rem;
}
.embed-image img {
  display: block;
  max-width: 100%;
  height: auto;
  image-rendering: pixelated;
}
.embed figcaption {
  font-family: var(--font-serif), serif;
  font-style: italic;
  letter-spacing: normal;
  text-transform: none;
  color: var(--muted);
  margin: 0.45rem 0 0;
}
.embed-link { margin: 0.4rem 0 0; font-size: var(--step--2); }
.embed-link a { color: var(--accent); text-decoration: none; }
.embed-link a:hover { text-decoration: underline; }

/* ---- narrow ----
 * The mockup's own breakpoint: below 30rem the gutter costs more than the
 * numbering is worth, so the entry number comes inline above the slug. */

@media (max-width: 30rem) {
  .post-card,
  .reply { padding-left: 0; }
  .post-card::before,
  .reply::before {
    position: static;
    display: block;
    width: auto;
    text-align: left;
    margin-bottom: var(--space-3xs);
  }
  .post-head { gap: var(--space-3xs); }
}
