/* 리뷰토리 컴포넌트 v2 — tokens.css 위에서만 동작한다.
 *
 * v1과 무엇이 달라졌나
 *   · 카드에서 1px 테두리를 뺐다. 면(surface)과 그림자와 여백으로 나눈다.
 *     테두리를 사방에 두르면 화면이 표처럼 보이고, 그게 오래된 인상의 큰 원인이었다.
 *   · 모서리를 키웠다(카드 14 / 시트 24). 작은 radius는 각져 보인다.
 *   · 제목 자간을 조였다. 한글은 큰 글자에서 자간을 좁혀야 현대적으로 읽힌다.
 *   · 이모지 대신 스트로크 아이콘(icons.ts)을 쓴다.
 *
 * 규칙
 *   · 이 파일에 hex 를 쓰지 않는다. 전부 var(--c-*).
 *   · 클래스 이름은 앱(React Native) 스타일 객체 이름과 1:1.
 */

/* ── 레이아웃 ──────────────────────────────────────── */
.rt-stack { display:flex; flex-direction:column; gap:var(--space-x3); }
.rt-stack-lg { gap:var(--space-x5); }
.rt-row { display:flex; align-items:center; gap:var(--space-x2); }
.rt-row-between { display:flex; align-items:center; justify-content:space-between; gap:var(--space-x3); }
.rt-grid { display:grid; gap:var(--space-x4); grid-template-columns:repeat(auto-fit, minmax(184px, 1fr)); }
.rt-grid-2 { display:grid; gap:var(--space-x3); grid-template-columns:1fr 1fr; }
.rt-spacer { flex:1; }

/* ── 타이포 ────────────────────────────────────────── */
.rt-hero { font-size:var(--text-hero-size); line-height:var(--text-hero-line); font-weight:var(--text-hero-weight); letter-spacing:var(--text-hero-track); margin:0; }
.rt-display { font-size:var(--text-display-size); line-height:var(--text-display-line); font-weight:var(--text-display-weight); letter-spacing:var(--text-display-track); }
.rt-h1 { font-size:var(--text-h1-size); line-height:var(--text-h1-line); font-weight:var(--text-h1-weight); letter-spacing:var(--text-h1-track); margin:0; }
.rt-h2 { font-size:var(--text-h2-size); line-height:var(--text-h2-line); font-weight:var(--text-h2-weight); letter-spacing:var(--text-h2-track); margin:0; }
.rt-h3 { font-size:var(--text-h3-size); line-height:var(--text-h3-line); font-weight:var(--text-h3-weight); letter-spacing:var(--text-h3-track); margin:0; }
.rt-caption { font-size:var(--text-caption-size); line-height:var(--text-caption-line); font-weight:var(--text-caption-weight); letter-spacing:var(--text-caption-track); color:var(--c-ink-faint); }
.rt-label { font-size:var(--text-label-size); line-height:var(--text-label-line); font-weight:var(--text-label-weight); letter-spacing:var(--text-label-track); color:var(--c-ink-faint); }
.rt-muted { color:var(--c-ink-muted); }
.rt-mini { font-size:var(--text-mini-size); line-height:var(--text-mini-line); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); }
.rt-micro { font-size:var(--text-micro-size); line-height:var(--text-micro-line); font-weight:var(--text-micro-weight); letter-spacing:var(--text-micro-track); }
.rt-faint { color:var(--c-ink-faint); }

/* 금액은 잉크색이다. 색으로 강조하지 않고 굵기와 크기로 강조한다. */
.rt-money { font-variant-numeric:tabular-nums; font-weight:720; letter-spacing:var(--text-numeric-track); }
.rt-money-plus { color:var(--c-brand); }
.rt-num { font-variant-numeric:tabular-nums; font-weight:600; }

/* ── 조작 상태 ─────────────────────────────────────
 * 한 규칙으로 고정한다. 컴포넌트마다 다르게 반응하면 각각은 멀쩡해도 합쳐 놓으면 짜깁기로 읽힌다.
 *
 *  hover   면에 잉크를 6% 섞는다 — 라이트에서는 어두워지고 다크에서는 밝아진다
 *  press   10% + 0.97 배로 눌린다
 *  focus   키보드로 왔을 때만 링이 보인다(:focus-visible). 마우스 클릭에는 안 뜬다
 *  disabled 투명도 0.45 + 커서 금지
 */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--focus-width) solid var(--c-brand);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-xs);
}

