/* 앱·관리자 화면 조합
   코어(app.css 의 appbar·tabbar·row-item·actionbar, components.css 의 card·table·sheet·drawer)를 조립해
   실제 화면 골격을 만든다. 새 부품을 만들기보다 코어를 어떻게 붙이는지를 정한다.
   원본: 앱 화면(screens/app.css·office.css)과 관리자 콘솔(screens/console.css)을 브랜드 중립 이름으로 일반화했다.
   색은 역할 토큰만 쓴다. 다크 모드는 역할 토큰이 따라온다. */

/* ══ 1. 앱 화면 껍데기 ═══════════════════════════════════
   body.app-page > .app > (.appbar · .app__body · .actionbar | .tabbar) 순서.
   문서가 아니라 본문(.app__body)만 스크롤한다. 앱바는 위, 탭바·동작 바는 아래에 늘 같은 자리. */
.app-page{height:100dvh;overflow:hidden;background:var(--canvas)}
/* --kb: 키보드가 가린 높이(px). 키보드를 따라 레이아웃이 줄지 않는 브라우저(iOS 사파리)에서
   화면 스크립트가 visualViewport 로 재서 적으면 하단 동작 바가 키보드 바로 위로 올라온다. */
.app-page .app{position:relative;height:calc(100% - var(--kb, 0px))}
/* 한국어 줄바꿈: span·div 로 쓰는 코어 조각(알림 배너 본문·빈 상태·시트 머리)도 낱말 중간에서 끊지 않는다(원본 .pw 와 같다) */
:is(.app-page,.admin) :where(.callout__body,.empty__title,.empty__desc,.sheet__head,.kv dd,.cell-stack,.row-item__title,.choice-card__text){word-break:keep-all;overflow-wrap:anywhere}
:is(.app-page,.admin) :where(.callout__body,.empty__desc,.kv dd){text-wrap-style:pretty}
/* PC(1024 이상): 폰 앱을 넓게 늘리지 않고 가운데 기둥 하나(--app-w)로 둔다. 바깥은 한 단계 들어간 면. */
@media (min-width:1024px){
  .app-page{--app-w:var(--w-sm);background:var(--canvas-alt)}
  .app-page .app{max-width:var(--app-w);margin-inline:auto;box-shadow:0 0 0 1px var(--line)}
}
/* 떠 있는 버튼은 앱 기둥 안에 둔다. PC 에서 화면 오른쪽 끝으로 달아나지 않게. */
.app-page .app > .fab{position:absolute}
.app--no-tabbar > .fab{bottom:calc(var(--safe-b) + var(--s-16))}
.app--dock > .fab{bottom:calc(var(--safe-b) + var(--tabbar-h) + var(--dock-h) + var(--s-16))}
/* 떠 있는 버튼이 있으면 본문 끝을 버튼 높이만큼 더 비워 마지막 행이 버튼 밑에 숨지 않게 */
.app:has(> .fab) .app__body{padding-bottom:calc(var(--safe-b) + var(--tabbar-h) + var(--c-lg-h) + var(--s-32))}
.app--no-tabbar:has(> .fab) .app__body{padding-bottom:calc(var(--safe-b) + var(--c-lg-h) + var(--s-32))}

/* 본문 안 여백: 좌우는 컨테이너가, 위아래는 여기서. */
:is(.app__body,.ptr__body) > .container{padding-block:var(--s-8) var(--s-32)}
/* 필터 칩 줄·가로 띠는 컨테이너 좌우 여백을 상쇄해 화면 끝까지 흐른다(끝에 걸린 칩이 더 있다는 걸 알린다) */
/* scroll-padding 을 여백과 같게 두어야 첫 칩의 스냅 자리가 여백 안쪽이 된다(없으면 여백만큼 밀려 첫 칩이 화면 끝에 붙는다) */
.app-page .container > .chiprow{margin-inline:calc(var(--s-20) * -1);padding-inline:var(--s-20);scroll-padding-inline:var(--s-20)}
@media (min-width:768px){.app-page .container > .chiprow{margin-inline:calc(var(--s-32) * -1);padding-inline:var(--s-32);scroll-padding-inline:var(--s-32)}}
/* 큰 제목은 본문 맨 위에서 함께 스크롤된다(앱바 안에 두면 빈 띠가 남는다). 넘어가면 앱바 가운데 작은 제목이 나타난다. */
:is(.app__body,.ptr__body) > .app__large{padding-block:var(--s-4)}
.app__large .appbar__large{padding:var(--s-4) 0 var(--s-2)}
.app__large-sub{padding-bottom:var(--s-8);font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);color:var(--ink-muted)}
/* 본문에 제목이 없는 화면(상세·단계 입력)은 작은 제목을 처음부터 보인다 */
.appbar--pinned .appbar__title{opacity:1}
/* 앱바 아래 진행 막대(단계 입력). 앱바와 함께 위에 붙는다. */
.appbar > .steps{padding:0 var(--s-20) var(--s-10)}
@media (min-width:768px){.appbar > .steps{padding-inline:var(--s-32)}}

/* 하단 동작 바(.app--actionbar): .actionbar 를 본문 밖(.app 의 자식, .app__body 다음)에 두면 늘 보이고
   본문은 그만큼 덜 비운다. 탭바는 두지 않는다(결론을 내는 화면에서 다른 탭으로 새지 않게). */
:is(.app--actionbar,.app:has(> .actionbar)) .app__body{padding-bottom:var(--s-24)}
:is(.app--actionbar,.app:has(> .actionbar)) > .tabbar{display:none}
.app > .actionbar{position:relative;flex:none}
/* 입력 바 자리(.app__dock, 높이 --dock-h): 탭바 바로 위에 붙는 불투명 띠. 탭바가 없으면 화면 맨 아래(안전 영역 포함).
   본문은 탭바 + 입력 바만큼 아래를 비운다. 뒤로 지나가는 글자가 비치지 않게 바탕은 불투명. */
.app__dock{position:absolute;left:0;right:0;bottom:calc(var(--tabbar-h) + var(--safe-b));z-index:var(--z-sticky);
  display:flex;align-items:flex-end;gap:var(--s-8);min-height:var(--dock-h);padding:var(--s-10) var(--s-12);
  background:var(--canvas);box-shadow:inset 0 1px 0 var(--line)}
.app--no-tabbar .app__dock{bottom:0;padding-bottom:calc(var(--s-10) + var(--safe-b))}
.app__dock > .input,.app__dock > .searchbar{flex:1;min-width:0;margin:0}
.app--dock .app__body{padding-bottom:calc(var(--safe-b) + var(--tabbar-h) + var(--dock-h) + var(--s-16))}
.app--no-tabbar.app--dock .app__body{padding-bottom:calc(var(--safe-b) + var(--dock-h) + var(--s-16))}
/* 토스트는 탭바·입력 바·동작 바 위에 뜬다(docs/03 하단 쌓임). body:has() 대신 형제 선택자로 쓴다(.app 과 .toasts 는 body 의 형제).
   body:has() 는 DOM 이 바뀔 때마다 문서 전체를 다시 훑어 요소가 많은 화면에서 스타일 계산이 몇 초씩 걸린다. */
   :where 로 우선순위를 낮춰 두어, 시트가 열려 있으면 코어 규칙(화면 위쪽에 띄우기)이 이긴다. */
:where(.app:has(> .tabbar)) ~ .toasts{bottom:calc(var(--safe-b) + var(--tabbar-h) + var(--s-12))}
:where(.app--dock) ~ .toasts{bottom:calc(var(--safe-b) + var(--tabbar-h) + var(--dock-h) + var(--s-12))}
:where(.app--dock.app--no-tabbar) ~ .toasts{bottom:calc(var(--safe-b) + var(--dock-h) + var(--s-12))}
:where(.app:has(> .fab)) ~ .toasts{bottom:calc(var(--safe-b) + var(--tabbar-h) + var(--c-lg-h) + var(--s-28))}
:where(.app--actionbar, .app:has(> .actionbar)) ~ .toasts{bottom:calc(var(--safe-b) + var(--c-lg-h) + var(--s-40))}

