/* ==========================================================================
   board.css — the controls bar and the entry card
   --------------------------------------------------------------------------
   CARD HEIGHT BUDGET, measured at 375px rather than assumed.

   The original target was ~260px so that two cards fit a 667px viewport.
   That turned out to be unreachable without either hiding genuinely useful
   information or shrinking touch targets below 44px, so it was revised
   against real numbers:

     ended / minimal card    266px
     sparse card             320px
     typical populated card  ~430px
     every field populated   ~570px   (the deliberate stress case)

   Where the height actually goes on the fullest card:
     actions   148px   3 wrapped rows of 44px touch targets
     schedule   80px
     facts      53px   chips wrapping to 2 rows
     title      50px   2-line clamp
     everything else under 45px each

   The actions row is the dominant cost and it is mostly irreducible: 44px
   is the accessibility floor for a touch target, not a style choice. The
   one real saving was dropping the per-card calendar button, which became
   redundant once saving moved to the site account and the calendar feed
   became a subscribe-once panel rather than a per-entry download.

   So the budget is now: keep the ABOVE-FOLD block (status + deadline,
   title, hook, chips) under ~160px, and let total height follow from how
   much the editor actually filled in. Density here means information, not
   compression.
   ========================================================================== */

/* ==========================================================================
   CONTROL BAR
   Sticky, and it NEVER grows in place. On mobile it is a compact row whose
   filter button opens a bottom sheet over the page. On wide screens the
   primary chip groups are inline and only the secondary axes live in the
   sheet.
   ========================================================================== */

/* NOT sticky. It used to be, and that was wrong twice over: it spent
   vertical space permanently on mobile, and being pinned to the top it
   covered any in-page anchor target — which is how the ★ help link ended
   up landing underneath it. Access while scrolled is handled by the
   floating button below instead, which costs nothing until it is needed. */
.tcg-controls {
  margin: 0 0 var(--tcg-space-lg);
  padding: var(--tcg-space-xs) 0;
  background: var(--tcg-color-bg);
}

.tcg-controls__sentinel { height: 1px; }

.tcg-controls__bar {
  display: flex;
  align-items: center;
  gap: var(--tcg-space-xs);
  flex-wrap: wrap;
}

.tcg-controls__group {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

.tcg-controls__group legend {
  padding: 0;
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  margin-bottom: var(--tcg-space-2xs);
}

.tcg-controls__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-2xs);
}

/* The filter groups are never shown here. board.js moves them into the
   filter sheet at every width (owner, 2026-10-15: inline on desktop they
   were a wall of controls above the content), so the bar stays one line -
   the 絞り込み button, 並び順 and the two switches - and the sheet is where
   the filtering happens. Hidden here so nothing flashes before the move. */
.tcg-controls__inline { display: none; }

.tcg-controls__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
}

.tcg-controls__sort select {
  min-height: 44px;
  padding: 0 var(--tcg-space-xs);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface);
  color: var(--tcg-color-text);
  font-weight: 700;
}

/* Toggle switches — 受付中のみ / ★4以上 */
.tcg-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-height: 44px;
  padding: 0 var(--tcg-space-xs);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-surface);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  cursor: pointer;
}
.tcg-switch:has(input:checked) {
  background: var(--tcg-color-brand-soft);
  border-color: var(--tcg-color-brand);
  color: var(--tcg-color-brand);
}
.tcg-switch input { accent-color: var(--tcg-color-brand); width: 18px; height: 18px; }

/* Filter button with a live count of active filters. */
.tcg-filterbtn__count {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-brand);
  color: var(--tcg-color-brand-contrast);
  font-size: var(--tcg-fs-2xs);
  font-variant-numeric: tabular-nums;
}
.tcg-filterbtn__count:empty { display: none; }

.tcg-controls__count {
  font-size: var(--tcg-fs-xs);
  color: var(--tcg-color-text-muted);
  font-variant-numeric: tabular-nums;
  margin: var(--tcg-space-2xs) 0 0;
}

/* ==========================================================================
   ENTRY LIST
   ========================================================================== */

.tcg-entrylist {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-sm);
}

.is-filtered-out { display: none; }