/* ── 버튼 ──────────────────────────────────────────── */
.rt-btn {
  font-family:inherit; font-weight:700; font-size:var(--text-body-sm-size); line-height:1.2;
  letter-spacing:var(--text-body-sm-track);
  padding:var(--space-x4) var(--space-x6); border-radius:var(--radius-sm); border:0;
  cursor:pointer; display:inline-flex; align-items:center; justify-content:center; gap:var(--space-x2);
  transition:transform var(--motion-fast) var(--motion-ease), background var(--motion-fast) var(--motion-ease),
             opacity var(--motion-fast) var(--motion-ease);
  white-space:nowrap; min-height:48px;
}
.rt-btn:active:not(:disabled) { transform:scale(var(--press-scale)); }
.rt-btn:disabled { opacity:var(--disabled-opacity); cursor:not-allowed; }
.rt-btn-primary { background:var(--c-brand); color:var(--c-on-brand); }
.rt-btn-primary:hover:not(:disabled) { background:var(--c-brand-hover); }
.rt-btn-ghost { background:var(--c-brand-soft); color:var(--c-brand-hover); }
.rt-btn-quiet { background:var(--c-surface-alt); color:var(--c-ink-muted); }
.rt-btn-danger { background:var(--c-danger-soft); color:var(--c-danger); }
/* 보조 버튼들의 hover·press — 색을 새로 만들지 않고 자기 배경에 잉크를 섞는다 */
.rt-btn-ghost:hover:not(:disabled) { background:color-mix(in srgb, var(--c-ink) var(--hover-tint), var(--c-brand-soft)); }
.rt-btn-quiet:hover:not(:disabled) { background:color-mix(in srgb, var(--c-ink) var(--hover-tint), var(--c-surface-alt)); }
.rt-btn-danger:hover:not(:disabled) { background:color-mix(in srgb, var(--c-ink) var(--hover-tint), var(--c-danger-soft)); }
.rt-btn-primary:active:not(:disabled) { background:color-mix(in srgb, var(--c-ink) var(--press-tint), var(--c-brand-hover)); }
/* 작은 버튼은 **글자만 작아진다.** 높이는 손가락 기준(44)을 지킨다 */
.rt-btn-sm { font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); padding:var(--space-x2) var(--space-x4); min-height:var(--touch-min); border-radius:var(--radius-xs); }
.rt-btn-block { width:100%; }

/* ── 칩 ────────────────────────────────────────────── */
.rt-chip {
  border:0; background:var(--c-surface-alt); color:var(--c-ink-muted);
  font-family:inherit; font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track);
  padding:var(--space-x2) var(--space-x4); border-radius:var(--radius-full);
  white-space:nowrap; cursor:pointer; min-height:var(--touch-min);
  transition:background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease),
             transform var(--motion-fast) var(--motion-ease);
}
.rt-chip:hover:not(:disabled) { background:color-mix(in srgb, var(--c-ink) var(--hover-tint), var(--c-surface-alt)); }
.rt-chip:active:not(:disabled) { transform:scale(var(--press-scale)); }
.rt-chip:disabled { opacity:var(--disabled-opacity); cursor:not-allowed; }
.rt-chip[aria-pressed="true"], .rt-chip.is-on { background:var(--c-ink); color:var(--c-surface); }
.rt-chip[aria-pressed="true"]:hover, .rt-chip.is-on:hover { background:color-mix(in srgb, var(--c-surface) var(--hover-tint), var(--c-ink)); }
/*
 * 가로 스크롤이 가능하다는 것을 **끝의 페이드로** 알린다.
 * 잘린 칩만 보이면 화면이 깨진 것처럼 보인다 — 스크롤바를 숨겼으면 힌트를 줘야 한다.
 * padding 은 4 이상이어야 한다 — overflow 가 걸린 상자는 자식의 포커스 링(오프셋 2 + 굵기 2)을 자른다.
 */
.rt-chipbar { display:flex; gap:var(--space-x2); overflow-x:auto; padding:var(--space-x1) 0; scrollbar-width:none;
  /* 마스크의 검정은 '불투명'이라는 뜻이지 색이 아니다 — 테마를 타지 않는다 */
  -webkit-mask-image:linear-gradient(to right, black 0, black calc(100% - 24px), transparent 100%);
  mask-image:linear-gradient(to right, black 0, black calc(100% - 24px), transparent 100%); }