/* 화면에 들어올 때 본문 덩어리가 차례로 올라온다. 각 180ms 라 200ms 넘는 전환이 겹치지 않는다.
   같은 화면을 새로 그릴 때(html[data-nav="none"])는 움직이지 않는다. */
@keyframes app-rise{from{opacity:0;transform:translateY(var(--s-10))}to{opacity:1;transform:none}}
html:not([data-nav="none"]) :is(.app__body,.ptr__body) > .container > *{animation:app-rise var(--d-2) var(--ease-out) backwards}
html:not([data-nav="none"]) :is(.app__body,.ptr__body) > .container > :nth-child(2){animation-delay:40ms}
html:not([data-nav="none"]) :is(.app__body,.ptr__body) > .container > :nth-child(3){animation-delay:80ms}
html:not([data-nav="none"]) :is(.app__body,.ptr__body) > .container > :nth-child(n+4){animation-delay:120ms}

/* ══ 2. 앱 화면 조각 ════════════════════════════════════ */

/* 구역: 머리(제목 + 오른쪽 링크 하나) + 목록 */
.app-sec{display:flex;flex-direction:column;gap:var(--s-8)}
.app-sec__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-8);min-height:var(--c-md-h);padding-inline:var(--s-2)}
.app-sec__title{font-size:var(--t-body-md);line-height:var(--t-body-md-lh);font-weight:700;color:var(--ink)}
.app-sec__head .link{display:inline-flex;align-items:center;min-height:var(--c-md-h);font-size:var(--t-caption);white-space:nowrap}
/* 구역 아래 한 줄 안내(밀어서 거절 등)와 화면 끝 안내 */
.app-sec__hint{padding-inline:var(--s-4);font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-subtle)}
.app-note{text-align:center;font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-subtle);word-break:keep-all}
/* 묶음 목록(.group)은 풀 폭 앱 목록용 바깥 여백(12·16)을 가진다. 컨테이너·시트·폼 안에서는 여백을 모른다. */
:is(.app-page .container,.sheet__body,.flow,.error-page__inner) .group{margin:0}

/* 지금 가장 중요한 한 가지: 연한 강조 면에 빛 한 덩어리. 화면에 하나만. */
.focus-card{position:relative;isolation:isolate;overflow:hidden;display:flex;flex-direction:column;gap:var(--s-12);
  padding:var(--s-20);border-radius:var(--r-2xl);background:var(--accent-soft);color:var(--ink)}
.focus-card::before{content:"";position:absolute;z-index:-1;top:calc(var(--s-64) * -1);right:calc(var(--s-48) * -1);
  width:var(--s-200);height:var(--s-200);border-radius:var(--r-full);
  background:radial-gradient(circle,var(--accent-soft-hover),transparent 70%)}
.focus-card__eyebrow{display:flex;align-items:center;gap:var(--s-6);font-size:var(--t-caption);line-height:var(--t-caption-lh);font-weight:600;color:var(--accent-text)}
.focus-card__title{font-size:var(--t-heading-md);line-height:var(--t-heading-md-lh);letter-spacing:var(--t-heading-md-ls);font-weight:700;color:var(--ink)}
.focus-card__meta{font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);color:var(--ink-body)}
.focus-card__actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-8);margin-top:var(--s-4)}
.focus-card__actions .btn{min-width:0}

/* 요약 칸 셋: 숫자가 주인공. 칸 사이는 세로 선. 누르면 그 구역으로 간다. */
.app-summary{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));padding:var(--s-12) var(--s-8);border-radius:var(--r-xl);background:var(--canvas-alt)}
.app-summary__item{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);min-width:0;min-height:var(--c-lg-h);
  padding-inline:var(--s-4);text-align:center;color:var(--ink);border-radius:var(--r-md);-webkit-tap-highlight-color:transparent}
.app-summary__item{position:relative}
.app-summary__item + .app-summary__item::before{content:"";position:absolute;left:0;top:var(--s-10);bottom:var(--s-10);width:1px;background:var(--line)}
.app-summary__label{font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted-alt);white-space:nowrap}
.app-summary__value{font-size:var(--t-heading-md);line-height:var(--t-heading-md-lh);letter-spacing:var(--t-heading-md-ls);font-weight:700;font-variant-numeric:tabular-nums}
.app-summary__value--accent{color:var(--accent-text)}
a.app-summary__item:active{opacity:.7}

/* 상세 머리: 상태 배지 줄 → 큰 제목 → 한 줄 요약 */
.detail-head{display:flex;flex-direction:column;gap:var(--s-8)}
.detail-head__badges{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-6)}
.detail-head__title{font-size:var(--t-heading-lg);line-height:var(--t-heading-lg-lh);letter-spacing:var(--t-heading-lg-ls);font-weight:700;color:var(--ink)}
.detail-head__sub{font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);color:var(--ink-muted)}
@media (max-width:640px){.detail-head__title{font-size:var(--t-heading-md);line-height:var(--t-heading-md-lh);letter-spacing:var(--t-heading-md-ls)}}

/* ── 진행 단계 ──────────────────────────────────────
   세로(기본): 체크리스트. 가로(--row): 상세 맨 위 진행 표시. 끝난 단계는 체크, 지금 단계는 강조색.
   상태를 색만으로 알리지 않는다. 표시 안 아이콘(체크·시계·경고)과 aria-current="step" 을 함께 쓴다. */
.stepper{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}
.stepper__item{position:relative;display:flex;align-items:flex-start;gap:var(--s-12);padding-block:var(--s-4);
  font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);color:var(--ink-muted)}
.stepper__item + .stepper__item::before{content:"";position:absolute;left:calc(var(--s-24) / 2 - 1px);top:calc(var(--s-8) * -1);
  width:2px;height:var(--s-12);border-radius:var(--r-full);background:var(--line)}
.stepper__mark{flex:none;display:grid;place-items:center;width:var(--s-24);height:var(--s-24);border-radius:var(--r-full);
  background:var(--surface);color:var(--ink-subtle);box-shadow:inset 0 0 0 1.5px var(--line-strong)}
.stepper__mark .ico{width:14px;height:14px}
.stepper__text{flex:1;min-width:0;padding-top:var(--s-2);word-break:keep-all;overflow-wrap:anywhere}
.stepper__at{flex:none;padding-top:var(--s-2);font-size:var(--t-micro);color:var(--ink-subtle);font-variant-numeric:tabular-nums}
.stepper__item[data-done]{color:var(--ink-body)}
.stepper__item[data-done] .stepper__mark{background:var(--good-soft);color:var(--good-text);box-shadow:none}
.stepper__item[data-done] + .stepper__item::before{background:var(--good-text);opacity:.5}
.stepper__item[data-live]{color:var(--ink);font-weight:600}
.stepper__item[data-live] .stepper__mark{background:var(--accent);color:var(--accent-ink);box-shadow:none;animation:step-live calc(var(--d-7) * 2) var(--ease-inout) infinite}
.stepper__item[data-failed]{color:var(--bad-text)}
.stepper__item[data-failed] .stepper__mark{background:var(--bad-soft);color:var(--bad-text);box-shadow:none}
@keyframes step-live{0%,100%{box-shadow:0 0 0 0 var(--accent-soft)}50%{box-shadow:0 0 0 var(--s-6) var(--accent-soft)}}
/* 가로: 칸이 같은 폭으로 나뉘고, 표시끼리 선으로 잇는다. 이름은 표시 아래 가운데. */
.stepper--row{flex-direction:row;align-items:flex-start}
.stepper--row .stepper__item{flex:1;min-width:0;flex-direction:column;align-items:center;gap:var(--s-6);padding-block:0;
  text-align:center;font-size:var(--t-caption);line-height:var(--t-caption-lh)}
