/* ==========================================================================
   components.css — shared components used by every page
   --------------------------------------------------------------------------
   Nothing in here knows about the board or about articles. If a rule only
   applies to one page type, it belongs in board.css or article.css.
   ========================================================================== */

/* ==========================================================================
   EYEBROW — ALL-CAPS Latin label above a Japanese heading.
   A current, very Japanese convention (DEADLINES / FEATURES / NEWS) that
   costs nothing and immediately reads as a JP information site.
   ========================================================================== */

.tcg-eyebrow {
  display: block;
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  letter-spacing: var(--tcg-ls-eyebrow);
  text-transform: uppercase;
  color: var(--tcg-color-brand);
  margin-bottom: var(--tcg-space-2xs);
}

/* ==========================================================================
   SECTION HEADINGS
   The colored left bar is near-universal on Japanese information sites and
   is a large part of why they read as structured rather than sparse.
   ========================================================================== */

.tcg-secthead {
  display: flex;
  align-items: center;
  gap: var(--tcg-space-xs);
  margin: 0 0 var(--tcg-space-md);
  padding: var(--tcg-space-xs) var(--tcg-space-sm);
  background: var(--tcg-color-brand-soft);
  border-left: 5px solid var(--tcg-color-brand);
  border-radius: 0 var(--tcg-radius-sm) var(--tcg-radius-sm) 0;
  font-size: var(--tcg-fs-xl);
}

/* Status-tinted variants. The board recolours the band per status so the
   grouping is readable before a single word is parsed. */
.tcg-secthead[data-status="open"]     { background: var(--tcg-color-open-bg);     border-left-color: var(--tcg-color-open-fg); }
.tcg-secthead[data-status="upcoming"] { background: var(--tcg-color-upcoming-bg); border-left-color: var(--tcg-color-upcoming-fg); }
.tcg-secthead[data-status="waiting"]  { background: var(--tcg-color-waiting-bg);  border-left-color: var(--tcg-color-waiting-fg); }
.tcg-secthead[data-status="ended"]    { background: var(--tcg-color-ended-bg);    border-left-color: var(--tcg-color-ended-bd); }

.tcg-secthead__count {
  margin-left: auto;
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   STATUS PILL
   Colour is NEVER the only channel. Every status carries a text label and a
   glyph, and the entry card adds a distinct border-style. The result stays
   readable in greyscale, under deuteranopia, and on a printout.
   ========================================================================== */

.tcg-status {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  padding: 2px var(--tcg-space-xs);
  border-radius: var(--tcg-radius-sm);
  border: 1px solid currentColor;
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  line-height: var(--tcg-lh-snug);
  letter-spacing: var(--tcg-ls-wide);
  white-space: nowrap;
}

.tcg-status--open     { color: var(--tcg-color-open-fg);     background: var(--tcg-color-open-bg); }
.tcg-status--upcoming { color: var(--tcg-color-upcoming-fg); background: var(--tcg-color-upcoming-bg); }
.tcg-status--waiting  { color: var(--tcg-color-waiting-fg);  background: var(--tcg-color-waiting-bg); }
.tcg-status--ended    { color: var(--tcg-color-ended-fg);    background: var(--tcg-color-ended-bg); }

.tcg-status__glyph { font-size: 0.9em; line-height: 1; }

/* ==========================================================================
   CHIPS — taxonomy values on cards, and filter buttons in the controls.
   Same visual language on purpose: a chip on a card and the filter button
   that matches it must look like the same thing, because they ARE the same
   term. (The term name is also the exact same string in both places — see
   the note about area vocabulary in the plan.)
   ========================================================================== */

.tcg-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-3xs);
  padding: 2px var(--tcg-space-xs);
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface-2);
  color: var(--tcg-color-text-muted);
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  line-height: var(--tcg-lh-snug);
  white-space: nowrap;
  text-decoration: none;
}

/* Game chips carry a little identity so the eye can group by game while
   scanning a deadline-ordered list. */
.tcg-chip--game {
  background: var(--tcg-color-brand-soft);
  border-color: var(--tcg-color-brand);
  color: var(--tcg-color-brand);
}

.tcg-chip--type {
  background: var(--tcg-color-surface);
  border-color: var(--tcg-color-border-strong);
  color: var(--tcg-color-text);
}