.rt-chipbar::-webkit-scrollbar { display:none; }

/* ── 배지 ──────────────────────────────────────────── */
.rt-badge {
  display:inline-flex; align-items:center; gap:var(--space-x05);
  font-size:var(--text-micro-size); font-weight:var(--text-micro-weight); letter-spacing:var(--text-micro-track);
  padding:var(--space-x05) var(--space-x15); border-radius:var(--radius-xs);
  white-space:nowrap; border:0;
}
.rt-badge .rt-icon { width:13px; height:13px; }
.rt-badge-fill    { background:var(--c-ink); color:var(--c-surface); font-variant-numeric:tabular-nums; }
.rt-badge-outline { background:var(--c-surface-alt); color:var(--c-ink-muted); }
.rt-badge-guard   { background:var(--c-brand-soft); color:var(--c-brand-hover); }
.rt-badge-pay     { background:var(--c-accent-soft); color:var(--c-accent); }
.rt-badge-danger  { background:var(--c-danger-soft); color:var(--c-danger); }
.rt-badge-muted   { background:var(--c-surface-alt); color:var(--c-ink-faint); }
.rt-badges { display:flex; gap:var(--space-x1); flex-wrap:nowrap; overflow:hidden; }
/* 좁은 카드에서 마지막 배지가 '여'처럼 반만 잘려 보였다 — 카드(한 줄로 자르는 곳)만 오른쪽 끝을 흐린다.
   줄바꿈하는 곳(콘솔 초안 등)까지 흐리면 다 보이는 배지의 대비만 떨어졌다(검토 L8) */
.rt-campaign .rt-badges {
  -webkit-mask-image:linear-gradient(to right, black calc(100% - var(--space-x6)), transparent);
          mask-image:linear-gradient(to right, black calc(100% - var(--space-x6)), transparent); }

/* ── 토스트 · 확인 대화상자 ────────────────────────
 * alert()/confirm() 자리를 대신한다. 일부 인앱 브라우저는 기본 창을 아예 띄우지 않아서,
 * "버튼이 죽었다"는 오해와 이중 실행의 원인이었다.
 */
.rt-toast { position:fixed; left:50%; transform:translateX(-50%);
  /* 탭바(약 68) 위에 얹는다 */
  bottom:calc(var(--space-x16) + var(--space-x3) + env(safe-area-inset-bottom));
  z-index:80; max-width:min(92vw, 420px); box-sizing:border-box;
  background:var(--c-ink); color:var(--c-surface); border-radius:var(--radius-sm);
  padding:var(--space-x3) var(--space-x4); box-shadow:var(--shadow-lg);
  font-size:var(--text-caption-size); font-weight:var(--text-caption-weight);
  line-height:var(--text-caption-line); letter-spacing:var(--text-caption-track); }
/*
 * 비어 있는 채로 항상 붙어 있다 — aria-live 는 "있던 영역이 바뀔 때" 읽는다.
 * 🔴 visibility:hidden 을 쓰면 안 된다. display:none 과 똑같이 **접근성 트리에서 빠져서**,
 *    미리 붙여 둔 의미가 사라진다(영역이 내용과 함께 나타난 것처럼 보인다).
 *    opacity 만으로 충분히 감춰지고, 클릭은 pointer-events 가 막는다.
 */
.rt-toast { opacity:0; pointer-events:none; }
.rt-toast.is-on { opacity:1; }
.rt-toast.is-in { animation:rt-toast-in var(--motion-base) var(--motion-ease); }
/*
 * 애니메이션에 opacity 를 넣지 않는다.
 * 토스트 영역은 화면에 **항상 붙어 있다**(aria-live 때문). 그런데 탭이 뒤에 있거나 렌더가 멈추면
 * 애니메이션이 첫 프레임에 멈추는데, 거기에 opacity:0 이 있으면 토스트가 통째로 안 보인다.
 * 움직임은 위치로만 준다 — 멈춰도 글자는 보인다.
 */
@keyframes rt-toast-in { from { transform:translate(-50%, 8px); } }
.rt-toast.is-error { background:var(--c-danger); }

.rt-dialog { position:fixed; inset:0; z-index:90; display:grid; place-items:center;
  background:rgba(15,23,32,.44); -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);
  padding:var(--space-x5); animation:rt-dialog-fade var(--motion-fast) var(--motion-ease); }
