/* 낙폭 관제 — 레이아웃.
 *
 * 화면은 보유 원장 하나다. 컬럼 제목을 클릭해 정렬하며, 종목명으로 정렬하면
 * 같은 종목이 모이고 계좌로 정렬하면 같은 계좌가 모인다.
 *
 * 반응형 전략은 "표를 줄이는 것"이 아니라 "레이아웃을 바꾸는 것"이다.
 *   ≥1100px  컬럼 헤더가 페이지 헤더 아래에 고정
 *   900–1099 가로 스크롤 (헤더 고정 없음)
 *   <900px   행 → 카드 전환. 본문은 가로로 절대 스크롤하지 않는다.
 *
 * 이 화면의 주제는 '최고가 대비'다. 손절 판단의 근거이므로 종목마다 보이고,
 * 황동 워시를 깐 전용 칸을 갖는다.
 */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--abyss);
  color: var(--chalk);
  font-family: var(--han);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* 수치는 등폭 숫자로. 자릿수가 세로로 맞아야 금액 크기를 눈으로 견줄 수 있다. */
.fig {
  font-family: var(--din);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.mono { font-family: var(--mono); letter-spacing: .02em; }

/* 라벨. 자간을 좁게 둔다 — 넓은 자간은 한글에서 글자가 흩어져 읽기 어렵다. */
.lab {
  font-family: var(--din);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--track-label);
  color: var(--faint);
}

button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }

:focus-visible { outline: 2px solid var(--brass); outline-offset: 3px; }

/* 스크린리더 전용 텍스트.
 *
 * `clip`만 쓰면 요소는 여전히 자기 정적 위치에 놓인다. 표 안(가로로 스크롤되는
 * .scroller 내부)에 있으면 그 위치가 뷰포트 밖이라, 클리핑 조상이 있어도
 * 문서 전체 폭을 밀어낸다 — 1000px 화면에서 문서가 1203px가 되어 **페이지가
 * 통째로 가로 스크롤**됐다. `clip-path`로 실제 그리기를 잘라내고 좌상단에
 * 못 박아 폭에 관여하지 못하게 한다. */
.sr-only {
  position: absolute; top: 0; left: 0;
  width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

.shell { max-width: 1400px; margin: 0 auto; padding: 0 var(--sp-6) var(--sp-12); }

/* ── 계기 헤더 ──────────────────────────────────────────────────────── */
.top {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  height: var(--header-h);
  padding: 0 var(--sp-6);
  background: color-mix(in srgb, var(--abyss) 88%, transparent);
  backdrop-filter: saturate(160%) blur(10px);
  border-bottom: 1px solid var(--rule);
}

.ident { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; }

.ident .mark {
  font-family: var(--din);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--brass);
  padding: 3px 7px;
  border: 1px solid var(--brass-dim);
  flex: none;
}

.ident h1 { margin: 0; font-size: .96rem; font-weight: 600; letter-spacing: -.01em; white-space: nowrap; }

.ident .en {
  font-family: var(--din);
  font-size: .7rem;
  letter-spacing: var(--track-label);
  color: var(--faint);
  white-space: nowrap;
}

.top .grow { flex: 1; }

.readout { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.28; }

.readout .v {
  font-family: var(--din);
  font-size: .9rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.lamp { display: inline-flex; align-items: center; gap: var(--sp-2); }

.lamp i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--faint);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--faint) 20%, transparent);
  flex: none;
}

.lamp[data-state="live"] i {
  background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
  animation: pulse 2.6s var(--ease) infinite;
}
.lamp[data-state="polling"] i { background: var(--brass); box-shadow: 0 0 0 3px var(--brass-wash); }
.lamp[data-state="error"] i { background: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 22%, transparent); }

@keyframes pulse { 50% { box-shadow: 0 0 0 7px transparent; } }

