/* ── 2026-09-28 · SEED Design 이식 (design/24) ──────────────────────────────────
   원천 `--c-*` 의 무채·판정을 당근 SEED(seed-design.io) 팔레트로 옮겼다. 구조(3층)는 그대로다.
     면   s0 gray-00 · s1 gray-100 · s2 gray-200 · s3/hairline gray-300 · 입력 테두리 gray-400
     글자 fg gray-1000 · fg2 gray-800 · fg3 **#666B75** (SEED gray-700 #868B94 는 흰 위 3.4:1 이라
          한 단 짙게 — 이 레포의 4.5 계약을 낮추지 않는다)
     판정 green/yellow/red **-800** (SEED -700 은 흰 위 3.8~4.1 — 같은 이유)
     램프 `--c-i*` 는 **유지**(데이터 색 · 아래 옛 머리말의 ★ 그대로)
   아래 옛 머리말의 표(OKLCH 재보정)는 이력이다 — 지금 값은 위 SEED 값이다.
   ────────────────────────────────────────────────────────────────────────── */
/* ── 팔레트 ───────────────────────────────────────────────────────────────────
   생성: scripts/theme-candidates.py --apply light
   결정: L. Ink on Paper - 흰 배경. Bone Pop 의 논리를 그대로 뒤집는다

   액센트가 무채라는 전제는 유지하되 방향만 반전한다. 흰 종이 위에서 가장 강한 무채는 잉크(near-black)다. 판정·램프는 흰 배경에서 읽히도록 전부 어둡게 내렸다 — 다크용 밝은 색을 그대로 쓰면 흰 위에서 전부 탈색된다.

   3층이다.
     원천    아래 `--c-*`. **이 앱의 색은 이게 전부다**
     팔레트  `--p-*` 101종이 원천을 가리킨다 (옛 색 이름을 흡수하는 층)
     의미    `--accent`·`--ok` 등. 컴포넌트가 실제로 쓰는 이름

   시작은 raw hex 72종 + 유채 rgba 20종 + 무채 오버레이 61곳이었다. 초록만 13종이었고
   같은 "이지 페이스"가 화면마다 다른 색이었다. 색이 그만큼 많으면 아무 색도 학습되지 않는다.

   ★ 티어 간 침범 금지. 액센트는 액션에만, 판정은 배지에만, 램프는 막대에만.
     지키지 않으면 다시 72종이 된다.

   ── 2026-09-05 · 판정 6색 OKLCH 재보정 (`scripts/palette-oklch.py verdict`) ──
   눈으로 고른 hex 라 같은 "판정"인데 인지 밝기가 달랐다 — risk 만 한 단 밝았고(L .543 vs
   .514/.512), 그 어긋남이 `_hi` 에서도 그대로 반복됐다(.463 vs .433/.436). 배지 셋을
   나란히 놓으면 빨강만 떠 보인다. hue·chroma 는 그대로 두고 **L 만** 하나로 모았다.

     토큰            before  →  after     L .514→        s0          s2        ΔE76
     --c-good        #1E7A45 → #217C47    .514→.520   5.35→5.20   4.64→4.51    0.8
     --c-caution     #8A5C09 → #8D5F0F    .512→.522   5.81→5.56   5.04→4.83    1.3
     --c-risk        #C0392B → #B83124    .543→.521   5.44→6.00   4.72→5.20    2.7
     --c-good_hi     #166035 → #1A6338    .433→.444   7.61→7.28   6.61→6.32    1.2
     --c-caution_hi  #6E4907 → #704B0B    .436→.443   8.02→7.78   6.96→6.75    1.0
     --c-risk_hi     #9A2D22 → #94271C    .463→.445   7.57→8.18   6.57→7.10    2.3

   ★ 공유 L 은 **평균이 아니라 '가능한 최대'** 다(.5218). 평균(.5230)에 맞추면 `--c-good`
     이 `--c-s2` 위에서 4.45 로 떨어진다 — 그때 "4.5 바닥을 낮추자 / good 만 예외로 두자"
     둘 다 검토했지만 **둘 다 필요 없었다.** L 을 0.0012 만 내리면 셋이 같은 L 을 유지한 채
     전부 4.5 를 넘는다. 대비는 계약이고 균등은 목표인데, 여기서는 싸우지 않았다.
   ⚠️ hex 를 고치면 **파생 트리플렛 일곱**이 같이 움직여야 한다(`--p-ok-rgb`·`--p-data-rgb`·
      `--p-warn-rgb`·`--p-ep-line-rgb`·`--p-err-rgb`·`--p-ok-inset-rgb`·`--p-gold-soft-rgb`).
      안 맞추면 배지 **글자**는 새 초록인데 그 **배경 틴트**는 옛 초록이 된다. 스크립트가
      값을 대조하고 함께 고친다 — 손으로 고치지 마라.
   ⚠️ `templates/letter.html` 도 `--good` 을 **값으로 베껴** 갖고 있다(그 문서는 서브리소스를
      링크할 수 없다). 같이 고쳐야 편지의 상승 초록이 앱과 갈라지지 않는다.
   ★ **강도 램프(`--c-i*`)는 손대지 않았다.** 균등 계단을 만들려면 i3·i4 가 ΔE 10~13 움직여야
      해서 보정이 아니라 교체가 된다(근거·수치는 `scripts/palette-oklch.py` 머리말 참조).
   ────────────────────────────────────────────────────────────────────────── */
:root {

  /* ── 면 ── */
  --c-s0: #FFFFFF;               /* 페이지 */
  --c-s1: #F7F8F9;               /* 카드 */
  --c-s2: #F3F4F5;               /* 떠 있는 면 */
  --c-s3: #EEEFF1;               /* 오버레이·선택 */
  --c-hairline: #EEEFF1;         /* 구분선 */

  /* ── 글자 ── */
  --c-fg: #1A1C20;              
  --c-fg2: #555D6D;             
  --c-fg3: #666B75;             

  /* ── 액센트 - 액션 전용 ── */
  --c-accent: #1A1C20;          
  --c-accent_hi: #1A1C20;       
  --c-accent_lo: #555D6D;       
  --c-on_accent: #FFFFFF;        /* 액센트 위 글자 */

  /* ── 브랜드 — 코발트 (design/24 D2 · 2026-09-28 carrot → cobalt) ──
     액센트(위)는 SEED 의 **neutral** 이다: 선택 상태·링크·진행 막대가 전부 그걸 쓴다(238곳).
     브랜드는 **화면의 주 행동 하나**(주 CTA·FAB·알림 배지)에만 명시적으로 입힌다 —
     `--accent` 를 브랜드색으로 바꾸면 "눌린 것"과 "누를 것"이 같은 색이 된다(SEED Chip: 선택=inverted).
     코발트는 인스타 캐러셀(@ompb.coach) 브랜드 블루와 같은 값이다 — 앱과 SNS 가 한 색으로 이어진다.
     흰 라벨/`--c-brand` 5.2:1 — carrot(2.9:1)과 달리 작은 글자에도 쓸 수 있어 "큰 라벨만" 예외가 없다.
     작은 **글자**로 쓰는 브랜드색은 여전히 `--c-brand_text`(7.2:1)다 — 흰 면 위 링크·강조. */
  --c-brand: #2E5BFF;            /* bg.brand-solid */
  --c-brand_pressed: #1F46E0;    /* bg.brand-solid-pressed · 흰 글자 7.0:1 */
  --c-brand_text: #2446D6;       /* 글자용 · 흰 면 7.2:1 · brand_weak 면 6.4:1 */
  --c-brand_weak: #EEF2FF;       /* bg.brand-weak */
  --c-brand_weak_pressed: #E2E8FF;
  --c-on_brand: #FFFFFF;
  --c-inverted: #2A3038;         /* gray-900 · bg.neutral-inverted — 칩·탭 선택, Help Bubble, 스낵바 */
  --c-stroke-weak: #DCDEE3;      /* gray-400 · stroke.neutral-weak — 입력 칸 테두리 */
  /* SEED bg.*-weak — Callout·토스트 면. 판정 알파 틴트(`--ok-bg` 등)는 배지용으로 남는다. */
  --c-positive_weak: #EDFAF6; --c-critical_weak: #FDF0F0;
  --c-warning_weak: #FFF7DE;  --c-informative_weak: #EFF6FF; --c-neutral_weak: #F3F4F5;

  /* ── 판정 ── */
  --c-good: #00745F;            
  --c-good_hi: #075445;         
  --c-caution: #755B22;         
  --c-caution_hi: #4F3E1F;      
  --c-risk: #CA1D13;            
  --c-risk_hi: #921708;         

  /* ── 강도 램프 ── */
  --c-i1: #2F6E85;               /* 회복·이지 */
  --c-i1d: #245566;             
  --c-i2: #2F7D5C;               /* 롱런 */
  --c-i2d: #246146;             
  --c-i3: #9B7B2E;               /* 템포 */
  --c-i3d: #7A6024;             
  --c-i4: #B25A24;               /* 인터벌 */
  --c-i4d: #8C471C;             
  --c-i5: #9C3222;               /* 레이스 */
  --c-i5d: #7A271A;             

  /* ── 그 밖 ── */
  --c-ink: 0 0 0;                /* 오버레이 잉크 - 면을 어느 쪽으로 미는가 */
  /* 어두운 사진·스크림 위의 글자. **테마를 따라가지 않는다** — 랜딩 히어로의 배경은
     면이 아니라 러닝 사진이라 라이트 테마에서도 글자는 밝아야 한다. --fg 로 두면
     테마를 뒤집는 순간 헤드라인이 사진 위에서 사라진다(이 함정을 네 번째로 만난다). */
  --c-on-media: #FFFFFF;
  /* 사진 위에 까는 어둠. 그림자(--c-shadow)와 **역할이 다르다** — 그림자는 면의
     재질이라 테마를 따라가지만, 스크림은 미디어 처리라 두 테마 모두 검정 쪽이다.
     라이트 전환 때 스크림이 --c-shadow 를 쓰고 있어서 히어로가 회색 덩어리가 됐다. */
  --c-scrim: #07080C;
  --c-scrim-rgb: 7 8 12;
  --c-shadow: #5A5A64;          
  --c-strava: #fc4c02;           /* 브랜드색 */

  /* ── 팔레트: 옛 색 이름 101종 → 원천 ── */
  --p-s0: var(--c-s0);                  
  --p-s1: var(--c-s1);                  
  --p-hairline: var(--c-hairline);      
  --p-fg: var(--c-fg);                  
  --p-fg2: var(--c-fg2);                
  --p-on-fill: var(--c-on_accent);       /* 채워진 면 위 글자 = 액센트 위 글자와 같은 규칙 */
  --p-on-fill-cool: var(--c-fg);        
  --p-on-fill-good: var(--c-fg);        
  --p-on-chart: var(--c-s0);            
  --p-placeholder: var(--c-fg3);        
  --p-chip-bg: var(--c-s2);             
  --p-bar-idle-1: var(--c-s3);          
  --p-bar-idle-2: var(--c-s2);          
  --p-rest-1: var(--c-s2);               /* 휴식일 카드도 그냥 면이다 */
  --p-rest-2: var(--c-s1);              
  --p-type-rest: var(--c-s3);           
  /* `--p-review-1/2/3` 을 지웠다 — 런 리뷰 카드의 3-스톱 그라디언트 하나만 쓰던 토큰인데
     그 그라디언트가 면이 되면서(design/21 R1) 참조가 0이 됐다. 라이트 전환 때 이미 셋 다
     `--c-s1`·`--c-s1`·`--c-s0` 으로 수렴해 사실상 흰색이었다. 안 쓰는 토큰을 남기면
     다음 사람이 "리뷰 카드는 자기 팔레트가 있다"고 읽는다. */
  --p-badge-rest: var(--c-fg2);         
  --p-badge-cross: var(--c-fg2);        
  --p-accent: var(--c-accent);          
  --p-accent-2: var(--c-accent_hi);     
  --p-accent-hi: var(--c-accent_hi);    
  --p-accent-lo: var(--c-accent_lo);    
  --p-accent-soft: var(--c-fg2);        
  --p-info: var(--c-fg2);                /* ★ info 폐기 - 중립 정보는 무채로 충분하다 */
  --p-info-2: var(--c-accent);          
  --p-dot-info: var(--c-fg3);           
  --p-bar-cur: var(--c-accent);         
  --p-hero-deep-1: var(--c-s1);          /* ★ 랜딩 히어로의 짙은 파랑을 면으로 */
  --p-hero-deep-2: var(--c-s0);         
  --p-ok: var(--c-good);                
  --p-ok-2: var(--c-good_hi);           
  --p-data: var(--c-good);               /* ★ --data 와 --ok 통합 (부채 ③) */
  --p-on-done: var(--c-good);           
  --p-dot-done: var(--c-good);          
  --p-warn: var(--c-caution);           
  --p-warn-2: var(--c-caution_hi);      
  --p-aq-mid: var(--c-caution_hi);      
  --p-dot-warn: var(--c-caution);       
  --p-ep-line: var(--c-caution);        
  --p-err: var(--c-risk);               
  --p-err-2: var(--c-risk_hi);          
  --p-err-3: var(--c-risk_hi);          
  --p-dot-error: var(--c-risk);         
  --p-pain-hi: var(--c-risk);           
  --p-done-1: var(--c-s2);               /* ★ 완료 카드가 채도 높은 초록 패널을 그만둔다 (theme lock) */
  --p-done-2: var(--c-s1);              
  --p-recovery: var(--c-i1);            
  --p-z1: var(--c-i1);                  
  --p-poly-easy-1: var(--c-i1);          /* ★ 브리핑 이지 */
  --p-poly-easy-2: var(--c-i1d);        
  --p-poly-easy-r1: var(--c-i1);         /* ★ 트레이닝 이지 - 브리핑과 같은 색이 된다 */
  --p-poly-easy-r2: var(--c-i1d);       
  --p-phase-base-1: var(--c-i1);        
  --p-phase-base-2: var(--c-i1d);       
  --p-phase-build-1: var(--c-i2);       
  --p-phase-build-2: var(--c-i2d);      
  --p-phase-taper-1: var(--c-i2);       
  --p-phase-taper-2: var(--c-i2d);      
  --p-phase-peak-1: var(--c-i4);        
  --p-phase-peak-2: var(--c-i4d);       
  --p-poly-hard-1: var(--c-i4);          /* ★ 트레이닝 하드 */
  --p-poly-hard-2: var(--c-i4d);        
  --p-poly-hard-db: var(--c-i4);         /* ★ 브리핑 하드 - 트레이닝과 같은 색이 된다 */
  --p-poly-hard-t1: var(--c-i5);        
  --p-poly-hard-t2: var(--c-i5d);       
  --p-poly-severe: var(--c-i5);         
  --p-poly-severe-2: var(--c-i5d);      
  --p-done-mid: var(--c-s2);             /* ★ 완료 카드 그라디언트 중간 - 여기가 초록이면 T3 이 안 고쳐진다 */
  --p-accent-inset: var(--c-accent);    
  --p-ok-inset: var(--c-good_hi);       
  --p-gold-soft: var(--c-caution_hi);   
  --p-grain: var(--c-s3);               
  --p-shadow: var(--c-shadow);          
  --p-shadow-2: var(--c-shadow);        
  --p-shadow-3: var(--c-shadow);        
  --p-shadow-4: var(--c-shadow);        
  --p-shadow-5: var(--c-shadow);        
  --p-strava: var(--c-strava);           /* 브랜드색 */

  /* ── 채널 (rgba 알파용) ── */
  --p-accent-rgb: 26 28 32;
  --p-ok-rgb: 0 116 95;
  --p-warn-rgb: 117 91 34;
  --p-err-rgb: 202 29 19;
  --p-data-rgb: 0 116 95;
  --p-info-rgb: 85 93 109;
  --p-ep-line-rgb: 117 91 34;
  --p-accent-2-rgb: 26 28 32;
  --p-s1-rgb: 247 248 249;
  --p-strava-rgb: 252 76 2;
  --p-fg2-rgb: 85 93 109;
  --p-accent-inset-rgb: 26 28 32;
  --p-ok-inset-rgb: 7 84 69;
  --p-gold-soft-rgb: 79 62 31;
  --p-grain-rgb: 238 239 241;
  --p-shadow-rgb: 90 90 100;
  --p-shadow-2-rgb: 90 90 100;
  --p-shadow-3-rgb: 90 90 100;
  --p-shadow-4-rgb: 90 90 100;
  --p-shadow-5-rgb: 90 90 100;
}

