/* 귀입딱 챌린지 — 디자인 시스템 v1.0 구현
   docs/design-system.md 의 토큰·컴포넌트 규칙을 따른다. */

:root {
  --color-brand-500: #fe6400;
  --color-brand-600: #e55a00;
  --color-brand-700: #c84e00;
  --color-brand-100: #ffe7d5;
  --color-brand-050: #fff3ea;
  --color-ink: #17171c;
  --color-ink-soft: #55555f;
  --color-muted: #85858f;
  --color-line: #e4e5e8;
  --color-surface-soft: #f5f6f8;
  --color-surface: #ffffff;
  --color-success: #168a57;
  --color-success-bg: #e8f7ef;
  --color-success-line: #a9dcc0;
  --color-danger: #d93d47;
  --color-danger-bg: #fdecee;
  --color-danger-line: #e6a3a3;
  --color-warning: #b56a00;
  --color-warning-bg: #fff3d6;
  --color-info: #2563b9;
  --color-focus: #1d4ed8;

  --space-1: 4px;   --space-2: 8px;   --space-3: 12px;  --space-4: 16px;
  --space-5: 20px;  --space-6: 24px;  --space-8: 32px;  --space-10: 40px;
  --space-12: 48px; --space-14: 56px; --space-16: 64px; --space-20: 80px;

  --radius-sm: 10px; --radius-md: 13px; --radius-lg: 16px;
  --radius-xl: 20px; --radius-2xl: 28px; --radius-pill: 999px;

  --shadow-float: 0 12px 30px rgb(23 23 28 / 12%);
  --shadow-hover: 0 8px 24px rgb(23 23 28 / 8%);
  --content-max: 1200px;
  --challenge-max: 720px;
  --header-h: 56px;
  /* 개발 서버 띠의 높이. 운영에서는 띠가 없으므로 0 이고 sticky 계산이 그대로다. */
  --env-banner-h: 0px;

  /* 모션 — design-system.md §전환(160ms ease-out)을 기준으로 세 단계만 둔다. */
  --motion-fast: 120ms;
  --motion-base: 200ms;
  --motion-slow: 320ms;
  --ease-out: cubic-bezier(0.2, 0.8, 0.3, 1);
  --ease-in: cubic-bezier(0.5, 0, 0.75, 0);
  /* 온보딩처럼 미끄러지듯 붙어야 하는 곳에만 쓰는 긴 감속 곡선(iOS 계열 감각). */
  --ease-soft: cubic-bezier(0.22, 0.85, 0.3, 1);
}

/* ── 페이지 전환 ────────────────────────────────────────────
   여러 장의 PHP 페이지라 이동할 때마다 화면이 통째로 갈린다.
   지원 브라우저에서는 크로스페이드로, 아닌 곳에서는 본문 fade-in 으로 받는다. */

@view-transition { navigation: auto; }

::view-transition-old(root) { animation: fade-out var(--motion-fast) var(--ease-in) both; }
::view-transition-new(root) { animation: fade-in var(--motion-base) var(--ease-out) both; }

/* View Transitions 가 없는 브라우저용. 헤더·하단탭은 고정된 채 본문만 들어온다. */
.main { animation: page-in var(--motion-base) var(--ease-out) both; }

@keyframes page-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }

/* ── 등장 ───────────────────────────────────────────────────
   .reveal 은 한 덩어리, .stagger 는 자식들을 조금씩 늦춰 순서대로 들어오게 한다.
   DAY 목록은 60개까지 길어질 수 있어 앞 8개만 늦춘다 — 전부 늦추면 아래쪽이 늦게 떠서 답답하다. */

.reveal { animation: rise-in var(--motion-slow) var(--ease-out) both; }
.stagger > * { animation: rise-in var(--motion-base) var(--ease-out) both; }
.stagger > *:nth-child(1) { animation-delay: 0ms; }
.stagger > *:nth-child(2) { animation-delay: 40ms; }
.stagger > *:nth-child(3) { animation-delay: 80ms; }
.stagger > *:nth-child(4) { animation-delay: 120ms; }
.stagger > *:nth-child(5) { animation-delay: 160ms; }
.stagger > *:nth-child(6) { animation-delay: 200ms; }
.stagger > *:nth-child(7) { animation-delay: 240ms; }
.stagger > *:nth-child(n + 8) { animation-delay: 280ms; }

@keyframes rise-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

*, *::before, *::after { box-sizing: border-box; }

/* .btn 등 display 를 지정한 요소는 [hidden] 을 덮어써서 그냥 보인다. 전역으로 막는다. */
[hidden] { display: none !important; }

/* 목록이 짧아져 세로 스크롤바가 사라지는 순간 본문 폭이 넓어지며 화면이 좌우로 튄다.
   자리를 미리 비워 두면 스크롤바 유무와 상관없이 폭이 고정된다. */
html { -webkit-text-size-adjust: 100%; scrollbar-gutter: stable; }

body {
  margin: 0;
  font-family: "Pretendard Variable", Pretendard, "Apple SD Gothic Neo",
    "Malgun Gothic", system-ui, sans-serif;
  font-size: 16px; font-weight: 500; line-height: 1.6;
  color: var(--color-ink);
  background: var(--color-surface);
  word-break: keep-all;
}

