/* ==========================================================================
   Shared publishing rail + card + popup (apps/publishing).

   ONE quiet card for every door that offers a published lesson: the
   AI-lessons library, the lesson idea box, mission moments, journey stops.
   Glyphs and numbers, one short "why" line, a popup for the rest — see
   templates/publishing/partials/{rail,card,card_group,card_detail,
   detail_host}.html and apps/publishing/services/cards.py.

   Restyled onto the module components (2026-09-11, docs/ux/
   journeys_missions_ux.md §9.6): the CARD's visual recipe is `.af-lcard`'s —
   written once, in alyfa.css's MODULE COMPONENTS section, as
   `.af-lcard, .pub-card` — so a pool card and a learner's own lesson card
   are the same object at rest; the POPUP is the one module sheet
   (`.af-sheet*` on #pub-detail, same ids and hooks); the rail header is an
   `.af-section-head`. What is left here is what only a publishing card has.
   The `pub-*` class names stay on the markup as HOOKS: publishing_rail.js
   removes `.pub-card` holders on `pub-released`, dismiss matches
   `.pub-card[data-pub]`, and a dozen tests count `class="pub-card"`.

   Tokens only (--af-*, from static/css/alyfa.css) — no literal hex here.
   ========================================================================== */

/* --- the rail -------------------------------------------------------------- */

.pub-rail { display: flex; flex-direction: column; gap: 0; }
.pub-rail-title { margin: 0; }
.pub-rail-count { font-variant-numeric: tabular-nums; }
/* The header's one quiet link ("Make a new one →"): the brand colour only on
   hover/focus so the title stays the loudest thing in the header. */
.pub-rail-cta {
  flex: none; font-size: 0.8125rem; font-weight: 600; color: var(--af-ink-soft);
  text-decoration: none; white-space: nowrap; border-radius: 8px; padding: 4px 6px;
  transition: color 0.15s ease, background-color 0.15s ease;
}
.pub-rail-cta:hover { color: var(--af-lapis-700); background: var(--af-lapis-50); }
.pub-rail-cta:focus-visible { outline: 2px solid var(--af-lapis-500); outline-offset: 2px; }
.pub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: 16px;
}
@media (max-width: 639px) { .pub-grid { gap: 12px; } }

/* The COMPACT variant (a preview or candidates sheet): ≤ 3 cards, no why
   line, two facts at most, and a snap track on a phone. */
.pub-rail-compact .pub-why { display: none; }
.pub-rail-compact .pub-signals > :nth-child(n + 3) { display: none; }
@media (max-width: 639px) {
  .pub-rail-compact .pub-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 84%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    padding-bottom: 4px;
  }
  .pub-rail-compact .pub-grid::-webkit-scrollbar { display: none; }
  /* The wrapper is `display: contents`, so the snap point is the card. */
  .pub-rail-compact .pub-card,
  .pub-rail-compact .pub-card > .af-lcard { scroll-snap-align: start; }
}

/* --- the card (the recipe itself is `.af-lcard, .pub-card` in alyfa.css) -- */

/* PASS 2: a LESSON pool card IS the shared lesson card. `card.html`'s
   `.pub-card` is now a WRAPPER around `modules/lesson_card.html` — it exists
   only to carry `data-pub` (publishing_rail.js's dismiss/release lookups) and
   the impression hooks the ranker reads, so it must add no box of its own and
   the card inside must be the grid item. `display: contents` is exactly that.

   The `:has()` gate is what keeps `card_group.html` a real card: a GROUP is
   one to three lessons with sibling chips and a checkbox toggle, neither of
   which `modules/lesson_card.html` has a slot for, and A3 §4.3 lists it
   outside the absorption. Its root has no `.af-lcard` child, so it keeps the
   full recipe.

   Placed BEFORE the `[hidden]` rule below on purpose: the two have equal
   specificity, so source order is what makes hiding a dismissed card win. */
.pub-card:has(> .af-lcard) { display: contents; }

/* static/js/publishing_rail.js hides a dismissed card via `card.hidden = true`
   (the ATTRIBUTE, never a display class); the card's own `display: flex`
   would otherwise beat the UA's low-priority `[hidden]{display:none}`. */
.pub-card[hidden] { display: none; }