/* Conditions are secondary information: outline only, no fill, so they
   recede behind the primary taxonomy row. */
.tcg-chip--cond {
  background: transparent;
  border-style: dashed;
  font-weight: 500;
}

a.tcg-chip:hover {
  border-color: var(--tcg-color-brand);
  color: var(--tcg-color-brand);
}

/* --- filter chips: real radio/checkbox inputs, visually a chip ---------- */

.tcg-chipbtn { position: relative; display: inline-flex; }

.tcg-chipbtn input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}

.tcg-chipbtn span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px minimum touch target. This is why the chips are generously
     padded rather than tight — thumbs, not cursors. */
  min-height: 44px;
  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);
  font-weight: 700;
  line-height: var(--tcg-lh-snug);
  transition: background var(--tcg-dur-fast) var(--tcg-ease),
              border-color var(--tcg-dur-fast) var(--tcg-ease);
}

.tcg-chipbtn input:checked + span {
  background: var(--tcg-color-brand);
  border-color: var(--tcg-color-brand);
  color: var(--tcg-color-brand-contrast);
}

.tcg-chipbtn input:focus-visible + span {
  outline: 3px solid var(--tcg-color-focus);
  outline-offset: 2px;
}

/* ==========================================================================
   BUTTONS
   ========================================================================== */

.tcg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--tcg-space-2xs);
  min-height: 44px;
  padding: var(--tcg-space-xs) var(--tcg-space-md);
  border: 1px solid transparent;
  border-radius: var(--tcg-radius-sm);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  line-height: var(--tcg-lh-snug);
  text-decoration: none;
  text-align: center;
  transition: background var(--tcg-dur-fast) var(--tcg-ease);
}

.tcg-btn--primary {
  background: var(--tcg-color-brand);
  color: var(--tcg-color-brand-contrast);
}
.tcg-btn--primary:hover { background: var(--tcg-color-brand-strong); color: var(--tcg-color-brand-contrast); }

.tcg-btn--ghost {
  background: var(--tcg-color-surface);
  border-color: var(--tcg-color-border-strong);
  color: var(--tcg-color-text);
}
.tcg-btn--ghost:hover { background: var(--tcg-color-surface-2); }

.tcg-btn--block { width: 100%; }

.tcg-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface);
  text-decoration: none;
  font-size: var(--tcg-fs-base);
}
.tcg-iconbtn:hover { background: var(--tcg-color-surface-2); }

/* ==========================================================================
   THE AD LINK — 景品表示法 / ステマ規制 compliance
   --------------------------------------------------------------------------
   CLAUDE.md hard rule 6: affiliate and sponsored links must carry a VISIBLE
   label and rel="sponsored nofollow", and the label must be baked into the
   component so it cannot be forgotten.

   Two mechanisms enforce that here:

   1. The badge lives INSIDE the anchor, so it is part of the link's
      accessible name and the future tcg_ad_link() PHP helper physically
      cannot emit the anchor without also emitting the badge.

   2. The self-policing rules at the bottom of this block make any
      non-compliant ad link loud and ugly during development. If the
      prototype ever looks wrong, that is the rule working.
   ========================================================================== */

.tcg-link--ad {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-xs);
  min-height: 44px;
  padding: var(--tcg-space-2xs) var(--tcg-space-sm);
  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-size: var(--tcg-fs-sm);
  font-weight: 700;
  text-decoration: none;
}
.tcg-link--ad:hover { background: var(--tcg-color-surface-2); }

/* Solid dark pill. Never brand-coloured — a brand-coloured ad label reads
   as an endorsement. Never a pastel tint — that would blend it into the
   status pills. Distinguishable by treatment alone. */
.tcg-adbadge {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 1px var(--tcg-space-2xs);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-ad-bg);
  color: var(--tcg-color-ad-fg);
  font-size: var(--tcg-fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tcg-ls-wide);
  line-height: 1.6;
  white-space: nowrap;
}

/* --- self-policing: development guardrail, must never fire in the wild --- */

.tcg-link--ad:not([rel~="sponsored"]),
.tcg-link--ad:not([rel~="nofollow"]),
.tcg-link--ad:not(:has(.tcg-adbadge)) {
  outline: 3px dashed var(--tcg-color-urgent-fg);
  outline-offset: 2px;
  background: var(--tcg-color-urgent-bg);
}