/* ==========================================================================
   ENTRY CARD
   --------------------------------------------------------------------------
   Reading order, and what sits above the mobile fold:

     1  status + deadline strip  ┐
     2  title (2-line clamp)     │ above the fold: answers
     3  hook (clamped)           │ "should I care, and when does it close"
     4  taxonomy chips + stars   ┘
     5  schedule
     6  conditions
     7  tip (collapsed)
     8  note
     9  actions
    10  verified line
   ========================================================================== */

.tcg-entry {
  border: 1px solid var(--tcg-color-border);
  border-left-width: 4px;
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-surface);
  box-shadow: var(--tcg-shadow-card);

  /* Per-CARD containment, not just per-section.
     Sections get content-visibility too (layout.css), but the 受付中 section
     is the long one and it is always expanded, so section-level containment
     does nothing for the case that actually matters.

     Measured on stress.html, 150 cards, JS + a forced full-document layout:

                        without        with       change
       filter           323.3ms      92.3ms       3.5x faster
       sort             129.1ms      87.2ms       1.5x faster

     That figure is a pessimistic bound — it forces synchronous layout, which
     real interactions do not. Ctrl+F, in-page anchors and assistive tech all
     still reach skipped cards, and the sort only append()s existing nodes so
     it never needs layout inside a skipped subtree.

     contain-intrinsic-size uses `auto`, so once a card has been rendered its
     real height is remembered; 330px is only the first-paint estimate, taken
     from the measured median card height. */
  content-visibility: auto;
  contain-intrinsic-size: auto 330px;
}

/* Status as a third channel, alongside the label and the glyph: the border
   STYLE differs too, so the card survives greyscale and colour-blindness. */
.tcg-entry[data-status="open"]     { border-left-color: var(--tcg-color-open-fg); }
.tcg-entry[data-status="upcoming"] { border-left-color: var(--tcg-color-upcoming-fg); }
.tcg-entry[data-status="waiting"]  { border-left-color: var(--tcg-color-waiting-fg); border-left-style: dashed; }
.tcg-entry[data-status="ended"]    { border-left-color: var(--tcg-color-ended-bd); }

/* --- ticket layout ------------------------------------------------------
   The card is a raffle ticket: a torn-off stub carrying the deadline, and
   the main body. That is not decoration for its own sake — 抽選 IS a raffle,
   and it puts the one thing every visitor came for (when does this close)
   in a fixed, always-findable position rather than inline with everything
   else competing for the same line.

   Orientation flips at 600px. On desktop the stub is a left column; on
   mobile it becomes a top band, because a permanent 92px left column would
   eat a third of a 375px screen and Japanese titles need the width. */

.tcg-entry__inner {
  display: grid;
  grid-template-columns: 1fr;   /* mobile: stub above, body below */
}

.tcg-entry__main {
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-xs);
  padding: var(--tcg-space-sm);
  min-width: 0;                 /* or long unbroken strings blow the grid out */
}

/* --- the stub ----------------------------------------------------------- */

.tcg-entry__stub {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: var(--tcg-space-2xs);
  padding: var(--tcg-space-xs) var(--tcg-space-sm);
  background-color: var(--tcg-color-surface-2);
  /* The perforation. Dashed rather than a dotted image so it stays crisp at
     every zoom level and needs no asset. */
  border-bottom: 2px dashed var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-md) var(--tcg-radius-md) 0 0;
  /* Notches at both ends of the perforation. */
  background-image:
    radial-gradient(circle at 0 100%,    var(--tcg-color-bg) 0 7px, transparent 7px),
    radial-gradient(circle at 100% 100%, var(--tcg-color-bg) 0 7px, transparent 7px);
}

/* The bites out of the edge that make it read as "torn off" rather than "a
   coloured box".

   These are BACKGROUND GRADIENTS, not positioned pseudo-elements, and that
   is deliberate. The card sets `content-visibility: auto` for the measured
   3.5x filter win, and content-visibility always applies paint containment —
   which clips anything drawn outside the element's box. Pseudo-element
   notches at negative offsets would simply vanish. Gradients stay inside the
   box, so the two features coexist.

   `background-origin: border-box` puts the coordinate origin outside the
   dashed border, so a circle at 100% straddles the perforation. */
.tcg-entry__stub {
  background-origin: border-box;
  background-repeat: no-repeat;
}

/* Status colours the stub. This is a fourth redundant channel on top of the
   label, the glyph and the border style — never the only one.

   background-COLOR, not the `background` shorthand: the shorthand resets
   background-image, which would silently wipe the notch and punch-hole
   gradients set above. */