/* ===========================================================================
   OMPB 코치 - Jinja 표면 전역 스타일  ★ 정본
   `base.html` 인라인 <style> 357줄에서 이관 (design/10 Phase B · P0-1)

   왜 옮겼나. 인라인이면 캐시가 안 되고, 재사용이 안 되고, `scripts/sync-v1-css.py`
   같은 코드 생성기가 HTML 을 파싱해야 한다. 무엇보다 1,657줄이 14개 템플릿에
   흩어져 있으면 토큰 재설계를 걸 지점이 없다.

   ⚠️ 이 파일은 `static/design.css`(SvelteKit SPA 용, 동결)와 **다른 시스템**이다.
   같은 이름의 변수가 다른 값을 갖는다 (`--accent` 등). 섞지 마라.
   SPA 가 되살아나면 `design.css` 가 정본이고 이 파일은 폐기 대상이다.

   로드: base.html <head> - 템플릿별 <style>({% block head %})보다 **먼저** 온다.
   =========================================================================== */

:root {
  /* 면 — 4단 elevation (부채 ①). 원래는 --bg·--bg2·--card 가 **전부 순검정**이라
     층이 없었고, 카드가 배경과 구별되지 않아 그림자로만 떠 보였다. 이제 명도차가 층이다. */
  --bg:var(--c-s0); --bg2:var(--c-s0); --card:var(--c-s1); --card2:var(--c-s2);
  --fg:var(--c-fg); --muted:var(--c-fg2); --line:var(--c-hairline);
  /* 글자 3단. `--muted` 보다 한 단 연한 톤 — 마이크로 대문자 킥커·카드 번호처럼 위계의
     바닥에 놓이는 라벨용.
     ⚠️ 이름을 `--fg2`/`--fg3` 로 두지 않는다. 그건 **SPA(design.css) 의 이름**이고 거기서는
     다크용 값(`#98A0AD`)이다. 두 토큰 시스템을 섞으면 값이 틀리거나 `var()` 가 조용히 비어
     버튼 글자가 사라진 전례가 있다. Jinja 표면은 Jinja 이름만 쓴다. */
  --muted-2:var(--c-fg3);
  /* 액센트 1색 — 주 액션 전용. 차트·데이터에 쓰면 28종이 다시 72종이 된다 */
  --accent:var(--p-accent); --accent-2:var(--p-accent-2); --on-accent:var(--p-on-fill);
  --glow:rgb(var(--p-accent-rgb) / .45); --glow-soft:rgb(var(--p-accent-rgb) / .16);
  /* 판정 — 배지·글자·테두리 전용 */
  --ok:var(--p-ok); --ok-bg:rgb(var(--p-ok-rgb) / .14); --ok-line:rgb(var(--p-ok-rgb) / .36);
  --warn:var(--p-warn); --warn-bg:rgb(var(--p-warn-rgb) / .12); --warn-line:rgb(var(--p-warn-rgb) / .32);
  --err:var(--p-err); --err-bg:rgb(var(--p-err-rgb) / .12); --err-line:rgb(var(--p-err-rgb) / .32);
  --info:var(--p-info); --info-bg:rgb(var(--p-info-rgb) / .14); --info-line:rgb(var(--p-info-rgb) / .32);
  /* 브랜드(코발트) — 주 행동 하나에만. 선택 상태는 `--inverted`(design/24 D2·D3) */
  --brand:var(--c-brand); --brand-pressed:var(--c-brand_pressed); --brand-text:var(--c-brand_text);
  --brand-weak:var(--c-brand_weak); --brand-weak-pressed:var(--c-brand_weak_pressed); --on-brand:var(--c-on_brand);
  --inverted:var(--c-inverted); --stroke-weak:var(--c-stroke-weak);
  /* SEED 타이포 t1~t10 (font-size / line-height). 굵기는 400·500·700 셋뿐이다. */
  --t1:.6875rem; --t1-lh:.9375rem;  --t2:.75rem; --t2-lh:1rem;   --t3:.8125rem; --t3-lh:1.125rem;
  --t4:.875rem;  --t4-lh:1.1875rem; --t5:1rem;   --t5-lh:1.375rem; --t6:1.125rem; --t6-lh:1.5rem;
  --t7:1.25rem;  --t7-lh:1.6875rem; --t8:1.375rem; --t8-lh:1.875rem; --t9:1.5rem; --t9-lh:2rem;
  --t10:1.625rem; --t10-lh:2.1875rem;
  /* ── 타이포 스케일 (design/10 Phase D · 부채 ⑦의 나머지 절반) ────────────────
     색은 거의 완벽히 토큰화됐는데 크기는 px 리터럴이 1,264회였다. 스케일이 문서로만
     있고 코드에는 없으면 다음 사람이 또 새 값을 쓴다.
     웨이트도 함께 못박는다 — 실측 분포가 800(108회) > 700(85) > 600(23) > 400(2) 였다.
     즉 **전부 굵어서 위계가 없었다.** 400/500 이 본문을 맡아야 800 이 강조가 된다. */
  /* SEED 스케일로 옮겼다(design/24) — 이름은 역할, 값은 t-토큰. */
  --fs-display:var(--t10);  /* 화면당 1개. 오늘의 주인공 */
  --fs-title:var(--t9);     /* 카드 제목 */
  --fs-section:var(--t6);   /* 섹션 머리 */
  --fs-body:var(--t5);      /* 본문 */
  --fs-meta:var(--t3);      /* 보조 */
  --fs-foot:var(--t2);      /* 각주·범례 */
  --fs-kicker:var(--t2);    /* 킥커 — 한국어 최소 12px */
  /* 디스플레이 800→700. 큰 숫자·제목은 크기가 위계를 만들고, 무게는 한 단 물러난다
     (design/21 R9 — 토스 '숫자를 크게', TPS '개성보다 콘텐츠'). */
  --fw-body:400; --fw-med:500; --fw-semi:500; --fw-bold:700; --fw-display:700;
  /* 보조 라벨 한 벌. 대문자 킥커(자간 .1~.24em)를 전부 이걸로 바꿨다(design/21 R2) —
     한국어에 대문자는 없고 자간만 벌어져 '이 달 러닝'이 '이 달  러 닝'이 됐다. */
  --fs-label:var(--t3); --fw-label:500;

  /* ── 형태 스케일 (design/21 R3 — Stripe Sail·당근 Seed '시스템이 보장') ──
     반경은 이 다섯에서만 고른다. 리터럴 8~22px 이 12종 섞여 있었다. */
  /* 다섯 단계가 SEED r2·r3·r4·r5·full 과 정확히 같다. 시트 윗모서리(r6)·배지(r1·r1.5)만 더한다. */
  --r-sm:8px; --r-md:12px; --r-lg:16px; --r-xl:20px; --r-pill:999px;
  --r-xs:6px; --r-sheet:24px; --r-callout:10px;
  --radius:var(--r-lg);
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px;
  /* 그림자 2단. 카드는 거의 없음(면·헤어라인이 층을 만든다), 떠 있는 것만 float.
     원래 `0 26px 60px -30px ink/.92` 였다 — 흰 종이 위에서 카드마다 검은 웅덩이가 생겼다. */
  /* SEED shadow s1·s3. 카드는 면·선이 층을 만들고(elevation: surface/stroke), 그림자는 떠 있는 것만. */
  --shadow:0 0 0 transparent;
  --shadow-s1:0 1px 4px rgb(var(--c-ink) / .08); --shadow-s2:0 2px 10px rgb(var(--c-ink) / .10);
  --shadow-float:0 4px 16px rgb(var(--c-ink) / .12);
  /* 모션은 두 상태의 관계만 보여 준다(design/21 R10). */
  /* SEED motion — micro ≤200ms `easing`, 등장 `enter`, 퇴장 `exit`. */
  --ease:cubic-bezier(.35,0,.35,1); --ease-enter:cubic-bezier(0,0,.15,1); --ease-exit:cubic-bezier(.35,0,1,1);
  --ease-enter-x:cubic-bezier(.03,.4,.1,1);
  --dur-fast:.1s; --dur:.15s; --dur-slow:.3s;
  /* 서체 한 벌(design/24 D6) — SEED 는 시스템 서체 + 웹은 Pretendard. Archivo 가 숫자 모양을 둘로 만들었다. */
  --display:'Pretendard',-apple-system,'Apple SD Gothic Neo',system-ui,sans-serif;
  --body:'Pretendard',-apple-system,'Apple SD Gothic Neo',system-ui,sans-serif;
  /* ── --data (run-review + hot paths).
     ⚠️ 원 주석은 "--data green stays semantically distinct from --ok" 였다.
     그 구별이 부채 ③이었다 — 초록 두 개가 같은 화면에서 다른 뜻인 척했지만
     사용자는 둘을 구별할 수 없었다. 이제 --data 와 --ok 는 같은 --c-good 이다. ── */
  --data:var(--p-data); --data-dim:rgb(var(--p-data-rgb) / .18); --data-line:rgb(var(--p-data-rgb) / .38);
  /* `--glass`/`--glass-hi`/`--glass-border` 를 지웠다 — `.glass` 가 `.rr-panel` 이 되면서
     프로스트를 그만뒀고, 그 셋을 읽던 규칙이 하나도 남지 않았다. */
  --radius-lg:var(--r-xl); --blur:saturate(1.3) blur(20px);
}
* { box-sizing:border-box; }
/* SEED 굵기는 400·500·700 셋뿐이다. `<b>` 의 기본값 `bolder` 는 700 부모 안에서 **900** 으로
   승격된다(1회차 심판 측정: 화면마다 한 곳씩). 강조는 한 단계, 700 이 천장이다. */
b, strong { font-weight:700; }
::selection { background:rgb(var(--p-accent-rgb) / .4); color:var(--p-on-fill); }
body {
  margin:0; background:var(--bg); color:var(--fg); min-height:100dvh;
  font-family:var(--body); -webkit-font-smoothing:antialiased; letter-spacing:-.01em;
  /* 이 앱의 본문에는 숫자가 늘 섞여 있다(거리·페이스·심박·주차). 비례폭 숫자는 값이
     갱신될 때 자릿수가 흔들려 같은 자리의 숫자가 좌우로 움직인다 — 계기판에서는 결함이다.
     개별 컴포넌트에 흩어 붙이던 것을 여기 한 줄로 올린다(원래 12곳에만 붙어 있었다).
     한글·라틴 글자폭에는 영향이 없다 — 숫자만 등폭이 된다. */
  font-variant-numeric:tabular-nums;
  /* 한국어는 단어 중간에서 끊지 않는다. 없으면 "이지답게"가 "이지 / 답게"로 깨진다.
     design.css(SPA)에는 있었는데 Jinja 에는 없었다. */
  word-break:keep-all; overflow-wrap:break-word;
  display:flex; flex-direction:column; align-items:center;
  padding:max(16px,env(safe-area-inset-top)) 16px max(28px,env(safe-area-inset-bottom));
  position:relative;
  /* 가로 넘침 클립. ⚠️ `hidden` 단독이면 안 된다 — overflow-x:hidden 은 body 를
     **스크롤 컨테이너**로 만들고(overflow-y 가 auto 로 강제된다), iOS WebKit 은 body 가
     스크롤러인 문서에서 `position:fixed` 를 스크롤 동안 재고정하지 않는다. 실물 사고
     (2026-08-31, iPhone PWA): 하단 FAB 이 스크롤 중 콘텐츠와 함께 끌려 올라가 화면
     **중간**에 앉았다. `clip` 은 클립만 하고 스크롤 컨테이너를 만들지 않으므로 이 오인의
     여지가 없다. 선언 순서가 곧 폴백이다: clip 미지원 구형(iOS<16)은 앞의 hidden 을 쓴다
     — 거기서는 예전 증상이 남지만, 그 폭은 이미 좁다. `--vv-lift`(굳은 뷰포트 회복)와는
     기전이 다르다 — 그쪽은 키보드가 layout viewport 를 줄여 놓는 사고다. 둘 다 남긴다. */
  overflow-x:hidden; overflow-x:clip;
}
a { color:var(--accent-2); }
/* ★ 여기 있던 `body::before`(라디얼 글로우) · `body::after`(feTurbulence 그레인)를 **지웠다**
   (design/21 R1). 둘 다 정보가 아니었다 — 흰 종이 위의 잉크 글로우는 상단 우측에 회색
   얼룩으로만 보였고, 그레인은 `position:fixed` 전면 레이어라 스크롤마다 합성 비용을 냈다.
   토스·당근·Stripe 어느 제품 표면에도 분위기 레이어가 없다. 되살리지 마라. */
.wrap { width:100%; max-width:480px; position:relative; z-index:1; }

.card { background:var(--card);
        border:1px solid var(--line);
        border-radius:var(--radius); padding:20px; margin-top:12px;
        box-shadow:var(--shadow);
        animation:rise var(--dur-slow) var(--ease) both; }
.wrap > .card:nth-of-type(1){animation-delay:.03s} .wrap > .card:nth-of-type(2){animation-delay:.09s}
.wrap > .card:nth-of-type(3){animation-delay:.15s} .wrap > .card:nth-of-type(4){animation-delay:.21s}
@keyframes rise { from{opacity:0; transform:translateY(6px)} to{opacity:1; transform:none} }
@media (prefers-reduced-motion:reduce){ *{animation:none!important} }

/* ── 리포트 패널 `.rr-panel` (원래 이름 `.glass` — 프로스트 유리였다) ─────────────
   ★ 유리를 걷었다(design/21 R1). 이 키트는 `--bg` 가 **순검정**이던 시절에 만들어졌다:
     어두운 배경 위에서 반투명 흰 패널이 층으로 읽혔고, `backdrop-filter` 가 뒤를 흐려
     그 층을 실제로 만들어 냈다. 라이트로 옮기면서 전제가 사라졌다 — 불투명한 밝은 면
     위에서 `--glass`(잉크 6%)는 그냥 옅은 회색이고, `backdrop-filter` 는 **보이는 것을
     아무것도 바꾸지 않으면서** 스크롤마다 합성 레이어 비용만 낸다. inset 하이라이트도
     흰 종이 위에서는 보이지 않는다.
     그래서 셋 다 지우고 면 + 헤어라인으로 둔다. 화면 결과는 거의 같고 비용만 사라진다.
     `@supports not (backdrop-filter)` 폴백도 함께 지웠다 — 막을 것이 없어졌다.
   ★ 이름을 `.rr-panel` 로 옮겼다(그 '별건'이 이번 것이다). 유리가 아닌 것을 유리라고
     부르면 다음 사람이 `backdrop-filter` 를 되살린다. `rr-` 는 이 카드의 원래 프로토타입
     접두사다(옛 주석의 `rr-ambient`).
   ⚠️ 이름을 나눠 갖는 곳이 **다섯**이다 — 한 곳만 고치면 화면이 조용히 스타일 없이 뜬다:
      `runs.html` 마크업 · 그 안에서 지표 카드 넷을 만드는 JS(`el.className`) ·
      `runs.css` 자식 선택자 · `v1-overrides.css` 의 `.wide` 변형 ·
      **SPA 의 `RunAnalysis.svelte`**(v1.css 를 통해 같은 규칙을 쓴다).
   ⚠️ `position:relative; overflow:hidden` 은 **남긴다.** 장식이 아니라 레이아웃이다
      (안의 막대·래더가 모서리에서 잘려야 한다). ── */
.rr-panel { background:var(--c-s2); border:1px solid var(--line); border-radius:var(--radius-lg);
         position:relative; overflow:hidden; }
.metric-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.metric { position:relative; }
.metric-label { font-family:var(--body); font-size:.72rem; font-weight:var(--fw-label); letter-spacing:0;
                color:var(--muted); display:flex; align-items:center; gap:5px; }