@keyframes rt-dialog-fade { from { opacity:0; } }
.rt-dialog__panel { background:var(--c-surface); border-radius:var(--radius-md); box-shadow:var(--shadow-lg);
  padding:var(--space-x6); width:100%; max-width:360px; box-sizing:border-box;
  animation:rt-dialog-rise var(--motion-base) var(--motion-ease); }
@keyframes rt-dialog-rise { from { opacity:0; transform:translateY(8px) scale(.98); } }
.rt-dialog__t { font-size:var(--text-h3-size); line-height:var(--text-h3-line);
  font-weight:var(--text-h3-weight); letter-spacing:var(--text-h3-track); }
.rt-dialog__b { margin-top:var(--space-x2); color:var(--c-ink-muted);
  font-size:var(--text-caption-size); font-weight:var(--text-caption-weight);
  line-height:var(--text-caption-line); letter-spacing:var(--text-caption-track);
  display:flex; flex-direction:column; gap:var(--space-x1); }
.rt-dialog__act { display:flex; gap:var(--space-x2); margin-top:var(--space-x6); }
.rt-dialog__act .rt-btn { flex:1; }

/* ── 스켈레톤 ──────────────────────────────────────
 * 스피너는 "기다려라"만 말한다. 스켈레톤은 **무엇이 올 자리인지**까지 말한다 —
 * 그래서 같은 대기 시간이 짧게 느껴지고, 화면이 튀지 않는다(레이아웃이 미리 자리를 잡는다).
 * 움직임은 prefers-reduced-motion 에서 tokens.css 가 통째로 끈다.
 */
.rt-skel { background:var(--c-surface-alt); border-radius:var(--radius-xs); height:12px;
  position:relative; overflow:hidden; }
.rt-skel::after { content:''; position:absolute; inset:0; transform:translateX(-100%);
  background:linear-gradient(90deg, transparent, color-mix(in srgb, var(--c-ink) 6%, transparent), transparent);
  animation:rt-shimmer 1.2s infinite; }
@keyframes rt-shimmer { to { transform:translateX(100%); } }
.rt-skel-line { height:16px; }
.rt-skel-thumb { aspect-ratio:1/1; height:auto; border-radius:0; }
.rt-skel-card { background:var(--c-surface); border-radius:var(--radius-card); overflow:hidden; box-shadow:var(--shadow-sm); }
.rt-skel-body { padding:var(--space-x3); display:flex; flex-direction:column; gap:var(--space-x2); }

/* ── 표면 ──────────────────────────────────────────
 * 테두리가 없다. 흰 면 + 아주 옅은 그림자로 바탕에서 떠 있게 한다.
 */
.rt-card { background:var(--c-surface); border-radius:var(--radius-md); padding:var(--space-x6); box-shadow:var(--shadow-sm); }
.rt-card-flush { padding:0; overflow:hidden; }
.rt-card-raised { box-shadow:var(--shadow-md); }
.rt-card-flat { background:var(--c-surface-alt); box-shadow:none; }

/* ── 캠페인 카드 ───────────────────────────────────── */
/*
 * v4(2026-09-21) — **카드에 면을 준다.**
 *
 * 전까지 카드는 투명이었다. 썸네일 블록만 떠 있고 글자는 바탕에 얹혀 있어서
 * 목록을 훑을 때 어디까지가 한 카드인지 눈으로 잡히지 않았다.
 * 면 + 아주 옅은 그림자면 덩어리가 생긴다 — 커머스 앱이 전부 이렇게 하는 이유다.
 */
.rt-campaign { display:block; width:100%; text-align:left; border:0; padding:0; cursor:pointer;
  background:var(--c-surface); color:inherit; font-family:inherit;
  border-radius:var(--radius-card); overflow:hidden; box-shadow:var(--shadow-sm);
  transition:transform var(--motion-fast) var(--motion-ease), box-shadow var(--motion-fast) var(--motion-ease); }
.rt-campaign:hover { box-shadow:var(--shadow-md); }
.rt-campaign:active { transform:scale(var(--press-scale)); }
/*
 * 1:1 — 토큰(component.campaignCard.thumbRatio)은 처음부터 '1 / 1' 이었는데 CSS 만 4:3 이었다.
 * 2열 목록에서는 정사각이 표준이다(쿠팡·무신사·29CM). 가로가 길면 카드가 세로로 짧아져
 * 제목·금액·메타가 들어갈 자리가 줄고, 한 화면에 보이는 캠페인 수도 줄어든다.
 */