.pub-head { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
.pub-sigil { flex-shrink: 0; }
.pub-title {
  flex: 1; min-width: 0; margin: 0;
  font-family: "Fraunces", Georgia, serif; font-optical-sizing: auto;
  font-size: var(--af-t-card); font-weight: 600; letter-spacing: -0.02em;
  line-height: 1.28; color: var(--af-ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* The level is a tag, sentence case — no uppercase pill inside a card. */
.pub-level {
  flex-shrink: 0; margin-top: 2px; padding: 2px 8px; border-radius: 999px;
  font-size: var(--af-t-micro); font-weight: 600; line-height: 1.35;
  color: var(--af-ink-soft); background: rgba(33, 31, 51, 0.06);
}
.pub-subtitle { margin: -4px 0 0; font-size: var(--af-t-meta); color: var(--af-ink-faint); }
/* The title PAIR (2026-09-11, §5.7): the English title, its «Finglish» as a
   meta line — the module lesson card's own grammar (`.af-lcard-titles`). */
.pub-titles { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pub-titles .pub-title { flex: none; }
.pub-fi {
  font-size: var(--af-t-meta); color: var(--af-ink-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A group card's moment / place, drawn as the lesson card's ⌖ pin (not a
   button here: the pool card has no unit sheet to open). */
.pub-pin { flex: 0 1 auto; min-width: 0; cursor: default; }
/* The empty ⧉ popup: a title row, then the one line — the dashed frame sits
   inside the body's padding, clear of the panel's ✕. */
.pub-empty-popup { margin-top: 12px; }
/* The ⧉ popup's own rail header sits INSIDE the sheet (QA r2): clear of the
   rim (the body has no top padding of its own) and of the ✕ — so no rule
   under it runs into the close button. */
#pub-detail-body > .pub-rail > .pub-rail-head { padding: 22px 48px 0 0; }
#pub-detail-body > .pub-rail > .pub-rail-head .af-section-rule { display: none; }

/* --- signals: glyphs + numbers, no sentences (the ONE fact row) ------------- */

.pub-signals {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px;
  font-size: var(--af-t-meta); color: var(--af-ink-soft); font-variant-numeric: tabular-nums;
}
.pub-sig { display: inline-flex; align-items: center; gap: 4px; }
.pub-sig svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--af-ink-faint); }
.pub-sig b { font-weight: 700; color: var(--af-ink); }
.pub-sig-words { gap: 5px; }
/* The one word after each number ("new", "idioms", "min", "took it"). */
.pub-sig-unit { font-size: var(--af-t-micro); color: var(--af-ink-faint); margin-right: 1px; }
.pub-explore {
  font-size: var(--af-t-micro); font-weight: 600;
  color: var(--af-turquoise-700); background: var(--af-turquoise-50);
  border-radius: 999px; padding: 2px 8px;
}

.pub-bar {
  display: inline-flex; width: 46px; height: 6px; border-radius: 999px;
  overflow: hidden; background: var(--af-line);
}
.pub-bar-new, .pub-bar-review, .pub-bar-known { height: 100%; }
.pub-bar-new { background: var(--af-lapis-500); }
.pub-bar-review { background: var(--af-saffron-400); }
.pub-bar-known { background: var(--af-line-strong); }

.pub-why {
  margin: 0; font-size: var(--af-t-meta); color: var(--af-ink-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --- footer: author, action, details ------------------------------------------ */

.pub-foot { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 2px; }
.pub-author {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 22px; height: 22px; border-radius: 999px;
  background: var(--af-lapis-100); color: var(--af-lapis-700); font-size: var(--af-t-micro); font-weight: 700;
}
.pub-controls { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.pub-action, .pub-details { white-space: nowrap; }
.pub-controls form { margin: 0; }

.pub-toggle { display: inline-flex; cursor: pointer; }
.pub-toggle-input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.pub-toggle-input:checked + .pub-toggle-face {
  background: var(--af-lapis-600); border-color: var(--af-lapis-600); color: #fff;
}
.pub-toggle-input:focus-visible + .pub-toggle-face {
  outline: 2px solid var(--af-lapis-400); outline-offset: 2px;
}

/* --- lesson chips (group card) ------------------------------------------------ */

.pub-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pub-chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 9px;
  border-radius: 999px; background: rgba(33, 31, 51, 0.05); color: var(--af-ink-soft);
  font-size: 12px; font-weight: 600; white-space: nowrap;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.pub-chip svg { width: 12px; height: 12px; flex-shrink: 0; color: var(--af-ink-faint); }

/* --- the popup's CONTENT ------------------------------------------------------
   The frame is the module sheet: #pub-detail carries .af-sheet, its panel
   .af-sheet-panel, its body .af-sheet-body (the scroll container, 24px side
   padding) — see detail_host.html. Below 640px it is the same bottom sheet
   every module overlay is. */

.pub-detail-content { display: flex; flex-direction: column; gap: 14px; padding: 22px 0 0; }
.pub-detail-head { display: flex; align-items: flex-start; gap: 12px; padding-right: 44px; }
.pub-detail-headtext { min-width: 0; flex: 1; }
.pub-detail-title {
  margin: 0; font-family: "Fraunces", Georgia, serif; font-optical-sizing: auto;
  font-size: var(--af-t-h); font-weight: 600; line-height: 1.2;
  letter-spacing: -0.018em; color: var(--af-ink);
}
.pub-detail-chips { margin: 4px 0 0; font-size: var(--af-t-meta); color: var(--af-ink-soft); }
/* Separators are GENERATED, so a conditionally-absent chip leaves no
   dangling middot — the same rule .af-meta holds, for the same reason. */
.pub-detail-chips span + span::before { content: " · "; color: var(--af-ink-faint); }

.pub-detail-summary { margin: 0; font-size: 0.9375rem; line-height: 1.6; color: var(--af-ink-soft); }
.pub-detail-section { display: flex; flex-direction: column; gap: 8px; }

.pub-words { display: flex; flex-wrap: wrap; gap: 8px; }
.pub-word {
  display: flex; flex-direction: column; gap: 1px; padding: 6px 10px; border-radius: 10px;
  background: rgba(33, 31, 51, 0.04); border: 1px solid var(--af-line);
}
.pub-word b { font-size: 13px; font-weight: 600; font-style: normal; color: var(--af-ink); }
.pub-word i { font-size: var(--af-t-micro); font-style: normal; color: var(--af-ink-faint); }
.pub-word-new { background: var(--af-lapis-50); border-color: var(--af-lapis-200); }
.pub-word-new b { color: var(--af-lapis-700); }
.pub-word-idiom { position: relative; }
.pub-word-idiom::after {
  content: "❞"; position: absolute; top: -7px; right: -5px; font-size: 11px; line-height: 1;
  color: var(--af-saffron-600); background: var(--af-surface); border-radius: 999px; padding: 1px 3px;
}

.pub-detail-stats { margin: 0; font-size: var(--af-t-meta); font-weight: 600; color: var(--af-ink-soft); }
.pub-detail-author { margin: 0; font-size: var(--af-t-meta); color: var(--af-ink-faint); }
/* Pinned to the bottom of the sheet's scroll, like every sheet footer. */
.pub-detail-foot {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; align-items: center; gap: 12px;
  margin: 6px -24px 0; padding: 14px 24px calc(16px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--af-line); background: rgba(255, 255, 255, 0.96);
}
@media (max-width: 639px) { .pub-detail-foot { margin-inline: -18px; padding-inline: 18px; } }
.pub-dismiss {
  margin-left: auto; background: none; border: 0; padding: 0; font: inherit;
  font-size: var(--af-t-meta); color: var(--af-ink-faint); text-decoration: underline;
  text-underline-offset: 2px; cursor: pointer;
}
.pub-dismiss:hover { color: var(--af-ink-soft); }
.pub-dismiss:focus-visible, .pub-details:focus-visible, .pub-action:focus-visible {
  outline: 2px solid var(--af-lapis-400); outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .pub-rail-cta { transition: none; }
}

/* --- Haft Khaan band cards (pass 2, B3) ------------------------------------
   The 4:3 frame is `cover_card`'s own `aspect` argument now (B3-S2, landed in
   B0 round 3), so the scoped `aspect-ratio` rule that held it is gone. What is
   left is the CROP: the Haft Khaan art is square and a 4:3 box still has to
   choose what to lose, so it spends the freed budget on the feet, the way the
   retired bespoke card's `.af-frame-top` did. */
.af-band-grid .af-cover-card-art > img { object-position: 50% 22%; }

/* --- the publishers' board (2026-09-12) --------------------------------------
   apps/publishing/services/{scoreboard,standing}.py, templates/publishing/
   board.html + partials/{standing,tier_disc,board_row,board_how}.html.

   One accent per region (§10.1): lapis = the viewer's own row and the act,
   turquoise = a finished count, SAFFRON = the collectible family only — the
   tier disc and a season win are double-ruled saffron rings, the same object
   as a passport stamp or a sofreh item (modules/collect_disc.html), because
   that is what they are: something earned, drawn the way the app draws
   earned things. A Newcomer's disc is the dashed "still to earn" ring.
   Tokens only; no Tailwind rebuild is needed for anything here. */

.pub-tier {
  display: inline-grid; place-items: center; flex: none;
  width: 32px; height: 32px; border-radius: var(--af-radius-pill);
  border: 3px double var(--af-saffron-300); background: var(--af-surface);
  color: var(--af-saffron-700); font-family: "Fraunces", Georgia, serif;
  font-size: 0.8125rem; font-weight: 600; line-height: 1; letter-spacing: 0.02em;
}
.pub-tier-lg { width: 56px; height: 56px; font-size: 1.375rem; border-width: 4px; }
.pub-tier-sm { width: 24px; height: 24px; font-size: 0.6875rem; border-width: 2px; }
.pub-tier-top { border-color: var(--af-saffron-500); background: var(--af-saffron-100); }
.pub-tier-none { border: 2px dashed var(--af-line-strong); background: transparent; color: var(--af-ink-faint); }

/* A season WIN: the podium place inside the same ring, 20px, strung after a
   name. The strip is aria-hidden; the row says the count in words. */
.pub-wins { display: inline-flex; align-items: center; gap: 3px; margin-left: 4px; vertical-align: middle; }
.pub-win {
  display: inline-grid; place-items: center; width: 18px; height: 18px;
  border-radius: var(--af-radius-pill); border: 2px double var(--af-saffron-400);
  background: var(--af-saffron-50); color: var(--af-saffron-700);
  font-size: 0.5625rem; font-weight: 700; line-height: 1;
}

/* --- the standing strip: the viewer's own card ---------------------------- */
.pub-standing {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.25rem 1rem; align-items: start;
  padding: 1rem 1.125rem; border-radius: var(--af-radius-card);
  background: var(--af-surface); border: 1px solid var(--af-line);
  box-shadow: 0 1px 2px rgba(33, 31, 51, 0.04), 0 4px 16px -8px rgba(33, 31, 51, 0.08);
}
.pub-standing-disc { grid-row: span 3; align-self: start; padding-top: 2px; }
.pub-standing-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.625rem; min-width: 0; }
.pub-standing-tier { margin: 0; font-family: "Fraunces", Georgia, serif; font-size: var(--af-t-h); font-weight: 600; color: var(--af-ink); line-height: 1.2; }
.pub-standing-rank { font-size: var(--af-t-meta); font-weight: 600; color: var(--af-ink-soft); font-variant-numeric: tabular-nums; }
.pub-standing-delta { font-size: var(--af-t-micro); font-weight: 600; color: var(--af-turquoise-700); background: var(--af-turquoise-50); padding: 2px 7px; border-radius: var(--af-radius-pill); }
.pub-standing-next { display: grid; gap: 0.375rem; min-width: 0; }
.pub-standing-next .af-progress { height: 6px; max-width: 420px; }
.pub-standing-next .af-progress-fill { background: linear-gradient(90deg, var(--af-saffron-300), var(--af-saffron-500)); }
.pub-standing-gap { margin: 0; font-size: var(--af-t-meta); color: var(--af-ink-soft); }
.pub-standing-gap b { color: var(--af-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
/* The clause that says WHICH slice of time a rung was measured over (owner
   ruling 2026-09-16 — the window is the forgetting pipeline). Quieter than
   the gap it trails, because it is the same sentence every time. */
.pub-standing-window { color: var(--af-ink-faint, var(--af-ink-soft)); }
.pub-standing-facts { margin-top: 0.25rem; }
.pub-standing-facts .af-fact-text a { color: inherit; font-weight: 600; text-decoration: none; }
.pub-standing-facts .af-fact-text a:hover { color: var(--af-lapis-700); text-decoration: underline; text-underline-offset: 2px; }
.pub-standing-enter { margin: 0; font-size: var(--af-t-body); color: var(--af-ink-soft); }
.pub-standing-enter a { font-weight: 600; color: var(--af-ink); }
.pub-standing-enter a:hover { color: var(--af-lapis-700); }
@media (max-width: 519px) {
  .pub-standing { grid-template-columns: minmax(0, 1fr); }
  .pub-standing-disc { grid-row: auto; }
}

/* --- the podium: the top three as cards ----------------------------------- */
.pub-podium { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); margin: 0 0 1rem; padding: 0; list-style: none; }
.pub-podium-card {
  position: relative; display: grid; gap: 0.5rem; padding: 1rem 1.125rem 0.875rem;
  border-radius: var(--af-radius-card); background: var(--af-surface); border: 1px solid var(--af-line);
  box-shadow: 0 1px 2px rgba(33, 31, 51, 0.04), 0 4px 16px -8px rgba(33, 31, 51, 0.08);
}
.pub-podium-card.is-me { border-color: var(--af-lapis-200); box-shadow: 0 0 0 3px var(--af-lapis-50); }
.pub-podium-place {
  position: absolute; top: 0.75rem; right: 0.875rem; font-family: "Fraunces", Georgia, serif;
  font-size: 1.75rem; font-weight: 600; line-height: 1; color: var(--af-saffron-300);
}
.pub-podium-card:first-child .pub-podium-place { color: var(--af-saffron-500); }
.pub-podium-head { display: flex; align-items: center; gap: 0.625rem; min-width: 0; padding-right: 2rem; }
.pub-podium-name { margin: 0; font-family: "Fraunces", Georgia, serif; font-size: var(--af-t-h); font-weight: 600; color: var(--af-ink); line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pub-podium-name.is-unnamed { color: var(--af-ink-faint); font-weight: 500; font-style: italic; }
.pub-podium-tier { margin: 0; font-size: var(--af-t-meta); font-weight: 600; color: var(--af-saffron-700); }
.pub-podium-points { margin: 0; font-family: "Fraunces", Georgia, serif; font-size: 1.75rem; font-weight: 600; line-height: 1.1; color: var(--af-ink); font-variant-numeric: tabular-nums; }
.pub-podium-points small { display: block; font-family: "Inter", system-ui, sans-serif; font-size: var(--af-t-meta); font-weight: 500; color: var(--af-ink-faint); margin-top: 2px; }

/* --- the ranked rows: `.af-list` + `.af-row`, with a rank gutter ---------- */
.pub-board-rank { flex: none; width: 2rem; text-align: right; font-family: "Fraunces", Georgia, serif; font-size: 1.0625rem; font-weight: 600; color: var(--af-ink-faint); font-variant-numeric: tabular-nums; }
.pub-board-row.is-me { background: var(--af-lapis-50); }
.pub-board-row.is-me .pub-board-rank { color: var(--af-lapis-700); }
.pub-board-name { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.pub-board-name .af-row-title { min-width: 0; }
.pub-board-name .is-unnamed { color: var(--af-ink-faint); font-weight: 500; font-style: italic; }
.pub-board-me { flex: none; font-size: var(--af-t-micro); font-weight: 600; color: var(--af-lapis-700); background: var(--af-surface); border: 1px solid var(--af-lapis-200); padding: 1px 6px; border-radius: var(--af-radius-pill); }
.pub-board-facts { display: flex; flex-wrap: wrap; gap: 0.25rem 0.875rem; margin: 0; padding: 0; list-style: none; }
.pub-board-facts li { display: inline-flex; align-items: center; gap: 4px; font-size: var(--af-t-meta); color: var(--af-ink-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }
.pub-board-facts .af-g { font-size: var(--af-icon-sm); color: var(--af-ink-faint); }
.pub-board-facts b { font-weight: 600; color: var(--af-ink); }
.pub-board-facts .is-energy .af-g { color: var(--af-lapis-500); }
@media (max-width: 639px) {
  .pub-board-facts li:not(.is-energy) { display: none; }
}

/* "How it works": the tier ladder inside the sheet */
.pub-ladder { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.625rem; }
.pub-ladder li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.125rem 0.75rem; align-items: center; }
.pub-ladder .pub-tier { grid-row: span 2; }
.pub-ladder b { font-size: var(--af-t-body); font-weight: 600; color: var(--af-ink); }
.pub-ladder b small { font-weight: 500; color: var(--af-ink-faint); margin-left: 0.375rem; }
.pub-ladder p { margin: 0; font-size: var(--af-t-meta); color: var(--af-ink-soft); }
.pub-ladder li.is-here b { color: var(--af-lapis-700); }