.icon-btn {
  font-family: var(--din);
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  color: var(--slate);
  border: 1px solid var(--rule);
  padding: 6px 10px;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.icon-btn:hover { color: var(--chalk); border-color: var(--brass-dim); }

/* ── 히어로: 숫자 하나와 그 산식 ────────────────────────────────────── */
/* 큰 숫자 하나와, 그 숫자가 나온 분수. 둘뿐이므로 양끝으로 벌린다 —
   1fr을 주면 분수가 남는 폭을 전부 먹어 라벨과 숫자가 화면 양끝으로
   찢어진다(space-between이라 사이가 비어 버린다). */
.lede {
  display: grid;
  grid-template-columns: auto minmax(0, max-content);
  justify-content: space-between;
  align-items: center;
  gap: 0 var(--sp-10);
  padding: var(--sp-10) 0 var(--sp-8);
}

.headline { display: flex; align-items: flex-start; gap: 5px; }

.headline .num {
  font-family: var(--din);
  font-size: clamp(3.6rem, 8vw, 5.8rem);
  font-weight: 600;
  line-height: .84;
  letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
}

.headline .pc {
  font-family: var(--din);
  font-size: clamp(1.1rem, 1.8vw, 1.55rem);
  font-weight: 500;
  color: var(--brass);
  margin-top: .36em;
}

.headline-cap { margin-top: var(--sp-4); }

/* 산식을 실제 분수로 세운다. 황동 한 줄이 분수선.
   황동선을 애니메이션으로 쓸어 그리지 않는다 — 합성이 멈춘 환경에서
   페이지 핵심 요소가 영구히 안 보이게 된다. */
.ratio { display: grid; gap: var(--sp-1); min-width: 12rem; }

.ratio .term { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-5); }
.ratio .term .t { font-size: var(--fs-sm); color: var(--slate); white-space: nowrap; }
.ratio .term .n { font-family: var(--din); font-size: var(--fs-md); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ratio .term .n .u { font-size: .72rem; color: var(--slate); margin-left: 2px; }
.ratio .bar { height: 0; border-top: 2px solid var(--brass); }

/* ── 계기 띠 — 시장 지수와 집계 ──────────────────────────────────────
 *
 * 위아래로 맞붙는 두 띠(지수 3칸 · 집계 3칸)다. 서로 다른 규칙으로 적어 두면
 * 패딩 한 단계 차이만으로도 타일 높이가 어긋나 두 개의 다른 물건처럼 보인다
 * (실제로 70px vs 82px로 어긋나 있었다). 그래서 **기하는 여기 한 곳에서만**
 * 정의하고, 아래에서는 각자 다른 것만 덧붙인다.
 *
 * 황동은 어느 쪽에도 쓰지 않는다 — 황동은 위 분수선과 % 에만 남아야
 * 헤드라인이 이 화면의 주인공으로 유지된다.
 */
.gauges,
.totals {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border-top: 1px solid var(--rule);
}

/* 두 띠를 하나의 계기판으로 묶는 아래 테두리. 집계가 마지막 칸이다. */
.totals { border-bottom: 1px solid var(--rule); }

/* 자식 선택자를 쓴다. `.totals div`로 두면 안쪽 .v 까지 셀 패딩을 물려받는다. */
.gauge,
.totals > div {
  padding: var(--sp-3) var(--sp-5);
  background: var(--abyss);
  min-width: 0;
}

.gauge .v,
.totals .v {
  margin-top: 2px;
  font-family: var(--din);
  font-size: var(--fs-md);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 라벨 줄 높이를 못 박는다. 지수 쪽에만 '종가' 칩이 붙는데, 칩이 줄 높이를
   1px 밀면 두 띠의 타일 높이가 어긋난다 — 붙어 있는 띠라 그게 보인다. */
.gauge .lab,
.totals .lab { line-height: 1.3; min-height: 1.3em; }

.gauges[hidden] { display: none; }

.gauge .lab { display: flex; align-items: baseline; gap: var(--sp-2); color: var(--faint); }

/* 장 마감 칩 — 원장의 '전일종가'와 같은 계열의 어휘다. */
.gauge .tag { flex: none; padding: 1px 4px; font-size: .58rem; }

/* 지수만의 차이: 값과 등락률이 한 줄에 나란히 선다. */
.gauge .v { display: flex; align-items: baseline; gap: var(--sp-3); }

/* 마감된 값은 '지금'이 아니라 '그때'다. 읽기 전에 톤으로 먼저 갈린다. */
.gauge[data-state="close"] .v { color: var(--slate); }

.gauge .d { font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
/* 색만으로 정보를 전달하지 않는다 — ▲▼를 항상 병기한다(pctCell과 동일). */
.gauge .d .s { font-size: .82em; margin-right: .1em; }

/* 반응형도 두 띠를 항상 함께 움직인다. 한쪽만 줄이면 붙어 있는 띠가
   서로 다른 물건처럼 보인다 — 실제로 그렇게 어긋나 있었다. */
@media (max-width: 899px) {
  .gauge,
  .totals > div { padding: var(--sp-3) var(--sp-4); }
}

@media (max-width: 560px) {
  /* 375px 칸은 약 113px다. '1억 8,414만원'이 여기 들어가려면 --fs-md로도
     모자란다 — 두 띠가 같은 크기를 유지하도록 함께 내린다. */
  .gauge,
  .totals > div { padding: var(--sp-3) var(--sp-3); }
  .gauge .v,
  .totals .v { font-size: var(--fs-base); }
  /* 이 폭에서는 '7,437.63 ▲1.66%'가 한 줄에 안 들어간다. 두 줄로 눕힌다. */
  .gauge .v { display: block; }
  .gauge .d { display: block; margin-top: 1px; }
}

/* 320px(구형 소형 단말)에서는 칸이 약 95px까지 좁아져 '1억 8,373만원'이
   --fs-base로도 잘린다. 잘린 금액은 숫자가 아니라 거짓말이므로 한 번 더 내린다. */
@media (max-width: 380px) {
  .gauge .v,
  .totals .v { font-size: var(--fs-sm); }
  .gauge, .totals > div { padding: var(--sp-3) var(--sp-2); }
}

/* ── 집계 — 조용한 세 칸 ────────────────────────────────────────────
   기하와 값 조판은 위 '계기 띠' 블록에서 지수와 함께 정의한다. */
.totals .u { font-size: .72rem; font-weight: 400; color: var(--slate); margin-left: 3px; }

/* 금액은 전체 표기와 축약 표기를 함께 그려 두고 폭에 따라 하나만 보여준다.
   좁은 화면에서 '184,207,676원'은 잘려서 첫 자리만 남는데, 그건 숫자가
   아니라 거짓말이다. '1억 8,420만원'이 잘린 숫자보다 정확하다. */
.totals .short { display: none; }

/* ── 도구모음 ───────────────────────────────────────────────────────── */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-8) 0 var(--sp-4);
}

.field {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: 2.1rem;
  padding: 0 var(--sp-3);
  border: 1px solid var(--rule);
  background: var(--hull);
  color: var(--slate);
  font-size: var(--fs-sm);
  transition: border-color var(--dur-fast) var(--ease);
}

.field:focus-within { border-color: var(--brass-dim); }
.field input, .field select { border: none; background: none; outline: none; min-width: 0; color: var(--chalk); }
.field input::placeholder { color: var(--faint); }
.field.search { flex: 1 1 13rem; max-width: 22rem; }
.field.search input { flex: 1; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 2.1rem;
  padding: 0 var(--sp-4);
  border: 1px solid var(--rule);
  background: var(--hull);
  color: var(--chalk);
  font-size: var(--fs-sm);
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.btn:hover:not(:disabled) { background: var(--hull-2); border-color: var(--brass-dim); }

.btn.primary { background: var(--brass); border-color: var(--brass); color: #12100B; font-weight: 600; }
.btn.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--brass) 86%, #fff); }

.btn.ghost { border-color: transparent; background: none; color: var(--slate); }
.btn.ghost:hover:not(:disabled) { color: var(--chalk); background: var(--hull); }

.btn.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 38%, transparent); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn .key { font-family: var(--din); font-size: .68rem; opacity: .6; }


/* ── 원장 ───────────────────────────────────────────────────────────── */
.ledger-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-2); }
.ledger-head h2 { margin: 0; font-family: var(--din); font-size: .7rem; font-weight: 600; letter-spacing: .2em; color: var(--brass); }