.tcg-entry[data-status="open"]     .tcg-entry__stub { background-color: var(--tcg-color-open-bg); }
.tcg-entry[data-status="upcoming"] .tcg-entry__stub { background-color: var(--tcg-color-upcoming-bg); }
.tcg-entry[data-status="waiting"]  .tcg-entry__stub { background-color: var(--tcg-color-waiting-bg); }
.tcg-entry[data-status="ended"]    .tcg-entry__stub { background-color: var(--tcg-color-ended-bg); }

@media (min-width: 600px) {
  .tcg-entry__inner {
    grid-template-columns: var(--tcg-stub-w) 1fr;
  }

  .tcg-entry__stub {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 2px;
    padding: var(--tcg-space-sm) var(--tcg-space-xs);
    border-bottom: 0;
    border-right: 2px dashed var(--tcg-color-border-strong);
    border-radius: var(--tcg-radius-md) 0 0 var(--tcg-radius-md);
    /* Two notches on the perforation. (A punch hole used to sit below them;
       removed — it read as a stray dot rather than as part of the ticket.) */
    background-image:
      radial-gradient(circle at 100% 0,    var(--tcg-color-bg) 0 7px, transparent 7px),
      radial-gradient(circle at 100% 100%, var(--tcg-color-bg) 0 7px, transparent 7px);
  }
}

/* --- 1. status ---------------------------------------------------------- */

.tcg-entry__statusbar {
  display: flex;
  align-items: center;
  gap: var(--tcg-space-xs);
  flex-wrap: wrap;
  /* Space is reserved for the relative-time badge BEFORE the script injects
     it, so the personalisation/enhancement pass causes zero layout shift. */
  min-height: 28px;
}

.tcg-deadline {
  display: inline-flex;
  align-items: baseline;
  gap: var(--tcg-space-2xs);
  flex-wrap: wrap;
}

@media (min-width: 600px) {
  .tcg-deadline {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
  }
  /* Date on one line, time under it — the stub is narrow and the pair reads
     as a stamped ticket rather than a run-on string. */
  .tcg-entry__stub .tcg-deadline__value small {
    display: block;
    margin-left: 0;
  }

  /* The year, when shown at all, takes its own line inside the stub so it
     costs no horizontal room. Elsewhere (schedule rows) it stays inline. */
  .tcg-entry__stub .tcg-deadline__year {
    display: block;
    font-size: var(--tcg-fs-2xs);
    font-weight: 700;
    line-height: 1.15;
    color: var(--tcg-color-text-muted);
  }

  /* Never break inside the date. Without this the closing paren of the
     weekday wraps alone onto a second line — "8/21(金" then ")" — which is
     both ugly and, on a deadline product, briefly unreadable. */
  .tcg-entry__stub .tcg-deadline__value { white-space: nowrap; }
}

/* The relative-time badge lives in the stub, under the date. Its space is
   reserved before the script fills it, so injection shifts nothing. */
.tcg-entry__stub .tcg-remain { align-self: flex-start; }

.tcg-deadline__label {
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
}

/* THE deadline. The only element on a card at this size — nothing may
   compete with it, because "when does it close" is the entire product. */