h1, h2, h3, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }
img, video { max-width: 100%; }

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.tnum { font-variant-numeric: tabular-nums; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── 타이포 ─────────────────────────────────────────────── */

.t-display { font-size: 36px; font-weight: 800; line-height: 1.12; letter-spacing: -0.02em; }
.t-h1      { font-size: 30px; font-weight: 800; line-height: 1.2; letter-spacing: -0.02em; }
.t-h2      { font-size: 26px; font-weight: 750; line-height: 1.25; letter-spacing: -0.01em; }
.t-h3      { font-size: 20px; font-weight: 700; line-height: 1.35; }
.t-challenge { font-size: 24px; font-weight: 600; line-height: 1.5; }
.t-body-l  { font-size: 17px; line-height: 1.65; }
.t-body    { font-size: 16px; line-height: 1.6; }
.t-label   { font-size: 14px; font-weight: 700; line-height: 1.4; }
.t-caption { font-size: 12px; line-height: 1.45; color: var(--color-muted); }

.t-soft  { color: var(--color-ink-soft); }
.t-muted { color: var(--color-muted); }
.t-en    { font-family: inherit; }

@media (min-width: 1024px) {
  .t-display { font-size: 54px; }
  .t-h1 { font-size: 44px; }
  .t-h2 { font-size: 36px; }
  .t-h3 { font-size: 24px; }
  .t-challenge { font-size: 30px; }
  .t-body-l { font-size: 18px; }
  .t-caption { font-size: 13px; }
}

/* ── 셸 ─────────────────────────────────────────────────── */

.shell {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--space-4);
}
@media (max-width: 359px) { .shell { padding: 0 14px; } }
@media (min-width: 600px) { .shell { padding: 0 var(--space-6); } }
@media (min-width: 1024px) { .shell { padding: 0 var(--space-8); } }

.app-header {
  position: sticky; top: var(--env-banner-h); z-index: 30;
  height: var(--header-h);
  display: flex; align-items: center;
  background: rgb(255 255 255 / 92%);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--color-line);
}
.app-header__inner {
  max-width: var(--content-max); width: 100%;
  margin: 0 auto; padding: 0 var(--space-4);
  display: flex; align-items: center; gap: var(--space-3);
}
.app-header__brand { font-size: 17px; font-weight: 800; letter-spacing: -0.02em; }
.app-header__brand b { color: var(--color-brand-500); }
.app-header__spacer { flex: 1; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px;
  background: none; border: 0; cursor: pointer;
  border-radius: var(--radius-md);
}
.icon-btn:hover { background: var(--color-surface-soft); }

@media (min-width: 1024px) {
  :root { --header-h: 64px; }
  .app-header__inner { padding: 0 var(--space-8); }
}

.main { padding: var(--space-6) 0 var(--space-20); }
.main--learn { padding-bottom: 120px; }

.stack   { display: flex; flex-direction: column; gap: var(--space-4); }
.stack-6 { display: flex; flex-direction: column; gap: var(--space-6); }
.stack-8 { display: flex; flex-direction: column; gap: var(--space-8); }
.row     { display: flex; align-items: center; gap: var(--space-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
/* 버튼 두 개가 나란히 있을 때 좁은 화면에서 글자가 두 줄로 접히는 걸 막는다. */
.row--wrap { flex-wrap: wrap; }
.row--wrap > * { flex: 1 1 200px; }

.learn-col { max-width: var(--challenge-max); margin: 0 auto; }

.section-title { margin-bottom: var(--space-4); }

/* ── 버튼 ───────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 54px; min-width: 120px; padding: 0 var(--space-6);
  border: 0; border-radius: var(--radius-md);
  font-size: 16px; font-weight: 700; line-height: 1;
  cursor: pointer;
  transition: background-color 160ms ease-out, transform 160ms ease-out;
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn--block { width: 100%; }
.btn--sm { min-height: 44px; min-width: 0; padding: 0 var(--space-4); font-size: 14px; }

.btn--primary { background: var(--color-brand-500); color: #fff; }
.btn--primary:hover:not(:disabled) { background: var(--color-brand-600); }
.btn--primary:active:not(:disabled) { background: var(--color-brand-700); }

.btn--secondary { background: var(--color-ink); color: #fff; }
.btn--secondary:hover:not(:disabled) { background: #2b2b33; }

.btn--tertiary { background: transparent; color: var(--color-brand-600); min-width: 0; padding: 0 var(--space-3); }
.btn--tertiary:hover:not(:disabled) { background: var(--color-brand-050); }

.btn--ghost { background: var(--color-surface); color: var(--color-ink); border: 1px solid var(--color-line); }
.btn--ghost:hover:not(:disabled) { background: var(--color-surface-soft); }

.btn:disabled { background: var(--color-line); color: var(--color-muted); cursor: not-allowed; }

/* ── 카드 ───────────────────────────────────────────────── */

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
}
@media (min-width: 1024px) { .card { padding: var(--space-8); } }

.card--soft { background: var(--color-surface-soft); border-color: transparent; }
.card--hero { background: linear-gradient(180deg, var(--color-brand-050) 0%, var(--color-surface) 100%); }

/* ── 배지 · 칩 ──────────────────────────────────────────── */

.badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 4px var(--space-3);
  border-radius: var(--radius-pill);
  font-size: 12px; font-weight: 700; line-height: 1.45;
  background: var(--color-surface-soft); color: var(--color-ink-soft);
}
.badge--brand   { background: var(--color-brand-100); color: var(--color-brand-700); }
.badge--success { background: var(--color-success-bg); color: var(--color-success); }
.badge--danger  { background: var(--color-danger-bg); color: var(--color-danger); }
.badge--warning { background: var(--color-warning-bg); color: var(--color-warning); }
.badge--lock    { background: var(--color-surface-soft); color: var(--color-muted); }
/* 세로 stack 안에서 배지가 가로로 늘어나지 않게 한다. */
.stack > .badge, .stack-6 > .badge, .stack-8 > .badge { align-self: flex-start; }

/* ── 진행률 ─────────────────────────────────────────────── */