.scroller { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }

/* 컬럼 라벨.
 *
 * 여기서 사고가 났다. `overflow-x: auto`인 .scroller 안에서 sticky를 쓰면 기준이
 * 뷰포트가 아니라 .scroller 가 된다(overflow-x만 줘도 overflow-y가 auto로
 * 계산되어 스크롤 컨테이너가 된다). 그래서 `top: var(--header-h)`가 헤더를
 * 스크롤러 상단에서 52px **아래로** 밀어 첫 행을 정확히 그만큼 덮었다.
 *
 * 스크롤은 전체 화면이 잡으므로, 좁은 폭에서는 고정을 포기하고(가로 스크롤이
 * 필요하니 컨테이너를 유지) 넓은 폭에서만 컨테이너를 없애 뷰포트 기준으로
 * 고정한다 — 아래 min-width: 1100px 블록 참고.
 */
thead th {
  z-index: 10;
  padding: var(--sp-3) var(--sp-3);
  background: var(--abyss);
  border-bottom: 1px solid var(--brass-dim);
  font-family: var(--din);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--track-label);
  color: var(--slate);
  text-align: right;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}

thead th.l { text-align: left; }
thead th.static { cursor: default; }

/* 이 화면의 주제 컬럼. */
thead th.subject { color: var(--brass); }