.tcg-link--ad:not(:has(.tcg-adbadge))::after {
  content: "⚠ 広告ラベル欠落";
  color: var(--tcg-color-urgent-fg);
  font-size: var(--tcg-fs-2xs);
  font-weight: 700;
}

.tcg-link--ad:not([rel~="sponsored"])::after,
.tcg-link--ad:not([rel~="nofollow"])::after {
  content: "⚠ rel属性が不正";
  color: var(--tcg-color-urgent-fg);
  font-size: var(--tcg-fs-2xs);
  font-weight: 700;
}

/* ==========================================================================
   AD SLOTS
   Built before any ad exists, precisely so the space is reserved and CLS
   stays zero once a network is wired in. Slot names here become the
   tcg-ads action hook names.
   ========================================================================== */

.tcg-adslot {
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-2xs);
  margin: var(--tcg-space-lg) 0;
}

.tcg-adslot::before {
  content: var(--tcg-ad-label);
  align-self: flex-start;
  padding: 1px var(--tcg-space-2xs);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-ad-bg);
  color: var(--tcg-color-ad-fg);
  font-size: var(--tcg-fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tcg-ls-wide);
}

/* Explicit reserved box. A third-party script that injects into an
   unsized container is the single most likely thing to destroy this
   design's CLS score. */
.tcg-adslot__box {
  display: grid;
  place-items: center;
  min-height: 100px;
  border: 1px dashed var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface-2);
  color: var(--tcg-color-text-faint);
  font-size: var(--tcg-fs-xs);
}

.tcg-adslot--banner .tcg-adslot__box { aspect-ratio: 8 / 1; min-height: 90px; }
.tcg-adslot--rect   .tcg-adslot__box { aspect-ratio: 4 / 3; min-height: 250px; }
/* Skyscraper. aspect-ratio + min-height reserve the full 600px before any ad
   script exists, which is the whole reason the slots are built now: reserved
   space today is CLS 0 later. */
.tcg-adslot--sky    .tcg-adslot__box { aspect-ratio: 160 / 600; min-height: 600px; }

/* An empty slot is not an advertisement, so it does not get an advertisement
   label. This is the only supported way to suppress it, and it is scoped to
   the container — never to a link that carries an affiliate URL.

   Reserved height is deliberately kept: the box still occupies its full size
   so nothing shifts when a real ad arrives. */
.tcg-adslot--nolabel::before { content: none; }

/* ==========================================================================
   STAR RATING — 入手難度
   Inline SVG off one page sprite, NOT emoji: ★ and ☆ metrics and colour
   vary wildly across Japanese font fallbacks, so an emoji rating renders
   differently on every machine.
   ========================================================================== */

.tcg-stars {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-3xs);
}

.tcg-stars__icon {
  width: 14px;
  height: 14px;
  fill: var(--tcg-color-star-off);
  flex: none;
}
.tcg-stars__icon.is-on { fill: var(--tcg-color-star-on); }

/* The numeral is what people actually scan; the stars are the texture. */
.tcg-stars__value {
  margin-left: var(--tcg-space-3xs);
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  font-variant-numeric: tabular-nums;
}

.tcg-stars__help {
  margin-left: var(--tcg-space-3xs);
  width: 16px; height: 16px;
  display: inline-grid;
  place-items: center;
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: 50%;
  font-size: 10px;
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  text-decoration: none;
  line-height: 1;
}

/* ==========================================================================
   DISCLOSURE — inline expansion.
   Used ONLY where an accordion is genuinely expected and overlaying would
   be worse: the tip inside an entry card, and the ended section at the
   bottom of the board. Every menu, filter panel and mobile table of
   contents is a fixed overlay instead — see overlay.css.
   ========================================================================== */

.tcg-disclosure {
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface);
}

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

.tcg-disclosure > summary::after {
  content: "▾";
  margin-left: auto;
  transition: transform var(--tcg-dur-fast) var(--tcg-ease);
}
.tcg-disclosure[open] > summary::after { transform: rotate(180deg); }

.tcg-disclosure__body {
  padding: 0 var(--tcg-space-sm) var(--tcg-space-sm);
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
}