.progress {
  height: 8px; border-radius: var(--radius-pill);
  background: var(--color-line); overflow: hidden;
}
.progress__fill {
  height: 100%; background: var(--color-brand-500);
  border-radius: var(--radius-pill);
  transition: width var(--motion-slow) var(--ease-out);
  /* 너비는 인라인 스타일로 정해지므로, 채워지는 느낌은 scaleX 로 낸다. */
  transform-origin: left center;
  animation: progress-in 480ms var(--ease-out) both;
}
@keyframes progress-in {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.stepdots { display: flex; gap: var(--space-1); }
.stepdots span {
  width: 8px; height: 8px; border-radius: var(--radius-pill);
  background: var(--color-line);
  transition: background-color var(--motion-base) var(--ease-out),
              transform var(--motion-base) var(--ease-out);
}
.stepdots span[data-on="1"] { background: var(--color-brand-500); transform: scale(1.15); }

/* ── DAY 목록 ───────────────────────────────────────────── */

.filter-tabs { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.filter-tabs a {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-surface-soft);
  font-size: 14px; font-weight: 700; color: var(--color-ink-soft);
  transition: background-color var(--motion-base) var(--ease-out),
              color var(--motion-base) var(--ease-out);
}
.filter-tabs a[aria-current="page"] { background: var(--color-brand-100); color: var(--color-brand-700); }

/* 필터는 페이지를 다시 불러오지 않고 감추기만 한다(days.php). 카드가 통째로 다시 그려지면
   스크롤 위치·페이지 높이가 함께 튀어 화면이 위아래·좌우로 흔들린다. */
.day-grid[data-filter="todo"]  > li:not([data-state="todo"]),
.day-grid[data-filter="doing"] > li:not([data-state="doing"]),
.day-grid[data-filter="done"]  > li:not([data-state="done"]) { display: none; }

/* 전환 표시는 투명도만 — 위치가 움직이는 연출은 넣지 않는다. */
.day-grid.is-refiltering > li { animation: fade-in var(--motion-base) var(--ease-out) both; }

.day-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: 1fr;
}
@media (min-width: 600px) { .day-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .day-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.day-card {
  display: block;
  background: var(--color-surface);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  transition: box-shadow 160ms ease-out, border-color 160ms ease-out;
}
.day-card:hover { box-shadow: var(--shadow-hover); border-color: var(--color-brand-100); }
.day-card--locked { background: var(--color-surface-soft); border-color: transparent; }
.day-card--locked:hover { box-shadow: none; border-color: transparent; }
/* 공개는 됐지만 이용권이 없어 못 여는 카드. 잠금과 달리 "열 수 있어 보이면" 안 된다. */
.day-card--blocked { background: var(--color-surface-soft); border-style: dashed; cursor: not-allowed; }
.day-card--blocked:hover { box-shadow: none; border-color: var(--color-line); }
.day-card--blocked .day-card__no,
.day-card--blocked .day-card__title { color: var(--color-muted); }
.day-card--done { border-color: var(--color-success-bg); }
.day-card__no { font-size: 14px; font-weight: 800; color: var(--color-brand-600); }
.day-card__title {
  margin-top: var(--space-1); font-size: 16px; font-weight: 700; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.day-card__stages { margin-top: var(--space-3); display: flex; gap: var(--space-1); }
.stage-pip {
  flex: 1; height: 6px; border-radius: var(--radius-pill);
  background: var(--color-line);
}
.stage-pip[data-state="done"] { background: var(--color-success); }
.stage-pip[data-state="available"] { background: var(--color-brand-100); }

/* ── 학습 화면 ──────────────────────────────────────────── */

.learn-header { padding: var(--space-4) 0 var(--space-2); }
.learn-header__meta { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }

.challenge-card {
  background: var(--color-surface);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-2xl);
  overflow: hidden;
  min-height: 300px;
  display: flex; flex-direction: column;
}
@media (min-width: 1024px) { .challenge-card { min-height: 380px; } }

/* 문항 1 → 2 → 3 교체. 나가는 쪽은 왼쪽으로 밀리고 들어오는 쪽은 오른쪽에서 들어온다.
   진행 방향이 보여야 "다음 문제로 넘어갔다"가 읽힌다. */
.challenge-card.is-leaving  { animation: card-out var(--motion-fast) var(--ease-in) both; }
.challenge-card.is-entering { animation: card-in var(--motion-base) var(--ease-out) both; }

@keyframes card-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateX(-12px); }
}
@keyframes card-in {
  from { opacity: 0; transform: translateX(16px); }
  to   { opacity: 1; transform: none; }
}

.challenge-card__body { padding: var(--space-6) var(--space-5); flex: 1; }
.challenge-card__answer { padding: var(--space-5); background: var(--color-brand-050); }
.challenge-card__answer--listening { background: var(--color-surface-soft); }
@media (min-width: 1024px) {
  .challenge-card__body { padding: var(--space-8); }
  .challenge-card__answer { padding: var(--space-6) var(--space-8); }
}

.prompt-ko { margin-bottom: var(--space-8); }

.slot-input {
  width: 100%; min-height: 48px;
  padding: var(--space-3) var(--space-4);
  background: var(--color-brand-050);
  border: 1px solid var(--color-brand-100);
  border-radius: var(--radius-md);
  font-size: 18px; line-height: 1.5;
  resize: vertical;
}
.slot-input:focus { border-color: var(--color-brand-500); background: var(--color-surface); }
.slot-input[aria-invalid="true"] { border-color: var(--color-danger); background: var(--color-danger-bg); }
.slot-input:disabled {
  opacity: 1;
  color: var(--color-ink);
  -webkit-text-fill-color: var(--color-ink);
}
.slot-input[data-result="correct"] {
  border-color: var(--color-success-line);
  background-color: var(--color-success-bg);
}
.slot-input[data-result="wrong"] {
  border-color: var(--color-danger-line);
  background-color: var(--color-danger-bg);
}

/* 오답·빈 입력을 눌렀을 때. 색만으로는 눈치채지 못하는 경우가 있다. */
.is-shaking { animation: shake 320ms var(--ease-out) both; }
@keyframes shake {
  0%, 100% { transform: none; }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(2px); }
}