thead th .arrow { display: inline-block; width: .75em; margin-left: .15em; color: var(--brass); opacity: 0; }
thead th[aria-sort] .arrow { opacity: 1; }

tbody td {
  padding: var(--sp-3);
  border-bottom: 1px solid var(--rule-soft);
  text-align: right;
  white-space: nowrap;
  vertical-align: middle;
  font-variant-numeric: tabular-nums;
}

tbody td.l { text-align: left; }
tbody tr.row:hover td { background: var(--row-hover); }

/* 종목명은 유일하게 길이를 예측할 수 없는 칸이다. 펀드 정식명은
   '삼성 글로벌액티브 적격TDF 2040 증권투자신탁H[주식혼합-재간접형]_Cpe(퇴직연금)'
   처럼 40자를 넘는다. 그대로 두면 표가 뷰포트를 밀어내 페이지 전체가 가로로
   스크롤된다 — 나머지 칸은 전부 숫자라 폭이 고정이므로, 여기만 잘라 준다.
   전체 이름은 title 속성으로 남는다. */
tbody td.l .nm {
  display: block;
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

tbody .nm { font-weight: 500; }
tbody .sub { display: block; font-family: var(--din); font-size: .67rem; letter-spacing: .03em; color: var(--faint); }

/* 한정어 — 앞 숫자에 붙는 괄호 주석.
 *
 * 이 화면의 보조정보는 성격이 셋이고, 조판으로 그 차이를 드러낸다.
 *   별도 줄 + 색  → 등락률.   오늘의 별개 측정값이라 스스로 한 줄을 갖는다.
 *   별도 줄 + 등폭 → 종목코드. 식별자다.
 *   괄호 + 작게   → 보유기간·비중. **앞 숫자 없이는 뜻이 없는 한정어다.**
 *
 * em 으로 잡아 표(작은 글자)와 카드(큰 글자) 어디서든 주값과의 비율이
 * 일정하게 유지된다. 괄호 자체를 한 단계 더 흐리게 두어, 읽을 때 숫자가
 * 먼저 들어오고 괄호는 배경으로 물러난다. */
.ann {
  font-family: var(--din);
  font-size: .78em;
  font-variant-numeric: tabular-nums;
  color: var(--faint);
  white-space: nowrap;
  margin-left: .35em;
}

/* 표에서는 괄호에 고정폭 트랙을 준다.
   `(5.6%)`와 `(15.6%)`는 한 자 차이지만, 오른쪽 정렬 열에서 그 한 자가 앞의
   금액 끝자리를 통째로 밀어 자릿수 정렬을 깬다. 폭을 못 박고 괄호를 왼쪽에
   붙이면 금액 끝은 고정되고 괄호는 늘 같은 자리에서 시작한다. */
tbody .ann {
  display: inline-block;
  min-width: 4.4em;
  text-align: left;
}

/* 카드는 한 장씩 독립이라 열 정렬이라는 개념이 없다. 고정폭이 오히려 빈
   공간을 만들므로 내용 폭 그대로 둔다. */
.card .ann { display: inline; min-width: 0; }

/* 자리만 잡는 빈 괄호는 카드에서 여백조차 남기지 않는다. */
.card .ann:empty { margin-left: 0; }

/* .sub 가 뒤에 오면서 .mono 를 덮는다. 종목코드는 등폭이어야 한다. */
tbody .sub.mono { font-family: var(--mono); font-size: .69rem; }






.tag {
  display: inline-block;
  font-family: var(--din);
  font-size: .62rem;
  letter-spacing: var(--track-label);
  padding: 2px 6px;
  border: 1px solid var(--rule);
  color: var(--slate);
  white-space: nowrap;
}

.tag.usd { border-color: var(--brass-dim); color: var(--brass); }
/* 펀드 — 실시간이 아닌 일 1회 기준가 자산임을 행 단위로 밝힌다 */
.tag.fund { border-color: var(--brass-dim); color: var(--brass); }

.up { color: var(--up); }
.down { color: var(--down); }
.flat { color: var(--slate); }
.muted { color: var(--faint); }

.pct { font-family: var(--din); font-weight: 600; font-variant-numeric: tabular-nums; }
.pct .s { font-size: .78em; margin-right: .12em; }

/* 최고가 대비 — 손절 판단의 근거. 종목마다 전용 칸을 주고 황동 워시로 열을 표시한다. */
td.subject { background: var(--brass-wash); border-left: 1px solid var(--rule-soft); }

td.subject .of { display: block; font-size: .7rem; color: var(--slate); }

td.subject .v {
  font-family: var(--din);
  font-size: var(--fs-md);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}



.stale-dot {
  display: inline-block;
  width: 5px; height: 5px;
  margin-left: .35em;
  border-radius: 50%;
  background: var(--brass);
  vertical-align: middle;
}

.row-actions { display: flex; justify-content: flex-end; gap: var(--sp-1); opacity: 0; transition: opacity var(--dur-fast) var(--ease); }
tr:hover .row-actions, tr:focus-within .row-actions { opacity: 1; }

.row-actions button {
  font-family: var(--din);
  font-size: .66rem;
  letter-spacing: var(--track-label);
  padding: 3px 6px;
  color: var(--faint);
}

.row-actions button:hover { background: var(--hull-2); color: var(--chalk); }

/* 시세 갱신 플래시 — 전체 리렌더 대신 바뀐 셀만 알린다. */
@keyframes flash { from { background: var(--flash); } to { background: transparent; } }
td.flash, .card .flash { animation: flash 700ms var(--ease); }

/* ── 정렬 바 (모바일) ───────────────────────────────────────────────
   카드 뷰에는 클릭할 컬럼 헤더가 없으므로 정렬을 여기서 고른다.
   데스크톱에서는 컬럼 제목이 그 일을 하니 숨긴다. */
.sort-bar { display: none; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.sort-bar .field { flex: 1; }
.sort-bar .field select { flex: 1; }
.sort-bar .lab { flex: none; }

/* ── 카드 (모바일) ──────────────────────────────────────────────────── */
.cards { display: none; flex-direction: column; gap: var(--sp-3); }

.card { border: 1px solid var(--rule); background: var(--hull); padding: var(--sp-4); }



.card .card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); }
.card .card-title { min-width: 0; }
.card .card-title .name { font-size: var(--fs-md); font-weight: 600; letter-spacing: -.01em; }
.card .card-title .meta { margin-top: 3px; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); color: var(--faint); }