.stepper--row .stepper__item + .stepper__item::before{left:calc(-50% + var(--s-16));right:calc(50% + var(--s-16));top:calc(var(--s-12) - 1px);width:auto;height:2px}
.stepper--row .stepper__text{flex:none;padding-top:0}
.stepper--row .stepper__at{padding-top:0;margin-top:calc(var(--s-4) * -1)}

/* ── 키·값 ──────────────────────────────────────────
   dl 하나에 dt·dd 를 번갈아 두거나, .kv__row(div)로 한 쌍씩 묶는다.
   --end 값 오른쪽 정렬(영수증) · --rows 쌍마다 구분선 · --stack 한 열 · --sm 관리자 조밀 */
.kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--s-10) var(--s-20);margin:0;
  font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh)}
.kv__row{display:contents}
.kv dt{color:var(--ink-muted);white-space:nowrap}
.kv dd{margin:0;min-width:0;color:var(--ink);font-weight:500;overflow-wrap:anywhere}
.kv__sub{display:block;margin-top:var(--s-2);font-size:var(--t-caption);line-height:var(--t-caption-lh);font-weight:400;color:var(--ink-muted)}
.kv--end dd{text-align:end}
.kv--stack{grid-template-columns:minmax(0,1fr);gap:var(--s-2)}
.kv--stack dd + dt{margin-top:var(--s-10)}
.kv--sm{gap:var(--s-6) var(--s-16)}
.kv--sm dt{font-size:var(--t-caption)}
.kv--sm dd{font-size:var(--t-body-xs);font-weight:400;color:var(--ink-body)}
.kv--rows{display:flex;flex-direction:column;gap:0}
.kv--rows .kv__row{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-16);padding-block:var(--s-12);box-shadow:inset 0 -1px 0 var(--line)}
.kv--rows .kv__row:last-child{box-shadow:none}
.kv--rows dd{text-align:end;font-variant-numeric:tabular-nums}
/* 긴 글(메모·주소)은 이름 아래 한 줄을 다 쓰고 왼쪽 정렬. 오른쪽 정렬 두 줄은 읽기 어렵다. */
.kv--rows .kv__row--stack{flex-direction:column;align-items:stretch;gap:var(--s-4)}
.kv--rows .kv__row--stack dd{text-align:start}
/* 합계 줄: 금액은 한 줄로. 이름이 길면 이름 쪽이 줄을 바꾼다. */
.kv__row--total{padding-top:var(--s-16)}
.kv--rows .kv__row--total{box-shadow:inset 0 1px 0 var(--line-strong)}
.kv__row--total :is(dt,dd){color:var(--ink);font-weight:700}
.kv__row--total dd{font-size:var(--t-body-lg);white-space:nowrap}

/* ── 설명이 붙는 선택 카드(라디오·체크) ─────────────
   카드 전체가 누르는 곳. 입력은 보이지 않게 두고 오른쪽 표시가 고른 상태를 보인다.
   고르면 연한 강조 면 + 2px 강조 테두리 + 체크 표시(색만으로 알리지 않는다). */
.choice-cards{display:flex;flex-direction:column;gap:var(--s-8);margin:0;padding:0;border:0}
.choice-card{position:relative;display:flex;align-items:center;gap:var(--s-12);min-height:var(--s-64);padding:var(--s-12) var(--s-16);
  border-radius:var(--r-lg);background:var(--canvas-alt);color:var(--ink);cursor:pointer;-webkit-tap-highlight-color:transparent;
  transition:background-color var(--d-2) var(--ease-inout),transform var(--d-1) var(--ease-inout)}
.choice-card::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 0 0 1px transparent;transition:box-shadow var(--d-2) var(--ease-inout)}
.choice-card:active{transform:scale(.985)}
.choice-card > input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.choice-card__lead{flex:none;display:grid;place-items:center;width:var(--s-40);height:var(--s-40);border-radius:var(--r-md);background:var(--surface);color:var(--ink-muted)}
.choice-card__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-2)}
.choice-card__title{font-size:var(--t-body-md);line-height:var(--t-body-md-lh);font-weight:600;color:var(--ink);word-break:keep-all}
.choice-card__desc{font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted-alt);word-break:keep-all;overflow-wrap:anywhere}
.choice-card__tail{flex:none;font-size:var(--t-caption);color:var(--ink-muted-alt);font-variant-numeric:tabular-nums}
.choice-card__mark{flex:none;display:grid;place-items:center;width:22px;height:22px;border-radius:var(--r-full);
  box-shadow:inset 0 0 0 1.5px var(--line-strong);color:transparent;
  transition:background-color var(--d-2) var(--ease-inout),box-shadow var(--d-2) var(--ease-inout),color var(--d-2) var(--ease-inout)}
.choice-card__mark .ico{width:14px;height:14px}
.choice-card:has(> input:checked){background:var(--accent-soft)}
.choice-card:has(> input:checked)::after{box-shadow:inset 0 0 0 2px var(--accent)}
.choice-card:has(> input:checked) .choice-card__mark{background:var(--accent);color:var(--accent-ink);box-shadow:none}
.choice-card:has(> input:checked) .choice-card__lead{color:var(--accent-text)}
.choice-card:has(> input:focus-visible)::after{box-shadow:inset 0 0 0 2px var(--focus),0 0 0 3px var(--accent-soft)}
.choice-card:has(> input:disabled){cursor:not-allowed;opacity:.5;transform:none}
.choice-card:has(> input[aria-invalid="true"])::after{box-shadow:inset 0 0 0 1.5px var(--bad-text)}

/* ── 설정 목록 행: 왼쪽 제목·설명, 오른쪽 스위치·선택기·값 ──
   행 전체를 label 로 만들면 어디를 눌러도 스위치가 바뀐다(스위치는 label 이 아니라 span.switch 로). */
.settings{display:flex;flex-direction:column}
.settings__row{display:flex;align-items:center;gap:var(--s-12);min-height:var(--s-64);padding:var(--s-12) var(--s-16);color:inherit;
  -webkit-tap-highlight-color:transparent;transition:background-color var(--d-1) var(--ease-inout)}
.settings__row + .settings__row{box-shadow:inset 0 1px 0 var(--line)}
:is(label,a,button).settings__row{cursor:pointer;width:100%;text-align:left}
:is(label,a,button).settings__row:active{background:var(--tint)}
@media (hover:hover){:is(a,button).settings__row:hover{background:var(--tint)}}
.settings__lead{flex:none;display:grid;place-items:center;width:var(--s-40);height:var(--s-40);border-radius:var(--r-md);background:var(--canvas-alt);color:var(--ink-muted)}
.settings__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-2)}
.settings__title{font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);font-weight:600;color:var(--ink);word-break:keep-all}
.settings__desc{font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted);word-break:keep-all;overflow-wrap:anywhere;text-wrap-style:pretty}
.settings__control{flex:none;display:flex;align-items:center;gap:var(--s-8)}
.settings__value{flex:none;font-size:var(--t-body-sm);font-weight:600;color:var(--ink-muted);white-space:nowrap}
.settings__row .switch{flex:none}
.settings__row .picker{flex:none;width:auto;min-width:calc(var(--s-96) + var(--s-24))}
/* 선택기가 길면 설명 아래로 내려 폭을 다 쓴다 */
.settings__row--stack{flex-direction:column;align-items:stretch}
.settings__row--stack .picker{width:100%}
.settings__row--danger .settings__title{color:var(--bad-text)}

/* ── 요금제 카드(앱 설정·관리자 사이드바) ──────────
   지금 쓰는 요금제는 2px 강조 테두리. 사용량은 .meter 막대. */
