/* site/app/screens/news.css — the noticeboard.
 * Depends on tokens.css + ui.css + shell.css. Every colour, size, easing and duration comes
 * from a token; a hex written here would not follow the palette into dark mode.
 *
 * ⚠ EVERY CLASS IS PREFIXED nw-. Six screens load their CSS into one document, so an
 *   unprefixed .row or .card here would silently restyle another tab's list. The shared
 *   vocabulary (.sec, .empty, .price, .enter, .mono-nums) is used as-is and never redefined.
 *
 * This is the most EDITORIAL screen in the app, so Fraunces does more work here than
 * anywhere else — the pinned headline, every announcement headline, and the day numbers in
 * the week strip. The serif is what makes a noticeboard read as written rather than
 * generated, and it is the whole reason this tab does not feel like a feed.
 */

/* ═══ 1 · THE PINNED NOTICE ════════════════════════════════════════════════
 * ⚠ DISTINCT BY INVERSION. Navy card on the cream page; cream card on the navy page. A
 *   card that is navy in both themes disappears in dark mode, because the page behind it
 *   is already navy — that is the bug this pattern exists to avoid, and it is invisible to
 *   anyone reviewing in light mode only.
 */
.nw-pin {
  background: var(--navy);
  color: var(--cream);
  margin: 0 var(--gutter) 22px;
  padding: 18px 18px 16px;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .nw-pin { background: var(--cream); color: var(--navy); }
}
:root[data-theme="dark"] .nw-pin { background: var(--cream); color: var(--navy); }

/* Secondary ink inside the pin is derived from currentColor rather than a token, because
   the token for "dimmed ink" is wrong here by definition: the card's ground is inverted
   relative to the page, so the page's ink tokens are the ground colour, not the text. */
.nw-pin-kick {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 10px;
  font: 600 11px/1 var(--font-ui); letter-spacing: .09em; text-transform: uppercase;
  color: color-mix(in srgb, currentColor 66%, transparent);
}
.nw-pin-ico svg { width: 14px; height: 14px; display: block; }

.nw-pin-head {
  font-family: var(--font-display); font-weight: 600;
  font-size: 23px; line-height: 1.16; letter-spacing: -.014em;
  margin: 0; color: inherit; text-wrap: balance;
}
.nw-pin-body {
  margin: 9px 0 0;
  font: 400 15px/1.5 var(--font-ui);
  color: color-mix(in srgb, currentColor 82%, transparent);
}
.nw-pin-foot {
  margin: 13px 0 0;
  font: 500 12px/1 var(--font-ui); letter-spacing: .02em;
  font-variant-numeric: tabular-nums;
  /* ⚠ 64%, NOT 58% — AND THE TWO THEMES ARE NOT SYMMETRIC. Deriving the dim inks from
     currentColor is right (the card's ground is inverted, so the page's ink tokens are the
     ground here), but the same percentage does NOT buy the same contrast in both directions:
     cream dimmed over navy stays far from its ground, navy dimmed over cream collapses
     toward it. At 58% this measured 8.9 in light and 4.14 in dark — passing in the theme
     everyone reviews in and failing in the other. 64% measures 5.02 dark / 8.2 light, and is
     still dimmer than .nw-pin-kick above it (5.34) so the hierarchy on the card is intact.
     Any new dim level on this card has to be checked in DARK; light will always flatter it. */
  color: color-mix(in srgb, currentColor 64%, transparent);
}

/* ═══ 2 · THE WEEK ═════════════════════════════════════════════════════════ */

.nw-range {
  font: 500 12px/1 var(--font-ui); letter-spacing: .02em;
  font-variant-numeric: tabular-nums; color: var(--ink-tertiary);
}

.nw-strip {
  position: relative;              /* the offsetParent the thumb is measured against */
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
  margin: 0 var(--gutter) 14px; padding: 3px;
  background: var(--bg-sunken); border-radius: 14px;
}

/* The sliding selection block. Positioned by JS (a spring), so no transition here —
   a CSS transition on top of a spring fights it and produces two motions at once. */
.nw-thumb {
  position: absolute; top: 3px; bottom: 3px; left: 0;
  background: var(--ink); border-radius: 11px;
  z-index: 0; will-change: transform;
}