.card .card-return { text-align: right; flex: none; }
.card .card-return .pct { font-size: var(--fs-lg); letter-spacing: -.02em; }
.card .card-return .amt { display: block; font-family: var(--din); font-size: .68rem; color: var(--slate); }

/* 카드 맨 위 오른쪽 — 현재가와 전일대비.
   모바일에서 종목을 훑을 때 "지금 얼마인가"가 가장 먼저 읽혀야 한다.
   .card-return 과 같은 조판을 쓰되 값이 숫자라 .fig 를 쓴다. */
/* 세로 두 줄 — 위는 현재가+전일대비, 아래는 (펀드만) 기준 표시.
 *
 * flex-wrap 으로 감싸면 안 된다. 줄바꿈 flex 컨테이너의 max-content 는 모든
 * 항목을 **한 줄에 늘어놓은 합**으로 계산돼서, 실제로는 두 줄로 접히는데도
 * 폭은 201px 로 잡혔다. 그만큼 종목명 자리가 96px 로 밀려 펀드 이름이 넉 줄로
 * 깨졌다. 줄을 명시적으로 나누면 각 줄의 max-content 만 본다. */
.card .card-price {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: none;
}
/* 현재가와 전일대비. 위 지수 띠(.gauge .v)와 같은 조판이다 — baseline 정렬이라
   크기가 달라도 밑선이 맞는다. */