.rt-campaign__thumb { position:relative; aspect-ratio:1/1; overflow:hidden;
  background:var(--c-surface-alt); }
.rt-campaign__thumb img { width:100%; height:100%; object-fit:cover; display:block; }

/*
 * 사진이 없을 때.
 *
 * 예전에는 브랜드색 그라데이션 위에 아이콘을 38% 크기 · 투명도 45% 로 얹었다.
 * 화면에서는 **빈 회색 박스**로 읽혔다 — 목록 전체가 깨진 것처럼 보였다.
 *
 * 지금은 "사진 없음"을 설계된 상태로 만든다.
 *  · 카테고리별 색조 바탕 (목록이 단조롭지 않고, 색으로 종류가 구분된다)
 *  · 이모지를 크게, 투명도 없이 (자리표시자가 아니라 표식으로 읽히게)
 *  · 카테고리 이름을 작게 (색만으로는 무슨 색인지 모른다)
 */
.rt-campaign__ph { position:absolute; inset:0; display:grid; place-items:center; align-content:center;
  gap:var(--space-x1); background:var(--c-cat-etc); }
.rt-campaign__ph.is-food { background:var(--c-cat-food); }
.rt-campaign__ph.is-cafe { background:var(--c-cat-cafe); }
.rt-campaign__ph.is-beauty { background:var(--c-cat-beauty); }
.rt-campaign__ph.is-leisure { background:var(--c-cat-leisure); }
.rt-campaign__ph.is-life { background:var(--c-cat-life); }
.rt-campaign__ph em { font-style:normal; font-size:var(--text-display-size);
  /* 이모지에 자간은 의미가 없지만, 토큰을 빼면 body 자간을 상속해 린트가 잡는다.
     예외를 만드는 대신 짝을 맞춘다 — 규칙에 구멍을 내는 쪽이 더 비싸다. */
  letter-spacing:var(--text-display-track); line-height:1; }
.rt-campaign__ph span { font-size:var(--text-micro-size); font-weight:var(--text-micro-weight);
  letter-spacing:var(--text-micro-track); color:var(--c-ink-muted); }
.rt-campaign__ph .rt-icon { width:34%; height:auto; }
/*
 * 🔴 **사진이 로드되면 폴백을 감춘다.**
 *
 * thumb() 은 <img> 와 폴백 타일을 **항상 함께** 내보내고, 실패하면
 * onerror="this.remove()" 로 <img> 를 지워 폴백이 드러나게 한다.
 * 그 설계의 나머지 반쪽이 이 규칙이다 — 이게 없으면 폴백이
 * position:absolute·불투명 배경으로 **사진을 100% 덮는다.**
 * 즉 실패는 올바르게 처리되고 성공이 깨진다. 정확히 반대로 동작했다.
 *
 * 형제 선택자인 이유: <img> 가 remove() 되면 이 규칙이 자동으로 풀린다.
 * JS 로 클래스를 토글하면 실패 경로에서 또 한 번 손대야 한다.
 */
.rt-campaign__thumb img + .rt-campaign__ph { display: none; }
.rt-campaign__dday { position:absolute; top:var(--space-x2); left:var(--space-x2);
  background:rgba(15,23,32,.72); -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  color:var(--c-on-scrim); font-size:var(--text-micro-size); letter-spacing:var(--text-micro-track); font-weight:700; font-variant-numeric:tabular-nums;
  padding:var(--space-x05) var(--space-x2); border-radius:var(--radius-full); }
.rt-campaign__dday.is-closed { background:rgba(15,23,32,.5); }
/* 사진이 카드의 주인공이다. 확률 판은 얇게 유지한다. */
.rt-campaign__odds { position:absolute; left:var(--space-x2); right:var(--space-x2); bottom:var(--space-x2);
  background:rgba(15,23,32,.7); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border-radius:var(--radius-sm); padding:var(--space-x15) var(--space-x2); }
.rt-campaign__odds .r { display:flex; justify-content:space-between; align-items:center;
  color:var(--c-on-scrim); font-size:var(--text-micro-size); letter-spacing:var(--text-micro-track); font-weight:500; opacity:.9; }