.nw-day {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  border: 0; background: transparent; cursor: pointer;
  padding: 8px 0 7px; border-radius: 11px;
  color: var(--ink-secondary);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: color var(--t-press) var(--ease-out);
}
.nw-day.is-on { color: var(--bg); }

.nw-dow {
  font: 600 9px/1 var(--font-ui); letter-spacing: .07em; text-transform: uppercase;
  opacity: .8;
}
/* Fraunces on the date. It is the one number on this screen you scan for. */
.nw-num {
  font-family: var(--font-display); font-weight: 600;
  font-size: 18px; line-height: 1; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.nw-bars { display: flex; gap: 2px; height: 3px; align-items: center; }
.nw-bars i {
  width: 4px; height: 3px; border-radius: 2px;
  background: currentColor; opacity: .55;
}
/* A closed day gets a rule, not an absent mark — an empty slot reads as a rendering bug. */
.nw-bars .nw-bar-none { width: 9px; height: 1.5px; opacity: .32; }

/* ── The selected day's detail ───────────────────────────────────────────── */
.nw-detail-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 0 var(--gutter) 8px;
}
.nw-detail-day {
  font: 600 13px/1.2 var(--font-ui); letter-spacing: -.004em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.nw-detail-count {
  font: 500 12px/1 var(--font-ui); letter-spacing: .02em; color: var(--ink-tertiary);
  white-space: nowrap;
}

.nw-ses {
  margin: 0 var(--gutter);
  background: var(--bg-raised); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1); overflow: hidden;
}
.nw-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 15px; border-bottom: 1px solid var(--rule);
}
.nw-row:last-child { border-bottom: 0; }

/* Fixed-width time column so seven days of start times line up down the card — the whole
   reason the block can be read at a glance instead of parsed row by row. */
/* ⚠ 68px AND nowrap: "– 10:45 PM" measures exactly 62px, and at 62 it broke "PM" onto a third line.
   A time is one line or it reads as two events (review, 2026-10-05). */
.nw-time {
  flex: none; width: 68px; display: flex; flex-direction: column; gap: 2px;
  font-variant-numeric: tabular-nums;
}
.nw-time-a { font: 600 13px/1.2 var(--font-ui); color: var(--ink); white-space: nowrap; }
.nw-time-b { font: 400 12px/1.2 var(--font-ui); color: var(--ink-tertiary); white-space: nowrap; }

.nw-mid { flex: 1; min-width: 0; }
.nw-name {
  font-family: var(--font-display); font-weight: 600;
  font-size: 16px; line-height: 1.2; letter-spacing: -.01em; color: var(--ink);
}
.nw-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }

/* A smaller sibling of shell.css's .tag. Not .tag itself: its 5px/9px padding is right for
   a single status pill and too heavy for three of them stacked in a dense timetable row. */
.nw-chip {
  display: inline-flex; align-items: center;
  font: 600 10px/1 var(--font-ui); letter-spacing: .05em; text-transform: uppercase;
  padding: 4px 7px; border-radius: var(--r-full);
  background: var(--bg-sunken); color: var(--ink-secondary);
  white-space: nowrap;
}
/* ⚠ NO SOLOS / DUOS / MEMBERS TINTS ANY MORE. Those chips came from news.js's own sample week, which
   carried an entry kind and a members-only flag; the week is the server's sessions read now
   (views/news.js), and a session row has neither — its `format` is drawn as a plain chip. A tint
   comes back with the field, not before it. */
.nw-chip--full {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  color: var(--danger);
}

.nw-price {
  flex: none; align-self: center;
  font: 600 15px/1 var(--font-ui); letter-spacing: -.01em; color: var(--ink);
}

/* ⛔ The payment rule, in the venue's own voice. Quiet, but never removed. */
.nw-note {
  margin: 10px var(--gutter) 0;
  font: 400 12px/1.45 var(--font-ui); color: var(--ink-tertiary);
}