/* ==========================================================================
   FEATURE CARD — the component that has to survive feature #2.
   The board is feature one; a user-generated stock map and a contribution
   ranking are planned. This card is designed properly rather than as
   filler precisely because it is the thing a new feature plugs into.
   ========================================================================== */

.tcg-featurecard {
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-xs);
  padding: var(--tcg-space-md);
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-surface);
  box-shadow: var(--tcg-shadow-card);
  text-decoration: none;
  color: inherit;
}

a.tcg-featurecard:hover { border-color: var(--tcg-color-brand); }

.tcg-featurecard__icon { font-size: 1.75rem; line-height: 1; }
.tcg-featurecard__title { font-size: var(--tcg-fs-lg); font-weight: 700; }
.tcg-featurecard__desc {
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
  line-height: var(--tcg-lh-base);
}

/* A genuine placeholder, not a dead link: visibly not-yet-available. */
.tcg-featurecard--soon {
  border-style: dashed;
  background: var(--tcg-color-surface-2);
  box-shadow: none;
}

.tcg-featurecard__badge {
  align-self: flex-start;
  padding: 1px var(--tcg-space-xs);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-waiting-bg);
  color: var(--tcg-color-waiting-fg);
  border: 1px solid var(--tcg-color-waiting-bd);
  font-size: var(--tcg-fs-2xs);
  font-weight: 700;
}

/* ==========================================================================
   PAGINATION
   ========================================================================== */

.tcg-pagination {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-2xs);
  justify-content: center;
  margin: var(--tcg-space-xl) 0;
}

.tcg-pagination a,
.tcg-pagination span {
  display: inline-grid;
  place-items: center;
  min-width: 44px;
  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);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}

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

/* ==========================================================================
   NOTICE / EMPTY STATE
   ========================================================================== */

.tcg-notice {
  padding: var(--tcg-space-lg);
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-surface);
  text-align: center;
}

.tcg-notice__title {
  font-size: var(--tcg-fs-lg);
  font-weight: 700;
  margin-bottom: var(--tcg-space-xs);
}

.tcg-notice__body {
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
  margin-bottom: var(--tcg-space-md);
}

/* Active filters shown as individually removable chips, so an empty result
   is actionable rather than apologetic. */
.tcg-notice__filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-2xs);
  justify-content: center;
  margin-bottom: var(--tcg-space-md);
}

.tcg-filterchip {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-height: 32px;
  padding: var(--tcg-space-3xs) var(--tcg-space-xs);
  border: 1px solid var(--tcg-color-brand);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-brand-soft);
  color: var(--tcg-color-brand);
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
}
.tcg-filterchip__x { font-size: var(--tcg-fs-sm); line-height: 1; }

/* ==========================================================================
   HERO — hub homepage only
   ========================================================================== */

.tcg-hero {
  padding: var(--tcg-space-lg) 0 var(--tcg-space-xl);
  border-bottom: 1px solid var(--tcg-color-border);
  margin-bottom: var(--tcg-space-xl);
}
.tcg-hero__title { font-size: var(--tcg-fs-2xl); margin-bottom: var(--tcg-space-xs); }
@media (min-width: 900px) { .tcg-hero__title { font-size: var(--tcg-fs-3xl); } }

.tcg-hero__lead {
  font-size: var(--tcg-fs-base);
  color: var(--tcg-color-text-muted);
  line-height: var(--tcg-lh-base);
  margin-bottom: var(--tcg-space-md);
}
.tcg-hero__actions { display: flex; flex-wrap: wrap; gap: var(--tcg-space-xs); }

/* ==========================================================================
   MINI ROW — compact deadline line used on the hub and マイページ
   --------------------------------------------------------------------------
   Deliberately NOT the full entry card. These surfaces answer "what is
   closing soon", not "should I apply" — the card's schedule, conditions and
   actions would be noise here, and would push the list below the fold.
   ========================================================================== */

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

.tcg-minirow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--tcg-space-xs);
  padding: var(--tcg-space-xs) var(--tcg-space-sm);
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface);
}

.tcg-minirow__title {
  flex: 1 1 100%;
  order: -1;
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
  color: var(--tcg-color-text);
  text-decoration: none;
  line-height: var(--tcg-lh-tight);
}
.tcg-minirow__title:hover { color: var(--tcg-color-brand); text-decoration: underline; }