.tcg-deadline__value {
  font-size: var(--tcg-fs-deadline);
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.tcg-deadline__value small {
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
}

/* Inline by default — inside a schedule row "2027年1/5(火)" reads as one
   string. The stub overrides this to a separate line. */
.tcg-deadline__year { font-size: 0.8em; font-weight: 700; }

/* Ambiguous end date: 「8/25(月)中」. Never invent 23:59. */
.tcg-deadline--ambiguous .tcg-deadline__value { color: var(--tcg-color-text); }

/* No end date at all. Muted, and sorted to the bottom of its group by a
   sentinel key — a null must never masquerade as "closes soonest". */
.tcg-deadline--none .tcg-deadline__value {
  font-size: var(--tcg-fs-base);
  color: var(--tcg-color-text-faint);
  font-weight: 700;
}

.tcg-deadline__info {
  display: inline-grid;
  place-items: center;
  width: 15px; height: 15px;
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: 50%;
  font-size: 9px;
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  cursor: help;
}

/* Midnight disambiguation: 「8/21(金) 00:00 (8/20の深夜)」.
   A 00:00 deadline is read as the wrong day by almost everyone. */
.tcg-deadline__midnight {
  font-size: var(--tcg-fs-xs);
  font-weight: 500;
  color: var(--tcg-color-text-muted);
}

/* Relative time, injected by board.js. Never server-rendered: the board is
   CDN-cached for minutes, and a cached "あと3時間" is not merely stale, it
   is actively harmful on a deadline product. */
.tcg-remain {
  display: inline-flex;
  align-items: center;
  padding: 1px var(--tcg-space-xs);
  border-radius: var(--tcg-radius-sm);
  border: 1px solid var(--tcg-color-border-strong);
  background: var(--tcg-color-surface-2);
  color: var(--tcg-color-text-muted);
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Red only within 24 hours. Nowhere else on the site. */
.tcg-remain.is-urgent {
  background: var(--tcg-color-urgent-bg);
  border-color: var(--tcg-color-urgent-bd);
  color: var(--tcg-color-urgent-fg);
}

/* --- 2. title ----------------------------------------------------------- */

.tcg-entry__title {
  font-size: var(--tcg-fs-lg);
  font-weight: 700;
  line-height: var(--tcg-lh-tight);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tcg-entry__title a { color: inherit; text-decoration: none; }
.tcg-entry__title a:hover { color: var(--tcg-color-brand); text-decoration: underline; }

/* --- 3. hook ------------------------------------------------------------ */

.tcg-entry__hook {
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
  line-height: var(--tcg-lh-base);
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
@media (min-width: 600px) {
  .tcg-entry__hook { -webkit-line-clamp: 2; }
}

/* --- 4. taxonomy chips + stars ----------------------------------------- */

.tcg-entry__facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--tcg-space-2xs);
}
.tcg-entry__facts .tcg-stars { margin-left: auto; }

/* --- 5. schedule --------------------------------------------------------
   Always visible, never collapsed: three lines at most, and the
   second-most-important content on the card. Only populated rows render. */

.tcg-entry__schedule {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--tcg-space-3xs) var(--tcg-space-xs);
  padding: var(--tcg-space-xs);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface-2);
  font-size: var(--tcg-fs-sm);
  line-height: var(--tcg-lh-snug);
}

.tcg-entry__schedule dt {
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  white-space: nowrap;
}
.tcg-entry__schedule dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.tcg-entry__schedule dd.is-unknown { color: var(--tcg-color-text-faint); }

/* --- 6. conditions ------------------------------------------------------ */

.tcg-entry__conds {
  list-style: none;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-2xs);
}

/* --- 7. tip -------------------------------------------------------------
   Collapsed everywhere, including desktop. The summary shows a one-line
   clamped preview of the actual opening text, so the flavour is free and
   expanding is a real choice. Deliberately not auto-opened on wide screens:
   that is a layout branch for no benefit, and it makes card heights
   unpredictable. */

.tcg-entry__tip {
  border: 1px solid var(--tcg-color-point-bd);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-point-bg);
}

.tcg-entry__tip > summary {
  display: flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-height: 40px;
  padding: var(--tcg-space-2xs) var(--tcg-space-xs);
  cursor: pointer;
  font-size: var(--tcg-fs-sm);
  list-style: none;
}
.tcg-entry__tip > summary::-webkit-details-marker { display: none; }

.tcg-entry__tip-preview {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--tcg-color-text-muted);
}
.tcg-entry__tip[open] .tcg-entry__tip-preview { display: none; }

.tcg-entry__tip[open] > summary::after { content: "閉じる"; }
.tcg-entry__tip > summary::after {
  content: "詳しく";
  margin-left: auto;
  flex: none;
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  color: var(--tcg-color-brand);
}

.tcg-entry__tip-body {
  padding: 0 var(--tcg-space-xs) var(--tcg-space-xs);
  font-size: var(--tcg-fs-sm);
  line-height: var(--tcg-lh-base);
}
.tcg-entry__tip-body ul { padding-left: 1.4em; }

/* --- 8. note ------------------------------------------------------------ */

.tcg-entry__note {
  font-size: var(--tcg-fs-xs);
  color: var(--tcg-color-text-muted);
  line-height: var(--tcg-lh-base);
}

/* --- 9. actions --------------------------------------------------------- */

.tcg-entry__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-xs);
  align-items: center;
}
.tcg-entry__actions .tcg-btn--primary { flex: 1 1 auto; min-width: 180px; }

/* Applied checkbox. Ships disabled with its full width already reserved, so
   when the personalisation call enables it nothing moves. Proving that is
   the point of including it in a static prototype. */