/* ═══ 3 · THE FEED ═════════════════════════════════════════════════════════
   Syd, 2026-09-16: "make the announcement tab look more like X.com style tweets."

   ⚠⚠ THE DIVIDER DOES THE SEPARATING, AND THAT IS WHAT MAKES A FEED A FEED. No border, no
     radius, no fill, no shadow on a row — the moment a post gets a box it reads as a card in a
     stack, which is exactly what this stopped being. The old `.nw-posts` panel had a raised
     ground and a shadow around all five; both are gone.
   ⚠ WHAT IS DELIBERATELY NOT BORROWED FROM X: no reply, repost, like or view row — there is no
     social graph here and nobody to reply to, so an engagement strip would be furniture that does
     nothing on a noticeboard. No verification tick: it means something on a network with
     impostors and nothing on a club's own board. And no infinite scroll — this tab is EDITED, five
     posts chosen and ordered, which the header of news.js is explicit about. */

.nw-posts { margin: 0; }

/* No posts: the "Announcements" heading over the empty card (news.js paintFeed). ⚠ ONLY AFTER THE
   WEEK, whose sticky bar ends in a hairline with 2px under it — the heading needs the 20px every other
   heading on the page has above it. With no week drawn the feed is the first thing under the title,
   and the title's own spacing is already there. */
.nw-week + .nw-feed.is-empty { padding-top: 20px; }

.nw-post {
  display: grid; grid-template-columns: 34px 1fr; gap: 10px;
  padding: 13px var(--gutter) 14px;
  border-bottom: 1px solid var(--rule);
}
.nw-post:last-child { border-bottom: 0; }

/* The pin sits above the byline and spans both columns, the way X places it. */
.nw-pin-kick {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 6px; margin: 0 0 2px; padding-left: 2px;
  font: 650 11px/1 var(--font-ui); letter-spacing: .06em; color: var(--ink-tertiary);
}
.nw-pin-ico svg { width: 11px; height: 11px; display: block; }

/* ⚠ THE CREST IS THE ACCOUNT'S MARK. Cream disc, navy letters, in the display face — the same
   inversion the old pinned card used, reduced to the one element that still needs it. */
.nw-crest {
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--selected-bg); color: var(--selected-ink);
  font-family: var(--font-display); font-weight: 700; font-size: 12px; letter-spacing: -.01em;
}

.nw-col { min-width: 0; }