.plan-cards{display:grid;gap:var(--s-12)}
@media (min-width:768px){.plan-cards{grid-template-columns:repeat(3,minmax(0,1fr))}}
.plan-card{position:relative;display:flex;flex-direction:column;gap:var(--s-12);padding:var(--s-20);border-radius:var(--r-xl);background:var(--surface);color:var(--ink-body)}
.plan-card::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;box-shadow:inset 0 0 0 1px var(--line)}
.plan-card--current::after{box-shadow:inset 0 0 0 2px var(--accent)}
.plan-card__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-8)}
.plan-card__name{display:inline-flex;align-items:center;gap:var(--s-6);font-size:var(--t-body-md);line-height:var(--t-body-md-lh);font-weight:700;color:var(--ink)}
.plan-card__price{font-size:var(--t-heading-md);line-height:var(--t-heading-md-lh);letter-spacing:var(--t-heading-md-ls);font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.plan-card__price small{margin-left:var(--s-2);font-size:var(--t-body-sm);font-weight:500;letter-spacing:0;color:var(--ink-muted)}
.plan-card__meta{font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted)}
.plan-card__usage{display:flex;flex-direction:column;gap:var(--s-6)}
.plan-card__usage-row{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-8);font-size:var(--t-caption);color:var(--ink-muted)}
.plan-card__usage-row strong{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.plan-card__list{display:flex;flex-direction:column;gap:var(--s-6);margin:0;padding:0;list-style:none;font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh)}
.plan-card__list li{display:flex;align-items:flex-start;gap:var(--s-8)}
.plan-card__list .ico{flex:none;margin-top:var(--s-2);color:var(--accent-text)}
.plan-card__actions{display:flex;gap:var(--s-8);margin-top:auto}
.plan-card__actions .btn{flex:1;min-width:0}
/* 작게(사이드바 맨 아래): 여백을 줄이고 면을 한 단계 올린다 */
.plan-card--compact{gap:var(--s-8);padding:var(--s-12) var(--s-14, 14px);border-radius:var(--r-lg);background:var(--surface)}
.plan-card--compact .plan-card__name{font-size:var(--t-body-xs)}

/* 비율 막대: 길이는 --p(0~100). 색 막대 옆에 늘 숫자를 함께 쓴다. */
.meter{position:relative;height:6px;border-radius:var(--r-full);background:var(--tint);overflow:hidden}
.meter__fill{position:absolute;inset:0 auto 0 0;width:calc(var(--p, 0) * 1%);border-radius:inherit;background:var(--accent);
  transition:width var(--d-5) var(--ease-out)}
/* 채움은 채움용 역할 토큰(--good --warn --bad). 글자용(--*-text)은 막대로 칠하면 탁하다. */
.meter__fill--good{background:var(--good)}
.meter__fill--warn{background:var(--warn)}
.meter__fill--bad{background:var(--bad)}
/* 막대 행: 이름 · 막대 · 값 세 칸 */
.bar-rows{display:flex;flex-direction:column;gap:var(--s-10);margin:0;padding:0;list-style:none}
.bar-row{display:grid;grid-template-columns:minmax(64px,112px) minmax(0,1fr) auto;align-items:center;gap:var(--s-12);
  min-height:var(--s-24);font-size:var(--t-caption);line-height:var(--t-caption-lh)}
.bar-row__label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink-body)}
.bar-row__value{min-width:3.5em;text-align:end;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.bar-row--wide{grid-template-columns:minmax(96px,160px) minmax(0,1fr) auto}

/* ── 단계 입력(한 화면 한 질문) ─────────────────────
   .flow 안에 .flow__step 을 여러 개 두고 지금 것만 보인다. 넘어갈 때 방향(data-enter)에 맞춰 옆에서 들어온다. */
.flow{display:flex;flex-direction:column}
.flow__step{display:flex;flex-direction:column;gap:var(--s-28);padding-top:var(--s-12)}
.flow__head{display:flex;flex-direction:column;gap:var(--s-8)}
.flow__title{font-size:var(--t-heading-lg);line-height:var(--t-heading-lg-lh);letter-spacing:var(--t-heading-lg-ls);font-weight:700;color:var(--ink)}
.flow__desc{font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);color:var(--ink-muted)}
@media (max-width:640px){.flow__title{font-size:var(--t-heading-md);line-height:var(--t-heading-md-lh);letter-spacing:var(--t-heading-md-ls)}}
.flow__step[data-enter="forward"]{animation:flow-in-next var(--d-4) var(--ease-out) both}
.flow__step[data-enter="back"]{animation:flow-in-prev var(--d-4) var(--ease-out) both}
@keyframes flow-in-next{from{opacity:0;transform:translateX(var(--s-24))}to{opacity:1;transform:none}}
@keyframes flow-in-prev{from{opacity:0;transform:translateX(calc(var(--s-24) * -1))}to{opacity:1;transform:none}}
/* 단계 입력 안에서 코어 조각을 폭 가득히 */
.flow .amount{padding:0}
.flow .segment{display:flex}
.flow .segment__item{flex:1;min-width:0;padding-inline:var(--s-8)}
.flow .cal{margin-inline:auto}
.flow__sub{display:flex;flex-direction:column;gap:var(--s-10)}
.flow__label{font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);font-weight:600;color:var(--ink-body)}

/* 밑줄 큰 입력칸: 한 가지만 묻는 화면(휴대폰 번호·이름)에서 쓴다. 포커스하면 밑줄이 강조색이 된다. */
.line-input{display:block;width:100%;min-height:var(--c-xl-h);padding:var(--s-8) 0;border:0;border-radius:0;background:transparent;
  box-shadow:inset 0 -2px 0 var(--line-strong);color:var(--ink);font-size:var(--t-heading-sm);line-height:1.4;font-weight:600;
  letter-spacing:-.01em;font-variant-numeric:tabular-nums;appearance:none;transition:box-shadow var(--d-2) var(--ease-inout)}
.line-input::placeholder{color:var(--ink-disabled);font-weight:500}
.line-input:focus,.line-input:focus-visible{outline:none;box-shadow:inset 0 -2px 0 var(--accent)}
.line-input[aria-invalid="true"]{box-shadow:inset 0 -2px 0 var(--bad-text)}

/* 오류 상태: 수량 조절(정원 초과 등)도 입력칸과 같은 면·테두리로 */
.stepper-num[aria-invalid="true"]{background:var(--bad-soft);box-shadow:inset 0 0 0 1.5px var(--bad-text)}

/* ══ 3. 인증 화면 ═══════════════════════════════════════
   휴대폰: 화면 전체가 한 장. 768 이상: 한 단계 들어간 바탕 위에 가운데 카드. */
.auth{min-height:100dvh;display:flex;flex-direction:column;background:var(--canvas)}
.auth__bar{display:flex;align-items:center;justify-content:space-between;gap:var(--s-8);height:calc(var(--s-48) + var(--s-4));
  padding:var(--safe-t) var(--s-8) 0}
.auth__card{flex:1;display:flex;flex-direction:column;gap:var(--s-28);width:100%;max-width:calc(var(--w-sm) - var(--s-160));margin-inline:auto;
  padding:var(--s-8) var(--s-20) calc(var(--safe-b) + var(--s-24))}
.auth__head{display:flex;flex-direction:column;gap:var(--s-8)}
.auth__phone{font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.auth__step{display:flex;flex-direction:column;gap:var(--s-24)}
.auth__step[hidden]{display:none}
.auth__resend{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:var(--s-4) var(--s-8);text-align:center;
  font-size:var(--t-body-xs);color:var(--ink-muted);font-variant-numeric:tabular-nums}
/* 기다리는 동안은 남은 시간, 끝나면 '문자가 오지 않았나요?' 로 바뀐다(dashboard.js 가 data-done 을 붙인다) */
.auth__resend [data-countdown][data-done]{display:none}
.auth__resend [data-countdown]:not([data-done]) ~ .auth__late{display:none}
.auth__or{display:flex;align-items:center;gap:var(--s-12);margin:0;color:var(--ink-subtle);font-size:var(--t-caption);line-height:var(--t-caption-lh)}
.auth__or::before,.auth__or::after{content:"";flex:1;height:1px;background:var(--line)}
.auth__social{display:flex;flex-direction:column;gap:var(--s-8)}
.auth__social .btn{justify-content:center;gap:var(--s-10)}
.auth__social .provider-mark{width:var(--s-20);height:var(--s-20)}
.auth__social .provider-mark--kakao{padding:3px;border-radius:var(--r-xs)}
.auth__foot{margin-top:auto;text-align:center;font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-subtle);word-break:keep-all}
.auth__foot a{color:var(--ink-muted);text-decoration:underline;text-underline-offset:3px}
@media (min-width:768px){
  .auth{align-items:center;justify-content:center;padding:var(--s-48) var(--s-24);background:var(--canvas-alt)}
  .auth__bar{position:fixed;top:0;left:0;right:0;padding-inline:var(--s-24);height:var(--s-64)}
  .auth__card{flex:none;width:min(440px,100%);max-width:none;min-height:560px;padding:var(--s-40);border-radius:var(--r-3xl);
    background:var(--surface);box-shadow:var(--sh-lg)}
}