/* 원어민 장면 — 클립이 짧아 탐색 막대 없이 탭해서 다시 보는 형태로 둔다. */
.scene {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #000;
  line-height: 0;
}
.scene__video {
  display: block;
  width: 100%; height: auto;
  max-height: 46vh;
  object-fit: contain;
  background: #000;
}
.scene__overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; padding: 0; cursor: pointer;
  background: rgba(0, 0, 0, 0.28);
  color: #fff;
}
.scene__overlay::before {
  content: "";
  position: absolute;
  width: 64px; height: 64px;
  border-radius: var(--radius-pill);
  /* blur 를 걸면 배우 얼굴이 모자이크처럼 보인다. 단색 원으로 둔다. */
  background: rgba(0, 0, 0, 0.62);
}
.scene__overlay svg { position: relative; transform: translateX(2px); }
.scene__overlay[hidden] { display: none !important; }

.player {
  display: flex; align-items: center; gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-4);
  background: var(--color-surface-soft);
  border-radius: var(--radius-lg);
}
.player > .stack { flex: 1 1 120px; min-width: 0; gap: 2px; }
.speed-group { display: flex; gap: var(--space-1); margin-left: auto; }
/* 좁은 화면에서는 속도 버튼을 아래 줄로 내려 문구가 눌리지 않게 한다. */
@media (max-width: 479px) {
  .speed-group { margin-left: 0; width: 100%; }
  .speed-group button { flex: 1; }
}
.speed-group button {
  min-height: 36px; padding: 0 var(--space-3);
  border: 1px solid var(--color-line); background: var(--color-surface);
  border-radius: var(--radius-sm); font-size: 13px; font-weight: 700; cursor: pointer;
}
.speed-group button[aria-pressed="true"] {
  background: var(--color-brand-100); border-color: var(--color-brand-100); color: var(--color-brand-700);
}

.play-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; flex: 0 0 56px;
  border: 0; border-radius: var(--radius-pill);
  background: var(--color-ink); color: #fff; cursor: pointer;
}

/* 마이크 */
.mic-wrap { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); }
.mic-btn {
  position: relative;
  width: 88px; height: 88px;
  border: 0; border-radius: var(--radius-pill);
  background: var(--color-surface); color: var(--color-ink);
  box-shadow: var(--shadow-float);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
@media (min-width: 1024px) { .mic-btn { width: 96px; height: 96px; } }
.mic-btn[data-state="recording"] { background: var(--color-brand-500); color: #fff; }
.mic-btn[data-state="recording"]::before,
.mic-btn[data-state="recording"]::after {
  content: ""; position: absolute; inset: 0;
  border-radius: var(--radius-pill);
  border: 2px solid var(--color-brand-500);
  animation: mic-ring 1.6s ease-out infinite;
}
.mic-btn[data-state="recording"]::after { animation-delay: 0.8s; }
@keyframes mic-ring {
  from { transform: scale(1); opacity: 0.6; }
  to   { transform: scale(1.45); opacity: 0; }
}
/* 분석·응답 대기 — 문구만 바뀌면 멈춘 것처럼 보인다. 도는 링으로 진행 중임을 보인다. */
.mic-btn[data-state="processing"] { background: var(--color-surface-soft); color: var(--color-muted); }
.mic-btn[data-state="processing"]::before {
  content: ""; position: absolute; inset: -4px;
  border-radius: var(--radius-pill);
  border: 3px solid var(--color-brand-100);
  border-top-color: var(--color-brand-500);
  animation: spin 900ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* 결과를 기다리는 자리 — 문구만 두면 멈춘 화면과 구분이 안 된다.
   종료 피드백은 AI 가 대화를 다시 읽고 만들기 때문에 몇 초가 걸린다. */
.wait-panel {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-10) var(--space-2);
}
.spinner {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: var(--radius-pill);
  border: 3px solid var(--color-brand-100);
  border-top-color: var(--color-brand-500);
  animation: spin 800ms linear infinite;
}
/* 버튼 안에서 글자 옆에 서는 작은 것.
   비활성 버튼은 배경이 밝게 바뀌므로 흰색으로 고정하면 묻힌다 → 글자색을 따라간다. */
.spinner--sm {
  width: 16px; height: 16px; border-width: 2px;
  border-color: currentColor; border-top-color: transparent;
  opacity: 0.75;
}

.mic-btn:disabled { background: var(--color-line); color: var(--color-muted); box-shadow: none; }
/* 위 disabled 규칙이 처리 중 배경까지 덮어써서 링이 묻히지 않게 한다. */
.mic-btn:disabled[data-state="processing"] { background: var(--color-surface-soft); }

/* 점 세 개 로더 — 마이크 라벨 옆, AI 응답 대기 말풍선 안에서 함께 쓴다. */
.dot-loader { display: inline-flex; align-items: center; gap: 5px; }
.dot-loader span {
  width: 7px; height: 7px; border-radius: var(--radius-pill);
  background: var(--color-brand-500);
  animation: dot-bounce 1.1s var(--ease-out) infinite;
}
.dot-loader span:nth-child(2) { animation-delay: 0.15s; }
.dot-loader span:nth-child(3) { animation-delay: 0.3s; }
@keyframes dot-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.45; }
  30%           { transform: translateY(-5px); opacity: 1; }
}

/* 버튼이 요청을 기다리는 동안 */
.btn.is-busy { position: relative; color: transparent !important; }
.btn.is-busy::after {
  content: ""; position: absolute;
  width: 18px; height: 18px; border-radius: var(--radius-pill);
  border: 2px solid rgb(255 255 255 / 45%); border-top-color: #fff;
  animation: spin 700ms linear infinite;
}

/* SPEAKING 결과의 모범 음성 · 내 녹음 버튼 — 한 번에 하나만 난다.
   재생 중인 쪽 = 브랜드색 + 움직이는 막대, 다른 쪽 = 흐리게. 어느 소리가 나는지 버튼만 보고 안다.
   (.play-btn 은 LISTENING 의 원형 재생 버튼이라 이름을 따로 둔다) */