.nw-by {
  display: flex; align-items: baseline; gap: 5px; margin: 0; min-width: 0;
  font: 400 13px/1.3 var(--font-ui);
}
.nw-by-who { font-weight: 650; color: var(--ink); white-space: nowrap; }
.nw-by-at {
  color: var(--ink-tertiary); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nw-by-when { color: var(--ink-tertiary); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ⚠ THE BODY IS ONE PARAGRAPH WITH A BOLD OPENING CLAUSE, not a heading and a paragraph. The old
   headline was Fraunces so it read as something a person wrote rather than a label; in a post the
   byline already says a person wrote it, so the lead-in can be the UI face at weight and the
   distinction is carried by who is speaking instead of by the typeface. */
.nw-body {
  margin: 3px 0 0;
  font: 400 13.5px/1.5 var(--font-ui); color: var(--ink); text-wrap: pretty;
}
.nw-body strong { font-weight: 650; }

/* ⚠⚠ CLAMPED TO THREE LINES, AND THE FULL TEXT IS STILL IN THE DOM. -webkit-line-clamp hides lines
   from the eye and from nobody else, so a screen reader gets the whole body whether it is open or
   not — which the old 0fr-grid collapse had to add aria-hidden to achieve. These bodies run about
   five lines each and there are five of them; unclamped, the screen is the wall of text the old
   collapsing rows existed to avoid. */
.nw-post:not(.is-open) .nw-body {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.nw-more {
  display: block; margin: 4px 0 0; padding: 6px 0; min-height: 0;
  border: 0; background: transparent; cursor: pointer;
  font: 600 13px/1.2 var(--font-ui); color: var(--action);
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.nw-more:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--r-sm); }

/* Outlined, uppercase, quiet — a label on the post, not a button. No per-category colour: four
   tinted chips on a quiet screen is a decoration budget spent on the least useful fact in the row.
   ⚠ `nw-post-chip`, because `.nw-chip` is the WEEK BLOCK's session tag and has been since this file
      was written. news.js carries the note. */
.nw-post-chip {
  display: inline-block; margin-top: 9px;
  font: 650 10px/1 var(--font-ui); letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-tertiary);
  border: 1px solid var(--rule); border-radius: var(--r-full); padding: 4px 9px;
}

/* ── The week, pinned ───────────────────────────────────────────────────────
   ⚠⚠ STICKY, WHICH IS A STRONGER GUARANTEE THAN BEING FIRST. news.js's header rule says the week
     block is why a regular opens this tab and must never be demoted below the announcements. As a
     sticky bar it cannot be scrolled away from at all — the feed runs under it. That is X's shape
     (pinned tabs, scrolling timeline) and it satisfies the rule rather than bending it.
   ⚠ TRANSLUCENT, SO CONTENT SHOWS THROUGH RATHER THAN HITTING AN OPAQUE STRIP — the same material
     the app's other floating chrome uses (DESIGN.md; apple-design §12). */
.nw-week-stuck {
  position: sticky; top: 0; z-index: 5;
  margin: 0 0 2px; padding: 10px 0 11px;
  background: var(--chrome);
  -webkit-backdrop-filter: var(--chrome-blur); backdrop-filter: var(--chrome-blur);
  border-bottom: 1px solid var(--chrome-edge);
}
@media (prefers-reduced-transparency: reduce) {
  .nw-week-stuck { background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ── Arrival ────────────────────────────────────────────────────────────────
   Same rule as the Tickets cards: the rest state is the VISIBLE one, the displaced state is a
   class removed by a timer, so a document that never paints ends on the finished layout rather
   than on three invisible rows. */
[data-ready] .nw-post { transition: opacity 520ms cubic-bezier(.22,.61,.36,1),
                                    transform 520ms cubic-bezier(.22,.61,.36,1); }
.nw-post.is-in { opacity: 0; transform: translateY(9px) scale(.985); }
@media (prefers-reduced-motion: reduce) {
  [data-ready] .nw-post { transition: none; }
  .nw-post.is-in { opacity: 1; transform: none; }
}

.nw-post-inner { min-height: 0; overflow: hidden; }
.nw-post-inner p {
  margin: 0; padding: 0 15px 16px 15px;
  font: 400 15px/1.52 var(--font-ui); color: var(--ink-secondary);
  max-width: 62ch;
}

/* ── Waiting for the feed's first answer ─────────────────────────────────────
   ⚠ NO WORDS, AND NOTHING FOR 600 MS (PLAN.md §5: "Loading has no words; a small spinner appears
     after 600 ms"). The ring is in the DOM from the first frame and fades in only after the delay,
     so an answer that comes back quickly — a fixture, or today's empty 404 — never flashes one.
   ⚠ REDUCED MOTION KEEPS THE RING AND STOPS THE TURN: a still ring still says "waiting". */
.nw-wait {
  display: block; width: 18px; height: 18px; margin: 22px auto;
  border-radius: 50%;
  border: 2px solid var(--rule); border-top-color: var(--ink-tertiary);
  opacity: 0;
  animation: nw-wait-in 1ms linear 600ms forwards, nw-wait-turn 900ms linear infinite;
}
@keyframes nw-wait-in { to { opacity: 1; } }
@keyframes nw-wait-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .nw-wait { animation: nw-wait-in 1ms linear 600ms forwards; }
}

.nw-sign {
  margin: 18px var(--gutter) 0; text-align: center;
  font-family: var(--font-display); font-style: italic;
  font-size: var(--caption-size); letter-spacing: var(--caption-track);
  color: var(--ink-tertiary);
}

/* ═══ 4 · ACCESSIBILITY ════════════════════════════════════════════════════
 * Reduced motion is a gentler equivalent, not an absent one. Naming this file's own
 * animations, per DESIGN.md §4.8: the week thumb (a JS spring — motion.js already snaps it,
 * so nothing to do here), the disclosure row, and the chevron.
 */
@media (prefers-reduced-motion: reduce) {
  [data-ready] .nw-post-body { transition-property: opacity; }
  [data-ready] .nw-post.is-open .nw-post-body { transition-property: opacity; }
  [data-ready] .nw-chev { transition: none; }
  .nw-post.is-open .nw-chev { transform: none; }
  /* The chevron still has to say which way it points, so with rotation gone it says it in
     opacity — the open row's marker is the solid one. */
  .nw-post.is-open .nw-chev { opacity: 1; }
}

@media (prefers-contrast: more) {
  .nw-chip { outline: 1px solid var(--ink-secondary); }
  .nw-thumb { background: var(--ink); }
  .nw-pin { outline: 2px solid var(--ink); outline-offset: -2px; }
}