.metric-value { font-family:var(--display); font-weight:700; font-size:1.8rem; letter-spacing:-.045em;
                color:var(--fg); font-variant-numeric:tabular-nums; line-height:1; margin-top:7px; }
.metric-unit { font-family:var(--body); font-size:.7rem; font-weight:var(--fw-label); color:var(--muted);
               letter-spacing:0; margin-top:2px; }
.metric-sub { font-size:.7rem; color:var(--muted); margin-top:3px; font-variant-numeric:tabular-nums; }
.hero-row { display:grid; grid-template-columns:1fr 1px 1fr 1px 1fr; padding:18px 6px;
            background:var(--c-s2); border:1px solid var(--line); border-radius:var(--r-lg); }
@supports not (backdrop-filter:blur(1px)) { .hero-row { background:rgb(var(--p-shadow-5-rgb) / .9); } }
.hero-divider { background:rgb(var(--c-ink) / .08); margin:4px 0; }
.hero-metric { text-align:center; padding:0 4px; }
.hero-label { font-family:var(--body); font-size:.72rem; font-weight:var(--fw-label); letter-spacing:0;
              color:var(--muted); }
.hero-value { font-family:var(--display); font-weight:700; font-size:2.05rem; letter-spacing:-.045em;
              color:var(--fg); font-variant-numeric:tabular-nums; line-height:1; margin-top:5px; }
.hero-metric.primary .hero-value { color:var(--data); }
.hero-unit { font-family:var(--body); font-size:.7rem; font-weight:var(--fw-label); color:var(--muted);
             letter-spacing:0; margin-top:3px; }

h1 { font-family:var(--display); font-weight:700; font-size:1.7rem; line-height:1.12;
     letter-spacing:-.03em; margin:.1em 0 .15em; }
h2 { font-family:var(--display); font-weight:700; font-size:1.12rem; letter-spacing:-.02em;
     margin:1.15em 0 .4em; }
h3 { font-family:var(--display); font-weight:700; }
p, li { color:var(--muted); line-height:1.62; }
/* ── 상단 고정 헤더 (브랜드 + 탭) ─────────────────────────────────────────
   모든 탭에서 스크롤해도 브랜드와 탭이 상단에 남는다.

   ★ 왜 두 개를 각각 sticky 로 두는가.
     `.brand` 는 `base.html` 이 찍고 `nav.tabs` 는 각 페이지의 `{% block body %}` 안에서
     `_nav.html` 로 들어온다 — **템플릿 경계가 둘 사이를 지나간다.** 하나의 래퍼로 감싸려면
     13개 페이지의 include 를 걷어내고 base 로 옮겨야 하는데, 탭이 없어야 하는 페이지
     (온보딩 단계·에러·공유 링크 등 17장)까지 플래그로 관리하게 된다. 그래서 각자 sticky 로
     두고 **오프셋으로 쌓는다.**

   ⚠️ 배경이 **불투명해야** 한다. 안 그러면 카드가 헤더 밑으로 지나가며 글자가 겹친다.
   ⚠️ `--brand-h` 는 탭이 브랜드 **아래**에 붙기 위한 오프셋이다. 어긋나면 그 틈으로
      내용이 지나간다(실측 사고: 기본값 62px vs 실제 54px → **9px 띠**가 생겼다).
      그래서 `base.html` 이 실측값을 이 변수에 넣는다 — CSS 기본값 54px 은 JS 가 못
      돌 때의 폴백이고, 둘 다 있어야 한다.
   ⚠️ `.wrap` 에 `overflow` 를 넣지 마라. 넣으면 sticky 가 조용히 죽는다(이 파일의
      `.wrap` 은 `position:relative` 만 있고 그게 의도다). */
/* SEED Root Top Navigation — 56px(android 규격) · 제목 t7 bold · bg.layer-default.
   아래 헤어라인은 SEED elevation "같은 층의 분리는 선으로"(Top Nav 가 스크롤 목록 위에 있을 때). */
.brand { font-family:var(--display); font-weight:700; letter-spacing:-.02em; font-size:var(--t7);
         display:flex; align-items:center; gap:8px; min-height:56px;
         position:sticky; top:0; z-index:20;
         /* 스크롤 전 여백을 margin 대신 padding 으로 — margin 은 고정된 뒤 사라져서
            글자가 화면 맨 위에 붙는다. PWA 표준알로 실행되면 노치까지 올라간다. */
         padding:calc(8px + env(safe-area-inset-top, 0px)) 0 8px;
         background:var(--bg); }
/* `.brand::after` 의 그라디언트 선을 **지웠다**(design/21 R1) — 워드마크 옆에서 화면 끝까지
   가던 2px 검정 띠는 아무것도 가리키지 않는 장식이었다. 헤더는 마크+이름으로 끝난다. */

/* ── 고정 띠의 배경은 **뷰포트 전폭**으로 흘려보낸다 ────────────────────────
   ★ 증상: 데스크톱에서 헤더와 탭 띠가 회색 그라디언트를 **직사각형으로 도려냈다.**
     둘 다 불투명 `--bg` 를 칠하는데 폭은 `.wrap` 의 480px 로 묶여 있고, 그 아래
     `body::before` 그라디언트는 `position:fixed` 라 뷰포트 전폭이다. 그래서 480px
     경계에서 흰 판이 각지게 끊겼다. 바로 아래 `.card` 는 radius 18px 이라 대비가 더 셌다.
     모바일에서는 띠 폭 == 화면 폭이라 **한 번도 보이지 않던** 결함이다(이 파일에
     폭 미디어 쿼리가 없는 이유이기도 하다).
   ★ 왜 radius 를 주지 않았나. 띠는 스크롤하면 화면 맨 위에 붙는다 — 그때 둥근 모서리는
     "떠 있는 카드"로 읽혀서 고정된 표면이라는 사실과 어긋난다. 경계를 다듬는 대신
     **경계를 없애는** 쪽이 이 레이아웃의 답이다.
   ⚠️ 배경을 투명하게 만드는 것은 답이 아니다 — 위 319행·445행의 사고 기록대로 카드가
      띠 밑으로 지나가며 글자가 겹친다. 여기서 바꾸는 것은 **칠하는 폭뿐**이다.
   ⚠️ 폭을 넓히는 데 **가짜 요소(`::before`)를 쓰지 않는다.** `left/right:-50vw` 로 늘린
      판은 레이아웃을 실제로 넘치게 만든다 — 실측으로 `scrollWidth - clientWidth = 247px`
      가 나왔다. `body` 의 `overflow-x:hidden` 이 가려 줄 뿐이라 "안 보이니 괜찮다"에
      기대는 처리가 되고, 그 위에 얹히는 `position:fixed` 요소나 다른 뷰포트 계산이
      조용히 어긋난다.
      `box-shadow` 의 spread 는 **레이아웃 밖**이라 넘침이 0 이다. 그래서 색을 그림자로
      칠하고, `clip-path` 로 위아래는 잘라 좌우로만 남긴다. */
.brand, .subtabs {
  box-shadow:0 0 0 100vmax var(--bg);
  clip-path:inset(0 -100vmax);
}
/* ★ 여기에 한 번 "띠 아래 12px 페이드"를 넣었다가 **뺐다.** 페이드는 가짜 요소라 폭이
   `.wrap` 의 480px 인데 위의 배경은 뷰포트 전폭이라, 480px 이 끝나는 자리에 **세로
   경계선 두 개**가 생겼다 — 없애려던 각진 경계를 방향만 바꿔 다시 만든 셈이다.
   그라디언트는 `box-shadow` 로 흘릴 수 없으니(spread 는 단색이다) 전폭 페이드를 이
   구조에서 만들려면 마크업이 필요하고, 그건 절단면 하나를 부드럽게 하자고 치르기엔
   비싼 값이다. 카드가 띠 밑에서 선명하게 끊기는 것은 원래 동작이고 문제 제기도 없었다.

   ⚠️ 탭 띠가 브랜드 **바로 아래**에 붙는지는 `--brand-h` 가 정한다. 그 값이 실측보다
      크면 그 차이가 그대로 **내용이 새는 띠**다(위 320행의 9px 사고, 그리고 서브픽셀
      반올림이 만든 0.5px 재발). `base.html` 이 내림으로 넘기고, 여기서 1px 더 당겨
      **겹치는 쪽으로만** 틀리게 만든다 — 겹침은 브랜드가 위에 그려져 보이지 않지만
      틈은 배경이 새어 가로 실선이 된다. 두 실패의 대가가 대칭이 아니다. */
/* (탭 띠는 하단으로 내려갔다 — design/24 D7. `--brand-h` 는 이제 `?focus=` 착지 오프셋만 쓴다.) */
/* 로고+서비스명 = 홈 링크. 색·굵기는 `.brand` 에서 상속받아 겉모습은 그대로 둔다. */
.brand-home { display:inline-flex; align-items:center; gap:8px; min-height:44px;
              color:inherit; text-decoration:none; font:inherit; letter-spacing:inherit; }
.brand-home:hover { opacity:.82; }

.brand-mark { flex:0 0 auto; width:1.15em; height:1.15em; display:inline-block; }
.brand-mark svg { width:100%; height:100%; display:block; }
/* 브랜드 마크가 인라인 SVG 에서 PNG/WebP 로 바뀌었다(brand_assets.py 산출).
   `svg` 규칙은 남겨 둔다 — 다른 화면이 아직 인라인 SVG 를 쓸 수 있다. */
.brand-mark img { width:100%; height:100%; display:block; object-fit:contain; }

/* ── 당겨서 새로고침 (PTR) ────────────────────────────────────────────────
   왜 필요한가: 홈 화면에 추가하면 standalone 으로 열려 **주소창과 새로고침 버튼이 함께
   사라진다.** 사용자가 늘 쓰던 탈출구(↻)가 설치하는 순간 증발한다 — 화면이 멈추거나
   낡은 값을 보고 있을 때 앱을 죽여 다시 켜는 것 말고는 방법이 없다. 그 탈출구를 우리가
   되돌려 준다.

   ⚠️ `overflow` 로는 절대 만들지 마라. 위 `.brand` 주석과 같은 함정이다 — 스크롤
      컨테이너가 새로 생기면 sticky 헤더가 조용히 죽는다(`.wrap` 에 overflow 금지가
      그 사고의 기록이다). 여기서 손대는 것은 `overscroll-behavior` 뿐이고, 그건
      스크롤 컨테이너를 만들지 않는다.
   ⚠️ 억제가 필요한 이유는 플랫폼이 갈리기 때문이다: **Android Chrome 은 네이티브
      PTR 을 제공하고 iOS 는 (브라우저든 standalone 이든) 하지 않는다.** 억제 없이
      커스텀을 얹으면 안드로이드에서 두 번 발화한다. 그리고 억제한 뒤에는 우리 PTR 이
      **모든 모드에서** 동작해야 한다 — standalone 조건을 걸면 안드로이드 일반
      브라우저에서 원래 있던 네이티브 PTR 만 사라지는 순수 퇴행이 된다.
   ⚠️ 인디케이터는 `.brand`(sticky, z-index:20) **위**로 띄운다. 아래에 두면 브랜드
      배경이 불투명해서(같은 주석) 당겨도 아무것도 보이지 않는다.
   ⚠️ 숨김은 `transform` 만으로 하지 않는다. 거리로 밀어낸 시트가 iOS 에서 닫힌 채로
      보인 사고가 있었다(띠 높이는 브라우저가 정하므로 고정 픽셀로는 못 맞춘다) —
      `visibility` 로 확실히 끊는다.
   ★ 랜딩(`body.lp`)은 이 억제에서 **빠진다** — `pages/landing.css` 가 되돌린다. 그 표면은
     `base_public.html`(이 파일의 형제 셸)을 쓰므로 `.ptr` 도 그 스크립트도 없고, 억제만
     남으면 얻는 것 없이 네이티브 PTR 을 잃는다. 그쪽 주석에 같은 판단을 남겨 뒀다. */
html { overscroll-behavior-y:contain; }
.ptr { position:fixed; left:50%; top:0; z-index:21; pointer-events:none;
       width:34px; height:34px; margin-left:-17px; border-radius:50%;
       display:grid; place-items:center;
       background:var(--card); border:1px solid var(--line); box-shadow:var(--shadow-float);
       transform:translateY(-46px); opacity:0; visibility:hidden; }
.ptr svg { width:19px; height:19px; display:block; color:var(--muted); }
/* 당기는 중 — JS 가 transform/opacity 를 직접 쓴다(손가락에 붙어야 하므로 transition 없음) */
.ptr.on { visibility:visible; }
.ptr.ready svg { color:var(--accent); }
/* 놓은 뒤 — 여기서만 애니메이션한다. reduced-motion 은 전역 규칙이 회전을 끄고,
   그때도 아이콘은 제자리에 보인다(동작이 일어났다는 신호가 사라지지 않는다). */
.ptr.spin { visibility:visible; opacity:1;
            transform:translateY(calc(12px + env(safe-area-inset-top,0px)));
            transition:transform .18s ease, opacity .18s ease; }
.ptr.spin svg { color:var(--accent); animation:ptr-spin .7s linear infinite; }
@keyframes ptr-spin { to { transform:rotate(360deg); } }

/* reusable page hero band (home / onboarding) */
/* ── 히어로는 **면**이다. 액센트로 채우지 않는다 (design/10 §4-1 티어 규칙 · taste-skill §4.4).
   원래는 액센트 그라디언트 채움이라 화면에서 가장 큰 덩어리가 액센트였다. 액센트가 액션을
   뜻하려면 액션에만 있어야 한다. 위계는 채움이 아니라 **활자 크기와 여백**이 만든다.
   ⚠️ 채움을 걷으면 "채움 위 글자"(--p-on-fill, 어두움)가 전부 안 보이게 된다 —
   C3 에서 완료 카드로 한 번 밟은 함정이다. 그래서 color 를 --fg 로 함께 되돌린다. ── */
/* ── 히어로는 탭바 **아래**에 온다 ──────────────────────────────────────────
   한때 `page-hero` 가 `_nav.html` 보다 **위**였다. 그러면 매일 쓰는 사람이 탭을 누르려고
   할 때마다 브랜드 문구 한 덩이를 지나쳐야 한다 — 문구는 처음 한 번만 정보이고 그 뒤로는
   순수한 내비게이션 비용이다. 순서를 뒤집어 탭이 먼저 오고 히어로가 그 아래로 갔다.
   문구 자체는 남긴다: 부상·도감의 `.hs` 는 브랜드 카피가 아니라 **그 화면이 무엇을
   해 주는지**를 설명한다("기록하면 주간 계획이 자동 조정돼요").

   ⚠️ 이 순서는 세 템플릿(`home`·`injury`·`codex`)이 각자 갖는다. 넷째로 `onboarding.html`
      에도 `.page-hero` 가 있지만 **거긴 탭바가 없다** — 그 화면에서는 이게 유일한 제목이라
      옮길 자리도 없다. 새 페이지에 히어로를 붙일 때 탭바가 있으면 반드시 그 **뒤**에 둔다.
   ⚠️ `.tabsbar` 는 `--brand-h` 기준 sticky 라 순서를 바꿔도 그대로 동작한다
      (주석에 `var(…)` 표기를 쓰지 마라 — `test_css_fill_contrast` 가 주석까지 스캔해서
       폴백 없는 참조를 미정의 토큰으로 잡는다. 실제로 이 줄에서 한 번 걸렸다.)
      (브랜드 헤더 아래에 붙는다). 여기 `margin-top` 을 건드려 간격을 맞추려 하지 마라 —
      탭바가 자기 `padding-bottom:10px` 을 이미 갖고 있다. ── */
.page-hero { margin-top:16px; border-radius:var(--r-xl); padding:20px; color:var(--fg); position:relative;
             overflow:hidden; box-shadow:var(--shadow); border:1px solid var(--line);
             background:var(--card);
             animation:rise var(--dur-slow) var(--ease) both; }
/* 등고선 텍스처(`::after` data-URI, mix-blend overlay)를 지웠다 — design/21 R1. 흰 면 위에서는
   보이지도 않았고, 보였을 때는 정보가 아니었다. */
.page-hero .hk { font-family:var(--body); font-weight:var(--fw-label); font-size:var(--fs-label); letter-spacing:0;
                 color:var(--muted); position:relative; z-index:1; }