@media (min-width: 600px) {
  /* On wider screens the title returns to the flow and takes the slack, so
     the row reads as one line rather than two. */
  .tcg-minirow__title { flex: 1 1 auto; order: 0; }
}

.tcg-minirow__when {
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  color: var(--tcg-color-text-muted);
  white-space: nowrap;
}
.tcg-minirow__when.is-unknown { color: var(--tcg-color-text-faint); }

.tcg-secthead__count a { font-size: var(--tcg-fs-sm); text-decoration: none; }

/* ==========================================================================
   NEWS LIST
   ========================================================================== */

.tcg-newslist { list-style: none; padding: 0; }
.tcg-newslist li {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tcg-space-xs);
  padding: var(--tcg-space-xs) 0;
  border-bottom: 1px solid var(--tcg-color-border);
  font-size: var(--tcg-fs-sm);
}
.tcg-newslist time { color: var(--tcg-color-text-faint); white-space: nowrap; }

/* ==========================================================================
   PROFILE + STATS — マイページ
   ========================================================================== */

.tcg-profile {
  display: flex;
  align-items: center;
  gap: var(--tcg-space-md);
  flex-wrap: wrap;
  padding-bottom: var(--tcg-space-md);
  border-bottom: 1px solid var(--tcg-color-border);
  margin-bottom: var(--tcg-space-md);
}
.tcg-profile > a { margin-left: auto; }

/* マイページ and 保存した情報の一覧 fill themselves in after load. Anything
   still waiting holds its space; visibility, not display, so nothing below
   moves when it arrives. */
[data-tcg-mypage] .is-pending,
[data-tcg-saved] .is-pending { visibility: hidden; }

.tcg-mypage__all { margin: var(--tcg-space-sm) 0 0; }

/* The 気になる / 応募した switch on the list page. */
.tcg-kindswitch {
  display: flex;
  gap: var(--tcg-space-2xs);
  margin-bottom: var(--tcg-space-lg);
}
.tcg-kindswitch .tcg-btn[aria-pressed="true"] {
  background: var(--tcg-color-brand);
  border-color: var(--tcg-color-brand);
  color: var(--tcg-color-brand-contrast);
}

/* The count after a label: 「（12件）」, nothing at all while unknown. */
[data-tcg-total]:not(:empty)::before { content: '（'; }
[data-tcg-total]:not(:empty)::after  { content: '件）'; }

.tcg-pagination [aria-disabled="true"] {
  opacity: 0.4;
  pointer-events: none;
}

.tcg-profile__avatar {
  width: 56px; height: 56px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--tcg-color-surface-2);
  border: 2px solid var(--tcg-color-border-strong);
  font-size: 1.75rem;
}
.tcg-profile__name { font-size: var(--tcg-fs-xl); }
.tcg-profile__handle { font-size: var(--tcg-fs-sm); color: var(--tcg-color-text-muted); }

.tcg-statgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--tcg-space-xs);
  margin-bottom: var(--tcg-space-xl);
}
@media (min-width: 600px) { .tcg-statgrid { grid-template-columns: repeat(4, 1fr); } }

.tcg-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--tcg-space-3xs);
  padding: var(--tcg-space-sm);
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-surface);
  text-align: center;
}
.tcg-stat__num {
  font-size: var(--tcg-fs-xl);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.tcg-stat__label { font-size: var(--tcg-fs-xs); color: var(--tcg-color-text-muted); }
.tcg-stat--soon { border-style: dashed; background: var(--tcg-color-surface-2); }
.tcg-stat--soon .tcg-stat__num { color: var(--tcg-color-text-faint); }

/* ==========================================================================
   BREADCRUMB
   ========================================================================== */

.tcg-breadcrumb {
  font-size: var(--tcg-fs-xs);
  color: var(--tcg-color-text-faint);
  margin-bottom: var(--tcg-space-md);
}
.tcg-breadcrumb a { color: var(--tcg-color-text-muted); text-decoration: none; }
.tcg-breadcrumb a:hover { color: var(--tcg-color-brand); text-decoration: underline; }

/* ==========================================================================
   DEADLINE PANEL — entry detail page only
   On a card the deadline is one line competing with everything else. On the
   detail page there is room to make the single question that matters
   impossible to miss.
   ========================================================================== */

.tcg-deadlinepanel {
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-3xs);
  padding: var(--tcg-space-md);
  margin-bottom: var(--tcg-space-lg);
  border: 2px solid var(--tcg-color-open-bd);
  border-radius: var(--tcg-radius-md);
  background: var(--tcg-color-open-bg);
}
.tcg-deadlinepanel__label {
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  letter-spacing: var(--tcg-ls-wide);
  color: var(--tcg-color-open-fg);
}
.tcg-deadlinepanel__value {
  font-size: var(--tcg-fs-2xl);
  font-weight: 700;
  line-height: 1.2;
}
@media (min-width: 600px) {
  .tcg-deadlinepanel__value { font-size: var(--tcg-fs-3xl); }
}
.tcg-deadlinepanel__sub {
  font-size: var(--tcg-fs-sm);
  color: var(--tcg-color-text-muted);
}