.tcg-applied {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-height: 44px;
  padding: 0 var(--tcg-space-sm);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-surface);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.tcg-applied input { accent-color: var(--tcg-color-brand); width: 18px; height: 18px; }
.tcg-applied:has(input:disabled) { opacity: .6; cursor: default; }
.tcg-applied:has(input:checked) {
  background: var(--tcg-color-open-bg);
  border-color: var(--tcg-color-open-bd);
  color: var(--tcg-color-open-fg);
}

/* --- 10. verified ------------------------------------------------------- */

.tcg-entry__verified {
  display: flex;
  align-items: center;
  gap: var(--tcg-space-3xs);
  font-size: var(--tcg-fs-2xs);
  color: var(--tcg-color-text-faint);
}
/* Degrades with age. Over 14 days old it is not rendered at all — a trust
   signal that lies is worse than no trust signal. */
.tcg-entry__verified.is-stale { color: var(--tcg-color-text-faint); font-style: italic; }

/* ==========================================================================
   ENDED VARIANT
   Never opacity or filter:saturate — both wreck text contrast and fail
   WCAG. De-emphasis is done by explicitly swapping colours instead, and by
   demoting the call to action so an ended entry can never render a
   live-looking apply button.
   ========================================================================== */

.tcg-entry--ended { background: var(--tcg-color-surface); }
.tcg-entry--ended .tcg-entry__title a { color: var(--tcg-color-text-muted); }
.tcg-entry--ended .tcg-chip--game {
  background: var(--tcg-color-surface-2);
  border-color: var(--tcg-color-border);
  color: var(--tcg-color-text-muted);
}
.tcg-entry--ended .tcg-deadline__value { color: var(--tcg-color-text-muted); }

.tcg-entry__closedlink {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
}

/* ==========================================================================
   ICS SUBSCRIBE PANEL
   ========================================================================== */

.tcg-calpanel {
  padding: var(--tcg-space-md);
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-surface);
  margin-bottom: var(--tcg-space-lg);
}

.tcg-calpanel__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-xs);
  align-items: center;
  margin-top: var(--tcg-space-xs);
}

/* A visible https URL with a copy button, because webcal:// handling is
   unreliable on Android and Chrome, and a broken subscribe button on the
   headline differentiator is a bad first impression. */

.tcg-calpanel select {
  min-height: 44px;
  padding: 0 var(--tcg-space-xs);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface);
}

.tcg-calpanel__note {
  font-size: var(--tcg-fs-xs);
  color: var(--tcg-color-text-faint);
  margin-top: var(--tcg-space-xs);
}

/* ==========================================================================
   ENDED SECTION — collapsed, capped to the last 14 days.
   The board HTML is what the CDN serves to every visitor during a traffic
   spike; an uncapped ended list would grow without bound.
   ========================================================================== */

.tcg-endedwrap > summary {
  cursor: pointer;
  list-style: none;
}
.tcg-endedwrap > summary::-webkit-details-marker { display: none; }
.tcg-endedwrap > summary .tcg-secthead { margin-bottom: 0; }
.tcg-endedwrap[open] > summary .tcg-secthead { margin-bottom: var(--tcg-space-md); }

.tcg-endedwrap__more {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: var(--tcg-space-xs);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
}

/* The control groups are MOVED into the filter sheet at every width by
   board.js, rather than duplicated — two copies would drift, and duplicate
   ids and names would break both. So they must be visible once inside it. */
[data-tcg-sheetbody] .tcg-controls__inline {
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-md);
  width: 100%;
}

/* ==========================================================================
   ACCOUNT CTA
   Saving lives in the site account (X login), not in a third-party calendar.
   This sits high on the page; the ICS feed is demoted to the bottom.
   ========================================================================== */

.tcg-accountcta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--tcg-space-md);
  padding: var(--tcg-space-md);
  margin-bottom: var(--tcg-space-lg);
  border: 1px solid var(--tcg-color-brand);
  border-left-width: 5px;
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-brand-soft);
}

.tcg-accountcta__body { flex: 1 1 260px; min-width: 0; }
.tcg-accountcta__title { font-size: var(--tcg-fs-lg); margin-bottom: var(--tcg-space-2xs); }
.tcg-accountcta__lead {
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
  line-height: var(--tcg-lh-base);
}