.card .card-price .line {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: var(--sp-2);
}
.card .card-price .fig {
  font-family: var(--din);
  font-size: var(--fs-lg);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
/* 현재가보다 한 단계 아래. 같은 줄에 있으니 크기 차이가 위계를 만든다. */
.card .card-price .amt {
  font-family: var(--din);
  font-size: var(--fs-md);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* 기준 표시 — 둘째 줄. 값이 아니라 '이 값이 무엇인가'를 말하므로 가장 흐리게
   두고, 첫 줄에 바짝 붙여 한 덩어리로 읽히게 한다. */
.card .card-price .basis {
  margin-top: -2px;
  font-family: var(--din);
  font-size: .68rem;
  letter-spacing: .03em;
  color: var(--faint);
}
/* 수익률은 이제 그리드 칸 안에 있다 — 칸 값 크기에 맞춘다. */
.card .grid .cell .pct { font-size: var(--fs-md); }
.card .grid .cell .amt { display: block; font-family: var(--din); font-size: .68rem; color: var(--slate); }

/* 카드에서도 최고가 대비는 전용 칸을 갖는다 — 종목마다 보여야 하는 값이다. */
.card .level-box {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  padding: var(--sp-2) var(--sp-3);
  background: var(--brass-wash);
  border-left: 2px solid var(--brass);
}

.card .level-box .k { font-family: var(--din); font-size: var(--fs-xs); letter-spacing: var(--track-label); color: var(--brass); }
.card .level-box .v { font-family: var(--din); font-size: var(--fs-md); font-weight: 600; font-variant-numeric: tabular-nums; }

.card .grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3) var(--sp-4);
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule-soft);
}

.card .cell .k { font-family: var(--din); font-size: var(--fs-xs); letter-spacing: .1em; color: var(--faint); }
.card .cell .v { margin-top: 2px; font-weight: 500; font-variant-numeric: tabular-nums; }
.card .cell.wide { grid-column: 1 / -1; }

.card .card-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-4); }
.card .card-actions .btn { flex: 1; justify-content: center; height: 1.95rem; }

/* ── 빈 상태 / 배너 ─────────────────────────────────────────────────── */
.panel { border: 1px solid var(--rule); background: var(--hull); }

.empty { padding: var(--sp-12) var(--sp-6); text-align: center; color: var(--slate); }
.empty h2 { margin: 0 0 var(--sp-2); font-size: var(--fs-md); color: var(--chalk); font-weight: 600; }
.empty p { margin: 0 auto var(--sp-5); max-width: 34rem; font-size: var(--fs-sm); line-height: 1.7; }

.banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  margin-top: var(--sp-4);
  border: 1px solid var(--brass-dim);
  background: var(--brass-wash);
  font-size: var(--fs-sm);
}

.banner.hidden { display: none; }

.skeleton { height: 1em; background: var(--skeleton); background-size: 200% 100%; animation: shimmer 1.4s linear infinite; }
@keyframes shimmer { to { background-position: -200% 0; } }

/* ── 모달 ───────────────────────────────────────────────────────────── */
dialog {
  padding: 0;
  border: 1px solid var(--rule);
  background: var(--hull);
  color: var(--chalk);
  box-shadow: var(--shadow);
  max-width: min(30rem, calc(100vw - 2rem));
  width: 100%;
}

dialog::backdrop { background: var(--backdrop); backdrop-filter: blur(3px); }

.modal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-5) var(--sp-5) var(--sp-3); }
.modal-head h2 { margin: 0; font-size: var(--fs-md); font-weight: 600; }