/* ==========================================================================
   RATING BREAKDOWN — the three axes, shown broken out
   --------------------------------------------------------------------------
   Deliberately visible on the detail page. Three judgement calls per entry,
   every day, from non-technical editors, is the highest-friction field in
   the data model — so what is being asked of them should be plain rather
   than hidden behind a single averaged star score.
   ========================================================================== */

.tcg-ratingbreak {
  display: flex;
  flex-direction: column;
  gap: var(--tcg-space-xs);
}
.tcg-ratingbreak > div {
  display: grid;
  grid-template-columns: 7em 1fr;
  align-items: center;
  gap: var(--tcg-space-xs);
}
.tcg-ratingbreak dt { font-size: var(--tcg-fs-sm); font-weight: 700; }
.tcg-ratingbreak dd {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--tcg-space-xs);
  font-size: var(--tcg-fs-sm);
  font-weight: 700;
}

/* ==========================================================================
   SEARCH FORM
   ========================================================================== */

.tcg-searchform {
  display: flex;
  gap: var(--tcg-space-xs);
  margin-bottom: var(--tcg-space-xl);
}
.tcg-searchform input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: 0 var(--tcg-space-sm);
  border: 1px solid var(--tcg-color-border-strong);
  border-radius: var(--tcg-radius-sm);
  background: var(--tcg-color-surface);
  color: var(--tcg-color-text);
}

/* --- light / dark switch --------------------------------------------------
   Sits in the header beside the nav. Fixed min-width so cycling through
   自動 / ライト / ダーク (2, 3 and 3 glyphs) cannot resize the header and
   nudge the nav sideways — the same no-shift rule the overlays follow. */
.tcg-themebtn {
  display: inline-flex;
  align-items: center;
  gap: var(--tcg-space-2xs);
  min-width: 6.5em;
  justify-content: center;
  padding: var(--tcg-space-2xs) var(--tcg-space-xs);
  min-height: 36px;
  border: 1px solid var(--tcg-color-border);
  border-radius: var(--tcg-radius-pill);
  background: var(--tcg-color-surface);
  color: var(--tcg-color-text-muted);
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}

.tcg-themebtn:hover {
  background: var(--tcg-color-surface-2);
  color: var(--tcg-color-text);
}

/* Fixed box. ◐ ☀ ☾ have different advance widths, so without this the
   button — and therefore the header — resizes on every press. Caught by
   measuring, not by looking: the jump is about 5px. */
.tcg-themebtn__icon {
  font-size: 1em;
  line-height: 1;
  width: 1.25em;
  text-align: center;
  flex: none;
}

/* The label is hidden below the nav breakpoint — the glyph alone carries it,
   and the accessible name on the button always spells the state out. */
@media (max-width: 599px) {
  /* Label drops, but the width stays pinned so the header still cannot move. */
  .tcg-themebtn { min-width: 0; width: 40px; padding-inline: 0; }
  .tcg-themebtn__text { display: none; }
}

/* --- ★入手難度: the scale, and the evidence table -------------------------
   The board explains what the star MEANS and what to do about it; the entry
   page shows the evidence behind that specific number. Two different jobs,
   so two different components — they used to be near-identical paragraphs,
   which made the detail page add nothing. */

/* The ★ is an editorial judgement and says so. Vagueness that implies a
   measurement is indefensible; vagueness that is disclosed as a judgement is
   just subjective, which is honest and true. The <strong> inside carries that
   sentence, so it is not styled down into the small print. */