.rt-campaign__odds .r b { font-weight:720; font-variant-numeric:tabular-nums; opacity:1; letter-spacing:var(--text-micro-track); }
.rt-campaign__gauge { height:var(--space-x05); background:rgba(255,255,255,.22); border-radius:var(--radius-full); margin-top:var(--space-x1); overflow:hidden; }
.rt-campaign__gauge i { display:block; height:100%; border-radius:var(--radius-full); background:var(--c-on-scrim); }
.rt-campaign__meta { padding:var(--space-x3); }
/*
 * 위계 — 제목보다 **금액이 크다.**
 *
 * 체험단 목록에서 먼저 읽히는 것은 "얼마짜리를 주는가"다. 전에는 제목 15/640 · 금액 13px 이라
 * 금액이 메타 정보보다도 작았고, 훑어서는 무엇이 중요한지 알 수 없었다.
 * 제목은 **두 줄까지 보여주되 자리는 항상 두 줄을 차지한다**(min-height).
 * 한 줄로 자르면 "[여의도] 한강 선셋 요…" 처럼 지역 대괄호가 제목을 다 먹고,
 * 그냥 두 줄 허용이면 카드마다 높이가 달라져 2열 그리드의 줄이 어긋난다. 둘 다 피한다.
 */
.rt-campaign__title { font-size:var(--text-body-sm-size); font-weight:700; letter-spacing:var(--text-body-sm-track); line-height:1.4;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  min-height:calc(2 * 1.4em); margin:var(--space-x15) 0 0; }
.rt-campaign__value { font-size:var(--text-numeric-size); font-weight:var(--text-numeric-weight);
  line-height:var(--text-numeric-line); letter-spacing:var(--text-numeric-track);
  font-variant-numeric:tabular-nums; margin-top:var(--space-x05); }
/* 원고료는 제공가와 성격이 다른 돈이다 — 브랜드색으로 한 줄 더 준다.
   비어 있어도 높이를 차지한다: 이 줄이 있는 카드와 없는 카드가 섞이면 2열 줄이 어긋난다.
   마감 카드에서는 같이 흐려져야 한다 — 회색 카드 위에 초록 원고료만 남으면
   신청할 수 없는 캠페인에서 그 줄이 제일 밝은 행동 유도처럼 읽힌다(독립 검토). */
.rt-campaign__reward { font-size:var(--text-mini-size); font-weight:700; letter-spacing:var(--text-mini-track);
  color:var(--c-brand); font-variant-numeric:tabular-nums; margin-top:var(--space-x05);
  min-height:1.48em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.rt-campaign__apply { font-size:var(--text-micro-size); font-weight:var(--text-micro-weight); letter-spacing:var(--text-micro-track); color:var(--c-ink-faint); margin-top:var(--space-x1); font-variant-numeric:tabular-nums;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.rt-campaign.is-closed { box-shadow:none; background:var(--c-surface-alt); }
.rt-campaign.is-closed .rt-campaign__thumb { opacity:.45; }
.rt-campaign.is-closed .rt-campaign__title,
.rt-campaign.is-closed .rt-campaign__reward,
.rt-campaign.is-closed .rt-campaign__value { color:var(--c-ink-faint); }

/* ── 폼 ────────────────────────────────────────────── */
.rt-field { display:flex; flex-direction:column; gap:var(--space-x15); }
.rt-field > label { font-size:var(--text-mini-size); letter-spacing:var(--text-mini-track); font-weight:600; color:var(--c-ink-muted); }
.rt-field .req { color:var(--c-danger); }
.rt-field .hint { font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); color:var(--c-ink-faint); line-height:1.5; }
.rt-input, .rt-select, .rt-textarea {
  box-sizing:border-box; width:100%; padding:var(--space-x4); border:1px solid var(--c-line);
  border-radius:var(--radius-sm); background:var(--c-surface); color:var(--c-ink);
  font-family:inherit; font-size:var(--text-body-sm-size); line-height:1.5; letter-spacing:-.011em;
  transition:border-color var(--motion-fast) var(--motion-ease), box-shadow var(--motion-fast) var(--motion-ease);
  min-height:52px;
}
.rt-textarea { resize:vertical; min-height:96px; }
.rt-input::placeholder, .rt-textarea::placeholder { color:var(--c-ink-faint); }
.rt-input:hover, .rt-select:hover, .rt-textarea:hover { border-color:var(--c-line-strong); }
.rt-input:focus, .rt-select:focus, .rt-textarea:focus {
  outline:none; border-color:var(--c-brand); box-shadow:0 0 0 3px var(--c-brand-soft);
}
/* 키보드로 온 포커스는 링까지 — 마우스 클릭에는 테두리 강조만 남는다 */
.rt-input:focus-visible, .rt-select:focus-visible, .rt-textarea:focus-visible {
  outline:var(--focus-width) solid var(--c-brand); outline-offset:var(--focus-offset); box-shadow:none;
}
.rt-input[readonly] { background:var(--c-surface-alt); color:var(--c-ink-muted); }
.rt-input:disabled, .rt-select:disabled, .rt-textarea:disabled {
  background:var(--c-surface-alt); color:var(--c-ink-faint); opacity:var(--disabled-opacity); cursor:not-allowed;
}

/* ── 미션 트래커 ───────────────────────────────────── */
.rt-track { display:flex; flex-direction:column; }
.rt-step { display:grid; grid-template-columns:28px minmax(0,1fr); gap:var(--space-x3); position:relative; padding-bottom:var(--space-x5); }
.rt-step:last-child { padding-bottom:0; }
.rt-step__dot { width:28px; height:28px; border-radius:var(--radius-full); display:grid; place-items:center;
  font-size:var(--text-micro-size); letter-spacing:var(--text-micro-track); font-weight:700; z-index:1; }
.rt-step__dot .rt-icon { width:16px; height:16px; }
.rt-step.is-done .rt-step__dot { background:var(--c-brand); color:var(--c-on-brand); }
.rt-step.is-now  .rt-step__dot { background:var(--c-brand-soft); color:var(--c-brand-hover); box-shadow:0 0 0 3px var(--c-brand-soft); }
.rt-step.is-todo .rt-step__dot { background:var(--c-surface-alt); color:var(--c-ink-faint); }
.rt-step:not(:last-child)::before { content:""; position:absolute; left:13px; top:30px; bottom:2px; width:2px; background:var(--c-surface-alt); border-radius:var(--radius-full); }
.rt-step.is-done:not(:last-child)::before { background:var(--c-brand); opacity:.35; }
.rt-step__t { font-size:var(--text-body-sm-size); font-weight:600; letter-spacing:-.015em; line-height:1.4; }
.rt-step__d { font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); color:var(--c-ink-faint); margin-top:2px; }
.rt-step.is-todo .rt-step__t { color:var(--c-ink-faint); font-weight:500; }