/* ══ 4. 오류 페이지(404·500) ═════════════════════════════
   레이아웃·스크립트 없이 열린다. 빛무리(.aurora)는 CSS 만으로 그린다. */
.error-page{position:relative;isolation:isolate;min-height:100dvh;display:grid;place-items:center;overflow:clip;
  padding:calc(var(--safe-t) + var(--s-48)) var(--s-20) calc(var(--safe-b) + var(--s-48));background:var(--canvas)}
.error-page > .aurora{z-index:-1}
.error-page__inner{width:100%;max-width:calc(var(--w-sm) - var(--s-80));display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-16)}
.error-page__panel{width:100%}
.error-page__brand{display:inline-flex;align-items:center;min-height:var(--c-md-h);margin-bottom:var(--s-24)}
.error-page__art{display:grid;place-items:center;width:var(--s-64);height:var(--s-64);border-radius:var(--r-xl);
  background:var(--surface);color:var(--accent-text);box-shadow:var(--sh-md)}
.error-page__art--bad{color:var(--bad-text)}
.error-page__code{font-size:var(--t-caption);line-height:var(--t-caption-lh);font-weight:600;letter-spacing:.04em;color:var(--ink-subtle)}
.error-page__title{font-size:var(--t-heading-xl);line-height:var(--t-heading-xl-lh);letter-spacing:var(--t-heading-xl-ls);font-weight:700;color:var(--ink)}
.error-page__desc{font-size:var(--t-body-md);line-height:var(--t-body-md-lh);color:var(--ink-muted);max-width:30em}
.error-page__actions{display:flex;flex-wrap:wrap;gap:var(--s-8);margin-top:var(--s-8)}
.error-page__help{width:100%;margin-top:var(--s-8)}
.error-page__meta{font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-subtle)}
.error-page__meta code{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:var(--t-micro);padding:var(--s-2) var(--s-6);border-radius:var(--r-xs);background:var(--tint);color:var(--ink-body)}
@media (max-width:640px){.error-page__title{font-size:var(--t-heading-lg);line-height:var(--t-heading-lg-lh);letter-spacing:var(--t-heading-lg-ls)}}

/* ══ 5. 관리자 골격 ═════════════════════════════════════
   .admin > (.admin__sidebar · .admin__main(.admin__topbar · .admin__content) · .admin__context?)
   사이드바 240. 1023px 이하에서는 왼쪽 서랍(ds.js 의 data-drawer 를 그대로 쓴다).
   오른쪽 컨텍스트 열은 선택. 1280 부터 셋째 열, 그 아래 폭에서는 본문 밑으로 내려간다. */
.admin{--admin-side-w:240px;--admin-ctx-w:320px;--admin-top-h:56px;
  display:grid;grid-template-columns:minmax(0,1fr);min-height:100dvh;background:var(--canvas);color:var(--ink-body)}
.admin__main{min-width:0;display:flex;flex-direction:column}
/* hidden 속성이 display 를 가진 클래스(.btn .empty .bulkbar)보다 앞서게 한다(필터·빈 상태·선택 바를 hidden 으로 바꾼다) */
.admin [hidden]{display:none !important}
/* 본문 덩어리 사이 간격은 여기서 한 번만 준다(덩어리가 여백을 모르게) */
.admin__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-20);padding:var(--s-20) var(--s-16) var(--s-48)}
.admin__content > .page-head{margin-bottom:0}

/* 사이드바: 좁은 폭에서는 서랍. 서랍 판은 왼쪽에서 나온다. */
.admin__sidebar .drawer__panel{left:0;right:auto;width:min(84vw,280px);translate:-100% 0;display:flex;flex-direction:column;
  padding:calc(var(--safe-t) + var(--s-12)) var(--s-12) calc(var(--safe-b) + var(--s-16));background:var(--canvas-alt)}
.admin__sidebar[data-open] .drawer__panel{translate:0 0}
/* 메뉴가 길면(작업공간의 프로젝트 나무) 판 안에서 스크롤한다. 줄어들지 않게 해야 버튼·카드가 눌려 납작해지지 않는다. */
.admin__sidebar .drawer__panel > *{flex-shrink:0}
.admin__brand{display:flex;align-items:center;gap:var(--s-8);min-height:var(--c-md-h);padding-inline:var(--s-10);margin-bottom:var(--s-4);
  border-radius:var(--r-sm);color:var(--ink);transition:background-color var(--d-1) var(--ease-inout)}
.admin__brand:hover{background:var(--tint)}
.admin__brand-name{padding-left:var(--s-8);box-shadow:inset 1px 0 0 var(--line-strong);font-size:var(--t-body-xs);font-weight:500;line-height:1.2;color:var(--ink-muted);white-space:nowrap}
.admin__nav{display:flex;flex-direction:column;gap:var(--s-4)}
.admin__nav-group{display:flex;flex-direction:column;gap:2px;margin-top:var(--s-12)}
.admin__nav-label{padding:var(--s-4) var(--s-10);font-size:var(--t-nano);line-height:var(--t-nano-lh);font-weight:600;letter-spacing:.06em;color:var(--ink-muted)}
.admin__nav-item{display:flex;align-items:center;gap:var(--s-10);min-height:var(--c-md-h);padding-inline:var(--s-10);border-radius:var(--r-sm);
  font-size:var(--t-body-xs);font-weight:500;color:var(--ink-muted);
  transition:background-color var(--d-1) var(--ease-inout),color var(--d-1) var(--ease-inout)}
.admin__nav-item:hover{background:var(--tint);color:var(--ink)}
.admin__nav-item[aria-current="page"]{background:var(--accent-soft);color:var(--accent-text);font-weight:600}
.admin__nav-item .ico{transition:transform var(--d-2) var(--ease-out)}
.admin__nav-item:hover .ico{transform:translateX(1px)}
.admin__nav-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.admin__nav-count{display:inline-grid;place-items:center;min-width:20px;height:20px;padding-inline:var(--s-6);border-radius:var(--r-full);
  background:var(--tint);font-size:var(--t-nano);font-weight:600;line-height:1;color:var(--ink-body);font-variant-numeric:tabular-nums}
.admin__nav-count--live{background:var(--good-soft);color:var(--good-text)}
.admin__nav-count--bad{background:var(--bad-soft);color:var(--bad-text)}
.admin__nav-item[aria-current="page"] .admin__nav-count{background:var(--surface);color:var(--accent-text)}
.admin__side-foot{margin-top:auto;padding-top:var(--s-20);display:flex;flex-direction:column;gap:var(--s-12)}
.admin__keys{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-6) var(--s-8);margin:0;padding:0 var(--s-10)}
.admin__keys > div{display:flex;align-items:center;gap:var(--s-6);min-width:0}
.admin__keys dt{display:flex;gap:2px;flex:none}
.admin__keys dd{margin:0;font-size:var(--t-micro);color:var(--ink-muted);white-space:nowrap}

/* 상단바: 흐린 유리. 좁은 폭에서는 검색이 숨 쉴 자리를 먼저 준다.
   흐림(backdrop-filter)은 가상 요소에 준다. 상단바 자체에 주면 그 안의 fixed 메뉴(사용자 메뉴·휴대폰 하단 시트)의
   기준 상자가 상단바로 바뀌어 메뉴가 엉뚱한 자리(화면 위쪽·사이드바 폭만큼 오른쪽)에 뜬다. */