.tcg-rating__disclaim {
  margin-top: var(--tcg-space-sm);
  font-size: var(--tcg-fs-xs);
  line-height: var(--tcg-lh-snug);
  color: var(--tcg-color-text-muted);
}
.tcg-rating__disclaim strong { color: var(--tcg-color-text); }

.tcg-ratingscale {
  list-style: none;
  margin: var(--tcg-space-xs) 0;
  padding: 0;
  display: grid;
  gap: var(--tcg-space-2xs);
  font-size: var(--tcg-fs-sm);
  line-height: var(--tcg-lh-snug);
}

.tcg-ratingscale li {
  display: grid;
  grid-template-columns: 5.5em 1fr;
  gap: var(--tcg-space-xs);
  align-items: baseline;
}

.tcg-ratingscale__n {
  font-weight: 700;
  color: var(--tcg-color-star-on);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@media (max-width: 479px) {
  .tcg-ratingscale li { grid-template-columns: 1fr; gap: 0; }
}

/* Article tables can exceed the prose measure; let them scroll rather than
   squash. Previously also a container-query context for the ★ rubric — removed
   with that feature, since containment is not free and nobody queries it. */
.tcg-tablewrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* --- ACCOUNT: the header's login / logout control -------------------------
   Both states live in the DOM at once because the header is inside the cached
   page — the true state cannot be known server-side without serving one
   visitor's session to everyone. shell.js reveals one after load.

   Grid-stacked rather than swapped: 「ログイン」 and 「ログアウト」 are
   different widths, so replacing one with the other would shift the whole
   header on every load. Same trick, and same reason, as .tcg-commentgate.

   visibility rather than display, so the cell keeps the width of the WIDER
   state and nothing moves when the swap happens. */
.tcg-account {
  display: grid;
  flex: none;
}

.tcg-account > * {
  grid-area: 1 / 1;
}

/* Signed out is the default, because that is what the cached HTML is for. */
.tcg-account [data-tcg-auth="in"] { visibility: hidden; }
.tcg-account.is-authed [data-tcg-auth="out"] { visibility: hidden; }
.tcg-account.is-authed [data-tcg-auth="in"] { visibility: visible; }

.tcg-accountbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--tcg-space-2xs);
  min-height: 36px;
  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);
  font-size: var(--tcg-fs-xs);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

.tcg-accountbtn:hover { background: var(--tcg-color-surface-2); }

/* Fixed box, like .tcg-themebtn__icon: 𝕏 and → have different advance widths
   and would resize the button between states. */
.tcg-accountbtn__icon {
  font-size: 1em;
  line-height: 1;
  width: 1.25em;
  text-align: center;
  flex: none;
}

/* Below the nav breakpoint the label drops to the glyph alone. The link text
   still carries the full wording for assistive technology. */
@media (max-width: 599px) {
  .tcg-accountbtn { padding-inline: var(--tcg-space-xs); }
  .tcg-accountbtn__text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* The drawer's copy of the account control. Full width and spelled out — the
   header's glyph-only form is tappable but not readable at 375px. */
/* No rule of its own: the last link in the list above already draws one. */
.tcg-drawernav__account {
  margin-top: var(--tcg-space-md);
}

.tcg-drawernav__account .tcg-accountbtn { display: flex; min-height: 44px; }

/* Overrides the header's mobile rule: in the drawer there is room for words. */
.tcg-drawernav__account .tcg-accountbtn__text {
  position: static;
  width: auto;
  height: auto;
  overflow: visible;
  clip-path: none;
}

/* --- AUTH SWAP: page-level, as opposed to the header's control ------------
   .tcg-account stacks its two states in one grid cell to pin the width. That
   is right for a button and wrong for a page body, where the two states have
   nothing like the same height — stacking them would reserve the taller one
   and leave a hole.

   So this one toggles display. The cost is a visible change for a signed-in
   reader once the cookie is read, on a utility page they pass through. The
   alternative is hiding both until JavaScript decides, which shows nothing at
   all to anyone without it. */
.tcg-authswap [data-tcg-auth="in"] { display: none; }
.tcg-authswap.is-authed [data-tcg-auth="out"] { display: none; }
.tcg-authswap.is-authed [data-tcg-auth="in"] { display: block; }