.snd-btn { transition: background-color 160ms ease-out, border-color 160ms ease-out, color 160ms ease-out, opacity 160ms ease-out, transform 160ms ease-out; }
.snd-btn__bars { display: none; align-items: flex-end; gap: 2px; height: 12px; }
.snd-btn__bars i {
  width: 3px; height: 100%; border-radius: 1px;
  background: currentColor; transform-origin: bottom;
  animation: eq-bar 900ms ease-in-out infinite;
}
.snd-btn__bars i:nth-child(2) { animation-delay: 0.2s; }
.snd-btn__bars i:nth-child(3) { animation-delay: 0.4s; }
.snd-btn.is-playing {
  background: var(--color-brand-050); border-color: var(--color-brand-500); color: var(--color-brand-600);
}
.snd-btn.is-playing:hover:not(:disabled) { background: var(--color-brand-100); }
.snd-btn.is-playing .snd-btn__bars { display: inline-flex; }
.snd-btn.is-dimmed { opacity: 0.5; }
@keyframes eq-bar {
  0%, 100% { transform: scaleY(0.35); }
  50%      { transform: scaleY(1); }
}
@media (prefers-reduced-motion: reduce) {
  .snd-btn__bars i { animation: none; transform: scaleY(0.7); }
}

/* 결과 패널 */
.result-panel {
  border-radius: var(--radius-2xl);
  padding: var(--space-5);
  border: 1px solid transparent;
}
@media (min-width: 1024px) { .result-panel { padding: var(--space-6); } }
.result-panel--correct { background-color: var(--color-success-bg); border-color: var(--color-success-line); }
.result-panel--wrong   { background-color: var(--color-danger-bg); border-color: var(--color-danger-line); }
.result-panel--neutral { background: var(--color-surface-soft); }
.result-panel--correct .result-panel__verdict { color: var(--color-success); }
.result-panel--wrong .result-panel__verdict { color: var(--color-danger); }
/* 채점 결과가 툭 나타나지 않도록. 결과는 화면에서 가장 중요한 순간이다. */
.result-panel.is-revealing { animation: rise-in var(--motion-slow) var(--ease-out) both; }

.answer-line { font-size: 20px; font-weight: 700; line-height: 1.5; }
.ipa { color: var(--color-ink-soft); font-size: 15px; }

/* 음성인식이 받아쓴 내 발화 — 무엇이 어떻게 들렸는지 보여준다. */
.heard {
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface); border: 1px dashed var(--color-line);
  border-radius: var(--radius-md);
}
.heard__text { font-size: 16px; font-weight: 600; }

/* 하단 고정 행동 */
.bottom-action {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
  background: rgb(255 255 255 / 94%);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--color-line);
}
.bottom-action__inner { max-width: var(--challenge-max); margin: 0 auto; display: flex; gap: var(--space-3); }

/* TALKING */
.sentence-strip { display: flex; flex-direction: column; gap: var(--space-2); }
.sentence-chip {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  text-align: left; width: 100%;
}
.sentence-chip[data-used="1"] { background: var(--color-success-bg); border-color: #cceadb; }
.sentence-chip { transition: background-color var(--motion-base) var(--ease-out),
                             border-color var(--motion-base) var(--ease-out); }

/**
 * 종료 피드백 목록.
 * 좁은 화면에서 "사용함/미사용" 칩을 문장 왼쪽에 두면 남는 폭이 좁아 문장이 잘게 접힌다.
 * 480px 이하에서는 칩을 윗줄로 올리고 문장이 한 줄 전체를 쓰게 한다.
 */
@media (max-width: 479px) {
  .sentence-strip--feedback .sentence-chip {
    flex-wrap: wrap;
    row-gap: var(--space-2);
  }
  .sentence-strip--feedback .sentence-chip > .badge { order: 1; }
  .sentence-strip--feedback .sentence-chip > .chip-play { order: 2; margin-left: auto; }
  /* 칩·재생 버튼이 첫 줄을 차지하고, 문장은 다음 줄에서 폭을 다 쓴다. */
  .sentence-strip--feedback .sentence-chip__main { order: 3; flex: 1 0 100%; }
}
/* 카드 안에 뜻 토글과 음성 재생 두 동작이 있다. 본문 쪽만 버튼으로 만든다. */
.sentence-chip__main {
  flex: 1; min-width: 0;
  border: 0; background: transparent; padding: 0;
  text-align: left; cursor: pointer; font: inherit; color: inherit;
}
.sentence-chip__en { display: block; font-size: 16px; font-weight: 700; }
.sentence-chip__ko { display: block; font-size: 14px; color: var(--color-ink-soft); margin-top: var(--space-1); }
.sentence-chip__ko[hidden] { display: none !important; }

/* 강세 — 시트 D~F 열에서 붉은 볼드로 표시된 구간(문장에서 힘이 들어가는 자리).
   원본은 빨강이지만 화면의 빨강은 오답·오류 자리라, 브랜드색으로 옮겨 오해를 막는다.
   색을 못 보는 사람도 알아볼 수 있게 굵기와 밑줄을 함께 준다(색만으로 뜻을 나르지 않는다). */
.stress {
  font-weight: 800;
  color: var(--color-brand-700);
  text-decoration: underline;
  text-decoration-color: var(--color-brand-100);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

.chip-play {
  flex: 0 0 36px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1px solid var(--color-line); border-radius: var(--radius-pill);
  background: var(--color-surface); color: var(--color-ink); cursor: pointer;
}
.chip-play:hover { background: var(--color-surface-soft); }
.chip-play svg { transform: translateX(1px); }

.chat-log { display: flex; flex-direction: column; gap: var(--space-3); min-height: 220px; }
.bubble {
  max-width: 82%; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg); font-size: 16px; line-height: 1.55;
}
.bubble--ai   { align-self: flex-start; background: var(--color-surface-soft); border-bottom-left-radius: var(--space-1); }
.bubble--me   { align-self: flex-end; background: var(--color-brand-100); border-bottom-right-radius: var(--space-1); }
.bubble--sys  { align-self: center; background: transparent; color: var(--color-muted); font-size: 13px; }
/* AI 응답은 읽어 주지 않고 학습자가 읽는다 — 영어 아래에 한국어 해석을 흐리고 작게 붙인다.
   같은 크기·색이면 눈이 한국어부터 읽어 영어를 건너뛴다. */
.bubble__en { display: block; }
.bubble__ko {
  display: block; margin-top: var(--space-2);
  font-size: 14px; line-height: 1.5; color: var(--color-muted);
}
.bubble.is-entering { animation: bubble-in var(--motion-base) var(--ease-out) both; }
@keyframes bubble-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
/* AI가 응답을 만드는 동안 자리를 잡아 두는 말풍선 */
.bubble--thinking { padding: var(--space-3) var(--space-4); }

/* 안내 · 빈 상태 */
.notice {
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-brand-050);
  font-size: 14px; line-height: 1.6; color: var(--color-ink-soft);
}
.notice--warning { background: var(--color-warning-bg); color: var(--color-warning); }
.notice--danger  { background: var(--color-danger-bg); color: var(--color-danger); }
/* 잠긴 카드를 눌렀을 때 "여기를 보라"고 알리는 짧은 강조 */
.notice.is-attention { animation: attention 600ms var(--ease-out) both; }
@keyframes attention {
  0%   { transform: none; box-shadow: 0 0 0 0 rgb(181 106 0 / 45%); }
  30%  { transform: translateY(-3px); box-shadow: 0 0 0 6px rgb(181 106 0 / 0%); }
  100% { transform: none; box-shadow: 0 0 0 0 rgb(181 106 0 / 0%); }
}