.admin__topbar{position:sticky;top:0;z-index:var(--z-header);display:flex;align-items:center;gap:var(--s-8);
  height:var(--admin-top-h);padding-inline:var(--s-8) var(--s-12);box-shadow:inset 0 -1px 0 var(--line)}
.admin__topbar::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:color-mix(in srgb,var(--canvas) 86%,transparent);
  backdrop-filter:saturate(160%) blur(12px);-webkit-backdrop-filter:saturate(160%) blur(12px)}
.admin__search{flex:1;min-width:0;max-width:520px}
.admin__search .searchbar{margin:0;height:var(--c-md-h);padding-inline:var(--s-12);background:var(--tint);color:var(--ink-subtle);cursor:text}
.admin__search .searchbar:focus-within{background:var(--surface);box-shadow:inset 0 0 0 1.5px var(--accent)}
.admin__search .searchbar__input{text-overflow:ellipsis}
.admin__search .kbd{display:none;flex:none}
.admin__tools{display:flex;align-items:center;gap:var(--s-2);margin-left:auto}
.admin__user .btn{gap:var(--s-8);padding-inline:var(--s-4)}
.admin__user-name,.admin__user .btn > .ico{display:none}
.admin__avatar{display:grid;place-items:center;background:var(--accent-soft);color:var(--accent-text);font-size:var(--t-caption);font-weight:600}
/* 메뉴 머리의 메일 주소는 대문자로 바꾸지 않는다(코어 .menu__label 은 영문 눈썹용 대문자) */
.admin__user .menu__label{text-transform:none;letter-spacing:0;font-size:var(--t-caption);font-weight:500;color:var(--ink-muted)}
/* 테마 버튼: 지금 테마의 반대 아이콘을 보인다(밝을 때 달, 어두울 때 해). 관리자·인증 어디서나 쓴다. */
.theme-ico-sun{display:none}
:root[data-theme="dark"] .theme-ico-moon{display:none}
:root[data-theme="dark"] .theme-ico-sun{display:inline-block}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .theme-ico-moon{display:none}
  :root:not([data-theme="light"]) .theme-ico-sun{display:inline-block}
}
/* 관리자 위쪽 막대(shared/_admin_topbar)의 아이콘 이름 admin__theme-dark(달)·admin__theme-light(해)도 같은 규칙. 두 아이콘이 함께 보이던 결함 */
.admin__theme-light{display:none}
:root[data-theme="dark"] .admin__theme-dark{display:none}
:root[data-theme="dark"] .admin__theme-light{display:inline-block}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .admin__theme-dark{display:none}
  :root:not([data-theme="light"]) .admin__theme-light{display:inline-block}
}
@media (min-width:768px){
  .admin__user .btn{padding-inline:var(--s-8) var(--s-10)}
  .admin__user-name{display:inline}
  .admin__user .btn > .ico{display:inline-block}
}

/* 오른쪽 컨텍스트 열(선택) */
.admin__context{min-width:0;padding:var(--s-20) var(--s-16) var(--s-40);background:var(--canvas-alt);box-shadow:inset 0 1px 0 var(--line)}

@media (min-width:1024px){
  .admin{grid-template-columns:var(--admin-side-w) minmax(0,1fr)}
  /* 서랍을 고정 열로 바꾼다. 서랍 덮개는 쓰지 않는다. */
  .admin__sidebar.drawer{position:sticky;inset:auto;top:0;z-index:auto;height:100dvh;visibility:visible}
  .admin__sidebar .drawer__scrim{display:none}
  .admin__sidebar .drawer__panel{position:relative;inset:auto;width:auto;height:100%;translate:none;transition:none;
    padding:var(--s-16) var(--s-12);box-shadow:inset -1px 0 0 var(--line)}
  .admin__nav-item{min-height:36px}
  .admin__menu{display:none}
  .admin__topbar{padding-inline:var(--s-28)}
  .admin__search{min-width:240px}
  .admin__search .searchbar{height:38px}
  .admin__search .kbd{display:inline-flex}
  .admin__content{padding:var(--s-24) var(--s-28) var(--s-64)}
  .admin:has(> .admin__context) .admin__context{grid-column:2}
  /* 컨텍스트가 본문 아래 줄로 내려가도 사이드바는 두 줄에 걸쳐 끝까지 붙어 있게 */
  .admin:has(> .admin__context) > .admin__sidebar{grid-row:span 2}
}
@media (min-width:1280px){
  .admin:has(> .admin__context){grid-template-columns:var(--admin-side-w) minmax(0,1fr) var(--admin-ctx-w)}
  .admin:has(> .admin__context) .admin__context{grid-column:auto;position:sticky;top:0;height:100dvh;overflow-y:auto;
    padding:var(--s-20);box-shadow:inset 1px 0 0 var(--line)}
}
@media (min-width:1600px){.admin{--admin-ctx-w:360px}}

/* ══ 5-2. 작업공간(layouts/workspace.html.erb) ═══════════════
   관리자 골격 위에 사이드바 조각만 더한다: 새로 만들기 버튼, 이름표 옆 작은 버튼, 펼치는 나무(프로젝트 → 하위 폴더).
     .admin__nav-toggle(button, aria-expanded · aria-controls) + .admin__nav-sub(id, hidden 으로 접힌다) > .admin__nav-item
   나무의 하위 항목은 한 칸 들여 쓰고 왼쪽에 안내 선을 둔다. 접고 펴기는 화면 스크립트가 aria-expanded 와 hidden 을 함께 바꾼다. */
.admin__new{margin:var(--s-4) 0 var(--s-4)}
.admin__nav-label:has(> .btn){display:flex;align-items:center;justify-content:space-between;gap:var(--s-8);padding-block:0}
.admin__nav-label > .btn{margin-right:calc(var(--s-6) * -1);color:var(--ink-subtle)}
.admin__nav-toggle{width:100%;text-align:left;cursor:pointer}
.admin__nav-toggle > .ico:first-child{flex:none;width:var(--s-16);height:var(--s-16);color:var(--ink-subtle);transition:rotate var(--d-2) var(--ease-out)}
.admin__nav-toggle[aria-expanded="false"] > .ico:first-child{rotate:-90deg}
.admin__nav-sub{position:relative;display:flex;flex-direction:column;gap:2px}
.admin__nav-sub::before{content:"";position:absolute;left:calc(var(--s-10) + 7px);top:var(--s-4);bottom:var(--s-4);width:1px;background:var(--line-strong)}
.admin__nav-sub > .admin__nav-item{padding-left:calc(var(--s-10) + var(--s-24))}
.admin__nav-item:focus-visible{outline-offset:-2px}
@media (prefers-reduced-motion:reduce){.admin__nav-toggle > .ico:first-child{transition:none}}

/* 화면이 바뀌면 본문 덩어리가 짧게 떠오른다(처음 몇 개만 40ms 간격).
   끝난 뒤 스타일을 남기지 않는다(backwards). transform 이 남으면 쌓임 맥락이 생겨 fixed 메뉴가 덮개 밑에 깔린다. */
.admin__content > *{animation:rise var(--d-3) var(--ease-out) backwards}
.admin__content > :nth-child(2){animation-delay:40ms}
.admin__content > :nth-child(3){animation-delay:80ms}
.admin__content > :nth-child(n+4){animation-delay:120ms}