/* ── 지갑 ──────────────────────────────────────────── */
.rt-wallet { background:var(--c-ink); color:var(--c-surface); border-radius:var(--radius-lg); padding:var(--space-x6); }
.rt-wallet__label { font-size:var(--text-mini-size); opacity:.65; font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); }
.rt-wallet__amount { font-size:var(--text-amount-size); font-weight:var(--text-amount-weight);
  line-height:var(--text-amount-line); letter-spacing:var(--text-amount-track);
  font-variant-numeric:proportional-nums; margin-top:var(--space-x05); }
.rt-wallet__split { display:flex; gap:var(--space-x5); margin-top:var(--space-x5); padding-top:var(--space-x4);
  border-top:1px solid rgba(255,255,255,.14); font-size:var(--text-micro-size); font-weight:var(--text-micro-weight); letter-spacing:var(--text-micro-track); }
.rt-wallet__split > div { flex:1; opacity:.65; }
.rt-wallet__split b { display:block; font-size:var(--text-caption-size); font-weight:700; margin-top:var(--space-x05);
  font-variant-numeric:tabular-nums; opacity:1; color:var(--c-surface); letter-spacing:var(--text-caption-track); }

/* ── 대가성 표시 (유일하게 편집 불가한 UI) ─────────── */
.rt-disclosure { background:var(--c-brand-soft); border-radius:var(--radius-sm); padding:var(--space-x4);
  font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); color:var(--c-brand-hover); line-height:1.6; }
.rt-disclosure__lock { font-size:var(--text-micro-size); font-weight:var(--text-micro-weight); letter-spacing:var(--text-micro-track); color:var(--c-ink-muted); margin-top:var(--space-x2);
  display:flex; gap:var(--space-x1); align-items:flex-start; }
.rt-disclosure__lock .rt-icon { width:14px; height:14px; margin-top:2px; }
.rt-disclosure__rules { margin:var(--space-x2) 0 0; padding-left:var(--space-x4); color:var(--c-ink-muted); line-height:1.7; }

/* ── 토리 말풍선 ───────────────────────────────────── */
.rt-tori { background:var(--c-brand-soft); border-radius:var(--radius-md); padding:var(--space-x4);
  display:flex; gap:var(--space-x3); align-items:center; }