.tcg-calpanel__title { font-size: var(--tcg-fs-lg); margin-bottom: var(--tcg-space-2xs); }
.tcg-calpanel__lead {
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
  line-height: var(--tcg-lh-base);
}

/* ==========================================================================
   WATCH BUTTON — 気になる
   --------------------------------------------------------------------------
   Distinct from 応募した on purpose. 応募した means DONE: once you have
   applied, a deadline reminder is useless to you. The reminder-worthy state
   is "watching but have not applied yet", and that is what this tracks —
   so it is also what the personal calendar feed and any future email
   reminder should be built on.

   Ships disabled with its width reserved, same as the applied checkbox, so
   the personalisation pass causes no layout shift.
   ========================================================================== */

.tcg-watch {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-height: 44px;
  padding: 0 var(--tcg-space-sm);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-surface);
  color: var(--tcg-color-text);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  white-space: nowrap;
}
.tcg-watch:hover:not(:disabled) { background: var(--tcg-color-surface-2); }
.tcg-watch:disabled { opacity: .6; cursor: default; }

.tcg-watch__icon { font-size: var(--tcg-fs-base); line-height: 1; }

.tcg-watch[aria-pressed="true"] {
  background: var(--tcg-color-waiting-bg);
  border-color: var(--tcg-color-waiting-bd);
  color: var(--tcg-color-waiting-fg);
}


/* --- floating filter button ----------------------------------------------
   Appears only once the real control bar has scrolled out of view, driven
   by the same sentinel + IntersectionObserver that used to toggle the
   sticky shadow — still no scroll listener.

   position: fixed, so it never displaces content, and it is small enough to
   sit in a corner rather than covering the list. Because it lives in the
   thumb zone, the sheet it opens rises from the bottom on a phone; on a wide
   screen the same sheet is a centred modal (overlay.css, --sheet-modal). */
.tcg-filterfab {
  position: fixed;
  right: var(--tcg-space-sm);
  bottom: calc(var(--tcg-space-sm) + env(safe-area-inset-bottom));
  z-index: var(--tcg-z-sticky);

  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-height: 48px;
  padding: 0 var(--tcg-space-md);
  border: 1px solid var(--tcg-color-brand-strong);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-brand);
  color: var(--tcg-color-brand-contrast);
  font-family: inherit;
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  cursor: pointer;
  box-shadow: var(--tcg-shadow-overlay);

  /* Hidden state. visibility is stepped, not interpolated — same reason as
     the overlays: a visibility:hidden element cannot take focus. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--tcg-dur-fast) var(--tcg-ease),
              transform var(--tcg-dur-fast) var(--tcg-ease),
              visibility 0s linear var(--tcg-dur-fast);
}

.tcg-filterfab.is-shown {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--tcg-dur-fast) var(--tcg-ease),
              transform var(--tcg-dur-fast) var(--tcg-ease),
              visibility 0s linear 0s;
}

.tcg-filterfab__count {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-brand-contrast);
  color: var(--tcg-color-brand);
  font-size: var(--tcg-fs-2xs);
  font-variant-numeric: tabular-nums;
}
.tcg-filterfab__count:empty { display: none; }

@media (prefers-reduced-motion: reduce) {
  .tcg-filterfab { transition: none; }
}

/* Anchor targets keep a little breathing room even now that nothing is
   pinned over them — the no-JS path still uses the plain #hash link. */
.tcg-disclosure[id] { scroll-margin-top: var(--tcg-space-md); }

/* ==========================================================================
   SECTION NAV — すべて / ポケカ / ワンピ … on the board pagehead
   ========================================================================== */

.tcg-sections {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-2xs);
  margin: var(--tcg-space-xs) 0 var(--tcg-space-sm);
}

.tcg-sections a {
  display: inline-flex;
  align-items: center;
  min-height: 44px; /* same touch target as the filter chips */
  padding: var(--tcg-space-2xs) var(--tcg-space-sm);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-surface);
  color: var(--tcg-color-text-muted);
  font-size: var(--tcg-fs-sm);
  text-decoration: none;
}

.tcg-sections a[aria-current="page"] {
  background: var(--tcg-color-brand);
  border-color: var(--tcg-color-brand);
  color: var(--tcg-color-brand-contrast);
}

.tcg-sections a:focus-visible {
  outline: 3px solid var(--tcg-color-focus);
  outline-offset: 2px;
}