/* 화면 제목 줄: 제목·한 줄 설명 | 오른쪽 동작(주버튼은 하나) */
.page-head{display:flex;align-items:flex-end;justify-content:space-between;flex-wrap:wrap;gap:var(--s-12) var(--s-16);margin-bottom:var(--s-20)}
/* 글 칸이 먼저 줄어 설명이 줄바꿈되고, 동작 버튼은 16rem 보다 좁아질 때만 아래로 내려간다(글꼴이 늦게 와도 줄이 덜 흔들린다) */
.page-head__text{display:flex;flex-direction:column;gap:var(--s-4);min-width:0;flex:1 1 16rem}
.page-head__title{font-size:var(--t-heading-md);line-height:var(--t-heading-md-lh);letter-spacing:var(--t-heading-md-ls);font-weight:700;color:var(--ink)}
.page-head__sub{font-size:var(--t-body-xs);line-height:var(--t-body-xs-lh);color:var(--ink-muted)}
.page-head__actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s-8)}
/* 휴대폰: 동작 줄이 폭을 다 쓴다. 분절은 한 줄 가득, 버튼은 아래 줄에 같은 폭으로 */
@media (max-width:639px){
  .page-head__actions{width:100%}
  .page-head__actions > .segment{display:flex;flex:1 1 100%}
  .page-head__actions > .segment .segment__item{flex:1;min-width:0}
  .page-head__actions > .btn{flex:1 1 0;min-width:0}
}

/* 관리자 격자: --2-1 넓은 칸 하나 + 좁은 칸 하나(1280 부터), --auto 카드 폭 280 이상으로 채움 */
.board{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-16);align-items:start}
.board--auto{grid-template-columns:repeat(auto-fit,minmax(min(280px,100%),1fr))}
@media (min-width:1280px){.board--2-1{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}
.admin:has(> .admin__context) .board--2-1{grid-template-columns:minmax(0,1fr)}
@media (min-width:1600px){.admin:has(> .admin__context) .board--2-1{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}

/* 카드 머리: 제목·보조 | 오른쪽 링크·분절 하나 */
.card-head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--s-4) var(--s-12);margin-bottom:var(--s-16)}
.card-head__text{display:flex;flex-direction:column;gap:2px;min-width:0}
.card-head__title{font-size:var(--t-body-sm);line-height:var(--t-body-sm-lh);font-weight:600;color:var(--ink)}
.card-head__sub{font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted)}
.card-head .link{font-size:var(--t-caption)}
.card--dense{padding:var(--s-16) var(--s-20)}
.card-note{margin-top:var(--s-16);font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted)}
.admin .empty .btn{margin-top:var(--s-8)}

/* ── 숫자 타일 ──────────────────────────────────────
   2열 → 768 3열 → 1280 6열(--4 는 4열). 오른쪽 컨텍스트 열이 있으면 1600 까지 3열로 둔다.
   늘거나 준 것은 색만이 아니라 부호(+·−)와 글자로 함께 쓴다. */