.rt-tori img, .rt-tori svg { width:48px; height:auto; flex:none; display:block; }
.rt-tori__text { font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); line-height:1.5; color:var(--c-brand-hover); }

/* ── 알림 ──────────────────────────────────────────── */
.rt-note { border-radius:var(--radius-sm); padding:var(--space-x4); font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); line-height:1.6;
  background:var(--c-surface-alt); color:var(--c-ink-muted); }
.rt-note-info  { background:var(--c-surface-alt); }
.rt-note-warn  { background:var(--c-accent-soft); color:var(--c-accent); }
.rt-note-error { background:var(--c-danger-soft); color:var(--c-danger); }
.rt-note-ok    { background:var(--c-brand-soft); color:var(--c-brand-hover); }

/* ── 빈 화면 ───────────────────────────────────────── */
/* 앱·운영 화면이 각자 만들어 쓰다가, 포트폴리오는 규칙 없이 글자만 왼쪽에 붙어 나왔다 — 여기 하나로 둔다.
   빈 화면은 "없다"로 끝나지 않는다: 토리 그림(선택) · 한 줄 제목 · 설명 · 다음에 할 일 버튼 */
.rt-empty { text-align:center; padding:var(--space-x8) var(--space-x5);
  display:flex; flex-direction:column; align-items:center; gap:var(--space-x1); }
.rt-empty img { width:96px; height:96px; display:block; margin:0 0 var(--space-x2); }
.rt-empty__t { font-size:var(--text-body-sm-size); font-weight:700; color:var(--c-ink);
  letter-spacing:var(--text-body-sm-track); }
.rt-empty .rt-btn { margin-top:var(--space-x2); }

/* ── 섹션 라벨 ─────────────────────────────────────── */
.rt-stepno { font-size:var(--text-label-size); line-height:var(--text-label-line); color:var(--c-brand);
  font-weight:var(--text-label-weight); letter-spacing:var(--text-label-track); display:block; margin-bottom:var(--space-x3); }

/* ── 표 ────────────────────────────────────────────── */
.rt-tablewrap { overflow-x:auto; border-radius:var(--radius-md); background:var(--c-surface); box-shadow:var(--shadow-sm); }
.rt-table { border-collapse:collapse; width:100%; font-size:var(--text-mini-size); font-weight:var(--text-mini-weight); letter-spacing:var(--text-mini-track); }
.rt-table th, .rt-table td { text-align:left; padding:var(--space-x3) var(--space-x4); vertical-align:top; }
.rt-table th { font-size:var(--text-micro-size); letter-spacing:var(--text-micro-track); color:var(--c-ink-faint); font-weight:600; background:var(--c-surface-alt); }
.rt-table td { border-top:1px solid var(--c-line); }

/* ── 앱바 ──────────────────────────────────────────── */
.rt-appbar { display:flex; align-items:center; justify-content:space-between; gap:var(--space-x6);
  padding:var(--space-x4) var(--space-x5); background:var(--c-surface); }
.rt-logo { font-size:var(--text-h2-size); font-weight:var(--logo-weight); letter-spacing:var(--logo-track); }
.rt-logo .a { color:var(--c-ink); }
.rt-logo .b { color:var(--c-brand); }

/* ── 진행 ──────────────────────────────────────────── */
.rt-progress { height:var(--space-x15); background:var(--c-surface-alt); border-radius:var(--radius-full); overflow:hidden; }
.rt-progress i { display:block; height:100%; background:var(--c-brand); border-radius:var(--radius-full);
  transition:width var(--motion-slow) var(--motion-ease); }

.rt-spin { display:inline-block; width:14px; height:14px; border:2px solid var(--c-surface-alt);
  border-top-color:var(--c-brand); border-radius:50%; animation:rt-spin .7s linear infinite; vertical-align:-2px; }
@keyframes rt-spin { to { transform:rotate(360deg); } }

.rt-hidden { display:none !important; }
/*
 * 포커스 링은 위(:50)의 토큰 규칙 하나만 남긴다.
 * 여기 있던 맨 `:focus-visible { outline:2px … }` 는 특이도가 같고 뒤에 있어서 그걸 덮고 있었다 —
 * tokens.ts 의 focusWidth 를 고쳐도 아무 일도 안 일어났다는 뜻이다(독립 검토).
 */