.empty { text-align: center; padding: var(--space-16) var(--space-4); color: var(--color-muted); }

/* 폼 */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field label { font-size: 14px; font-weight: 700; }
.field input {
  min-height: 52px; padding: 0 var(--space-4);
  border: 1px solid var(--color-line); border-radius: var(--radius-md);
  background: var(--color-surface);
}
.field input:focus { border-color: var(--color-brand-500); }

/* 데스크톱 Rail */
.layout-rail { display: block; }
@media (min-width: 1024px) {
  .layout-rail {
    display: grid;
    grid-template-columns: 208px minmax(0, 1fr);
    gap: var(--space-10);
    align-items: start;
  }
  .rail { position: sticky; top: calc(var(--env-banner-h) + var(--header-h) + var(--space-6)); }
}
.rail { display: none; }
@media (min-width: 1024px) { .rail { display: block; } }
.rail a {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-weight: 700; color: var(--color-ink-soft);
}
.rail a[aria-current="page"] { background: var(--color-brand-100); color: var(--color-brand-700); }
.rail a:hover { background: var(--color-surface-soft); }

/* 모바일 하단 내비 */
.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  display: flex;
  background: rgb(255 255 255 / 96%);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--color-line);
  padding-bottom: env(safe-area-inset-bottom);
}
.bottom-nav a {
  flex: 1; min-height: 56px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  font-size: 12px; font-weight: 700; color: var(--color-muted);
}
.bottom-nav a[aria-current="page"] { color: var(--color-brand-600); }
@media (min-width: 1024px) { .bottom-nav { display: none; } }

/* ── 온보딩 3장 ─────────────────────────────────────────── */

/* 오른쪽으로 미는 동작을 브라우저가 "뒤로 가기"로 채가지 않게 한다(앞 장으로 돌아가야 한다). */
body.onboarding { overscroll-behavior-x: contain; }

.onboard { padding-top: var(--space-8); }
.onboard__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.onboard__dots { display: flex; gap: var(--space-2); }
.onboard__dots span {
  width: 8px; height: 5px; border-radius: var(--radius-pill);
  background: var(--color-line);
  transition: width var(--motion-slow) var(--ease-soft),
              background-color var(--motion-base) var(--ease-out);
}
.onboard__dots span[data-on="1"] { background: var(--color-brand-500); }
/* 지나온 장은 색만 채우고, 지금 보고 있는 장만 길게 늘어난다. */
.onboard__dots span[data-current="1"] { width: 28px; }

/* 세 장을 같은 격자 칸에 겹쳐 둔다. 흐름에서 빼지 않으므로 장을 넘겨도
   덱 높이(=가장 긴 장)가 그대로다 — 아래 내용이 밀렸다 사라지지 않는다.
   clip 은 옆에서 들어오는 장이 화면 밖으로 삐져나가 가로 스크롤을 만드는 걸 막는다. */
.onboard__deck { display: grid; overflow-x: clip; --onboard-shift: 24px; }
.onboard__deck[data-dir="back"] { --onboard-shift: -24px; }

.onboard__card {
  grid-area: 1 / 1;
  min-height: 380px;
  opacity: 0;
  visibility: hidden;      /* 보조기술·탭 이동에서도 빠진다 */
  pointer-events: none;
  will-change: opacity, transform;
}
@media (min-width: 1024px) { .onboard__card { min-height: 420px; } }

.onboard__card.is-active {
  opacity: 1; visibility: visible; pointer-events: auto;
  animation: onboard-in var(--motion-slow) var(--ease-soft) both;
}
/* 나가는 장은 자리를 차지한 채로 사라지기만 한다(레이아웃 영향 없음).
   들어오는 장보다 빨리 비켜야 두 장이 겹쳐 보이는 구간이 짧다. */
.onboard__card.is-leaving {
  visibility: visible;
  animation: onboard-out var(--motion-fast) var(--ease-in) both;
}

@keyframes onboard-in {
  from { opacity: 0; transform: translate3d(var(--onboard-shift), 0, 0) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes onboard-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translate3d(calc(var(--onboard-shift) * -0.5), 0, 0) scale(0.99); }
}