.kpi{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-12)}
@media (min-width:768px){.kpi{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:1280px){.kpi{grid-template-columns:repeat(6,minmax(0,1fr))}.kpi--4{grid-template-columns:repeat(4,minmax(0,1fr))}.kpi--3{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:1280px) and (max-width:1599px){.admin:has(> .admin__context) .kpi{grid-template-columns:repeat(3,minmax(0,1fr))}}
.kpi__tile{position:relative;display:flex;flex-direction:column;gap:var(--s-4);min-width:0;padding:var(--s-16);border-radius:var(--r-lg);
  background:var(--surface);color:inherit;
  transition:background-color var(--d-2) var(--ease-inout),transform var(--d-2) var(--ease-out)}
.kpi__tile::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;box-shadow:inset 0 0 0 1px var(--line)}
a.kpi__tile:hover{background:var(--surface-alt);transform:translateY(-2px)}
.kpi__label{display:flex;align-items:center;gap:var(--s-6);min-width:0;font-size:var(--t-caption);line-height:var(--t-caption-lh);color:var(--ink-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kpi__value{font-size:var(--t-heading-lg);line-height:1.15;letter-spacing:-.02em;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap;
  animation:fade-in var(--d-4) var(--ease-out) backwards}
.kpi__value--good{color:var(--good-text)}
.kpi__value--bad{color:var(--bad-text)}
.kpi__unit{margin-left:var(--s-2);font-size:var(--t-body-sm);font-weight:500;letter-spacing:0;color:var(--ink-muted)}
.kpi__foot{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s-2) var(--s-6);min-width:0}
.kpi__delta{display:inline-flex;align-items:center;gap:2px;font-size:var(--t-micro);line-height:var(--t-micro-lh);font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.kpi__delta .ico{width:12px;height:12px}
/* 좋고 나쁨(색)과 오르내림(화살표)을 따로 준다. 노쇼가 줄면 화살표는 아래, 색은 좋음. */
.kpi__delta--good{color:var(--good-text)}
.kpi__delta--bad{color:var(--bad-text)}
.kpi__delta--flat{color:var(--ink-muted)}
.kpi__delta--fall .ico{rotate:180deg}
.kpi__sub{font-size:var(--t-micro);line-height:var(--t-micro-lh);color:var(--ink-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ── 필터 줄: 검색·선택기·날짜. 바꾸면 곧바로 목록이 바뀐다(제출 버튼 없이). ── */
.filters{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s-8)}
.filters .input-group{flex:1 1 220px;max-width:320px}
.filters .input{min-width:0;min-height:var(--c-sm-h);padding-block:6px;border-radius:var(--c-sm-r)}
.filters .input-group .input{padding-left:38px}
.filters .input-group__icon{left:12px}
.filters .picker{flex:none;width:auto;min-width:128px}
.filters__spacer{flex:1}
.filters__count{font-size:var(--t-caption);color:var(--ink-muted);font-variant-numeric:tabular-nums;white-space:nowrap}
/* 검색칸의 브라우저 기본 지우기(×)는 디자인과 어긋난다. 지우기는 data-clearable(extras)로 붙인다. */
:is(.admin,.app-page) input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
/* 손가락으로 쓰는 폭에서는 누르는 높이를 44 로 */
@media (max-width:1023px){
  .filters .input,.filters .picker__btn{min-height:var(--c-md-h)}
  .filters .input-group{flex-basis:100%;max-width:none}
  .filters .picker{flex:1 1 128px}
}

/* ── 표 카드와 조밀한 표 ────────────────────────────
   .card.card--line.table-card > (.table-card__head · .table-scroll > table.table.table-dense · .table-card__foot)
   행 선택: tr[data-row] 을 누르면 상세, 키보드 J·K 로 옮긴 행은 data-selected. 체크한 행은 연한 강조 면. */
.table-card{padding:var(--s-4) var(--s-16) var(--s-4)}
.table-card__head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--s-10) var(--s-12);padding-block:var(--s-12)}
/* 상태 분절이 좁은 폭보다 길면 옆으로 민다(줄바꿈으로 칸이 찌그러지지 않게) */
.table-card__head > .segment{max-width:100%;overflow-x:auto;scrollbar-width:none}
.table-card__head > .segment::-webkit-scrollbar{display:none}
.table-card__head > .segment .segment__item{flex:none;white-space:nowrap}
.table-card__foot{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--s-8) var(--s-12);
  padding-block:var(--s-12);box-shadow:inset 0 1px 0 var(--line)}
/* 표 안의 sr-only(절대 위치)가 가로 스크롤 상자 밖으로 새어 페이지를 넓히지 않게 */
.table-card .table-scroll{position:relative}
.table-dense{font-size:var(--t-body-xs);line-height:var(--t-body-xs-lh)}
.table-dense th{padding:var(--s-10) var(--s-12) var(--s-8) 0;letter-spacing:.02em}
.table-dense td{padding:var(--s-8) var(--s-12) var(--s-8) 0;line-height:1.45;word-break:keep-all}
.table-dense :is(td,th):last-child{padding-right:0}
/* 행 면(올림·고름)이 칸 끝에 딱 붙지 않게 첫 칸과 끝 칸에 안쪽 여백을 둔다. 고른 행의 띠(3px)는 이 여백 안에 그린다 */
.table-dense :is(td,th):first-child{padding-left:var(--s-12)}
.table-dense :is(td,th):last-child{padding-right:var(--s-12)}
.table-dense tbody tr[data-row]{cursor:pointer;transition:background-color var(--d-1) var(--ease-inout),box-shadow var(--d-1) var(--ease-inout)}
.table-dense tbody tr[data-row]:hover{background:var(--tint)}
.table-dense tbody tr:has(.td-check input:checked){background:var(--accent-soft)}
.table-dense tbody tr[data-selected]{background:var(--accent-soft)}
.table-dense tbody tr[data-selected] > :first-child{box-shadow:inset 3px 0 0 var(--accent)}
.table-dense tbody tr[data-selected] td{color:var(--ink)}
.table-dense tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px;border-radius:0}
.table-dense tr.is-dim td{color:var(--ink-muted)}
.table-dense tfoot td{padding-top:var(--s-10);font-weight:600;color:var(--ink);box-shadow:inset 0 1px 0 var(--line-strong)}
.table-dense .td-num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.table-dense .td-nowrap{white-space:nowrap}
.table-dense .td-wrap{min-width:200px;max-width:420px;overflow-wrap:anywhere}
.table-dense .td-muted{color:var(--ink-muted)}
.table-dense .td-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:var(--t-caption);white-space:nowrap}
/* 말줄임: 폭을 넘는 글은 한 줄에서 자르고, 전체는 title 로 보인다 */
.table-dense .td-clip{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.table-dense .td-actions{text-align:right;white-space:nowrap}
.table-dense .td-actions .btn + .btn{margin-left:var(--s-4)}
.table-dense .td-check{width:36px;padding-block:0}
.table-dense .td-check .choice{min-height:var(--c-md-h);gap:0}
.table-dense .td-check .choice input{width:18px;height:18px}
/* 표 칸 두 줄(제목 + 작은 부제)과 사람 칸(아바타 + 이름) */
.cell-stack{display:flex;flex-direction:column;gap:2px;min-width:0}
.cell-stack__title{color:var(--ink);font-weight:500}
.cell-stack__sub{font-size:var(--t-micro);line-height:var(--t-micro-lh);color:var(--ink-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cell-person{display:flex;align-items:center;gap:var(--s-10);min-width:0}
.cell-person .avatar{display:grid;place-items:center;font-size:var(--t-nano);font-weight:700;color:var(--ink-muted)}
.link-quiet{color:inherit;transition:color var(--d-1) var(--ease-inout)}
.link-quiet:hover{color:var(--accent-text)}
/* 좁은 폭에서 표를 카드 목록으로(--cards). 칸마다 data-label 을 두면 작은 이름 아래 값이 오는 2열 격자가 된다.
   .td-primary(제목)·.td-wrap·.td-clip(긴 글)·.td-actions 는 한 줄을 다 쓴다. .td-check 는 왼쪽 위. */
@media (max-width:767px){
  .table-dense--cards thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  .table-dense--cards,.table-dense--cards tbody{display:block;width:auto}
  .table-dense--cards tbody tr{position:relative;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-10) var(--s-16);
    padding:var(--s-16) var(--s-12);box-shadow:inset 0 -1px 0 var(--line)}
  .table-dense--cards tbody tr:last-child{box-shadow:none}
  .table-dense--cards tbody tr[data-row]:hover{box-shadow:inset 0 -1px 0 var(--line)}
  .table-dense--cards tbody tr[data-row]:hover{background:none}
  /* 고른 카드: 연한 강조 면 + 왼쪽 띠(행의 좌우 여백 12px 안에 그려 체크 칸과 겹치지 않는다) */
  .table-dense--cards tbody tr[data-selected]{box-shadow:inset 3px 0 0 var(--accent),inset 0 -1px 0 var(--line)}
  .table-dense--cards tbody tr:has(.td-check){padding-left:calc(var(--s-12) + var(--s-40))}
  .table-dense--cards td{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0;padding:0;box-shadow:none !important;text-align:left}
  .table-dense--cards td[data-label]::before{content:attr(data-label);font-size:var(--t-micro);line-height:var(--t-micro-lh);color:var(--ink-muted)}
  .table-dense--cards :is(.td-primary,.td-wrap,.td-clip,.td-actions){grid-column:1 / -1}
  .table-dense--cards .td-primary{padding-bottom:var(--s-2)}
  .table-dense--cards .td-primary .cell-stack__title{font-size:var(--t-body-sm);font-weight:600}
  .table-dense--cards .td-check{position:absolute;left:var(--s-12);top:var(--s-6);width:auto}
  .table-dense--cards .td-actions{flex-direction:row;justify-content:flex-end}
  .table-dense--cards .td-actions:empty{display:none}
  .table-dense--cards .td-num{text-align:left}
  .table-dense--cards :is(.td-wrap,.td-clip){min-width:0;max-width:none;white-space:normal;overflow:visible}
  .table-dense--cards .cell-stack__sub{white-space:normal}
}

/* 고른 행 동작 바: 체크하면 표 아래에 떠서 화면 아래에 붙는다(반전 면이라 어느 바탕에도 읽힌다) */
.bulkbar{position:sticky;bottom:calc(var(--safe-b) + var(--s-16));z-index:var(--z-raised);display:flex;align-items:center;gap:var(--s-8);
  width:max-content;max-width:100%;margin:var(--s-12) auto 0;padding:var(--s-6) var(--s-6) var(--s-6) var(--s-16);border-radius:var(--r-full);
  background:var(--surface-invert);color:var(--ink-invert);box-shadow:var(--sh-xl);animation:rise var(--d-3) var(--ease-out) both}
.bulkbar__count{font-size:var(--t-body-sm);font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.bulkbar .btn--ghost{color:var(--ink-invert)}
.bulkbar .btn--ghost:hover:not(:disabled){background:color-mix(in srgb,var(--ink-invert) 14%,transparent);color:var(--ink-invert)}

/* ── 오른쪽 상세 시트 ───────────────────────────────
   휴대폰에서는 코어 시트 그대로(아래에서 올라옴). 768 부터 오른쪽에서 밀려 나오는 높이 가득 판. */
.sheet--side .sheet__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-12)}
.sheet--side .sheet__body{display:flex;flex-direction:column;gap:var(--s-24)}
.sheet__sec{display:flex;flex-direction:column;gap:var(--s-10)}
.sheet__sec-title{font-size:var(--t-caption);line-height:var(--t-caption-lh);font-weight:600;letter-spacing:.02em;color:var(--ink-muted)}
@media (min-width:768px){
  .sheet--side{left:auto;right:0;top:0;bottom:0;translate:none;width:min(440px,calc(100vw - var(--s-48)));height:100%;max-height:none;
    border-radius:var(--r-2xl) 0 0 var(--r-2xl)}
  .sheet--side .sheet__head{padding-top:var(--s-24)}
  .overlay[data-state="open"] .sheet--side{animation:side-in var(--d-5) var(--ease-sheet) both}
  .overlay[data-state="closing"] .sheet--side{animation:side-out var(--d-3) var(--ease-in) both}
}
@keyframes side-in{from{transform:translateX(100%)}to{transform:none}}
@keyframes side-out{from{transform:none}to{transform:translateX(100%)}}

/* 시트 안 목록: 행이 시트 폭을 다 쓰고 글자는 제목과 같은 선에서 시작한다(누를 때 덮개가 가장자리까지) */
.sheet__body > .list{margin-inline:calc(var(--s-24) * -1)}
.sheet__body > .list > .row-item{padding-inline:var(--s-24)}

/* 상태 글자: 점 + 글자를 나란히(색만으로 알리지 않는다) */
.status{display:inline-flex;align-items:center;gap:var(--s-6);white-space:nowrap}
.txt-good{color:var(--good-text)}
.txt-bad{color:var(--bad-text)}
.txt-warn{color:var(--warn-text)}

/* ══ 모션 축소 ══════════════════════════════════════════
   움직임만 멈추고 내용은 완료 상태로 둔다. */
@media (prefers-reduced-motion:reduce){
  :is(.app__body,.ptr__body) > .container > *,.admin__content > *,.kpi__value,.flow__step,.bulkbar,
  .stepper__item[data-live] .stepper__mark{animation:none !important}
  .choice-card,a.kpi__tile{transform:none !important}
}