form.modal-body { display: grid; gap: var(--sp-4); padding: 0 var(--sp-5) var(--sp-5); }
.form-row { display: grid; gap: var(--sp-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }

label.f { display: grid; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--slate); min-width: 0; }

label.f > input, label.f > select {
  height: 2.3rem;
  padding: 0 var(--sp-3);
  border: 1px solid var(--rule);
  background: var(--hull-2);
  color: var(--chalk);
  outline: none;
  transition: border-color var(--dur-fast) var(--ease);
}

label.f > input:focus, label.f > select:focus { border-color: var(--brass); }
label.f > input:disabled { color: var(--faint); }
label.f .hint-inline { color: var(--faint); font-size: var(--fs-xs); }

/* 종목 검색 콤보박스 — 마스터 캐시에서만 찾으므로 타이핑이 네트워크를 타지 않는다. */
.combo { position: relative; display: block; }

.combo .results {
  position: absolute;
  top: calc(100% + 3px);
  left: 0; right: 0;
  z-index: 5;
  max-height: 15rem;
  overflow-y: auto;
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--brass-dim);
  background: var(--hull);
  box-shadow: var(--shadow);
}

.combo .results li {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  cursor: pointer;
  border-bottom: 1px solid var(--rule-soft);
}

.combo .results li:last-child { border-bottom: none; }
.combo .results li[aria-selected="true"], .combo .results li:hover { background: var(--brass-wash); }

.combo .results .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--chalk); font-weight: 500; }
.combo .results .cd { font-family: var(--mono); font-size: .69rem; color: var(--slate); flex: none; }
.combo .results .mk { font-family: var(--din); font-size: var(--fs-xs); letter-spacing: var(--track-label); color: var(--faint); flex: none; }
.combo .results .none { color: var(--faint); cursor: default; }

.hint { font-size: var(--fs-xs); color: var(--faint); min-height: 1.15em; line-height: 1.4; }
.hint.err { color: var(--danger); }
.hint.ok { color: var(--slate); }

.modal-actions { display: flex; justify-content: flex-end; gap: var(--sp-2); padding-top: var(--sp-1); }

/* ── 로그인 관문 ────────────────────────────────────────────────────── */
.gate-body { display: grid; place-items: center; min-height: 100vh; padding: var(--sp-6); }

.gate-card {
  width: min(26rem, 100%);
  border: 1px solid var(--rule);
  border-top: 2px solid var(--brass);
  background: var(--hull);
  padding: var(--sp-8) var(--sp-6) var(--sp-6);
  box-shadow: var(--shadow);
}

.gate-card .ident { margin-bottom: var(--sp-5); }
.gate-card .ident h1 { font-size: var(--fs-lg); }

.gate-lead {
  margin: 0 0 var(--sp-6);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--slate);
}

.gate-lead b { color: var(--chalk); font-weight: 600; }

.gate-btn { width: 100%; justify-content: center; height: 2.5rem; }

.gate-btn span[aria-hidden] {
  font-family: var(--din);
  font-weight: 700;
  margin-right: var(--sp-1);
}

.gate-note {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--faint);
}

.gate-note code { font-family: var(--mono); color: var(--slate); }

.gate-error { margin: 0 0 var(--sp-5); align-items: flex-start; }
.gate-error code { font-family: var(--mono); }

/* 헤더의 계정 칩 — 인증이 켜졌을 때만 그려진다 */
.account { display: flex; align-items: center; gap: var(--sp-2); }

.account img {
  width: 1.5rem; height: 1.5rem;
  border-radius: 50%;
  border: 1px solid var(--rule);
}

.account .who {
  font-size: var(--fs-xs);
  color: var(--slate);
  max-width: 11rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── 토스트 ─────────────────────────────────────────────────────────── */
.toasts {
  position: fixed;
  left: 50%;
  bottom: var(--sp-6);
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: min(27rem, calc(100vw - 2rem));
  pointer-events: none;
}

.toast {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--rule);
  border-left: 2px solid var(--brass);
  background: var(--hull);
  box-shadow: var(--shadow);
  font-size: var(--fs-sm);
}

.toast.err { border-left-color: var(--danger); color: var(--danger); }