/* 카드가 통째로 미끄러지기만 하면 밋밋하다. 안쪽 줄이 살짝 늦게 따라 올라온다. */
.onboard__card.is-active > * { animation: onboard-el var(--motion-slow) var(--ease-soft) both; }
.onboard__card.is-active > *:nth-child(1) { animation-delay: 40ms; }
.onboard__card.is-active > *:nth-child(2) { animation-delay: 90ms; }
.onboard__card.is-active > *:nth-child(3) { animation-delay: 140ms; }
.onboard__card.is-active > *:nth-child(n + 4) { animation-delay: 190ms; }

@keyframes onboard-el {
  from { opacity: 0; transform: translate3d(0, 10px, 0); }
  to   { opacity: 1; transform: none; }
}

.onboard__flow { display: flex; flex-direction: column; gap: var(--space-4); }
.onboard__flow li { display: flex; align-items: flex-start; gap: var(--space-3); }
.onboard__flow-no {
  flex: 0 0 28px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: var(--radius-pill);
  background: var(--color-brand-100); color: var(--color-brand-700);
  font-size: 13px; font-weight: 800;
}
.onboard__flow-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.onboard__notes { display: flex; flex-direction: column; gap: var(--space-4); }
.onboard__notes li { display: flex; flex-direction: column; gap: var(--space-1); }

.onboard__actions { display: flex; gap: var(--space-3); align-items: stretch; }
.onboard__actions > form { flex: 1; }
.onboard__actions .btn--ghost { flex: 0 0 auto; }

/* ── 완주 인증서 ────────────────────────────────────────── */

.cert-page { background: var(--color-surface-soft); }

.cert-bar {
  position: sticky; top: 0; z-index: 30;
  background: rgb(255 255 255 / 94%);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--color-line);
}
.cert-bar__inner {
  max-width: var(--content-max); margin: 0 auto;
  padding: var(--space-3) var(--space-4);
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
}
.cert-bar__inner .t-caption { flex: 1 1 200px; }

.cert-stage { padding: var(--space-8) var(--space-4) var(--space-16); }

/* 완주 배지 — canvas 로 그린 뒤 투명 PNG 로 내려받는다 */
.badge-stage { width: min(100%, 420px); margin: 0 auto; }
.badge-stage canvas {
  display: block; width: 100%; height: auto;
  animation: rise-in var(--motion-slow) var(--ease-out) both;
}

/* A4 세로 비율. 화면에서는 폭에 맞춰 줄어들고, 인쇄하면 한 장에 딱 들어간다. */
.cert {
  position: relative;
  width: min(100%, 794px);
  aspect-ratio: 210 / 297;
  margin: 0 auto;
  padding: clamp(24px, 6%, 64px);
  background: var(--color-surface);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  /* 기울인 워터마크가 증서 밖으로 삐져나와 가로 스크롤을 만들지 않게 한다. */
  overflow: hidden;
  animation: rise-in var(--motion-slow) var(--ease-out) both;
}
.cert::after {
  content: ""; position: absolute; inset: clamp(12px, 2.4%, 24px);
  border: 2px solid var(--color-brand-100);
  border-radius: var(--radius-md);
  pointer-events: none;
}

.cert__head { display: flex; flex-direction: column; gap: var(--space-2); align-items: center; }
.cert__brand { font-size: clamp(15px, 2.4vw, 19px); font-weight: 800; letter-spacing: -0.02em; }
.cert__brand b { color: var(--color-brand-500); }
.cert__kicker {
  font-size: clamp(10px, 1.5vw, 12px); font-weight: 700; letter-spacing: 0.22em;
  color: var(--color-muted);
}

.cert__title { margin-top: clamp(10px, 5%, 44px); font-size: clamp(26px, 5.4vw, 40px); font-weight: 800; letter-spacing: -0.02em; }
.cert__lead { margin-top: clamp(10px, 5%, 40px); font-size: clamp(13px, 1.9vw, 15px); color: var(--color-ink-soft); }
.cert__name {
  margin-top: var(--space-3);
  font-size: clamp(28px, 6vw, 46px); font-weight: 800; letter-spacing: -0.02em;
  border-bottom: 2px solid var(--color-brand-500);
  padding: 0 clamp(16px, 4vw, 40px) var(--space-2);
}
.cert__course { margin-top: clamp(10px, 5%, 40px); font-size: clamp(15px, 2.4vw, 20px); font-weight: 700; }
.cert__body { margin-top: var(--space-3); font-size: clamp(13px, 2vw, 16px); line-height: 1.9; color: var(--color-ink-soft); }

.cert__meta {
  margin: auto 0 0; padding-top: clamp(10px, 5%, 40px);
  display: flex; gap: clamp(16px, 4vw, 48px); flex-wrap: wrap; justify-content: center;
}
.cert__meta div { display: flex; flex-direction: column; gap: var(--space-1); }
.cert__meta dt { font-size: clamp(10px, 1.5vw, 12px); font-weight: 700; color: var(--color-muted); }
.cert__meta dd { margin: 0; font-size: clamp(12px, 1.8vw, 15px); font-weight: 700; }

.cert__foot {
  margin-top: clamp(10px, 5%, 40px);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
}
.cert__seal {
  display: inline-flex; align-items: center; justify-content: center;
  width: clamp(52px, 9vw, 72px); height: clamp(52px, 9vw, 72px);
  border: 2px solid var(--color-brand-500); border-radius: var(--radius-pill);
  color: var(--color-brand-600); font-weight: 800; font-size: clamp(12px, 1.8vw, 15px);
}
.cert__issuer { font-size: clamp(12px, 1.8vw, 15px); font-weight: 700; color: var(--color-ink-soft); }

/**
 * 인쇄 — 반드시 한 장에 들어가야 한다.
 *
 * ⚠️ 크롬 인쇄 대화상자의 **여백 설정이 `@page { margin }` 을 덮어쓴다.** 그래서 인쇄 영역 높이를
 *    미리 알 수 없다. `height: 100vh` 처럼 페이지를 꽉 채우게 만들면 여백이 "기본값" 인 순간
 *    1~2mm 넘쳐서 두 장으로 잘린다.
 *    → 높이를 고정하지 않고, 어떤 여백 설정에서도 남는 245mm 안에 들어가게 둔다.
 */