.page-hero .ht { font-family:var(--display); font-weight:700; font-size:1.7rem; letter-spacing:-.03em;
                 margin-top:6px; line-height:1.06; position:relative; z-index:1; }
.page-hero .hs { font-size:.86rem; opacity:.93; margin-top:8px; line-height:1.5; position:relative; z-index:1; }

/* ── 버튼 3단 — 위계는 **채움**이 만든다, 테두리가 아니다 (design/21 R4 · 토스 버튼).
   기본(보조) = 회색 면 · primary = 잉크 면 · ghost = 투명+헤어라인(약한 동작).
   원래 기본이 테두리 알약이라 화면마다 윤곽선 버튼이 세 개씩 서 있었다. 그림자는 없다 —
   잉크 글로우(`--glow`, 검정 45%)는 흰 종이 위에서 버튼 밑의 얼룩이었다. */
/* ── SEED Action Button (design/24 S2) ──
   Large = 52px · r3 · bold. 변형: 기본 = neutralWeak(회색 면) · `.btn-primary` = neutralSolid(잉크) ·
   `.btn-brand` = **brandSolid** · `.btn-ghost` = neutralOutline.
   ★ 브랜드는 **화면의 주 행동 하나**다(SEED). 앱 화면에서는 그게 FAB(＋ 추가하기)이라 나머지 확정
     버튼은 잉크다 — `.btn-primary` 가 80곳이라 화면마다 주황이 서너 개씩 섰다(s1 캡처).
     **탭 셸 밖의 화면**(온보딩 단계·초대·위저드)은 그 화면의 `.btn-primary` 가 곧 주 행동이라 브랜드색이 된다.
     판정은 하단 탭의 존재(`body:has(#tabbar)`)다 — FAB 유무로 가르면 FAB 을 걷은 탭 화면(채팅·예상기록)의
     확정 버튼 네댓 개가 한꺼번에 브랜드색이 된다.
   pressed 는 **면만** 바뀌고 **2px 거리만큼** 줄어든다(SEED feedback/scale: scale = (b−2)/b,
   b = max(높이, 폭/4, 24)). 고정 비율이 아니라 거리라서 컴포넌트마다 값이 다르다 —
   풀폭 버튼(52×358 → b 89.5) .978 · 칩(40) .95 · FAB(48) .958 · 탭 아이콘(24) .917.
   hover 는 SEED 에 없다 — 모바일 앱 표면이라 pressed 가 피드백의 전부다. */
.btn { display:flex; align-items:center; justify-content:center; gap:8px; width:100%;
       min-height:52px; padding:14px 20px; border-radius:var(--r-md); border:1px solid transparent;
       background:var(--c-s2); color:var(--fg); font-size:var(--t5); line-height:var(--t5-lh);
       font-weight:700; font-family:var(--body);
       text-decoration:none; cursor:pointer; margin-top:10px; -webkit-tap-highlight-color:transparent;
       transition:transform var(--dur) var(--ease), background-color var(--dur) var(--ease),
                  border-color var(--dur) var(--ease); }