/* ── 푸터 ───────────────────────────────────────────────────────────── */
.foot {
  margin-top: var(--sp-10);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-xs);
  color: var(--faint);
  line-height: 1.85;
}

.foot p { margin: 0 0 var(--sp-2); max-width: 84ch; }
.foot b { color: var(--slate); font-weight: 500; }

/* ── 도입 모션: 한 번만, 조용히 ─────────────────────────────────────
   황동 분수선을 쓸어 그리는 연출을 넣었다가 뺐다. 애니메이션이 진행되지
   않는 환경에서 페이지 핵심 요소가 영구히 안 보이게 된다. 화면의 뜻이
   모션에 의존하면 안 된다. */
@media (prefers-reduced-motion: no-preference) {
  .lede { animation: lift .5s var(--ease) backwards; }
  @keyframes lift { from { opacity: 0; transform: translateY(10px); } }
}

/* 표가 컨테이너 안에 들어가는 폭(≥1100px)에서는 가로 스크롤이 필요 없다.
   오버플로 컨테이너를 없애면 sticky가 뷰포트를 기준으로 잡히므로, 페이지를
   스크롤해도 컬럼 라벨이 페이지 헤더 바로 아래에 남는다. */
/* 표가 컨테이너 안에 온전히 들어가는 폭에서만 스크롤 컨테이너를 없애 헤더를
   뷰포트 기준으로 고정한다.
   임계값을 1100px로 뒀더니 1100~1239px 구간에서 페이지 전체가 가로로
   스크롤됐다 — 컬럼 11개의 자연 폭이 약 1,191px인데 그 폭에서 내용 영역은
   1,052px뿐이라, overflow가 visible이 되는 순간 표가 뷰포트를 밀어냈다.
   1240px = 표 1,191px + .shell 좌우 여백 48px. */
@media (min-width: 1240px) {
  .scroller { overflow-x: visible; }

  thead th {
    position: sticky;
    top: var(--header-h);
  }
}

/* ── 반응형 ─────────────────────────────────────────────────────────── */
@media (max-width: 1240px) {
  .lede { gap: var(--sp-6) var(--sp-8); }
}


@media (max-width: 899px) {
  .shell { padding: 0 var(--sp-4) var(--sp-10); }
  .ledger-panel { display: none; }
  .cards { display: flex; }
  .sort-bar:not([hidden]) { display: flex; }
  .lede { grid-template-columns: minmax(0, 1fr); gap: var(--sp-5); padding: var(--sp-6) 0 var(--sp-5); }
  .headline .num { font-size: clamp(3.2rem, 18vw, 4.6rem); }
  .ident .en { display: none; }
  .top { padding: 0 var(--sp-4); gap: var(--sp-3); }
  .top .grow { flex: 1 0 auto; }
  /* 두 계기 띠의 반응형은 위 '계기 띠' 블록에 함께 모아 두었다 —
     여기서 .totals만 따로 손대면 지수 띠와 다시 어긋난다. */
  .form-row { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  /* 칸이 3개라 2열로 두면 마지막 하나가 혼자 둘째 줄에 떨어진다.
     바로 위 지수 스트립도 3칸이므로 열을 맞추면 두 띠가 나란히 선다. */
  .totals { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .totals .u { font-size: .66rem; margin-left: 2px; }
  /* 잘린 금액을 보여주느니 축약형을 쓴다 — '1억 8,414만원'이 '184,20…'보다 정확하다. */
  .totals .full { display: none; }
  .totals .short { display: inline; }
  .field.search { flex-basis: 100%; max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

@media print {
  .top, .toolbar, .row-actions, .card-actions, .toasts { display: none !important; }
  body { background: #fff; color: #000; }
}

/* 관리자 화면 — 표 안의 작은 POST 폼들을 한 줄에 세운다. */
.inline-form { display: inline; }
.inline-form button {
  font: inherit; font-size: var(--fs-xs);
  padding: 3px 8px; border: 1px solid var(--rule);
  background: none; color: var(--slate); cursor: pointer;
}
.inline-form button:hover { border-color: var(--brass-dim); color: var(--chalk); }
.inline-form button.danger:hover { border-color: var(--down); color: var(--down); }
.banner .inline-form { margin-left: auto; }