/* 완주 전 안내 — 화면에는 안 보이고 인쇄에만 나온다(아래 @media print) */
.cert-printblock { display: none; }


/* ── 개발 서버 표시 ────────────────────────────────────────
 * 운영에는 이 마크업이 아예 나가지 않는다(config.php renderEnvBanner).
 * 브랜드 주황과 섞이면 서비스의 일부로 보이므로 일부러 다른 계열로 칠한다.
 */
body.has-env-banner { --env-banner-h: 30px; }

.env-banner {
  position: sticky; top: 0; z-index: 40;
  height: var(--env-banner-h);
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  background: repeating-linear-gradient(
    -45deg, #1F2430, #1F2430 10px, #2A3040 10px, #2A3040 20px);
  color: #FFD166;
  font-size: 12px; font-weight: 600; line-height: 1;
  letter-spacing: 0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-bottom: 1px solid #0F1319;
  /* 아래 화면 요소를 가리지 않게 — 띠는 읽히기만 하면 된다. */
  user-select: none;
}
.env-banner__tag {
  background: #FFD166; color: #1F2430;
  padding: 2px var(--space-2); border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 800; letter-spacing: 0.04em;
}

/* 좁은 화면에서는 'DEV' 칩만 남긴다 — 줄바꿈으로 띠 높이가 흔들리지 않게 */
@media (max-width: 380px) {
  .env-banner__tag + span { display: none; }
}

@media print {
  @page { size: A4 portrait; margin: 10mm; }

  html, body { height: auto; background: #fff; }
  .cert-bar, .bottom-nav, .app-header, .env-banner { display: none !important; }
  body.has-env-banner { --env-banner-h: 0px; }
  .cert-page { background: #fff; }
  .cert-stage { padding: 0; }

  /**
   * 완주 전에는 인쇄로도 받을 수 없다.
   * 버튼만 막으면 Ctrl+P 로 그대로 빠져나간다 — 증서 자체를 인쇄 대상에서 뺀다.
   */
  .cert-page--preview .cert-stage { display: none !important; }
  .cert-page--preview .cert-printblock {
    display: block;
    padding: 40mm 20mm;
    text-align: center;
    font-size: 12pt; line-height: 1.9;
  }

  .cert {
    width: 100%;
    height: auto;
    /* 여백을 가장 넓게 잡아도(20mm) 인쇄 영역은 257mm 다. 252mm 면 어느 설정에서도 남는다. */
    min-height: 252mm;
    aspect-ratio: auto;
    padding: 14mm 16mm;
    border: 0; border-radius: 0; box-shadow: none;
    overflow: visible;
    animation: none;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  /* 뒤에 빈 블록이 하나라도 남으면 그것만으로 둘째 장이 생긴다. */
  .cert-stage, .cert { break-after: avoid; page-break-after: avoid; }

  .cert::after { inset: 0; }

  /* vw 기반 clamp 는 인쇄 폭에서 예측이 어렵다. 인쇄에서는 pt 로 고정한다. */
  .cert__brand    { font-size: 15pt; }
  .cert__kicker   { font-size: 9pt; }
  .cert__title    { font-size: 30pt; margin-top: 14mm; }
  .cert__lead     { font-size: 11pt; margin-top: 14mm; }
  .cert__name     { font-size: 34pt; margin-top: 4mm; padding: 0 12mm 3mm; }
  .cert__course   { font-size: 15pt; margin-top: 14mm; }
  .cert__body     { font-size: 12pt; margin-top: 5mm; }
  .cert__meta     { padding-top: 14mm; gap: 20mm; }
  .cert__meta dt  { font-size: 9pt; }
  .cert__meta dd  { font-size: 11pt; }
  .cert__foot     { margin-top: 12mm; }
  .cert__seal     { width: 20mm; height: 20mm; font-size: 10pt; }
  .cert__issuer   { font-size: 11pt; }

  /* 배경색·테두리가 인쇄에서 빠지면 증서로 보이지 않는다. */
  .cert, .cert::after, .cert__name, .cert__seal {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* 위 규칙은 지속시간만 없앤다. 이동·확대는 여기서 따로 지운다(design-system.md §모션). */
  .reveal, .stagger > *, .cert,
  .challenge-card.is-leaving, .challenge-card.is-entering,
  .onboard__card.is-active, .onboard__card.is-active > *,
  .bubble.is-entering, .result-panel.is-revealing,
  .main, .is-shaking, .notice.is-attention {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  /* 보이지 않아야 할 장까지 opacity 를 되살리면 세 장이 겹쳐 보인다 — 여기서는 지우기만 한다. */
  .onboard__card.is-leaving { animation: none !important; transform: none !important; }
  .progress__fill { animation: none !important; transform: none !important; }
  /* 스피너는 "돌고 있다"가 곧 정보라 멈추면 안 된다. 지우는 대신 느리게 돌린다. */
  .spinner { animation: spin 2.4s linear infinite !important; }
  @view-transition { navigation: none; }
}

/* ── 인앱 브라우저 탈출 다리 (open.php) ────────────────────────
   카톡에서 링크를 누른 사람이 제일 먼저 보는 화면이다. 앱 화면이 아니라
   "여기서 나가세요" 한 장이므로 헤더·하단바 없이 가운데로만 세운다. */
.open-bridge__main {
  max-width: 420px;
  margin: 0 auto;
  padding: max(24px, env(safe-area-inset-top)) var(--space-4) 48px;
  min-height: 100dvh;
  display: flex; flex-direction: column; justify-content: center;
}
.open-bridge code {
  padding: 1px 5px; border-radius: 4px;
  background: var(--color-surface-soft); font-size: 0.95em;
}