.btn:active { background:var(--c-s3); transform:scale(.978); }
.btn-primary, .btn-neutral { background:var(--accent); color:var(--on-accent); border-color:transparent; box-shadow:none; }
.btn-primary:active, .btn-neutral:active { background:var(--inverted); }
.btn-brand, body:not(:has(#tabbar)) .btn-primary { background:var(--brand); color:var(--on-brand); }
.btn-brand:active, body:not(:has(#tabbar)) .btn-primary:active { background:var(--brand-pressed); }
.btn-ghost { background:transparent; color:var(--fg); border-color:var(--stroke-weak); }
.btn-ghost:active { background:rgb(var(--c-ink) / .05); }
.btn:disabled, .btn[aria-disabled="true"] { background:var(--c-s2); color:var(--p-placeholder);
       border-color:transparent; cursor:not-allowed; transform:none; }
@media (hover:hover) { .btn:hover { filter:brightness(.97); } }
.row { display:flex; gap:8px; } .row .btn { flex:1; }
.muted { color:var(--muted); font-size:.9rem; }

.stat { display:flex; justify-content:space-between; align-items:baseline; gap:12px;
        padding:11px 0; border-bottom:1px solid var(--line); font-variant-numeric:tabular-nums; }
.stat:last-child { border-bottom:0; }
.stat > :first-child { color:var(--muted); font-size:.86rem; }
.stat > :last-child { font-family:var(--display); font-weight:700; letter-spacing:-.01em; text-align:right; }
form { margin:0; }
.opt { padding:16px 0; border-bottom:1px solid var(--line); }
.opt:last-child { border-bottom:0; }
.opt h3 { margin:0 0 4px; font-size:1rem; }
.opt .sub { color:var(--muted); font-size:.85rem; margin:0 0 10px; }

/* ── 존 경계 직접 조정(설정 · 내 훈련 존) ─────────────────────────────────────
   손잡이 4개짜리 밴드 슬라이더. 숫자 칸이 아니라 **밴드를 잡아 끄는** 물건이라야
   "내 역치는 여기서 시작한다"가 손에 남는다.
   밴드 색은 러닝 상세의 존 바(`pages/runs.css` 의 `.z-Z*`)와 **같은 팔레트 토큰**을 쓴다 —
   같은 Z4 가 화면마다 다른 색이면 두 화면이 남처럼 보인다. 클래스를 공유하지 않는 이유는
   `.z-Z*` 가 runs 페이지 전용 CSS 에만 있기 때문이다(설정 화면은 그 파일을 안 싣는다).
   가로축은 %HRmax 40~100 구간이다 — Z1 의 진짜 하한 0% 는 막대로 그릴 값이 아니다. */
.zedge { margin:14px 0 2px; }
.zedge-track { position:relative; height:44px; border-radius:12px;
               border:1px solid var(--line); background:var(--card2);
               touch-action:none; -webkit-user-select:none; user-select:none; }
.zedge-bands { position:absolute; inset:0; display:flex; border-radius:12px; overflow:hidden; }
.zedge-band { height:100%; min-width:0; overflow:hidden; display:flex;
              align-items:center; justify-content:center; color:var(--p-on-fill);
              font-family:var(--display); font-size:.6875rem; font-weight:700; letter-spacing:.04em; }
.zedge-band.zb-Z1 { background:var(--p-z1); }
.zedge-band.zb-Z2 { background:var(--p-accent); }
.zedge-band.zb-Z3 { background:var(--p-ok); }
.zedge-band.zb-Z4 { background:var(--p-warn); }
.zedge-band.zb-Z5 { background:var(--p-err); }
/* ⚠️ 손잡이를 트랙 **위**에 통통한 알약으로 얹으면 좁은 존이 통째로 사라진다. 실측:
   Z3 를 3%p(70~73%)로 좁혔더니 양옆 손잡이가 그 밴드를 완전히 덮어 화면에서 없어졌다.
   그래서 트랙 위에는 경계선만 긋고, 잡는 곳(동그란 그립)은 트랙 **아래로** 내린다. */
.zedge-thumb { position:absolute; top:0; left:0; width:3px; height:100%; padding:0; margin:0;
               transform:translateX(-50%); border:0; border-radius:0;
               background:var(--card); cursor:ew-resize; touch-action:none; }
/* 그립 안의 작은 세로 눈 — 없으면 빈 동그라미가 라디오 버튼처럼 읽힌다. */
.zedge-thumb::after { content:''; position:absolute; left:50%; top:100%; width:20px; height:20px;
                      transform:translate(-50%,-4px); border-radius:50%;
                      background:var(--card) linear-gradient(var(--muted),var(--muted)) center/2px 8px no-repeat;
                      border:1px solid var(--line); box-shadow:var(--shadow-float); }
/* 손가락 표적은 보이는 선보다 넓다 — 3px 을 정확히 짚으라고 요구할 수는 없다. */
.zedge-thumb::before { content:''; position:absolute; left:50%; top:0;
                       width:34px; height:calc(100% + 24px); transform:translateX(-50%); }
.zedge-thumb:focus-visible { outline:none; }
.zedge-thumb:focus-visible::after,
.zedge-thumb.drag::after { border-color:var(--accent); box-shadow:0 0 0 3px rgb(var(--p-accent-rgb) / .28); }
.zedge-thumb.drag { background:var(--accent); }
.zedge-scale { position:relative; height:15px; margin-top:22px; }
.zedge-scale span { position:absolute; top:0; transform:translateX(-50%); font-size:.6875rem;
                    color:var(--muted); font-variant-numeric:tabular-nums; white-space:nowrap; }
/* 양 끝 눈금은 가운데 정렬하면 카드 밖으로 잘린다(실측: "40%"·"100%" 가 반씩 날아갔다). */
.zedge-scale span:first-child { transform:none; }
.zedge-scale span:last-child { transform:translateX(-100%); }
/* SEED Text Input (Large) — 52px · pad-x 16 · r3 · 1px stroke.neutral-weak → 포커스 2px contrast */
.field { width:100%; min-height:52px; padding:13px 16px; border-radius:var(--r-md); border:1px solid var(--stroke-weak);
         background:var(--c-s0); color:var(--fg); font-size:var(--t5); font-family:var(--body);
         transition:border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.field::placeholder { color:var(--p-placeholder); }
/* 포커스 링은 얇게(28%→12%) — 굵은 검정 링은 '에러' 로 읽혔다. 테두리가 잉크가 되는 것으로 충분하다. */
.field:focus { outline:none; border-color:var(--fg); box-shadow:inset 0 0 0 1px var(--fg); }
input[type=file].field { padding:10px; }
/* 여러 줄 입력. 높이는 `ompbAutoGrow` 가 내용에 맞춰 잡으므로 손잡이는 없앤다 —
   손잡이를 남기면 사용자가 끌어 놓은 높이와 자동 높이가 매 입력마다 싸운다.
   ⚠️ `.grid2 .field { flex:1 }` 와 함께 쓰이므로 `align-items` 는 호출부에서 끝으로 내린다
      (기본 `center` 면 칸이 자랄 때 버튼이 가운데에 떠서 바닥선이 어긋난다). */
textarea.field { resize:none; line-height:1.5; overflow-y:hidden; display:block;
                 min-height:46px; }
/* SEED Callout — pad 14 · r2.5 · 테두리 없음 · t4 */
.toast { margin-top:12px; padding:14px; border-radius:var(--r-callout); font-size:var(--t4); line-height:1.5;
         display:none; border:1px solid transparent; }
.toast.show { display:block; }
.toast.okk { background:var(--c-positive_weak); color:var(--ok); }
.toast.err { background:var(--c-critical_weak); color:var(--err); }
/* SEED Snackbar — 설정 화면의 저장 결과. 제자리 콜아웃(`.toast`)은 **놓인 자리**에 뜬다 — 설정은 그 자리가
   페이지 맨 아래(로그아웃 밑)라, 위에서 목표를 저장하면 "저장됐어요"가 화면 밖에서 떴다가 4초 뒤 사라졌다.
   제보: "설정 탭에서 변경을 했을 때 변경이 완료되었음을 정확히 알 수 있도록." 그래서 설정만 **떠 있게** 한다
   (다른 화면의 `.toast` 는 버튼 옆에 놓여 있어 제자리가 맞다). FAB 위, 시트(70)보다 아래. */
.toast.snack { position:fixed; z-index:60; margin:0; left:16px; right:16px; max-width:448px; margin-inline:auto;
               bottom:calc(var(--fab-base) + 60px + env(safe-area-inset-bottom, 0px));
               display:flex; align-items:center; gap:10px; padding:13px 16px; border-radius:var(--r-md);
               box-shadow:var(--shadow-float); font-weight:700;
               opacity:0; visibility:hidden; transform:translateY(8px);
               transition:opacity .18s ease, transform .18s ease, visibility 0s linear .18s; }
.toast.snack.show { opacity:1; visibility:visible; transform:none; transition:opacity .18s ease, transform .18s ease; }
.toast.snack.okk { background:var(--inverted); color:#fff; }
.toast.snack.err { background:var(--err); color:#fff; }
.toast.snack .ic { color:#6FE3B4; font-size:1.15em; }
.toast.snack.err .ic { color:#fff; }
/* 누른 버튼 자리에서도 끝났다고 말한다 — 눈은 방금 누른 곳에 있다. */
.btn.saved-flash { background:var(--c-positive_weak) !important; color:var(--ok) !important; border-color:transparent !important; }
.grid2 { display:flex; gap:8px; align-items:center; } .grid2 .field { flex:1; min-width:0; }
.grid2 .btn { width:auto; flex:0 0 auto; margin-top:0; white-space:nowrap; }
button.btn { font-family:var(--body); }

/* ── SEED Bottom Navigation (design/24 D7) ──────────────────────────────────
   하단 고정 5탭. 아이콘 24 · 라벨 t1(11px, SEED: 내비는 폰트 스케일을 받지 않는다) ·
   선택 = fg.neutral / 비선택 = 3단 회색 · 위 0.5px 헤어라인 · 내부 폭 480px 제한.
   ⚠️ `--vv-lift` 를 뺀다 — 하단 고정 요소 전부의 계약(`test_fixed_bottom_recovery`):
      iOS 키보드가 layout viewport 를 줄인 채 굳으면 이 띠가 화면 중간에 뜬다.
   ⚠️ 배경은 불투명 `--bg`. 반투명이면 내용이 띠 밑으로 비친다(옛 상단 띠의 같은 사고).
   ⚠️ 바닥 예약은 `body:has(#tabbar) .wrap` — 띠가 있는 화면만. 예약과 노출이 **같은 요소**
      (띠의 존재)에서 갈라져 나온다(`has-addfab` 과 같은 규칙). */
:root { --tabbar-h:56px; }
.tabsbar { position:fixed; left:0; right:0; z-index:30;
           bottom:calc(0px - var(--vv-lift, 0px));
           background:var(--bg); border-top:1px solid var(--line);
           padding-bottom:env(safe-area-inset-bottom, 0px); }
nav.tabs { display:flex; align-items:stretch; max-width:480px; margin:0 auto; height:var(--tabbar-h); }
nav.tabs a { flex:1 1 0; min-width:0; position:relative;
             display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
             text-decoration:none; color:var(--muted-2); -webkit-tap-highlight-color:transparent;
             font-size:var(--t1); line-height:var(--t1-lh); font-weight:500; white-space:nowrap;
             transition:color var(--dur) var(--ease); }
nav.tabs a .ic svg { width:24px; height:24px; }
nav.tabs a .ic { transition:transform var(--dur) var(--ease); }
nav.tabs a:active .ic { transform:scale(.917); }
nav.tabs a.on { color:var(--fg); font-weight:700; }
body:has(#tabbar) .wrap { padding-bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 24px); }

/* ── SEED Notification Badge — 탭 아이콘 우상단에 붙는다(iconAttached inset 8/14 근사) ──
   ★ 숫자는 **큐**(처리해야 끝나는 항목)에만 붙인다. 사건형은 점이다 —
     "3건 남았다"는 처리할 수 있을 때만 참인 말이다(`badges.py` 의 kind 구분).
   ⚠️ 절대배치 기준은 `nav.tabs a`(relative). 옛 알약은 `overflow-x:auto` 라 배지가 잘렸지만
      지금 띠는 넘치지 않으므로 아이콘 위로 튀어나와도 된다. */
/* 면은 SEED 원문대로 brand-solid 다. carrot 때는 11px 흰 숫자가 2.9:1 이라 `--brand-text` 로 우회했지만
   코발트 위 흰 숫자는 5.2:1 — 우회가 필요 없다(design/24 D2). */
/* ★ 2026-09-28 — **빨강**(레드닷)이다. 요청: "기록·체크해야 할 것들을 레드닷으로 쌓아 놓고."
   코발트는 이 앱의 **주 행동 색**(버튼)이라, 할 일 표시까지 코발트면 "누르라는 버튼"과
   "남은 일"이 같은 색으로 읽힌다. 알림 점은 빨강이 관례이고 SEED red-800(`--err`) 위 흰 숫자는
   대비 기준을 넘는다. 헤더 벨의 점(`.todo-dot`)과 같은 면이다. */
.tab-badge { position:absolute; top:6px; left:calc(50% + 6px);
             border-radius:var(--r-pill); background:var(--err); color:var(--on-brand);
             font-size:var(--t1); font-weight:700; line-height:18px; text-align:center; }
.tab-badge.count { min-width:18px; height:18px; padding:0 4px; }
.tab-badge.dot { width:6px; height:6px; top:8px; left:calc(50% + 9px); }

/* ── SEED Tabs (line · fill) — 부모 탭 안의 두 화면(브리핑↔예상기록 · 설정↔부상) ──
   높이 44 · 라벨 t5 bold · 비선택 fg.neutral-subtle · 인디케이터 2px fg.neutral. */
.subtabs { display:flex; margin:0 -16px 4px; border-bottom:1px solid var(--line);
           position:sticky; top:calc(var(--brand-h, 56px) - 1px); z-index:18; background:var(--bg); }
.subtabs a { flex:1; min-height:44px; display:flex; align-items:center; justify-content:center;
             text-decoration:none; color:var(--muted-2); font-size:var(--t5); font-weight:700;
             box-shadow:inset 0 -2px 0 transparent; -webkit-tap-highlight-color:transparent;
             transition:color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.subtabs a.on { color:var(--fg); box-shadow:inset 0 -2px 0 var(--fg); }
.actions { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:8px; }
.action { display:block; padding:16px; border-radius:var(--r-lg); border:1px solid transparent; text-decoration:none;
          color:var(--fg); background:var(--c-s2); transition:background-color var(--dur) var(--ease), transform var(--dur-fast); }
.action:hover { background:var(--c-s3); }
.action:active { transform:scale(.985); }
.action .ic { font-size:1.5rem; } .action .t { font-family:var(--display); font-weight:700; margin-top:6px; }
.action .d { color:var(--muted); font-size:.82rem; margin-top:2px; }
/* ── 공용 칩 (`.wz-chip`) ──────────────────────────────────────────────────
   게스트 위저드에서 왔지만 이제 **공용**이다: 캡처 날짜 제안 · `/setup` 의 목표
   기록 프리셋 · 대회 선택 · 리듬 3택이 전부 이 하나를 쓴다.

   ★ 2026-09-25 까지 이 정의가 `pages/welcome.css` 에만 있었다. 그 파일을 부르는
     템플릿은 `welcome.html` **하나뿐**이라, `/setup` 의 칩 네 묶음이 전부
     `border:2px outset` · `radius:0` · 배경 `#efefef` · **폰트 Arial** 인 브라우저
     기본 버튼으로 그려지고 있었다(실측). 마크업도 클래스도 맞으니 문자열 테스트는
     전부 초록이었고, 스크린샷에서도 "테두리 있는 칩" 으로 잘못 읽힌다.
     → 공용으로 쓸 것은 공용 시트에 둔다. 페이지 시트는 그 페이지만의 것만.
        `tests/test_shared_class_reachability.py` 가 되돌아가는 것을 막는다.
   아래 `.cap-date-chips .wz-chip` 처럼 자리별 조정은 특이도로 덮는다(순서 무관). */
.wz-chips { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
/* SEED Chip (solid · Large 40) — 선택 = neutral-inverted. 브랜드색을 선택에 쓰지 않는다(design/24 D3). */
.wz-chip { font-family:var(--body); font-weight:500; font-size:var(--t4);
           min-height:40px; padding:0 14px; border-radius:var(--r-pill); border:1px solid transparent;
           background:rgb(var(--c-ink) / .05); color:var(--fg); cursor:pointer;
           display:inline-flex; align-items:center; justify-content:center; gap:4px;
           font-variant-numeric:tabular-nums; -webkit-tap-highlight-color:transparent;
           transition:background-color var(--dur) var(--ease), transform var(--dur) var(--ease); }
.wz-chip:active { background:rgb(var(--c-ink) / .08); transform:scale(.95); }
.wz-chip.on { background:var(--inverted); color:var(--on-accent); font-weight:700; }

/* 캡처 확인 — 날짜 미인식 제안 (실사용 결함: "입력 필요"만 띄우고 달력을 열게 했다).
   `.wz-chip` 은 게스트 위저드에서 온 공용 칩이라 여기서 재사용한다(새 칩을 만들지 않는다). */
.cap-date-ask { margin:12px 0 0; padding:12px 14px; border-radius:var(--r-md);
                background:rgb(var(--c-ink) / .04); border:1px solid var(--line);
                font-size:.86rem; line-height:1.5; color:var(--fg); }
.cap-date-chips { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }

.answer { white-space:pre-wrap; line-height:1.65; margin-top:12px; padding:16px 18px; border-radius:var(--r-lg);
          background:var(--card2); border:1px solid var(--line); }
/* ── inline icon system (de-AI: replaces decorative label/section emoji with a
   consistent lucide-style stroke set). Icons inherit color via currentColor and
   scale to 1em, so they sit inside text/labels like a glyph. JS builds them via
   window.ompbIcon(name); static templates inline the same <svg>. ── */
.ic { display:inline-flex; align-items:center; justify-content:center; vertical-align:-.14em; flex:0 0 auto; }
.ic svg { width:1em; height:1em; display:block; }
.ic.lg svg { width:1.35em; height:1.35em; }
.ic.xl svg { width:1.6em; height:1.6em; }
/* ── conversational kit — shared bubbles + chips (coach / run review / plan) ── */
.msg { margin-top:13px; max-width:92%; animation:rise .4s cubic-bezier(.2,.7,.2,1) both; }
.msg.coach { margin-right:auto; }
.msg.me { margin-left:auto; }
.msg .who { font-family:var(--body); font-weight:var(--fw-label); font-size:var(--fs-label); letter-spacing:0;
            color:var(--muted); margin-bottom:5px; }
.msg.coach .who { color:var(--accent-2); }
.bubble { padding:13px 15px; border-radius:var(--r-lg); line-height:1.64; white-space:pre-wrap; font-size:.94rem; }
/* 코치 말풍선의 좌측 3px 액센트 레일을 걷었다(design/21 R6) — 이 저장소가 세 번째로 같은 결론에
   도착한 자리다(`coach.css` 주제 카드, `runs.css` 강도 램프). 면과 헤어라인이면 누구 말인지 충분히 갈린다. */
.msg.coach .bubble { background:var(--c-s2); border:1px solid transparent;
                     border-top-left-radius:var(--r-sm); color:var(--fg); }
.msg.me .bubble { background:var(--accent); color:var(--p-on-fill); border-top-right-radius:var(--r-sm); font-weight:500; text-align:left; }
.chip-row { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px;
            animation:rise .4s .05s both cubic-bezier(.2,.7,.2,1); }
/* 칩은 회색 면이다(design/21 R7 · 토스 Chip). 액센트 틴트+테두리 칩은 화면마다 윤곽선을 여섯 개씩
   세웠다. 선택·주 동작은 잉크 채움이 맡는다. */
/* SEED Chip (solid · Large 40) — `.wz-chip` 과 같은 해부도. 두 이름은 호출부가 달라서 남는다. */
.qchip { background:rgb(var(--c-ink) / .05); border:1px solid transparent; color:var(--fg);
         border-radius:var(--r-pill); min-height:40px; padding:0 14px; font-size:var(--t4); font-weight:500; cursor:pointer;
         font-family:var(--body); letter-spacing:0; -webkit-tap-highlight-color:transparent;
         transition:background-color var(--dur) var(--ease), transform var(--dur) var(--ease), border-color var(--dur);
         /* render <a class="qchip"> identically to <button> (download links use anchors) */
         display:inline-flex; align-items:center; justify-content:center; text-decoration:none; }
.qchip:active { background:rgb(var(--c-ink) / .08); transform:scale(.95); }
/* outline 변형 — SEED Chip outline: 1px stroke.neutral-muted */
.qchip.ghost { background:transparent; border-color:var(--stroke-weak); color:var(--muted); min-width:40px; }
.qchip.ghost:active { background:rgb(var(--c-ink) / .05); }
.coach-typing { color:var(--muted); font-size:.9rem; margin-top:13px; }
.coach-typing::before { content:'▮'; color:var(--accent-2); margin-right:6px; animation:blink 1s steps(2) infinite; }
.spin { color:var(--muted); font-size:.9rem; margin-top:10px; }
.spin::before { content:'▮'; color:var(--accent-2); margin-right:6px; animation:blink 1s steps(2) infinite; }
@keyframes blink { 50%{opacity:.2} }
/* ── 코치 대기 — thinking-orbs 구체 + 대본 (`static/pages/coach_think.js`) ──────────────
   두 모양뿐이다. 인라인(20px): 대화 말풍선·버튼 옆처럼 **흐름 안**의 기다림. 블록(64px):
   카드 하나가 통째로 코치를 기다릴 때(런 리뷰·진단·캡처·오늘 세션).
   구체는 캔버스가 그리므로 색 토큰이 없다 — 글자만 SEED 토큰(fg · muted · t2/t4)을 쓴다.
   `.spin`·`.coach-typing` 의 깜빡이는 ▮ 는 구체가 대신하므로 같은 칸에 둘이 서지 않게 끈다.
   움직임 줄이기: 구체는 엔진이 정지 프레임을 그리고, 문구·경과는 그대로 바뀐다(정보라서). */
.orb { display:block; flex:none; }
.orb-20 { width:20px; height:20px; }
.orb-64 { width:64px; height:64px; }
.think.spin::before, .think.coach-typing::before { content:none; }
.think-inline { display:flex; flex-wrap:wrap; align-items:flex-start; gap:8px; }
.think-inline .orb { margin-top:1px; }
.think-inline .think-txt { flex:1 1 0; min-width:0; display:flex; flex-wrap:wrap; column-gap:6px; row-gap:2px;
                           align-items:baseline; }
.think-line { color:var(--fg); font-size:var(--t4); line-height:var(--t4-lh); overflow-wrap:anywhere; }
.think-meta { color:var(--muted); font-size:var(--t2); line-height:var(--t2-lh);
              font-variant-numeric:tabular-nums; white-space:nowrap; }
.think-block { display:flex; flex-direction:column; align-items:center; gap:10px;
               text-align:center; padding:14px 8px; }
.think-block .think-txt { display:flex; flex-direction:column; align-items:center; gap:4px; max-width:30em; }
.think-block .think-line { font-weight:600; }
.think-hint { display:block; margin-top:6px; font-size:var(--t2); }
.think-inline .think-hint { flex-basis:100%; margin-top:0; padding-left:28px; }
.orb-listen { display:inline-block; vertical-align:middle; flex:0 0 auto; align-self:center; }
.orb-listen[hidden] { display:none; }
/* 동기화 줄의 6px 점 자리에 서는 구체 — 줄 높이를 밀지 않게 위아래를 먹는다. */
.sync-orb { margin:-7px -4px; }
.ob-prog-head .sync-orb { align-self:center; margin:-4px -2px; }
.ob-prog-head .sync-orb + b { margin-right:auto; }
.dp-orb { display:inline-block; vertical-align:middle; margin:-4px 2px -4px 0; }
.dt-orb { display:inline-block; vertical-align:middle; margin:0 0 0 6px; }
/* SEED Notification Badge (Large) — 18px · brand-solid · t1 bold (코발트 위 흰 숫자 5.2:1 — `.tab-badge` 와 같은 면) */
.badge { display:none; min-width:18px; padding:0 4px; margin-left:4px; border-radius:999px;
         background:var(--brand); color:var(--on-brand); font-size:var(--t1); line-height:18px; text-align:center; font-weight:700; }
/* ── 러닝 추가 버튼 (우측 하단 플로팅) ────────────────────────────────────
   ★ 이 버튼은 세 번 이사했다: 좌하단 FAB → 헤더 우측 `＋ Run`(`design/17`) → **우하단**.
     ⚠️ 옛 `.syncfab` 은 **좌**하단이었다. 여기는 **우**하단이다 — 되돌린 것이 아니라 옮긴 것이고,
        오른손 엄지가 닿는 자리다(참고한 실물 앱들도 전부 우하단이다).
     헤더로 올렸을 때의 이유는 진짜였다. 그때 갚지 않고 넘어갔던 부채를 여기 적어 둔다 —
     되돌아왔으므로 **다시 갚아야 하는 것**이 무엇인지가 이 주석의 요점이다.

     (a) `position:fixed` 라 문서 흐름 밖이다 → 바닥을 비워 두지 않으면 끝까지 스크롤했을 때
         푸터를 덮는다(실물 캡처에서 "개인정보 처리방침 · 이용약관"이 잘려 보였다).
         예약은 바로 아래 `body.has-addfab .wrap`, 그리고 그 클래스는 버튼 마크업과 **같은
         `show_addbtn`** 에서 갈라져 나온다(`base.html`). 예전 사고는 예약(`has-fabs`)과
         노출(`show_fabs`)을 **따로** 판정해서 갈라진 것이었다 — 그 실수를 반복하지 마라.
     (b) iOS 홈 화면 PWA 에서 고정 요소가 화면 **중간**에 떴다(바닥 +325px = 키보드 291 +
         safe-area 34). 처방은 `base.html` 에 두 겹으로 서 있다 —
         `interactive-widget=resizes-visual` 선언 + 시트를 감추기 전 `ompbBlurWithin`.

   그래도 되돌린 이유. 헤더로 올린 대가로 **기록을 넣는 문이 상단 구석의 작은 알약 하나**가
   됐다. 기록 입력은 이 앱의 첫 행동이자 가장 잦은 행동이고, 모바일 전용 화면에서 그 자리는
   엄지에서 가장 먼 곳이다. 라벨과 함께 엄지 아래로 내린다.

   ⚠️ `right` 는 뷰포트 오른쪽이 아니라 **컨텐츠 컬럼(`.wrap`, max 480px)의 오른쪽 변**이다.
      데스크톱에서 뷰포트 기준으로 두면 버튼만 화면 구석에 홀로 떨어져 컬럼과 무관해 보인다.
      `body` 의 좌우 패딩이 16px 이라 좁은 화면에서는 그대로 16px 로 수렴한다.
   ⚠️ z-index 는 시트(`.add-back` 70 · `.add-sheet` 71)보다 **낮다.** 시트가 열리면 백드롭이
      덮어 주므로 버튼을 따로 숨기는 규칙(`sheet-open`)이 필요 없다 — 되살리지 마라. */
/* ★ SEED Floating Action Button (extended) — 화면의 주 행동 하나라 **brand-solid**, 색 변경 불가.
     위치: 우하단, 하단 고정 요소(하단 탭)에서 위로 띄운다. `--fab-base` 가 그 바닥이다 —
     탭이 있는 화면은 탭 위, 없는 화면은 16px(`body:has(#tabbar)` 가 가른다). */
:root { --fab-base:16px; }
body:has(#tabbar) { --fab-base:calc(var(--tabbar-h) + 16px); }
.add-fab { position:fixed; z-index:40;
           right:max(16px, calc(50vw - 240px));
           bottom:calc(var(--fab-base) + env(safe-area-inset-bottom, 0px) - var(--vv-lift, 0px));
           display:none; align-items:center; gap:4px;
           height:48px; padding:0 18px 0 14px; border:0; cursor:pointer;
           border-radius:var(--r-pill); background:var(--brand); color:var(--on-brand);
           box-shadow:var(--shadow-float); -webkit-tap-highlight-color:transparent;
           transition:transform var(--dur) var(--ease), background-color var(--dur) var(--ease),
                      opacity .18s ease, visibility 0s linear 0s; }
.add-fab.show { display:inline-flex; }
.add-fab:active { background:var(--brand-pressed); transform:scale(.958); }
.add-fab svg { width:20px; height:20px; flex:0 0 auto; }
/* ★ 라벨 서체는 `--body`(Pretendard)다. 예전 `Run` 은 `--display`(Archivo 900)로 그렸는데
   **Archivo 에는 한글 글리프가 없다** — 한국어 라벨에 그대로 두면 브라우저가 조용히 폴백해서
   의도한 위계는 사라지고 자간 설정만 남아 어긋난다. 위계는 이제 서체가 아니라 **자리와
   크기**(바닥에 뜬 48px 알약)가 만든다. */
.add-fab-t { font-family:var(--body); font-weight:700; font-size:var(--t5);
             letter-spacing:0; line-height:1; }
/* 비동기 캡처 진행을 버튼에 공유: 처리 중이면 아이콘 회전(클릭은 유지), 배지로 대기/실패 수 */
.add-fab.working svg { animation:spin .9s linear infinite; }
.add-fab .badge { position:absolute; top:-6px; right:-6px; margin:0;
                  background:var(--p-on-fill); color:var(--brand-text);
                  border:2px solid var(--brand); font-weight:700; }
.add-fab .badge.warn { color:var(--warn); border-color:var(--warn); }
@keyframes spin { to { transform:rotate(360deg); } }

/* ── 시트가 떠 있는 동안 버튼은 그 밑으로 내려간다 ─────────────────────────
   ★ z-index 만으로는 부족하다. 버튼(40)은 이미 모든 시트(`.db-sheet-ov` 50 · `.add-back` 70 ·
     `.sheet-backdrop` 80)보다 아래에 있는데, 백드롭이 **55~62% 불투명**이라 그 밑에서 브랜드색
     알약이 그대로 비친다. 그리고 버튼이 우하단으로 내려온 지금 그 자리는 **시트의 윗변과
     맞붙는 자리**다 — 시트 모서리에 알약이 반쯤 걸쳐 보인다.
   ★ 예전에도 같은 이유로 하단 FAB 을 감추는 body 클래스가 있었고, `design/17` 이 버튼을
     헤더로 올리면서 걷어냈다("시트 모서리와 맞붙어 보여서 숨겨야 했던 것이고, 그 이유는
     위치와 함께 사라졌다"). 위치가 돌아왔으니 이유도 돌아왔다.
   ⚠️ 그때의 부채는 **감추는 규칙**이 아니라 **손으로 하는 배선**이었다: 5개 템플릿에
     `classList.add('sheet-open')` 이 8곳. 하나를 빠뜨려도 아무 테스트가 안 깨지고, 그
     화면을 열어 본 사람만 안다. 그래서 이번에는 클래스를 **손으로 붙이지 않는다** —
     `base.html` 의 관찰자가 "화면을 거의 다 덮는 `position:fixed` 요소가 보이는가" 를
     **모양으로** 판정해 `body.overlay-open` 을 켠다. 시트 이름이 어디에도 안 적히므로
     새 시트가 생겨도 고칠 곳이 없다.
   ⚠️ `visibility` 까지 끈다. 투명하기만 하면 시트 위의 빈 자리를 눌렀을 때 **버튼이 먹는다**
      (백드롭이 `pointer-events` 를 받지만 버튼은 그 위 문맥이 아니라 형제다). */
body.overlay-open .add-fab { opacity:0; visibility:hidden; pointer-events:none;
                             transform:translateY(10px) scale(.92);
                             transition:transform .18s ease, opacity .18s ease,
                                        visibility 0s linear .18s; }

/* ★ 바닥 예약. 이것이 (a) 부채의 본체다 — 없으면 끝까지 스크롤했을 때 알약이 푸터 링크를
   덮는다. 노출 조건과 **한 플래그**에서 갈라져야 한다(`base.html` 의 `show_addbtn`).
   값의 근거: 버튼 바닥 오프셋 16 + 버튼 높이 48 = 64 를 넘겨야 겹치지 않는다. `body` 가
   이미 바닥에 `max(28px, safe-area)` 를 두고 있으므로 여기서는 그 위로 60px 만 더 얹는다. */
body.has-addfab .wrap { padding-bottom:calc(60px + env(safe-area-inset-bottom, 0px)); }
/* 하단 탭이 있으면 탭 + FAB 두 층을 비운다(탭 56 + 틈 16 + FAB 48 + 여유 16). */
body.has-addfab:has(#tabbar) .wrap { padding-bottom:calc(var(--tabbar-h) + 80px + env(safe-area-inset-bottom, 0px)); }


/* ── 확인이 필요한 것: 요약 목록 + 항목 칩 (`design/17`) ────────────────────
   ★ 탭 배지는 러너를 화면까지 데려다 놓고 2.4초 강조한다. 카드가 서너 장이면 그걸로
     충분한데 설정은 **열네 장**이다 — 링이 사라지는 순간 다시 미아가 된다.
     그래서 두 층으로 말한다: 위에 **목록**(무엇을), 각 카드에 **칩**(여기).
   ⚠️ 칩은 `focus-pulse` 와 **다른 수명**이다. 링은 얹혔다 사라지고(도착 신호), 칩은
      그 일이 끝날 때까지 남는다(상태 표시). 하나로 합치면 둘 중 하나를 잃는다. */
.pending-sum { border-color:var(--accent); background:rgb(var(--p-accent-rgb) / .05); }
.ps-head { display:flex; align-items:center; gap:9px; margin-bottom:11px; }
/* 개수를 **먼저** 보여 준다 — "몇 개나 남았지"가 이 카드를 여는 첫 질문이다. */
.ps-n { flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
        min-width:24px; height:24px; padding:0 7px; border-radius:var(--r-pill);
        background:var(--err); color:var(--on-brand);   /* 남은 일 = 빨강(탭 점·벨·칩과 한 신호) */
        font-family:var(--display); font-weight:700; font-size:.82rem; line-height:1; }
.ps-t { margin:0; font-size:1rem; }
.ps-list { list-style:none; margin:0; padding:0; }
.ps-list li + li { border-top:1px solid var(--line); }
/* 행 전체가 링크다 — 화살표만 누를 수 있으면 손가락이 자주 빗나간다(모바일 전용 화면). */
.ps-row { display:flex; align-items:center; gap:8px; padding:11px 2px;
          text-decoration:none; color:var(--fg); }
.ps-row:hover { color:var(--accent-2); }
.ps-row:active { opacity:.7; }
.ps-l { flex:1 1 auto; font-weight:700; font-size:.92rem; letter-spacing:-.01em; }
.ps-c { flex:0 0 auto; color:var(--muted); font-size:.8rem; }
.ps-go { flex:0 0 auto; color:var(--muted); font-size:.95rem; }
/* ★ **끝내는 방법**을 한 줄로 말한다. "하러 가기" 만 있으면, 그 일을 할 생각이 없는
   러너에게는 영원히 안 끝나는 할 일이 된다(제보로 확인). 작게 두되 숨기지는 않는다. */
.ps-how { display:flex; align-items:center; flex-wrap:wrap; gap:6px;
          margin:-4px 0 10px; padding-left:2px;
          color:var(--muted); font-size:.76rem; line-height:1.4; }
.ps-skip { background:none; border:1px solid var(--line); border-radius:999px;
           padding:3px 9px; cursor:pointer; color:var(--muted);
           font-family:var(--body); font-size:.74rem; font-weight:700; }
.ps-skip:hover { color:var(--fg); border-color:var(--accent-2); }
.ps-skip:disabled { opacity:.5; cursor:default; }

/* 항목 칩 — 그 일이 끝날 때까지 카드 제목 옆에 남는다.
   ⚠️ `<i>` 라 기본 기울임이 붙는다. 라벨이라 정체를 세운다. */
/* ★ 2026-09-28 — 잉크 → **빨강**. 탭 점·벨·버튼 위 할 일 표시(`.todo-flag`)와 같은 색이어야 "남은 일"이
   한 가지 신호로 읽힌다. 잉크 칩은 선택 칩·버튼과 같은 색이라 할 일로 안 보였다. */
.pending-chip { font-style:normal; display:inline-block; vertical-align:middle;
                margin-left:6px; padding:2px 8px; border-radius:999px;
                background:var(--err); color:var(--on-brand);
                font-family:var(--body); font-weight:700; font-size:.6875rem;
                letter-spacing:0; line-height:1.5; }

/* ── 할 일 표시: 대상 카드 **바로 위** 한 줄 + 그 안의 주 버튼에 레드닷 (`base.html` 의 할 일 스크립트) ──
   탭 점(`.tab-badge`)·벨 점(`.todo-dot`)과 같은 빨강. 문구는 서버 라벨("오늘 뛴 런 리뷰 받기") 그대로다 —
   점만 있으면 "무언가 있다"까지만 말하고, 무엇을 해야 하는지는 여전히 추측이다. */
.todo-flag { display:flex; align-items:center; gap:7px; margin:14px 2px 8px;
             color:var(--err); font-size:var(--t4); font-weight:700; line-height:1.35; letter-spacing:-.01em; }
.todo-flag-dot { flex:0 0 auto; width:8px; height:8px; border-radius:50%; background:var(--err);
                 box-shadow:0 0 0 3px rgb(var(--p-err-rgb) / .18); }
.todo-btn { position:relative; }
.todo-btn::after { content:''; position:absolute; top:-4px; right:-4px; width:12px; height:12px;
                   border-radius:50%; background:var(--err); box-shadow:0 0 0 2px var(--bg);
                   pointer-events:none; }
.subtabs a .sub-dot { display:inline-block; width:6px; height:6px; margin-left:5px; border-radius:50%;
                      background:var(--err); vertical-align:top; transform:translateY(-2px); }
@media (prefers-reduced-motion: no-preference) {
  .todo-flag-dot { animation:todoPulse 2.4s ease-in-out 2; }
}
@keyframes todoPulse { 50% { box-shadow:0 0 0 6px rgb(var(--p-err-rgb) / .08); } }

/* ── 배지가 데려다 놓은 컴포넌트의 강조 (`?focus=`, `design/17`) ────────────
   ★ 색을 칠하지 않고 **테두리 링**을 두른다. 카드 배경을 바꾸면 그 카드의 원래 상태
     (경고 배너·비활성 등)를 덮어써서, 강조가 끝난 뒤에도 무엇이 원래 색이었는지 알 수
     없게 된다. 링은 얹혔다 사라진다.
   ⚠️ `outline` 을 쓴다(`border` 아님) — 레이아웃을 밀지 않는다. 두께가 요소 폭에
      더해지면 강조가 켜지는 순간 카드가 흔들리고, 그건 "찾았다"가 아니라 "깨졌다"로 읽힌다.
   ⚠️ `prefers-reduced-motion` 을 존중한다. 깜빡임은 전정기관 민감군에게 실제 증상을 만든다 —
      그때는 링을 **켜 둔 채** 두는 것으로 충분하다(목적은 "여기다"를 말하는 것이다). */
.focus-pulse { outline:2px solid var(--accent); outline-offset:3px; border-radius:14px;
               animation:focus-pulse 1.2s ease-out 2; }
@keyframes focus-pulse {
  0%   { outline-color:var(--accent); }
  55%  { outline-color:transparent; }
  100% { outline-color:var(--accent); }
}
@media (prefers-reduced-motion: reduce) {
  .focus-pulse { animation:none; }
}

/* ── 러닝 추가 ＋ 첫 안내 ──────────────────────────────────────────────────
   버튼 **바로 위**에 떠서 아래쪽 화살표로 그 버튼을 가리키는 말풍선.

   ⚠️ 버튼과 **오른쪽 변을 공유한다** — 둘 다 컨텐츠 컬럼의 오른쪽 끝에 붙는다. 그래야
      화살표의 가로 위치를 "말풍선 오른쪽에서 버튼 중심까지" 하나로 말할 수 있고, 그 값이
      곧 버튼 폭의 절반이다.
   ⚠️ 화살표의 가로 위치를 상수로 박지 마라. 버튼 폭은 라벨·웹폰트·줌에 따라 변하고,
      상수면 라벨을 한 글자만 고쳐도 안내가 아무것도 아닌 곳을 가리킨다 — 마크업은
      멀쩡해 보이므로 아무도 안 잡는다. `base.html` 의 ResizeObserver 가 실측해
      `--addbtn-c`(버튼 폭의 절반)로 넘긴다. 폴백 60px 은 그 스크립트가 못 돌 때의 값이다.
   ⚠️ 배경이 **불투명**이어야 한다. 예전엔 흐름 안에 살아서 반투명(8%)으로 충분했는데,
      이제는 카드 위를 지나므로 반투명이면 밑의 글자가 비쳐 읽히지 않는다.
   ⚠️ 세로 위치는 버튼 위 76px(= 버튼 바닥 16 + 높이 48 + 틈 12)이다. 버튼 높이를 바꾸면
      여기도 함께 바꿔야 한다 — `jinja.header-add.spec.mjs` 가 그 틈을 잰다. */
/* SEED Callout (brand-weak) — 흐름 안의 첫 안내. 떠 있지 않으므로 아무것도 덮지 않는다(base.html ★). */
.addhint { position:relative; margin:4px 0 12px; padding:12px 44px 4px 14px;
           border-radius:var(--r-callout); background:var(--brand-weak); color:var(--fg); }
/* 두 줄 Callout — 모든 화면 첫 줄에 뜨므로 길면 그 화면의 주인공을 민다(s2 캡처: 세 줄 120px).
   설명 문장은 접고(제목이 이미 자리를 말한다) 행동 둘은 **텍스트 버튼**으로 낮춘다. */
.addhint .addhint-d { display:none; }
.addhint .addhint-row { margin:2px 0 0 -8px; gap:0; }
.addhint-t { font-family:var(--body); font-weight:700; font-size:var(--t4); line-height:var(--t4-lh);
             letter-spacing:0; margin:0; color:var(--fg); }
.addhint-t b { color:var(--brand-text); }
.addhint-d { color:var(--muted); font-size:var(--t4); line-height:1.45; margin:4px 0 0; }
.addhint-row { display:flex; gap:8px; margin-top:12px; }
.addhint-go, .addhint-ok { border-radius:var(--r-sm); min-height:40px; padding:0 16px; cursor:pointer;
                           font-family:var(--body); font-size:var(--t4); font-weight:700; border:0;
                           letter-spacing:0; transition:transform var(--dur), background-color var(--dur); }
.addhint-go { background:transparent; color:var(--brand-text); padding:0 8px; }
.addhint-ok { background:transparent; color:var(--muted); padding:0 8px; }
.addhint-go:active, .addhint-ok:active { transform:scale(.96); }
.addhint-x { position:absolute; top:4px; right:4px; width:40px; height:40px;
             display:inline-flex; align-items:center; justify-content:center;
             background:none; border:0; cursor:pointer; color:var(--muted);
             font-size:.9rem; line-height:1; border-radius:var(--r-sm); }
/* ── 러닝 추가 시트: 이미지 입력(=앞으로 기본, 베타) vs Strava 동기화(=종료 예정) ── */
/* SEED bg.overlay = static-black-alpha-700(≈45%). 흐림은 SEED 에 없다 — 층은 어둠 하나로 충분하다. */
.add-back { position:fixed; inset:0; bottom:calc(0px - var(--vv-lift, 0px)); z-index:70; background:rgb(0 0 0 / .45);
            opacity:0; pointer-events:none; transition:opacity .2s var(--ease); }
.add-back.show { opacity:1; pointer-events:auto; }
/* ★ 닫힌 시트는 `transform` 으로만 밀어내면 안 된다 — `visibility` 로 함께 잠근다.
   iOS Chrome 실물에서 **닫힌 시트의 머리(그랩 핸들 + "러닝 추가" + ✕)가 화면 바닥에
   그대로 떠 있었다.** 원인은 시트가 아니라 `position:fixed` 의 기준선이다: 스크롤로
   하단 툴바가 숨은 iOS Chrome 은 fixed 요소의 바닥을 **실제 화면 바닥보다 위**에 잡는다
   (같은 화면에서 두 FAB 도 나란히 그만큼 떠 있었다 — 같은 원인의 다른 증상이다).
   그 아래로 생긴 빈 띠는 여전히 사용자에게 보이는 픽셀인데, `translateY(102%)` 가
   비켜 주는 거리는 시트 높이의 2% 뿐이라 머리가 띠 안에 남는다.
   숨기는 거리를 늘리는 것은 고침이 아니다 — 띠의 높이는 브라우저가 정하고 우리는 모른다.
   `visibility:hidden` 은 뷰포트 계산과 무관하게 결정적으로 그리지 않는다(닫힌 시트 안의
   버튼이 탭 포커스를 먹던 것도 같이 사라진다). 미끄러지는 연출은 그대로다: 열 때는 즉시
   `visible`, 닫을 때는 슬라이드가 끝난 뒤에 `hidden` 이 되도록 지연을 준다.
   ⚠️ 그래서 게이트는 **슬라이드 뒤**다 — 닫는 탭 직후 260ms 동안은 여전히 그려져 있고
   눌린다. 연출을 지키는 대가이고, 그 창에서의 오탭은 이 변경 전에도 있었다.
   ⚠️ 열림 규칙에서 지연만 0 으로 돌린다(`transition-delay`). transition 을 다시 적으면
   시간·이징이 파일마다 두 벌이 되고, 한쪽만 고쳐도 아무 테스트가 안 깨진다.

   ── 추기: 같은 겉모습에 **기전이 둘**이었다 ──────────────────────────────
   위 진단(iOS Chrome 의 하단 툴바)은 그 사고에 대해서는 맞다. 그런데 뒤이어 **iOS 홈
   화면 PWA(standalone)** 에서 같은 증상이 다시 나왔다 — 거기엔 숨을 툴바가 애초에 없다.
   실측이 갈랐다: FAB 하단이 화면 바닥에서 **325px** 위였고, 이는 아이폰 세로 키보드
   291 + `env(safe-area-inset-bottom)` 34 와 정확히 일치한다(툴바는 49 다).
   즉 두 번째 기전은 **키보드가 layout viewport 를 줄여 놓고 되돌리지 않는 것**이다.
   ★ 그래서 처방도 여기(CSS)가 아니다. `base.html` 이 두 겹으로 막는다 —
     ① `viewport` 메타의 `interactive-widget=resizes-visual` 선언,
     ② 시트를 감추기 전에 포커스를 내리는 `window.ompbBlurWithin`(포커스를 쥔 입력창이
        그대로 사라지는 것이 우리가 만들던 트리거였다).
   ★ "함께 떠 있던 FAB 은 여기서 안 고친다"던 그 청구서가 이걸로 닫혔다. **FAB 의 위치
     규칙(이 파일 558~606)은 한 줄도 안 바꿨다** — 값은 옳았고 틀린 것은 브라우저가 재던
     기준선이다. 아래 `visibility` 우회는 **떼지 마라**: 기전이 둘이므로 방어선도 둘이다.

   ── 추기 2 (2026-08-31): 예방 둘로도 새는 경로가 실물로 또 나왔다 ─────────────
   /predict 화면(입력창이 없는 화면!)에서 FAB 이 바닥에서 ~325px 위에 떠 있는 스크린샷.
   ①·② 는 **우리가 만드는 트리거**만 없앤다 — 입력에 포커스를 둔 채 페이지를 떠나는 등
   우리가 못 막는 경로가 남는다. 그래서 셋째 방어선은 **회복**이다: `base.html` 의
   visualViewport 감시가, 진짜 보이는 바닥이 layout viewport 바닥보다 아래면 그 차이를
   `--vv-lift` 로 문서에 적고, 하단 고정 4종(.add-fab · .tabsbar · .add-sheet · .add-back — 2026-09-28
   첫 안내가 흐름 안으로 들어가고 하단 탭이 들어왔다)
   의 bottom 이 그만큼 내려간다. 정상 상태에선 변수 자체가 없다(폴백 0px = 현행 그대로). */
.add-sheet { position:fixed; z-index:71; left:0; right:0; bottom:calc(0px - var(--vv-lift, 0px));
             margin:0 auto; width:100%; max-width:480px;
             /* SEED Bottom Sheet — layer-floating 면 · 윗모서리 r6(24) · 본문 좌우 전역 거터 16 ·
                최대 높이 90vh · 등장 d6 enter-expressive / 퇴장 d4 exit. 층은 그림자 대신 백드롭이 만든다.
                ⚠️ `visibility` 게이트는 그대로다(아래 ★ — 밀어낸 것은 숨은 것이 아니다). */
             background:var(--c-s0); border:0; border-radius:var(--r-sheet) var(--r-sheet) 0 0;
             padding:6px 16px calc(16px + env(safe-area-inset-bottom)); max-height:90vh; overflow-y:auto;
             transform:translateY(102%); visibility:hidden;
             transition:transform .2s var(--ease-exit), visibility 0s linear .2s;
             box-shadow:none; }
.add-sheet.show { transform:none; visibility:visible; transition:transform .3s var(--ease-enter-x), visibility 0s linear 0s; }
.add-grab { width:36px; height:4px; border-radius:99px; background:var(--stroke-weak); margin:0 auto 14px; }
.add-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; }
.add-head h2 { margin:0; font-size:var(--t7); line-height:var(--t7-lh); font-family:var(--display); font-weight:700; }
.add-x { background:transparent; border:0; color:var(--muted); font-size:1.25rem; cursor:pointer; line-height:1;
         min-width:40px; min-height:40px; margin-right:-10px; border-radius:var(--r-sm); }
.add-x:hover { color:var(--fg); }
.add-card { display:block; width:100%; text-align:left; cursor:pointer; border-radius:16px; padding:15px 16px;
            margin-bottom:11px; transition:transform .14s ease, border-color .14s ease, box-shadow .2s ease; }
.add-card:active { transform:scale(.99); }
.add-card.primary { background:linear-gradient(180deg,rgb(var(--p-accent-rgb) / .14),rgb(var(--p-accent-rgb) / .05));
                    border:1.5px solid rgb(var(--p-accent-rgb) / .55); box-shadow:0 12px 30px -16px var(--glow); }
.add-card.primary:hover { border-color:var(--accent); box-shadow:0 16px 36px -14px var(--glow); }
.add-card.legacy { background:transparent; border:1px solid var(--line); opacity:.82; }
.add-card.legacy:hover { opacity:1; border-color:var(--accent-2); }
.add-card .t { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
               font-family:var(--display); font-weight:700; font-size:1.02rem; color:var(--fg); }
.add-card .d { color:var(--muted); font-size:.84rem; margin-top:5px; line-height:1.5; }
.add-card.primary .d { color:var(--p-accent-soft); }
.add-pill { font-family:var(--display); font-weight:700; font-size:.6875rem; letter-spacing:.04em;
            text-transform:uppercase; padding:2px 7px; border-radius:999px; }
.add-pill.beta { background:var(--accent); color:var(--p-on-fill); }
.add-pill.sunset { background:rgb(var(--c-ink) / .08); color:var(--muted); border:1px solid var(--line); }
.cap-step { display:none; } .cap-step.on { display:block; }
/* 런 슬롯 — 한 슬롯 = 한 런(최대 3). 채워진 슬롯은 점선→실선으로 바뀌어 "이건 런 하나"가
   눈으로 읽힌다. 저장 대상이 몇 개인지 세는 것이 이 화면의 유일한 판단이라 카운트(.sh .c)와
   시작 버튼 라벨에 같은 숫자를 둘 다 낸다. */
.cap-slot { border:1.5px dashed var(--line); border-radius:14px; padding:12px 13px 11px; margin-bottom:9px;
            transition:border-color .16s ease, background .16s ease; }
.cap-slot.filled { border-style:solid; border-color:var(--accent); background:rgb(var(--p-accent-rgb) / .05); }
.cap-slot .sh { display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.cap-slot .sh .n { font-family:var(--display); font-weight:700; font-size:.9rem; }
.cap-slot .sh .c { font-size:.74rem; color:var(--muted); font-variant-numeric:tabular-nums; }
.cap-slot .sfiles { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.cap-slot .sfiles:empty { margin-top:0; }
.cap-slot .sfile { display:inline-flex; align-items:center; gap:4px; max-width:100%; font-size:.74rem;
                   color:var(--muted); background:rgb(var(--c-ink) / .05); border:1px solid var(--line);
                   border-radius:999px; padding:2px 4px 2px 9px; }
.cap-slot .sfile .nm { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:11rem; }
.cap-slot .sfile .x { border:0; background:transparent; color:var(--muted); cursor:pointer;
                      font-size:.95rem; line-height:1; padding:0 3px; }
.cap-slot .sfile .x:hover { color:var(--warn); }
.cap-slot .spick { display:flex; align-items:center; justify-content:center; gap:6px; width:100%;
                   margin-top:9px; padding:8px 10px; border:1px solid var(--line); border-radius:10px;
                   background:var(--card); color:var(--fg); font-size:.84rem; cursor:pointer;
                   transition:border-color .16s ease, color .16s ease; }
.cap-slot .spick:hover { border-color:var(--accent-2); color:var(--accent-2); }
/* ⚠️ 아이콘 규격은 `.ic` 에 걸려 있다(`.ic svg { width:1em }`). width/height 없는 인라인 SVG 는
   컨테이너를 꽉 채우므로, 규격 클래스를 빠뜨리면 카메라 아이콘 하나가 시트 절반을 먹는다 —
   예전 드롭존에서 `.ico` 오타로 실제로 일어났던 일이다. */
.cap-slot .spick .ic svg { width:1em; height:1em; display:block; }
.cap-slot-add { display:block; width:100%; padding:9px 10px; margin-bottom:2px; border:1px dashed var(--line);
                border-radius:10px; background:transparent; color:var(--muted); font-size:.82rem; cursor:pointer;
                transition:border-color .16s ease, color .16s ease; }
.cap-slot-add:hover { border-color:var(--accent-2); color:var(--accent-2); }
.cap-loading { display:none; text-align:center; padding:26px 14px;
               border:1.5px solid var(--line); border-radius:14px; }
.cap-loading.on { display:block; }
.cap-loading .lab { font-family:var(--display); font-weight:700; }
.cap-loading .sub2 { color:var(--muted); font-size:.8rem; margin-top:4px; }
.add-status { font-size:.85rem; border-radius:10px; padding:9px 12px; margin-top:8px; line-height:1.45;
              background:rgb(var(--c-ink) / .04); color:var(--muted); border:1px solid var(--line); }
.add-status.ok { background:var(--ok-bg); color:var(--ok); border-color:var(--ok-line); }
.add-status.warn { background:var(--warn-bg); color:var(--warn); border-color:var(--warn-line); }
.cap-back { display:block; background:transparent; border:0; color:var(--muted); font-size:.82rem;
            cursor:pointer; padding:10px 0 2px; margin:0 auto; }
.cap-back:hover { color:var(--fg); }
/* 확인 단계: 조립 요약 배너 + 필드별 검증/인라인 수정 그리드 */
.cap-sum { font-size:.85rem; color:var(--ok); background:var(--ok-bg); border:1px solid var(--ok-line);
           border-radius:10px; padding:8px 12px; margin-bottom:10px; display:flex; gap:7px; flex-wrap:wrap; align-items:center; }
.cap-sum .chip { font-size:.72rem; color:var(--muted); background:rgb(var(--c-ink) / .05);
                 border:1px solid var(--line); border-radius:999px; padding:1px 8px; }
.cap-fields { border:1px solid var(--line); border-radius:14px; overflow:hidden; }
.cap-f { display:flex; align-items:center; gap:10px; padding:11px 13px; border-top:1px solid var(--line); }
.cap-f:first-child { border-top:0; }
.cap-f.warn { background:var(--warn-bg); }
.cap-f .k { width:48px; flex:0 0 auto; color:var(--muted); font-size:.82rem; }
.cap-f .v { flex:1; min-width:0; font-family:var(--display); font-weight:700; font-size:.98rem; color:var(--fg);
            font-variant-numeric:tabular-nums; }
.cap-f .v .unit { color:var(--muted); font-weight:500; font-size:.8rem; margin-left:3px; }
.cap-f.editable .v { cursor:pointer; }
.cap-f.editable .v .txt { border-bottom:1px dashed var(--muted); }
.cap-f.editable .v:hover .txt { border-bottom-color:var(--accent-2); color:var(--accent-2); }
.cap-f.editable .v .pen { color:var(--muted); font-size:.72rem; margin-left:6px; opacity:.6; }
.cap-f .v.empty .txt { color:var(--warn); }
.cap-f .ein { flex:1; min-width:0; width:100%; padding:7px 10px; border-radius:10px; border:1px solid var(--accent);
              background:var(--card); color:var(--fg); font-family:var(--display); font-weight:700; font-size:.98rem;
              box-shadow:0 0 0 3px rgb(var(--p-accent-rgb) / .25); outline:none; font-variant-numeric:tabular-nums; }
.cap-f .src { flex:0 0 auto; font-size:.6875rem; color:var(--muted); background:rgb(var(--c-ink) / .05);
              border:1px solid var(--line); border-radius:999px; padding:2px 8px; white-space:nowrap; }
.cap-f .src.conf { color:var(--warn); border-color:var(--warn-line); background:var(--warn-bg); }
.cap-hint { text-align:center; font-size:.76rem; color:var(--muted); margin-top:8px; }

/* 수신 영수증 카드 (`design/23` §3-1) — 확정 직후 "코치가 무엇을 받았나" 한 문장.
   토스트가 아니라 카드인 이유는 `base.html` 의 마크업 주석에 있다(읽고 **판단**해야 하는
   문장이고, 매칭이 애매하면 여기에 확인 칩이 붙는다 — 사라지는 것에는 버튼을 못 단다). */
.cap-receipt { margin:12px 0 0; padding:11px 13px; border-radius:var(--r-lg);
               border:1px solid var(--ok-line); background:var(--ok-bg); }
.cap-receipt-t { font-size:.85rem; line-height:1.5; color:var(--fg); }
.cap-receipt-q { font-size:.82rem; margin-top:8px; color:var(--muted); }
.cap-receipt-chips { display:flex; flex-wrap:wrap; gap:6px; margin-top:7px; }
.cap-receipt-chip { appearance:none; cursor:pointer; font-family:inherit; font-size:.78rem;
                    font-weight:700; padding:5px 11px; border-radius:var(--r-pill);
                    border:1px solid var(--line); background:var(--card); color:var(--fg); }
.cap-receipt-chip[disabled] { opacity:.55; cursor:default; }
.cap-receipt-n { font-size:.76rem; color:var(--muted); margin-top:6px; display:block; }
/* 첫 업로드 커버리지: 이 이미지로 어디까지 분석되고 뭐가 비었는지 */
.cap-cov { margin-top:12px; border:1px solid var(--line); border-radius:14px; padding:12px 14px; }
.cap-cov .h { font-size:.78rem; color:var(--muted); font-family:var(--display); font-weight:700; margin-bottom:6px; }
.cap-cov .r { display:flex; align-items:flex-start; gap:8px; padding:5px 0; font-size:.86rem; line-height:1.45; }
.cap-cov .r .ic { flex:0 0 auto; width:15px; text-align:center; font-weight:700; }
.cap-cov .r.on .ic { color:var(--ok); }
.cap-cov .r.off .ic { color:var(--muted); }
.cap-cov .r .lab { color:var(--fg); font-weight:700; }
.cap-cov .r.off .lab { color:var(--muted); }
.cap-cov .r .sub { color:var(--muted); }
.cap-cov .r .add { color:var(--accent-2); }
.cap-cov .foot { font-size:.74rem; color:var(--muted); margin-top:7px; padding-top:7px; border-top:1px solid var(--line); }
/* 여러 런 분리(#4) 목록 — 런별 선택 저장 */
.cap-multi-h { font-family:var(--display); font-weight:700; font-size:.95rem; margin:2px 0 3px; }
.cap-multi-sub { font-size:.76rem; color:var(--muted); margin:0 0 10px; }
.cap-multi-sub .selc { color:var(--accent-2); font-weight:700; font-variant-numeric:tabular-nums; }
.cap-run { display:flex; align-items:flex-start; gap:11px; border:1px solid var(--line); border-radius:12px;
           padding:11px 13px; margin-bottom:9px; cursor:pointer; -webkit-tap-highlight-color:transparent;
           transition:border-color .15s ease, background .15s ease, opacity .15s ease, transform .08s ease; }
.cap-run:active { transform:scale(.992); }
.cap-run .rck { flex:0 0 auto; width:20px; height:20px; border-radius:50%; border:2px solid var(--line);
                margin-top:1px; display:flex; align-items:center; justify-content:center;
                transition:border-color .15s ease, background .15s ease; }
.cap-run .rck svg { width:11px; height:11px; opacity:0; transform:scale(.4); transition:opacity .15s ease, transform .15s ease; }
.cap-run .rbody { flex:1; min-width:0; }
.cap-run.sel { border-color:var(--accent); background:rgb(var(--p-accent-rgb) / .07); }
.cap-run.sel .rck { border-color:var(--accent); background:var(--accent); }
.cap-run.sel .rck svg { opacity:1; transform:scale(1); }
.cap-run.off { opacity:.52; }
.cap-run .rl { font-family:var(--display); font-weight:700; font-size:.92rem; color:var(--fg); }
.cap-run .rc { font-size:.76rem; color:var(--muted); margin-top:3px; }
.cap-run .rd { font-size:.74rem; color:var(--warn); margin-top:4px; }
.btn.btn-primary:disabled { opacity:.45; cursor:default; }
/* 중복 경고(#3) 안의 보강 버튼 */
#capDup .dup-enrich { display:inline-block; margin-top:6px; font-size:.78rem; color:var(--accent-2);
                      background:transparent; border:0; cursor:pointer; padding:0; text-decoration:underline; }
/* site footer — legal links on every page (Strava review needs a reachable
   privacy policy + terms; in normal flow so fixed FABs float above it) */
.site-foot { text-align:center; margin:30px 0 4px; font-size:.76rem; color:var(--muted); line-height:1.7; }
.site-foot a { color:var(--muted); text-decoration:none;
               /* 글자 높이 15px 가 곧 누를 자리였다(2026-09-23 QA). 줄 간격은 그대로 두고
                  누를 자리만 위아래로 넓힌다(줄바꿈된 두 줄의 누를 자리가 겹치지 않을 만큼만). */
               display:inline-block; padding:6px 2px; margin:-6px 0; }
.site-foot a:hover { color:var(--accent-2); text-decoration:underline; }

/* ── 주간 계획 요일 그리드 ('내가 입력하기') ─────────────────────────────────
   한 줄 `<input>` 하나로 일곱 요일을 받던 자리를 대체한다. prod 실측으로 이 입력에
   줄바꿈이 들어온 적이 0/37 이었고(넣을 수가 없었다), 요일을 0~1개만 언급한 단편
   제출이 45% 였다 — 그 단편이 매번 나머지 엿새를 지웠다.
   ⚠️ 칸은 **미리 채우지 않는다.** 채우면 '지운 칸'과 '안 건드린 칸'이 같아 보인다.
      현재 내용은 `.dg-now` 회색 힌트로만 보여 주고 칸은 비워 둔다. */
.dg-lead { font-size:.84rem; color:var(--muted); line-height:1.6; margin:2px 0 12px; }
.dg-lead b { color:var(--fg); }
.dg-row { display:grid; grid-template-columns:52px 1fr; gap:8px 10px; align-items:start;
          padding:9px 0; border-bottom:1px solid var(--line); }
.dg-row:last-of-type { border-bottom:0; }
.dg-d { font-weight:700; font-size:.9rem; line-height:1.35; padding-top:9px; }
.dg-d small { display:block; font-weight:500; font-size:.72rem; color:var(--muted);
              font-variant-numeric:tabular-nums; }
/* 오늘이 낀 주에서 '오늘'을 알아보게 — 요일 이름만으로는 매주 같은 화면이 된다. */
.dg-row.is-today .dg-d { color:var(--accent-2); }
.dg-now { grid-column:2; font-size:.76rem; color:var(--muted); line-height:1.5;
          margin:0 0 5px; overflow-wrap:anywhere; }
.dg-now b { color:var(--fg); font-weight:700; }
/* 여러 줄이 이 그리드의 존재 이유다 — 이중세션('아침 조깅 / 저녁 인터벌')을 한 칸에
   두 줄로 적을 수 있어야 한다. 세로 리사이즈만 허용한다(가로는 시트를 깨뜨린다). */
.dg-row textarea.field { grid-column:2; min-height:44px; resize:vertical; line-height:1.55;
                         font-family:inherit; }
#draft-grid > .btn { width:100%; margin-top:14px; }
.dg-alt { font-size:.76rem; color:var(--muted); margin:10px 0 2px; text-align:center; }

/* ── 용어 팝오버 · "왜?" 칩 (`design/23` §6-2 ②③) ────────────────────────────
   ★ `.gloss` 는 **본문 흐름을 흔들지 않는다.** 답변 한가운데 들어가는 요소라
     `padding`·`border` 로 부풀리면 줄바꿈 위치가 바뀌어 같은 답이 다르게 읽힌다 —
     밑줄 하나가 "누를 수 있다"를 말하는 데 충분하다.
   ⚠️ `button` 의 기본값(폰트·배경·테두리)을 전부 되돌려야 한다. 안 그러면 답변 가운데
      회색 버튼이 박혀서, 앱이 쓴 것과 코치가 쓴 것이 섞여 보인다(철학 §5). */
.gloss { font:inherit; color:inherit; background:none; border:0; padding:0; margin:0;
         cursor:pointer; text-decoration:underline; text-decoration-style:dotted;
         text-underline-offset:3px; text-decoration-color:var(--accent-2);
         -webkit-text-decoration-color:var(--accent-2); }
.gloss:hover, .gloss:focus-visible { text-decoration-style:solid; color:var(--accent-2); }

/* 시트는 `.ks-sheet`(코치가 아는 것)와 같은 층·같은 연출이다. 두 시트가 동시에 열리는
   경로는 없다 — 백드롭이 화면을 막는다. */
.gl-back { position:fixed; inset:0; bottom:calc(0px - var(--vv-lift, 0px)); z-index:70;
           background:rgb(0 0 0 / .45);
           opacity:0; transition:opacity .22s ease; }
.gl-back.show { opacity:1; }
.gl-sheet { position:fixed; z-index:71; left:0; right:0;
            bottom:calc(0px - var(--vv-lift, 0px));
            max-height:70vh; overflow-y:auto; -webkit-overflow-scrolling:touch;
            padding:14px 18px calc(18px + env(safe-area-inset-bottom));
            background:var(--card); border-top:1px solid var(--line);
            border-radius:var(--r-sheet) var(--r-sheet) 0 0;
            transform:translateY(102%); transition:transform .26s cubic-bezier(.22,.61,.36,1); }
.gl-sheet.show { transform:none; }
.gl-head { display:flex; align-items:center; justify-content:space-between; gap:10px;
           margin-bottom:8px; }
.gl-head strong { font-size:1rem; letter-spacing:-.01em; }
.gl-x { background:none; border:0; color:var(--muted); font-size:1rem; cursor:pointer;
        padding:4px 6px; line-height:1; }
.gl-x:hover { color:var(--fg); }
.gl-short { margin:0 0 8px; font-size:.92rem; font-weight:500; line-height:1.6; }
.gl-cv { margin:0 0 8px; font-size:.86rem; line-height:1.6;
         background:var(--err-bg); border:1px solid var(--err-line);
         border-radius:10px; padding:8px 10px; }
.gl-cv b { font-weight:700; }
/* 일반 정의와 **이 사람의 숫자**를 시각적으로 갈라 둔다 — 사고는 둘을 섞어 읽어서 났다
   ("z2 구간 알려줘"에 사전의 일반 문구로 답한 자리, `design/23` §0-6). */
.gl-mine { margin:0 0 10px; font-size:.9rem; line-height:1.6;
           background:var(--card2); border:1px solid var(--line);
           border-radius:10px; padding:8px 10px; overflow-wrap:anywhere; }
.gl-mine b { color:var(--muted); font-size:.78rem; font-weight:700; }
.gl-more { display:inline-block; font-size:.84rem; font-weight:700; color:var(--accent-2);
           text-decoration:none; }

/* "왜?" — 처방 아래의 결정론 근거. 접혀 있는 것이 기본이다(펼쳐 두면 숫자가 처방을 덮는다). */
.why-wrap { margin-top:10px; }
.why-list { margin-top:8px; padding:9px 11px; border:1px solid var(--line);
            border-radius:10px; background:var(--card2); }
.why-row { font-size:.82rem; line-height:1.7; color:var(--fg); overflow-wrap:anywhere;
           font-variant-numeric:tabular-nums; }
.why-row.muted { color:var(--muted); }

/* ⚠️ 전정기관 민감군에게 미끄러지는 시트는 실제 증상을 만든다. 위쪽 `prefers-reduced-motion`
   블록은 `animation` 만 끄므로(app.css 상단), 여기서 `transition` 을 따로 끈다. */
@media (prefers-reduced-motion: reduce) {
  .gl-back, .gl-sheet { transition:none; }
}

/* ── 답변 속 그래프 한 장 (`answer_charts` · `static/answer_chart.js`) ──────────────
   결론 한 문장 · 그래프 한 장 · 출처 한 줄 · '숫자로 보기'. 색은 네 가지 뜻만 쓴다:
   잉크 = 지금·이 런·이번 주 / 회색 = 비교용 맥락 / Z 램프 = 강도(한 색조) / 옅은 판정 면 = 구간.
   ★ 차트 전용 값은 여기서만 정의한다(원천 `--c-*` 에 없는 것만 hex). 앱의 러닝 유형 램프
     (`--c-i*`)는 인접 색 구분이 안 돼(색각 ΔE 3.0) 쓰지 않는다 — Z 램프는 검증된 한 색조 5단계.
   ⚠️ 판정 면은 **글자와 함께**만 쓴다(meter 의 구간 라벨). 색만으로 뜻을 싣지 않는다. */
.ac-card {
  --ac-ink:var(--c-fg); --ac-fg2:var(--c-fg2); --ac-fg3:var(--c-fg3); --ac-hair:var(--c-hairline);
  --ac-s0:var(--c-s0); --ac-s1:var(--c-s1); --ac-s2:var(--c-s2);
  --ac-ctx:#C9C9C3;
  --ac-z1:#98B2C1; --ac-z2:#7196AB; --ac-z3:#4F7890; --ac-z4:#335A6E; --ac-z5:#1A3746;
  --ac-good:#D3E5D9; --ac-caution:#EEDFC4; --ac-risk:#F2D3CF; --ac-riskink:var(--c-risk);
  --ac-h1:#C9CDD0; --ac-h2:#8E959B; --ac-h3:#4E555C;
  --ac-p1:#D9D9D4; --ac-p2:#B9B9B3;
  box-sizing:border-box; background:var(--c-s1); border-radius:var(--r-lg);
  padding:16px 16px 12px; margin:6px 0 12px; display:flex; flex-direction:column; gap:12px;
  color:var(--c-fg); max-width:520px;
}
.ac-head { display:flex; flex-direction:column; gap:4px; }
.ac-title { font-size:16px; font-weight:700; line-height:1.35; letter-spacing:-.01em;
            color:var(--c-fg); overflow-wrap:anywhere; }
.ac-sub { font-size:12px; font-weight:500; line-height:1.4; color:var(--c-fg2); }
.ac-plot { padding:4px 0; }
.ac-svg { display:block; width:100%; height:auto; overflow:visible; }
.ac-t { font-family:var(--body); }
.ac-n { font-family:var(--display); font-variant-numeric:tabular-nums; }
.ac-foot { display:flex; justify-content:space-between; align-items:center; gap:8px;
           border-top:1px solid var(--c-hairline); padding-top:10px; }
.ac-src { font-size:11px; font-weight:500; line-height:1.4; color:var(--c-fg3); }
.ac-more { font:inherit; font-size:11px; font-weight:500; color:var(--c-fg2); background:none;
           border:0; padding:6px 0 6px 8px; margin:-6px 0; white-space:nowrap; cursor:pointer; }
.ac-more:hover { color:var(--c-fg); }
.ac-tblwrap { overflow-x:auto; }
.ac-tbl { width:100%; border-collapse:collapse; font-size:12px; font-variant-numeric:tabular-nums; }
.ac-tbl th { text-align:left; font-weight:500; color:var(--c-fg2); padding:4px 6px 4px 0;
             border-bottom:1px solid var(--c-hairline); white-space:nowrap; }
.ac-tbl td { padding:4px 6px 4px 0; color:var(--c-fg); border-bottom:1px solid var(--c-s2); }


/* ══ 알림 벨 + 할 일 시트 (2026-09-28) — base.html 의 마크업 주석이 정본 ══════════════════
   벨 = 헤더 오른쪽 끝(44px 터치 영역). 점 = 탭 배지와 같은 빨간 면에 전체 건수.
   시트 = 하단에서 올라오는 목록. 탭별로 묶고, 줄마다 기록/확인 칩 + 이름 + 건수 + 꺾쇠.
   끌 수 있는 항목만 "필요 없어요"가 붙는다(끌 수 없는 것에 끄는 버튼은 눌러도 안 없어진다). */
.todo-bell { margin-left:auto; position:relative; width:44px; height:44px; flex:0 0 auto;
             display:inline-flex; align-items:center; justify-content:center;
             background:none; border:0; border-radius:var(--r-pill); color:var(--fg); cursor:pointer;
             -webkit-tap-highlight-color:transparent; }
.todo-bell:active { background:rgb(var(--c-ink) / .06); }
.todo-bell svg { width:24px; height:24px; }
.todo-dot { position:absolute; top:5px; right:3px; min-width:18px; height:18px; padding:0 4px;
            border-radius:var(--r-pill); background:var(--err); color:var(--on-brand);
            font-family:var(--body); font-size:var(--t1); font-weight:700; line-height:18px;
            text-align:center; letter-spacing:0; box-sizing:border-box; }
.todo-dot[hidden] { display:none; }
.todo-backdrop { position:fixed; inset:0; z-index:90; background:rgb(var(--p-shadow-4-rgb) / .5);
                 opacity:0; pointer-events:none; transition:opacity .25s ease; }
.todo-backdrop.show { opacity:1; pointer-events:auto; }
.todo-sheet { position:fixed; left:0; right:0; bottom:0; z-index:91; margin:0 auto; max-width:480px;
              max-height:78vh; overflow-y:auto; box-sizing:border-box;
              padding:18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
              background:var(--bg); border-radius:var(--r-xl) var(--r-xl) 0 0;
              transform:translateY(102%); visibility:hidden;
              transition:transform .25s var(--ease), visibility 0s linear .25s; }
.todo-sheet.show { transform:none; visibility:visible; transition:transform .25s var(--ease); }
.todo-sheet[hidden] { display:none; }
.ts-head { display:flex; align-items:center; gap:8px; }
.ts-title { margin:0; font-size:var(--t6, 1.1rem); }
.ts-count { font-size:.85rem; font-weight:700; color:var(--err); }
.ts-x { margin-left:auto; width:36px; height:36px; border:0; background:none; color:var(--muted);
        font-size:1rem; cursor:pointer; border-radius:var(--r-pill); }
.ts-lede { margin:6px 0 12px; font-size:.84rem; color:var(--muted); line-height:1.5; }
.ts-group + .ts-group { margin-top:14px; }
.ts-tab { font-size:.76rem; font-weight:700; color:var(--muted); margin:0 2px 6px; }
.ts-item { border:1px solid var(--line); border-radius:var(--r-md); background:var(--card); }
.ts-item + .ts-item { margin-top:8px; }
.ts-item.is-first { border-color:var(--brand); }
.ts-row { display:flex; align-items:center; gap:10px; width:100%; padding:13px 14px; box-sizing:border-box;
          background:none; border:0; font:inherit; color:var(--fg); text-align:left; cursor:pointer; }
.ts-act { flex:0 0 auto; font-size:.7rem; font-weight:800; padding:2px 7px; border-radius:var(--r-pill); }
.ts-act.rec { background:var(--brand-weak); color:var(--brand); }
.ts-act.chk { background:rgb(var(--c-ink) / .06); color:var(--muted); }
.ts-l { flex:1; min-width:0; font-size:.92rem; font-weight:700; }
.ts-c { flex:0 0 auto; font-size:.8rem; font-weight:700; color:var(--err); }
.ts-go { flex:0 0 auto; color:var(--muted); font-size:1.1rem; }
.ts-skip { display:block; margin:-4px 14px 10px auto; padding:2px 0; border:0; background:none;
           font-size:.78rem; color:var(--muted); text-decoration:underline; cursor:pointer; }
.ts-empty { margin:18px 0 8px; font-size:.9rem; color:var(--muted); text-align:center; }
