/* ═══════════════════════════════════════════════════════════
   디자인 토큰
   ───────────────────────────────────────────────────────────
   겉면은 애플(절제된 무채색·큰 모서리·경계선 위주),
   뼈대는 Fluent(밀도 높은 표·분명한 계층)를 따른다.

   가장 중요한 원칙: 여백은 바깥, 밀도는 안쪽.
   페이지 가장자리와 카드 사이는 넉넉하게, 표와 폼 안쪽은 빽빽하게.
   사정률 구간표는 40행이 넘어서 표에 여백을 주면 도구가 못 쓰게 된다.

   강조색은 하나만 쓴다. 상태 색(경고·성공·위험)은 의미가 있을 때만.
   ═══════════════════════════════════════════════════════════ */

:root {
  /* ── 윈도우 대비 ──
     맥은 스크롤바가 콘텐츠 위에 겹쳐 떠서 폭을 0px 먹지만,
     윈도우는 15~17px 를 실제로 잡아먹는다. 1280px 창의 본문이 ~1263px 가 된다.
     stable 로 두면 스크롤이 생기든 말든 자리를 미리 비워, 목록을 거를 때마다
     스크롤바가 나타났다 사라지며 표 전체가 좌우로 흔들리는 일이 없다.
     같은 이유로 폭 계산에 100vw 를 쓰지 않는다 — vw 는 스크롤바를 포함한 값이라
     윈도우에서 항상 15~17px 넘친다. (현재 사용처 0곳) */
  scrollbar-gutter: stable;

  /* 체크박스·라디오·range 를 브라우저 기본 파랑이 아니라 이 앱의 강조색으로.
     윈도우 크롬/엣지는 맥과 다른 기본색·모양으로 그린다. */
  accent-color: var(--c-accent);

  /* ── 표면 4단계 ── 페이지 < 카드 < 카드 안 중첩 순으로 뜬다 */
  --c-bg:        #f4f6f9;   /* 페이지 바탕 — 옅은 청회색 */
  --c-surface:   #ffffff;   /* 카드 — 순백 */
  --c-surface-2: #eef2f7;   /* 카드 안 중첩·표 머리 */
  --c-input:     #ffffff;   /* 입력칸 */
  --c-line:      rgba(20, 26, 34, 0.12);
  --c-line-2:    rgba(20, 26, 34, 0.20);

  /* ── 잉크 3단계 ── 기본 / 보조 / 흐림 */
  --c-text:   #141a22;
  --c-text-2: #4a5568;
  /* 흰 카드 위 대비 5.01:1 — 계산해서 고른 값이다(눈짐작 금지).
     표 머리(#eef2f7) 위에서도 이 색은 5.0 이라 안전하다. 옛 #75808f 은 흰 위 4.01 로 미달이었다. */
  --c-text-3: #667080;

  /* ── 강조 ── 하나만 */
  --c-accent:      #2f6fed;
  --c-accent-hov:  #2560d8;
  --c-accent-soft: rgba(47, 111, 237, 0.10);
  --c-on-accent:   #ffffff;

  /* ── 상태 ── 의미가 있을 때만 쓴다 */
  --c-warn-bg: #fff8e6;  --c-warn-line: #e8c26a;  --c-warn-text: #6b4e00;
  --c-ok-bg:   #e9f5ed;  --c-ok-line:   #b6ddc4;  --c-ok-text:   #1a6b3c;
  --c-bad-bg:  #fdecec;  --c-bad-line:  #f0bcbc;  --c-bad-text:  #c62828;

  /* ── 여백 ── 4의 배수로만.
     관계 있는 것끼리 가깝게: 라벨↔입력칸 6, 필드↔필드 16, 섹션↔섹션 32 */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;

  /* ── 타이포 5단계 ──
     인접 단계가 1~2px 차이면 위계가 아니다. 확실히 벌린다. */
  --f-hero:  48px;   /* 히어로 숫자. 한 화면에 하나만 */
  --f-stat:  26px;   /* 스탯 타일의 값. 히어로는 아니지만 본문보다는 읽혀야 하는 숫자 */
  --f-title: 21px;   /* 카드 제목 */
  --f-body:  14px;   /* 본문·값 */
  --f-label: 12px;   /* 라벨 (보조 잉크). 본문과 2px 벌려 층을 확실히 나눈다 */
  --f-note:  12px;   /* 보조 설명 (흐린 잉크). 크기는 라벨과 같고 색(text-3)으로 물러난다 */

  /* ── 숫자 글꼴 ──
     큰 단독 숫자(.v.big 48px, .v.mid 36px, 스탯 26px)가 쓰는 글꼴이다.
     여기 한 줄만 바꾸면 세 자리가 같이 따라온다. 다른 데 적지 마라.

     **세리프(ui-serif / New York)에서 시스템 산세리프로 되돌렸다.**
     이 앱의 큰 숫자에는 '9,739건' · '896,202,702원' 처럼 **한글 단위가 늘 붙는다.**
     라틴 전용 글꼴을 걸면 숫자는 그 글꼴로, 한글은 대체 글꼴로 떨어져
     한 덩어리 안에서 획과 폭이 갈린다.
     실측(48px, canvas measureText)으로 Georgia 가 그 모습을 그대로 보여준다:
       Georgia            숫자 104.9 (고유)   '건' 41.5 (대체됨)
       Apple SD Gothic Neo 숫자 100.9         '건' 41.5
     숫자만 Georgia 이고 '건' 은 한글 글꼴로 떨어진다는 뜻이다.
     New York·SF Rounded·SF Mono 는 헤드리스에서 아예 안 잡혀 같은 방식으로는 재지 못했지만,
     셋 다 한글이 없는 라틴 전용 얼굴이라 같은 갈라짐을 피할 수 없다.

     **외부 폰트를 부르지 않는다** — file:// 실행과 오프라인을 지켜야 한다.
     바꾸고 싶으면 여기서 고른다. 다만 위 이유로 **라틴 전용 얼굴은 모두 같은 문제를 안는다**
     (ui-serif/New York · ui-rounded/SF Rounded · ui-monospace/SF Mono · Georgia).
     숫자에 성격을 주고 싶다면 글꼴을 바꾸는 대신 크기·굵기·자간으로 주는 편이 안전하다.
     한글 단위를 `<span>` 으로 떼어 따로 글꼴을 주는 길도 있지만,
     금액을 만드는 자리가 여러 곳이라 마크업이 그만큼 늘어난다.

     표의 숫자 열은 여기 해당 없다. 거기는 자릿수를 맞추는 게 우선이라 그대로 둔다. */
  --font-num: -apple-system, BlinkMacSystemFont, "Segoe UI",
              "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", sans-serif;

  /* 예전 이름 — 기존 규칙이 그대로 돌게 이어둔다 */
  --f-1: var(--f-note);
  --f-2: var(--f-note);
  --f-3: var(--f-label);
  --f-4: var(--f-body);
  --f-5: var(--f-title);
  --f-6: var(--f-hero);

  /* ── 아이콘 ── 인라인 SVG(선 굵기 1.5)를 data-URI 로. 외부 라이브러리를 부르지 않는다.
     mask 로 찍으므로 SVG 안의 색은 뜻이 없다(알파만 쓴다). 쓰는 곳은 .warn::before / .empty::before. */
  --icon-warn: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E");
  --icon-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5'/%3E%3Cpath d='M12 8h.01'/%3E%3C/svg%3E");

  /* ── 폭 ── 바깥 여백은 여기서 나온다 */
  /* PC 전용(최소 1280px). 반응형은 신경 쓰지 않는다. */
  --w-content: 1760px;   /* 넓은 화면에선 목록 표가 양옆 여백까지 써서 열이 안 잘린다 */
  --w-side: 480px;       /* 계산·예비가격 탭의 입력 열 */
  --w-gutter: 32px;

  /* ── 모서리 ── */
  --r-1: 2px;      /* 입력칸·작은 버튼 — 각지게 */
  --r-2: 3px;      /* 카드 — 각지게 */
  --r-3: 3px;      /* 큰 카드 — 각지게 */
  --r-pill: 999px;

  /* ── 그림자 ── 아주 옅게. 경계선이 주고 그림자는 거들 뿐 */
  --sh-1: 0 1px 2px rgba(0, 0, 0, 0.04);
  --sh-2: 0 4px 14px rgba(0, 0, 0, 0.06);

  /* ── 예전 이름 ── 기존 규칙이 그대로 돌게 이어둔다 */
  --bg: var(--c-bg);
  --card: var(--c-surface);
  --line: var(--c-line);
  --text: var(--c-text);
  --dim: var(--c-text-2);
  --accent: var(--c-accent);
  --warn-bg: var(--c-warn-bg);
  --warn-line: var(--c-warn-line);
  --warn-text: var(--c-warn-text);
  --ok: var(--c-ok-text);
}

/* 애플 다크모드는 순검정이 아니라 짙은 회색이다. 대비를 과하게 주지 않는다. */
/* 다크 토큰. data-theme 속성으로 켠다. 초기값은 head 인라인 스크립트가
   OS 설정(prefers-color-scheme)을 읽어 넣고, 버튼으로 바꾸면 localStorage 에 기억한다.
   JS 필수 앱이라(app.js 없으면 화면이 안 뜬다) 속성 방식이 안전하다. */
:root[data-theme="dark"] {
    --c-bg:        #16181c;
    --c-surface:   #1d2025;
    --c-surface-2: #24282e;
    --c-input:     #14161a;
    --c-line:      rgba(233, 236, 240, 0.13);
    --c-line-2:    rgba(233, 236, 240, 0.22);

    --c-text:   #e9ecf0;
    --c-text-2: #a8afb8;
    /* 회탄 카드(#1d2025) 위 대비 4.5 이상. 옛 #8a8478 은 3.76 으로 미달이었다. */
    --c-text-3: #8a929c;

    /* 앰버 — 어두운 회탄에서 대비 8:1, 계기판 성격을 준다. */
    --c-accent:      #e8a219;
    --c-accent-hov:  #f2b33a;
    --c-accent-soft: rgba(232, 162, 25, 0.15);
    --c-on-accent:   #16181c;

    --c-warn-bg: #2a2413;  --c-warn-line: #6b551f;  --c-warn-text: #e5c87a;
    --c-ok-bg:   #16311f;  --c-ok-line:   #27573a;  --c-ok-text:   #62cf90;
    --c-bad-bg:  #35191b;  --c-bad-line:  #63292d;  --c-bad-text:  #ff9a9a;

    /* 어두운 바탕에서 그림자는 거의 보이지 않는다. 더 옅게 둔다. */
    --sh-1: 0 1px 2px rgba(0, 0, 0, 0.20);
    --sh-2: 0 4px 14px rgba(0, 0, 0, 0.28);
}

* { box-sizing: border-box; }

/* ═══════════════════════════════════════════════════════════
   E. 상태·잔손질 — 포커스 링 · 미세 전환 · 아이콘
   ═══════════════════════════════════════════════════════════ */

/* ── 포커스 링 (통일) ──
   전에는 세 곳이 제각각이었다(제목 버튼은 2px outline, 입력·버튼은 box-shadow).
   키보드로 옮겨 다닐 때 모양이 자리마다 바뀌면 '지금 여기'가 흔들린다.
   하나로 모은다: 2px 강조색 outline, :focus-visible 라 마우스 클릭 때는 안 뜬다.
   입력칸은 클릭해도 :focus-visible 가 잡히므로(글자를 받는 요소라) 눌러도 링이 보인다 — 맞는 동작이다. */
:where(a, button, input, select, textarea, [tabindex], summary):focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}
/* 마우스 클릭에 남는 기본 파란 테두리는 지운다. 링은 위 focus-visible 만 낸다. */
:where(a, button, input, select, textarea, summary):focus:not(:focus-visible) {
  outline: none;
}

/* ── 미세 전환 ──
   색·배경·테두리·그림자가 바뀌는 곳에만 150ms. transform·크기에는 걸지 않는다(레이아웃이 튄다).
   개별 :hover 마다 걸지 않고 상호작용 요소의 바탕에 한 번 건다 — 들어올 때와 나갈 때 모두 부드럽게. */
:where(a, button, .tab, .btn, .badge, input, select, textarea, summary,
       tbody tr td, .probbtn) {
  transition: color .15s ease, background-color .15s ease,
              border-color .15s ease, box-shadow .15s ease;
}
/* 움직임을 끄는 사용자에게는 전환도 끈다. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ── 아이콘 (경고·정보·빈 상태) ──
   인라인 SVG 를 data-URI 로 박아 mask 로 찍는다. 외부 라이브러리를 부르지 않는다.
   mask + currentColor 라 아이콘이 글자와 같은 색으로 따라온다 — 별도 장식색이 아니다.
   16px, 선 굵기 1.5 는 SVG 안에 있다. index.html·app.js 는 손대지 않는다 —
   .warn·.empty 클래스에 붙으므로 20곳 넘는 문구를 하나도 고치지 않는다.
   장식으로 뿌리지 않는다: 경고 박스(.warn)와 빈 상태(.empty)에만 붙는다. */
/* 아이콘 자리(position·padding-left)는 원래 .warn 정의 안에 둔다.
   여기서 padding-left 만 따로 주면 뒤에 오는 .warn { padding: … } 단축 속성이 덮어 12px 로 돌아간다. */
.warn::before {
  content: "";
  position: absolute;
  left: var(--s-3);
  top: var(--s-2);
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask: var(--icon-warn) center / contain no-repeat;
  mask: var(--icon-warn) center / contain no-repeat;
}
/* 빈 상태 안내는 한 줄짜리가 많고 <span> 인 곳도 있어 인라인으로 아이콘을 앞세운다. */
.empty::before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 16px;
  margin-right: var(--s-2);
  vertical-align: -3px;
  background-color: currentColor;
  -webkit-mask: var(--icon-info) center / contain no-repeat;
  mask: var(--icon-info) center / contain no-repeat;
}

body {
  margin: 0;
  padding: 0 0 var(--s-6);
  background: var(--c-bg);
  color: var(--c-text);
  /* 시스템 폰트만 쓴다. 맥은 SF, 윈도우는 Segoe UI 가 그대로 나온다.
     외부 폰트를 링크로 불러오면 file:// 에서 막히고 오프라인에서 깨진다. */
  font: var(--f-4)/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI",
        "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ── 상단바 ── 고정 + 반투명. 스크롤해도 어디 있는지 알 수 있게 */
.top {
  position: sticky;
  top: 0;
  z-index: 50;
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--c-line);
  background: color-mix(in srgb, var(--c-surface) 72%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}
/* 콘텐츠와 같은 폭, 같은 시작점. 헤더가 화면을 먹을 이유가 없다. */
.top-inner {
  max-width: var(--w-content);
  margin: 0 auto;
  /* main 의 안쪽 여백과 같은 값을 줘야 제목이 카드 내용과 같은 선에서 시작한다 */
  padding: 0 var(--w-gutter);
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.top h1 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -0.021em; }
/* 제목은 누르면 메인으로 가는 버튼이다. 생김새는 제목 그대로 두고 기능만 얹는다.
   버튼처럼 보이면 헤더에 버튼이 하나 더 있는 것으로 읽혀 탭과 헷갈린다. */
.top h1 button {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.top h1 button:hover { color: var(--c-accent); }
.top h1 button:focus-visible { border-radius: var(--r-1); }
.top .sub { margin: 0; color: var(--c-text-2); font-size: var(--f-3); }

/* 탭 — 세그먼티드 컨트롤.
   전체 폭을 채우지 않는다. 내용 폭에 맞춰 왼쪽에 붙인다.
   선택된 것을 진한 파랑으로 채우면 결과 금액과 무게가 같아져 뭐가 중요한지 안 보인다. */
/* 탭 — 세그먼티드 컨트롤.
   전체 폭을 채우지 않는다. 내용 폭에 맞춰 왼쪽에 붙인다.
   선택된 것을 진한 파랑으로 채우면 결과 금액과 무게가 같아져 뭐가 중요한지 안 보인다. */
.tabsbar {
  max-width: var(--w-content);
  margin: var(--s-5) auto 0;
  padding: 0 var(--w-gutter);
}
.tabs {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--c-surface-2);
  border-radius: var(--r-2);
  max-width: 100%;
  overflow-x: auto;
}
.tab {
  flex: 0 0 auto;
  height: 34px;
  padding: 0 var(--s-4);
  border: 1px solid transparent;   /* 선택됐을 때와 높이를 맞춘다 */
  border-radius: 8px;
  background: transparent;
  color: var(--c-text-2);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
}
.tab:hover { color: var(--c-text); }
.tab.is-on {
  background: var(--c-surface);
  color: var(--c-text);
  font-weight: 600;
  box-shadow: var(--sh-1);
  border-color: var(--c-line);
}

/* 여백은 바깥에. 다만 무제한으로 늘리지 않는다 — 폭을 묶어야 읽힌다. */
main { max-width: var(--w-content); margin: 0 auto; padding: var(--s-5) var(--w-gutter); }
/* 넓힌 폭은 목록 표에만 쓸모 있다. 계산·상세·이력은 글을 읽는 화면이라 너무 넓으면 되레 불편하니
   예전 폭(1440)으로 가운데 모은다. 목록(notices) 패널만 넓은 main 폭을 그대로 쓴다. */
main > .panel[data-panel="calc"],
main > .panel[data-panel="detail"],
main > .panel[data-panel="history"] { max-width: 1440px; margin-left: auto; margin-right: auto; }

/* ── 좌우 2단 ──
   왼쪽에서 입력을 고치면 오른쪽 결과가 스크롤 없이 바뀌는 게 보여야 한다.
   그게 이 배치의 존재 이유다. */
.split {
  display: grid;
  grid-template-columns: var(--w-side) minmax(0, 1fr);
  gap: var(--s-5);
  align-items: start;
}
/* 입력 열이 짧으므로 결과 열을 붙잡아 둔다. */
.split-main { position: sticky; top: var(--s-5); align-self: start; }
.split-side .card:last-child,
.split-main .card:last-child { margin-bottom: 0; }

/* 조건 필터는 3열 2행으로 압축한다. 여섯 줄로 늘어놓을 이유가 없다. */
.filtergrid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-4);
}
.filtergrid .field { margin-bottom: 0; }

/* 빈 화면이 화면 전체를 먹지 않게 한다. 높이는 내용만큼만. */
.card.is-empty-ok { max-width: 480px; }

.panel { display: none; }
.panel.is-on { display: block; }

/* 폴백(추측) 하한율·예가에 기반한 계산 결과 옆 꼬리표 — 큰 숫자를 확정값처럼 읽지 않게 경고색으로. */
.fbtag {
  display: inline-block; margin-left: 6px; padding: 0 7px; vertical-align: middle;
  font-size: var(--f-note); font-weight: 600; white-space: nowrap; border-radius: 999px;
  color: var(--c-warn-text); background: var(--c-warn-bg); border: 1px solid var(--c-warn-line);
}

/* 붙여넣기로 채우기 상자(계산 탭). 접어 두고 필요할 때만 편다. */
/* 파일 입력을 감추되 DOM 에는 남긴다. display:none 이면 사파리가 프로그램 클릭을 무시할 수 있다. */
.offscreen { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.pastebox { margin-bottom: var(--s-4); }
.pastebox > summary { cursor: pointer; font-size: var(--f-3); color: var(--c-text-2); padding: 4px 0; }
.pastebox textarea {
  width: 100%; min-height: 84px; margin-top: 6px; resize: vertical; box-sizing: border-box;
  font: inherit; font-size: var(--f-3); padding: 8px; border-radius: var(--r-1);
  border: 1px solid var(--c-line-2); background: var(--c-surface); color: var(--c-text);
}
.pasterow { display: flex; align-items: center; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
.pasterow .note { margin: 0; }

/* 전략 백테스트 여유 곡선 (SVG 라인차트). viewBox 로 그리고 폭은 CSS 로 맞춘다. */
.sweepchart { margin: 8px 0 2px; }
.sweepchart svg { width: 100%; height: auto; max-width: 440px; display: block; }

/* 카드 제목 옆 옅은 부제 */
.subtle { font-weight: 400; font-size: var(--f-3); color: var(--c-text-3); }
/* 입력 묶음(안전여유·금액) 위에 붙는 설명 줄 — 바로 아래 입력칸과 붙여 둔다 */
.fieldhead { margin: 12px 0 4px; }

/* 계산 탭 '참고' 카드 — 기본은 접어두고 필요할 때만 편다(답은 위 카드, 이건 참고 상세). */
.refbox > summary { cursor: pointer; font-weight: 600; font-size: var(--f-2); padding: 2px 0; color: var(--c-text); }
.refbox[open] > summary { margin-bottom: 10px; }

/* 추천 투찰금액 — 계산 탭의 '답'. 박스 없이 강조색 48px 히어로 숫자로만 강조한다. */
.bidhero { display: flex; flex-direction: column; gap: 2px; margin: 6px 0 12px; }
.bidhero .bh-k { font-size: var(--f-3); color: var(--c-text-2); }
.bidhero .bh-v {
  font-family: var(--font-num); font-size: var(--f-hero); font-weight: 700;
  line-height: 1.05; color: var(--c-accent); font-variant-numeric: normal;
}
.bidhero .bh-sub { font-size: var(--f-3); color: var(--c-text-2); margin-top: 3px; }
.bidhero .bh-sub b { color: var(--c-text); }

.card {
  background: var(--c-surface);
  border: 1px solid var(--c-line-2);
  border-radius: 12px;
  box-shadow: var(--sh-1);
  padding: 20px;
  margin-bottom: var(--s-4);
}
.card h2 {
  margin: 0 0 var(--s-4);
  font-size: var(--f-title);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--c-text);
}
/* 카드 안 소제목. 섹션↔섹션은 32px 로 띄워 묶음을 구분한다. */
.card h3 {
  margin: var(--s-6) 0 var(--s-2);
  font-size: var(--f-label);
  font-weight: 600;
  color: var(--c-text-2);
  letter-spacing: 0.01em;
}
.card > h3:first-child { margin-top: 0; }

/* ── 폼 ── 밀도는 안쪽. 입력과 결과가 한 화면에 같이 보여야 한다. */
.field { display: block; margin-bottom: var(--s-4); }
.field > span {
  display: block;
  font-size: var(--f-label);
  color: var(--c-text-2);
  margin-bottom: 6px;   /* 라벨과 입력칸은 한 묶음이므로 바짝 붙인다 */
}
.field em { font-style: normal; font-size: var(--f-note); color: var(--c-text-3); }
/* .field 는 display:block 이라 hidden 속성(UA display:none)을 이겨버린다 — 눌러서 숨겨지게 한다. */
.field[hidden] { display: none; }
/* 출처 배지(공고/폴백/직접)가 라벨에서 아랫줄로 내려가지 않게 한 줄에 붙인다.
   좁은 입력열(예가범위 시작 등)에서 라벨+배지가 접히던 것을 막는다. */
.field > span:has(.src) { white-space: nowrap; }

/* 이 규칙은 글자를 넣는 칸을 위한 것이다.
   체크박스·라디오까지 잡으면 폭 100%·높이 36px 로 부풀어 옆 라벨을 짓눌러
   글자가 세로로 접힌다(실제로 '열린 공고' 토글에서 995px 짜리 체크박스가 됐다). */
input:not([type="checkbox"]):not([type="radio"]), select, textarea {
  width: 100%;
  height: 36px;
  padding: 0 var(--s-3);
  border: 1px solid var(--c-line-2);
  border-radius: var(--r-1);
  background: var(--c-input);
  color: var(--c-text);
  font: inherit;
  font-size: var(--f-body);
}
textarea { height: auto; padding: var(--s-2) var(--s-3); }

/* 숫자 입력칸의 위아래 화살표(스피너)는 윈도우에서 맥보다 크게 그려져
   오른쪽 값 위로 올라온다. 하한율 '89.745' 의 끝자리가 가려지면 안 되므로
   오른쪽만 넉넉히 비운다. 스피너를 없애지는 않는다 — 0.001 단위로 올리는 데 쓴다.
   위 규칙의 선택자를 그대로 받아 [type=number] 를 더한다.
   `input[type="number"]` 만 쓰면 특정도가 낮아 위 padding 에 진다. */
input:not([type="checkbox"]):not([type="radio"])[type="number"] {
  padding-right: var(--s-5);
}

/* 윈도우 크롬/엣지는 select 오른쪽 화살표 자리를 맥보다 넓게 잡는다.
   업종처럼 긴 값이 화살표에 겹치지 않게 여유를 준다. */
select { padding-right: var(--s-5); }
input:hover, select:hover, textarea:hover { border-color: var(--c-text-3); }
/* 값을 입력하는 칸이라 테두리는 마우스로 눌러도 강조색으로 바뀌어 '지금 여기'를 보인다.
   그 위에 얹히는 포커스 링(2px 강조색)은 키보드로 왔을 때만 뜬다 — 아래 통일 규칙이 준다. */
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--c-accent);
}
input:disabled { background: var(--c-surface-2); color: var(--c-text-3); }
textarea {
  resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--f-3);
}

.row { display: flex; gap: var(--s-3); align-items: flex-end; }
.row > * { flex: 1; min-width: 0; }

.note { font-size: var(--f-note); color: var(--c-text-3); margin: 0 0 var(--s-3); }
.note b { color: var(--c-text); font-variant-numeric: tabular-nums; }

/* ── 경고 ── 지우지 말 것. 없으면 사용자가 돈을 잃는다.
   눈에 덜 띄게는 해도 존재는 분명해야 한다. */
.warn {
  position: relative;   /* ::before 아이콘의 기준 */
  background: var(--c-warn-bg);
  border: 1px solid var(--c-warn-line);
  color: var(--c-warn-text);
  border-radius: var(--r-1);
  /* 왼쪽만 아이콘(16px)+틈(8px)만큼 더 준다. 나머지는 원래대로 */
  padding: var(--s-2) var(--s-3) var(--s-2) calc(var(--s-3) + 16px + var(--s-2));
  font-size: var(--f-2);
  line-height: 1.5;
  margin: var(--s-3) 0 0;
}
.warn strong { font-weight: 700; }

/* ── 이름-값 요약 ── */
.summary { display: grid; gap: 0; }
.summary .line {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--c-line);
}
.summary .line:last-child { border-bottom: 0; }
.summary .k { color: var(--c-text-2); font-size: var(--f-label); }
.summary .v {
  font-size: var(--f-body);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  text-align: right;
  color: var(--c-text);
}
/* 히어로 숫자 — 한 화면에 하나만.
   큰 단독 숫자에는 tabular-nums 를 쓰지 않는다. 모든 자릿수를 0 폭으로 만들어 헐렁해 보인다. */
.summary .v.big {
  font-family: var(--font-num);
  font-size: var(--f-hero);
  font-weight: 600;
  color: var(--c-accent);
  letter-spacing: -0.03em;
  font-variant-numeric: normal;
  line-height: 1.1;
}
/* 히어로 바로 아래 단계. 계산 탭에는 큰 숫자가 둘인데(투찰 하한금액 / 이 확률에서의 투찰금액)
   둘 다 48px 이면 어느 쪽을 기준으로 삼아야 하는지가 안 보인다. 하한금액만 히어로로 두고
   이쪽을 32px 로 내린다.
   크기만 낮추고 색은 잉크로 둔다 — 강조색까지 빼면 결과가 아니라 보조 설명으로 읽힌다. */
.summary .v.mid {
  font-family: var(--font-num);
  font-size: 36px;
  font-weight: 600;
  color: var(--c-text);
  letter-spacing: -0.02em;
  font-variant-numeric: normal;   /* 큰 단독 숫자는 비례 숫자로. .v.big 과 같은 규칙 */
  line-height: 1.15;
}
.summary .line:has(.v.big),
.summary .line:has(.v.mid) { padding: var(--s-3) 0; align-items: center; }

/* ── 스탯 타일 — 계산 결과(#summary)에만 ──
   좌우로 벌린 라벨-값은 금액이 길수록 눈이 가운데 빈 곳을 건너뛰어야 한다.
   '예정가격 범위'처럼 값이 `1,234,567,890 ~ 1,345,678,901` 로 오면 특히 그렇다.
   라벨을 값 위에 올려 한 덩어리로 묶으면 건너뛸 일이 없고, 값이 길어져도 라벨과 안 떨어진다.

   **범위를 #summary 로 좁힌 이유** — .summary 는 일곱 군데가 함께 쓴다
   (dataStatus·rateSummary·summary·pMarginStat·pOut·dMoney·dCalcOut).
   전역으로 바꾸면 공고 검색 요약과 공고 상세까지 같이 뒤집힌다. 지시가 지목한 것은
   계산 탭의 네 줄(예정가격·예정가격 범위·투찰 하한 범위·A값 적용)이다. */
#summary .line {
  display: block;              /* 좌우 flex 배치를 끈다 */
  padding: var(--s-3) 0;
}
#summary .k {
  display: block;
  margin-bottom: var(--s-1);   /* 라벨↔값 4px. 붙여야 한 덩어리로 읽힌다 */
}
#summary .v {
  display: block;
  font-family: var(--font-num);
  text-align: left;            /* 라벨과 왼쪽을 맞춘다 */
  font-size: var(--f-stat);
  line-height: 1.3;
  /* 큰 단독 숫자는 비례 숫자로. tabular 는 모든 자릿수를 '0' 폭으로 만들어 헐렁해 보인다.
     세로로 비교할 열이 아니라 한 줄짜리 값이므로 자릿수를 맞출 이유가 없다. */
  font-variant-numeric: normal;
}
/* 히어로는 자기 크기를 지킨다. 위 규칙의 --f-stat 에 덮이면 안 된다. */
#summary .v.big { font-size: var(--f-hero); }

/* ═══════════════════════════════════════════════════════════
   표 — 이 도구의 본체
   ───────────────────────────────────────────────────────────
   사정률 구간표는 61행이다. 한 화면에 다섯 줄만 나오면 쓸모가 없다.
   그래서 표 안쪽은 의도적으로 빽빽하게 둔다. 여백은 카드 바깥에서 준다.

   금액은 자릿수가 세로로 맞아야 비교가 된다.
   오른쪽 정렬 + tabular-nums 가 이 표의 존재 이유다.
   ═══════════════════════════════════════════════════════════ */

.tablewrap {
  /* 표는 자기 안에서 가로로 스크롤한다. 페이지 본문이 밀리면 안 된다. */
  overflow-x: auto;
  overflow-y: auto;
  max-height: 420px;
  border: 1px solid var(--c-line);
  border-radius: var(--r-1);
  background: var(--c-surface);
  /* 가로만 가둔다. 넓은 표를 트랙패드로 옆으로 쓸었을 때
     페이지가 밀리거나 브라우저 뒤로가기 제스처가 걸리는 것을 막는다.

     세로는 가두지 않는다. 이 표들은 모달이 아니라 긴 문서 한가운데 박힌 인라인 표라,
     끝까지 읽었으면 페이지가 이어서 내려가는 것이 읽는 사람이 기대하는 동작이다.
     예전에는 세로까지 contain 이었는데, 표 위에 마우스가 있으면 페이지가 멈춰
     마우스를 옆으로 치워야 하는 함정이 됐다. 상한을 푼 공고 목록에서는
     세로로 갈 데가 아예 없어 휠이 통째로 먹히기까지 했다.
     "읽다가 끝에서 페이지가 튄다"는 반대쪽 걱정은 브라우저의 스크롤 래칭이 막아 준다 —
     한 번의 휠 동작은 시작한 상자 안에 머문다. 손을 뗐다 다시 굴려야 페이지로 넘어간다. */
  overscroll-behavior-x: contain;
}

table {
  width: 100%;
  border-collapse: separate;   /* sticky 헤더 밑줄이 스크롤에 묻히지 않게 */
  border-spacing: 0;
  font-size: var(--f-label);
}

th, td {
  padding: 5px var(--s-3);
  /* 본문 줄간격(1.55)을 그대로 쓰면 한 행이 33px가 되어
     41행짜리 사정률 구간표가 한 화면에 12행밖에 안 들어온다.
     표 안에서는 줄간격을 좁혀 밀도를 올린다. */
  line-height: 1.4;
  /* 기본은 숫자다. 금액·비율은 오른쪽 정렬 + 고정폭 숫자. */
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--c-surface-2);
  border-bottom: 1px solid var(--c-line-2);
  color: var(--c-text-2);
  font-size: var(--f-note);
  font-weight: 600;
  letter-spacing: 0.01em;
}

td {
  /* 행 구분선은 옅게. 61행이 이어질 때 줄무늬처럼 보이면 안 된다. */
  border-bottom: 1px solid var(--c-line);
  color: var(--c-text);
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--c-surface-2); }

/* 양 끝은 조금 더 띄워 숨통을 준다 */
th:first-child, td:first-child { padding-left: var(--s-4); }
th:last-child,  td:last-child  { padding-right: var(--s-4); }

/* 이름·기관 같은 글자 칸은 왼쪽 정렬이 읽기 편하다 */
td.left, th.left { text-align: left; }

/* 사정률 0% 줄처럼 기준이 되는 행 */
tr.mid td {
  font-weight: 700;
  color: var(--c-text);
  background: var(--c-surface-2);
}
tbody tr.mid:hover td { background: var(--c-surface-2); }

/* 파랑은 결과의 투찰 하한금액 한 곳에만 쓴다.
   버튼까지 파랗게 하면 무엇이 중요한지 안 보인다. */
.btn {
  height: 34px;
  padding: 0 var(--s-4);
  border: 1px solid var(--c-line-2);
  border-radius: var(--r-1);
  background: var(--c-surface);
  color: var(--c-text);
  font: inherit;
  font-size: var(--f-3);
  font-weight: 500;
  cursor: pointer;
  margin-top: var(--s-3);
}
.btn:hover { background: var(--c-surface-2); border-color: var(--c-text-3); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--c-text-2); }
.btn.ghost:hover { background: var(--c-surface-2); color: var(--c-text); }
.saved { margin-left: var(--s-2); font-size: var(--f-3); color: var(--c-ok-text); }

/* ── 사정률 빈도 막대 ────────────────────────────────────
   세 곳이 같은 구조를 쓴다: #rateHist · 예가범위 무리 · 공고 상세(#dRateHist).
   만드는 쪽은 app.js 의 histHTML() 하나뿐이다.

   **색을 쓰지 않는다.** 막대는 잉크 한 색조이고 값이 클수록 진해진다
   (--c-text-3 → --c-text, 섞는 비율은 app.js 가 인라인으로 넣는다).
   강조색을 안 쓰는 이유: 강조가 딥레드라 위험색(#a52019)과 RGB 거리 15 다.
   그 색으로 최빈 구간을 칠하면 '가장 흔한 자리'가 '위험'으로 읽힌다.
   딥레드 단일 색조로 가도 화면이 통째로 붉어져 마감(D-0) 경고와 섞인다.
   색을 빼면 이 충돌이 원천적으로 사라지고, sequential·하나만 강조라는 요구는
   명도와 굵기로 그대로 만족한다.

   열 폭을 변수로 둔 이유: 격자선 겹침(.histgrid)이 라벨·건수 열을 비켜 가야 하는데,
   그 계산에 같은 값이 필요하다. 두 곳에 숫자를 적으면 반드시 어긋난다. */
.hist {
  font-size: 12px;
  --lab-w: 130px;
  --cnt-w: 44px;
  --col-gap: 8px;
}
/* 막대 사이를 갈라 놓는 것은 2px 표면색 틈이다. 테두리를 그리지 않는다 —
   테두리는 데이터가 아닌 잉크를 얹어 막대를 무겁게 만든다. 틈만으로 충분히 갈린다. */
.histbody { display: grid; gap: 2px; position: relative; }
.hist .bar {
  display: grid;
  grid-template-columns: var(--lab-w) 1fr var(--cnt-w);
  gap: var(--col-gap);
  align-items: center;
  position: relative;   /* 격자선 위에 올라온다 */
  z-index: 1;
}
.hist .lab { color: var(--dim); font-variant-numeric: tabular-nums; text-align: right; }
/* 두께는 24px 상한 안에 둔다(여기는 14px). 슬롯을 꽉 채우지 않고 남는 만큼은 공기로 둔다.
   모서리는 데이터가 끝나는 쪽만 4px 로 둥글리고, 기준선(왼쪽 0)은 각지게 둔다 —
   시작점이 둥글면 막대가 기준선에서 뜬 것처럼 보여 길이를 눈으로 견주기 어려워진다.
   background 는 값마다 달라서 app.js 가 인라인으로 넣는다. */
.hist .fill { height: 14px; border-radius: 0 4px 4px 0; min-width: 1px; }
.hist .cnt { color: var(--dim); font-variant-numeric: tabular-nums; }

/* 격자선 — 1px 실선. **점선 금지.** 표면에서 한 단계만 뜬 회색으로 뒤로 물린다.
   막대 뒤에 깔리도록 절대배치하고, 라벨·건수 열은 비켜 간다. */
.histgrid {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--lab-w) + var(--col-gap));
  right: calc(var(--cnt-w) + var(--col-gap));
  z-index: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(to right, var(--c-line) 0 1px, transparent 1px 25%);
}
/* 값 축 눈금. 막대마다 숫자를 붙이지 않는 대신 이쪽이 값을 읽어 준다. */
.histaxis {
  display: grid;
  grid-template-columns: var(--lab-w) 1fr var(--cnt-w);
  gap: var(--col-gap);
  margin-top: var(--s-2);
}
.histaxis .ticks {
  display: flex;
  justify-content: space-between;
  color: var(--c-text-3);
  font-size: var(--f-note);
  font-variant-numeric: tabular-nums;
}
/* 최빈 구간 — 색을 따로 쓰지 않는다. 막대는 가장 진한 잉크(app.js 가 100%로 넣는다)이고
   여기서는 라벨·건수의 굵기와 잉크만 한 단계 올린다. 색맹에도 위계가 남는다. */
.hist .bar.peak .lab,
.hist .bar.peak .cnt { color: var(--c-text); font-weight: 700; }

/* 항목을 누르면 그 계산이 계산 탭에 다시 뜬다. 눌리는 것처럼 보이게 손가락 커서·호버를 준다. */
.hist-item { border-bottom: 1px solid var(--c-line); padding: var(--s-3) var(--s-2);
             margin: 0 calc(-1 * var(--s-2)); border-radius: var(--r-1); cursor: pointer; }
.hist-item:hover { background: var(--c-surface-2); }
.hist-item:focus-visible { outline: 2px solid var(--c-accent); outline-offset: -2px; }
.hist-item:last-child { border-bottom: 0; }
.hist-item .t { font-weight: 600; }
.hist-item .d { font-size: var(--f-2); color: var(--c-text-2); margin-top: 2px; }
.hist-item .del { float: right; background: none; border: 0; color: var(--c-text-3); cursor: pointer; font-size: var(--f-3); }
.hist-item .del:hover { color: var(--c-bad-text); }

/* ── 전략 백테스트 탭 ──
   전략 폼이 설명·경고 글이 많아 넓게 쓴다. 그만큼 결과 자리를 위해 패널도 1600 까지 편다. */
main > .panel[data-panel="strategy"] { max-width: 1600px; margin-left: auto; margin-right: auto; }
/* 왼쪽 전략(입력·설명) 열을 300 → 420 으로 넓혀 글자가 덜 접히게 한다. */
[data-panel="strategy"] .split { --rail-w: 420px; }

#btSummary { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-3); }
#btSummary .empty { grid-column: 1 / -1; }
.btstat { display: flex; flex-direction: column; gap: 2px; padding: var(--s-3);
          border: 1px solid var(--c-line); border-radius: var(--r-1); background: var(--c-surface); }
.btstat .k { font-size: var(--f-label); color: var(--c-text-2); }
.btstat .v { font-family: var(--font-num); font-size: var(--f-stat);
             font-variant-numeric: tabular-nums; line-height: 1.2; }
.btstat .v.is-ok     { color: var(--c-ok-text); }
.btstat .v.is-accent { color: var(--c-accent); }
.btstat .v.is-warn   { color: var(--c-warn-text); }
.btstat .v.is-bad    { color: var(--c-bad-text); }

.btbar { display: flex; height: 30px; margin-top: var(--s-3); border-radius: var(--r-1);
         overflow: hidden; border: 1px solid var(--c-line); }
.btbar .seg { display: flex; align-items: center; justify-content: center; white-space: nowrap;
              font-size: var(--f-note); color: #fff; overflow: hidden; min-width: 0; }
.btbar .seg.is-bad    { background: var(--c-bad-text); }
.btbar .seg.is-accent { background: var(--c-accent); }
.btbar .seg.is-dim    { background: var(--c-text-3); }

#btTable b.is-accent { color: var(--c-accent); }
#btTable b.is-bad    { color: var(--c-bad-text); }
#btTable b.is-dim    { color: var(--c-text-2); font-weight: 400; }

/* ── 조건 알림 카드 ── */
.savecond { margin-top: var(--s-3); display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
#watchBody .dim { color: var(--c-text-3); }
.witem { border-top: 1px solid var(--c-line); padding: var(--s-2) 0; }
.witem:first-child { border-top: 0; }
.whead { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.wtoggle { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s-2);
           background: none; border: 0; text-align: left; cursor: pointer; color: var(--c-text);
           font: inherit; padding: var(--s-2); border-radius: var(--r-1); }
.wtoggle:hover:not([disabled]) { background: var(--c-surface-2); }
.wtoggle[disabled] { cursor: default; opacity: 0.75; }
.wbadge { flex: none; font-size: var(--f-note); font-weight: 700; padding: 2px 8px;
          border-radius: var(--r-pill); background: var(--c-surface-2); color: var(--c-text-2); }
.wbadge.is-new { background: var(--c-accent); color: #fff; }
.wname { font-weight: 600; }
.wmeta { color: var(--c-text-3); font-size: var(--f-note); }
.wact { display: flex; gap: var(--s-3); flex: none; font-size: var(--f-note); align-items: center; }
.wdetail { margin-top: var(--s-2); }
.wtable { width: 100%; }
.wtable td { padding: var(--s-2); border-top: 1px solid var(--c-line); vertical-align: middle; }
.wtitle { font-weight: 600; }
.wsub { font-size: var(--f-note); margin-top: 2px; }
.wrec { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.wcompete { font-size: var(--f-note); color: var(--c-text-2); margin-top: 3px; font-variant-numeric: tabular-nums; }
.wstrong { font-weight: 700; color: var(--c-accent); }
.wtype { font-weight: 700; font-size: var(--f-note); padding: 1px 6px; border-radius: var(--r-pill);
         background: var(--c-surface-2); color: var(--c-text-2); }

/* 상세 사정률 빈도 — 발주처별 소제목(전체 아래에 구분해 둔다) */
.dRateSub { margin: var(--s-4) 0 var(--s-2); padding-top: var(--s-3);
            border-top: 1px solid var(--c-line); font-size: var(--f-label); color: var(--c-text-2); }
/* 빈 상태는 뜻이 있는 안내다. 흐린 잉크(#898781)는 라이트 바탕에서 대비 3.50이라
   본문 크기로 쓰면 안 읽힌다. 보조 잉크를 쓴다. */
.empty { color: var(--c-text-2); font-size: var(--f-body); padding: var(--s-2) 0; }

/* 표에서 이름·기관은 왼쪽 정렬이 읽기 편하다. 숫자만 오른쪽 정렬. */
td.left, th.left { text-align: left; }
#noticeTable td.left { max-width: 380px; overflow: hidden; text-overflow: ellipsis; }
#noticeTable a { color: var(--c-text); text-decoration: none; }
#noticeTable a:hover { text-decoration: underline; }
code {
  background: var(--c-surface-2); padding: 1px 6px; border-radius: var(--r-1);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--f-2);
}

/* 값의 출처 배지 — 지금 보는 숫자가 공고값인지 폴백인지 드러낸다.
   지우지 말 것. 폴백을 공고값으로 착각하면 투찰이 무효가 된다. */
.src {
  display: inline-block;
  font-size: var(--f-1);
  font-weight: 600;
  line-height: 1.6;
  padding: 0 var(--s-2);
  border-radius: var(--r-pill);
  margin-left: var(--s-1);
  border: 1px solid transparent;
  vertical-align: 1px;
}
.src.is-notice   { background: var(--c-ok-bg);   color: var(--c-ok-text);   border-color: var(--c-ok-line); }
.src.is-fallback { background: var(--c-warn-bg); color: var(--c-warn-text); border-color: var(--c-warn-line); }
.src.is-manual   { background: var(--c-surface-2); color: var(--c-text-2);  border-color: var(--c-line); }
button.mini {
  padding: 2px var(--s-2); font-size: var(--f-2); font-family: inherit;
  border: 1px solid var(--c-line-2); border-radius: var(--r-1);
  background: var(--c-surface); color: var(--c-text-2); cursor: pointer;
}
button.mini:hover { border-color: var(--c-text-3); color: var(--c-text); background: var(--c-surface-2); }

/* ── 하한금액이 어디까지 나올 수 있나 (#pQuantile) ──
   다섯 줄 중 읽는 사람이 기준으로 삼는 것은 중앙(50%) 하나다.
   나머지 넷은 '그 값이 이 폭 안에서 움직인다'를 보여주는 맥락이라 한 단계 물린다.
   색만으로 뜻을 전하지 않는다 — 굵기도 같이 올려서 색을 못 보는 눈에도 위계가 남는다. */
#pQuantile tr:not(.is-key) th,
#pQuantile tr:not(.is-key) td { color: var(--c-text-3); }
#pQuantile tr.is-key th { color: var(--c-text); font-weight: 600; }
#pQuantile tr.is-key td { color: var(--c-text); font-weight: 700; }

/* 본문/사이드바 2단 구조 */

/* 공고 상세 */
.detail-head { margin: 4px 0 16px; }
.detail-head h2 { margin: 0 0 6px; font-size: 26px; font-weight: 700; line-height: 1.3; letter-spacing: -0.02em; text-wrap: balance; }
.detail-meta { color: var(--dim); font-size: 13px; display: flex; gap: 14px; flex-wrap: wrap; }
.detail-meta a { color: var(--c-text-2); }
.detail-meta a:hover { color: var(--c-accent); }

.detail-grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 16px; align-items: start; }
.detail-side {
  position: sticky;
  top: 12px;
  /* `.rail` 과 똑같은 함정이다(거기는 2026-07-28 에 고쳤는데 여기는 남아 있었다).
     사이드바가 화면보다 길면 sticky 가 위에 붙박여 아래쪽이 계속 잘린 채로 남는다.
     스크롤해도 오른쪽만 안 움직이니 "오른쪽이 스크롤이 안 된다"로 느껴진다.

     2026-07-31 실측(1440×900, 공고 상세 '평택 만호지구 …석면 해체제거 공사'):
     사이드바 1145px > 뷰포트 900px → 245px 초과. 사정률 빈도 밑에 '이 발주처만'
     히스토그램(370px)까지 그려질 때 넘친다. 표본 5건 미만이면 문구만 나와 안 넘치므로,
     공고에 따라 되기도 하고 안 되기도 해서 더 헷갈린다.

     제 높이를 뷰포트로 묶고 넘치면 자기 안에서 스크롤한다. */
  max-height: calc(100vh - 24px);
  overflow-y: auto;
}
@media (max-width: 900px) {
  .detail-grid { grid-template-columns: 1fr; }
  /* 쌓이면 sticky·자체 스크롤이 되레 방해된다. 자연스럽게 흐르게 푼다(.rail 과 같다). */
  .detail-side { position: static; max-height: none; overflow: visible; }
}

/* ── 이름-값 표 ── 공고 상세의 일시정보·자격·요약.
   여기는 글자가 길어 줄바꿈이 필요하므로 nowrap 을 풀고, 머리칸을 왼쪽에 세운다. */
table.kv { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--f-3); }
table.kv th, table.kv td {
  padding: 7px var(--s-3);
  border-bottom: 1px solid var(--c-line);
  text-align: left;
  white-space: normal;   /* 긴 값이 표를 옆으로 밀지 않게 줄을 바꾼다 */
  word-break: break-word;
  position: static;      /* 머리칸이 세로라 sticky 가 필요 없다 */
}
table.kv th {
  width: 34%;
  color: var(--c-text-2);
  font-size: var(--f-2);
  font-weight: 500;
  background: var(--c-surface-2);
  border-right: 1px solid var(--c-line);
}
table.kv td { font-variant-numeric: tabular-nums; color: var(--c-text); }
table.kv tr:last-child th, table.kv tr:last-child td { border-bottom: 0; }
table.kv.tight th, table.kv.tight td { padding: 5px var(--s-2); }
/* 금액은 오른쪽으로 붙여 자릿수를 맞춘다 */
table.kv td.num { text-align: right; font-weight: 700; }
table.kv td em { font-style: normal; color: var(--c-text-3); font-size: var(--f-2); }

/* kv 표는 카드 안에서 통째로 테두리를 두른다 */
.card > table.kv,
.card > .tablewrap > table.kv {
  border: 1px solid var(--c-line);
  border-radius: var(--r-1);
  overflow: hidden;
}

/* 진행 타임라인 */
.timeline { display: flex; gap: 4px; margin: 8px 0 4px; }
.timeline .step { flex: 1; text-align: center; position: relative; padding-top: 22px; font-size: 12px; color: var(--dim); }
.timeline .step::before {
  content: ''; position: absolute; top: 6px; left: 50%; transform: translateX(-50%);
  width: 11px; height: 11px; border-radius: 50%; background: var(--line); z-index: 1;
}
.timeline .step::after { content: ''; position: absolute; top: 11px; left: 50%; width: 100%; height: 2px; background: var(--line); }
.timeline .step:last-child::after { display: none; }
.timeline .step.done::before { background: var(--c-text-3); }
.timeline .step.done::after { background: var(--c-text-3); }
.timeline .step.now::before { background: var(--c-text); box-shadow: 0 0 0 4px var(--c-surface-2); }
.timeline .step .t { display: block; font-weight: 600; color: var(--text); margin-bottom: 2px; }
.timeline .step.todo .t { color: var(--dim); font-weight: 500; }

/* 첨부파일 */
/* 원문 링크 앞 문서 아이콘 — 텍스트에 살짝 물려 세로 가운데로. */
.fileicon { vertical-align: -2px; margin-right: 5px; opacity: .65; }
a:hover .fileicon { opacity: 1; }

/* 첨부파일 형식 칩 — HWP·PDF·XLS 등 확장자를 색으로 구분(파일 형식대로). */
.fchip {
  display: inline-block; min-width: 30px; text-align: center;
  padding: 1px 5px; margin-right: 7px; border-radius: 3px;
  font-size: 10px; font-weight: 700; letter-spacing: .3px; line-height: 1.5;
  color: #fff; vertical-align: 1px; flex: 0 0 auto;
}

.files { display: grid; gap: 6px; }

/* 첨부파일 한 줄(이름 링크 + PDF 미리보기 버튼) */
.frow { display: flex; align-items: center; gap: 8px; }
.frow a { min-width: 0; }
.pdfbtn, .pvclose {
  margin-left: auto; flex: 0 0 auto; font: inherit; font-size: var(--f-label); cursor: pointer;
  padding: 2px 9px; border-radius: var(--r-1);
  border: 1px solid var(--c-line-2); background: var(--c-surface); color: var(--c-text-2);
}
.pdfbtn:hover, .pvclose:hover { background: var(--c-surface-2); color: var(--c-text); }

/* PDF 미리보기 영역 — blob 을 iframe 으로 인라인 표시 */
.pdfpreview { margin-top: 12px; }
.pvhead { display: flex; align-items: center; gap: 8px; margin-bottom: 6px;
  font-size: var(--f-3); color: var(--c-text-2); }
.pvhead span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pdfpreview iframe {
  /* 한 장 정도는 한눈에 보이게 넉넉히. 짧은 화면에서도 min-height 로 최소 한 장은 확보. */
  width: 100%; height: 88vh; min-height: 780px; border: 1px solid var(--c-line-2);
  border-radius: var(--r-1); background: #fff;
}
/* 이미지 미리보기 */
.pvimg { max-width: 100%; height: auto; border: 1px solid var(--c-line-2); border-radius: var(--r-1); }
/* 텍스트 미리보기 */
.pvtext {
  max-height: 80vh; overflow: auto; white-space: pre-wrap; word-break: break-all; margin: 0;
  font-family: var(--font-num, monospace); font-size: 12px; line-height: 1.5;
  background: var(--c-surface-2); color: var(--c-text); padding: 12px; border-radius: var(--r-1);
}
/* 엑셀 미리보기 — SheetJS 가 만든 표를 담는다 */
/* 엑셀 표는 늘 밝은 종이처럼(테마 무관). 셀에 색을 직접 못박아 상속으로 흐려지지 않게 한다. */
.pvexcel { max-height: 80vh; overflow: auto; border: 1px solid var(--c-line-2);
  border-radius: var(--r-1); background: #fff; color-scheme: light; }
.pvexcel table { border-collapse: collapse; font-size: 12px; background: #fff; }
.pvexcel td, .pvexcel th {
  border: 1px solid #d0d0d0; padding: 2px 6px; white-space: nowrap;
  color: #111; background: #fff;
}
.pvexcel th { background: #f0f0f0; font-weight: 600; }
.pvsheet { margin: 8px 8px 2px; font-size: var(--f-3); color: var(--c-text-2); }
/* 한글(HWP) 미리보기 — hwp.js 가 자체 페이지를 그린다. 밝은 종이 배경으로 고정. */
.pvhwp { max-height: 80vh; overflow: auto; background: #fff; color-scheme: light;
  border: 1px solid var(--c-line-2); border-radius: var(--r-1); }
.files a { color: var(--c-text); font-size: var(--f-3); text-decoration: underline; text-decoration-color: var(--c-line-2); word-break: break-all; }
.files a:hover { color: var(--c-accent); text-decoration-color: currentColor; }

/* 사이드바 히스토그램은 좁으므로 라벨을 줄인다 */
/* 좁은 자리용. 열 폭만 줄이고 규격(두께·라운드·틈)은 그대로다.
   격자선이 이 변수를 그대로 읽으므로 .bar 에 직접 적지 않고 변수로 바꾼다. */
.hist.mini { --lab-w: 82px; --cnt-w: 34px; }
.hist.mini .bar { font-size: 11.5px; }
.hist.mini .fill { height: 11px; }
/* 최빈 막대의 빨강(#e5484d)은 걷어냈다. 그 색은 이 앱의 '위험' 상태색인데
   '가장 흔한 구간'을 뜻하는 데 쓰이고 있었다. 이제 최빈은 가장 진한 잉크로 구분한다. */

/* 목록에서 공고명은 눌러서 상세로 간다 */
#noticeTable a.open {
  color: var(--c-text); text-decoration: none; cursor: pointer;
  border-bottom: 1px solid var(--c-line-2);
}
#noticeTable a.open:hover { color: var(--c-accent); border-bottom-color: var(--c-accent); }

/* 상세 안 계산기 */
.slider { margin: 4px 0 14px; }
.slider-head { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; color: var(--dim); margin-bottom: 6px; }
.slider-head b { font-size: 17px; color: var(--c-text); font-variant-numeric: tabular-nums; }
.slider input[type=range] { width: 100%; accent-color: var(--accent); }
.slider-ends { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--dim); margin-top: 2px; }

.judge {
  margin-top: var(--s-3); padding: var(--s-2) var(--s-3);
  border-radius: var(--r-1); font-size: var(--f-3); border: 1px solid transparent;
}
.judge.ok  { background: var(--c-ok-bg);  color: var(--c-ok-text);  border-color: var(--c-ok-line); }
.judge.bad { background: var(--c-bad-bg); color: var(--c-bad-text); border-color: var(--c-bad-line); }
.judge b { font-variant-numeric: tabular-nums; font-weight: 700; }

details.fold { margin-top: var(--s-4); }
details.fold > summary { cursor: pointer; font-size: var(--f-3); color: var(--c-text-2); padding: var(--s-1) 0; }
details.fold > summary:hover { color: var(--c-text); }
details.fold > summary::marker { color: var(--c-text-3); }
details.fold .tablewrap { margin-top: var(--s-2); max-height: 420px; }

/* 목표 유효확률 고르기 */
.probpick { display: flex; gap: var(--s-1); margin: 0 0 var(--s-3); flex-wrap: wrap; }
.probbtn {
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--c-line-2);
  border-radius: var(--r-pill);
  background: var(--c-surface);
  color: var(--c-text-2);
  font: inherit;
  font-size: var(--f-3);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.probbtn:hover { border-color: var(--c-text-3); color: var(--c-text); }
.probbtn.is-on {
  background: var(--c-surface-2);
  border-color: var(--c-text-3);
  color: var(--c-text);
  font-weight: 600;
}

/* 위험·방식 선택 버튼 — 숫자와 설명을 위아래로 */
.probbtn { display: inline-flex; flex-direction: column; align-items: center; gap: 1px; line-height: 1.25; }
.probbtn b { font-weight: 600; font-variant-numeric: tabular-nums; }
.probbtn i { font-style: normal; font-size: var(--f-1); color: var(--c-text-3); }
.probbtn.is-on i { color: var(--c-text-2); }
.probbtn[disabled] { opacity: 0.45; cursor: not-allowed; }

/* ═══════════════════════════════════════════════════════════
   공고 검색 — 이 앱의 주 화면
   ───────────────────────────────────────────────────────────
   쓰는 흐름이 '값 직접 입력'이 아니라 '검색 → 계산'이다.
   수집분 22,166건 중 실제로 투찰할 수 있는 것은 1,700건 남짓이라
   열린 공고만 먼저 보여주고, 마감이 급한 것을 위로 올린다.
   ═══════════════════════════════════════════════════════════ */

/* 열린 공고 토글 — 조건 영역 맨 위. 켜진 것이 기본이라는 게 보여야 한다.
   높이는 입력칸 하나(36px)와 같은 리듬으로. 조건 카드 위에 덩어리를 만들지 않는다. */
.toggle {
  /* 한 줄 구조를 못박는다. 체크박스가 어떤 크기로 그려지든(맥 14 / 윈도우 13 / 배율에 따라 더)
     라벨이 옆으로 밀리거나 세로로 접히지 않는다.
     한 번 깨진 적이 있다 — 일반 input 규칙을 받아 995px 짜리 체크박스가 됐다. */
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--s-2);
  width: fit-content;
  max-width: 100%;
  min-height: 36px;
  line-height: 1.3;
  margin: 0 0 var(--s-4);
  padding: 0 var(--s-3);
  border: 1px solid var(--c-line);
  border-radius: var(--r-1);
  background: var(--c-surface-2);
  font-size: var(--f-label);
  color: var(--c-text-2);
  cursor: pointer;
  user-select: none;
}
.toggle:has(input:checked) { color: var(--c-text); font-weight: 600; }
.toggle span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.toggle input[type="checkbox"] {
  /* 폭·높이를 못박아 둔다. 안 그러면 브라우저·글꼴 크기에 따라 달라지고,
     flex-shrink 가 0 이 아니면 라벨이 길 때 체크박스가 납작하게 눌린다. */
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  min-width: 15px;
  margin: 0;
  accent-color: var(--c-accent);
  cursor: pointer;
}

/* 발주 시스템 토글 묶음 — 세로로 쌓는다. toggle 기본 아래 여백을 걷어내고
   gap 으로 촘촘히 붙여 한 덩어리로 보이게 한다. */
.srcpick { display: flex; flex-direction: column; gap: var(--s-2); }
.srcpick .toggle { margin: 0; }

/* 업종은 10개라 세로로 다 펼치면 사이드바가 길어진다. 높이를 고정하고 넘치면 스크롤한다.
   가로 줄바꿈은 라벨이 길어(기계설비ㆍ가스공사업 등) 줄 수가 들쭉날쭉해 안 쓴다. */
#indPick { max-height: 150px; overflow-y: auto; gap: var(--s-1); padding-right: var(--s-1); }
#indPick .toggle { min-height: 28px; padding: 0 var(--s-2); }

/* 헤더 클릭 정렬 — 지금 무엇으로 정렬돼 있는지 화살표로 남긴다 */
#noticeTable th.sortable { cursor: pointer; user-select: none; }
#noticeTable th.sortable:hover { color: var(--c-text); }
#noticeTable th.sortable::after {
  content: '';
  display: inline-block;
  width: 0.7em;
  margin-left: 2px;
  color: var(--c-text-3);
}
#noticeTable th.is-sorted { color: var(--c-text); }
#noticeTable th.is-sorted::after { content: '▲'; font-size: 9px; color: var(--c-accent); }
#noticeTable th.is-sorted.is-desc::after { content: '▼'; }

/* 페이지 넘김 — 50건씩. 3,000행을 한 번에 그리지 않는다. */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  margin-top: var(--s-3);
}
.pager:empty { display: none; }
.pager-now {
  font-size: var(--f-label);
  color: var(--c-text-2);
  font-variant-numeric: tabular-nums;
  min-width: 72px;
  text-align: center;
}
.pager .mini[disabled] { opacity: 0.4; cursor: not-allowed; }

/* 목록은 한 페이지가 50행이다. 사정률 구간표(41행) 기준의 420px 상한에 걸리면
   표 안에서 또 스크롤돼 마감 순서를 훑을 수 없다. 이 표만 상한을 푼다. */
#listCard .tablewrap { max-height: none; }

/* 기관별 표도 같은 이유로 푼다. 2026-07-31 실측(1440×900, 공고 탭):
   오른쪽 열에서 세로로 넘치는 상자가 여기 하나뿐이었는데 418px 창에 1577px 가 들어 있었다.
   긴 페이지 한가운데의 스크롤 상자는 휠을 붙잡는다 — 브라우저의 스크롤 래칭 때문에
   그 위에서 시작한 휠 동작은 상자 끝에 닿아도 페이지로 안 넘어가고, 손을 뗐다 다시
   굴리거나 마우스를 옆으로 치워야 한다. "오른쪽이 스크롤이 잘 안 된다"의 정체가 이것이다.
   상한을 풀면 상자가 사라져 페이지가 통째로 자연스럽게 흐른다.
   대신 페이지가 1.1k px 길어지는데, 이 표는 맨 아래에 있고 CLAUDE.md 대로
   투찰 근거로 쓰면 안 되는 참고용이라 길어지는 쪽이 낫다. */
#agencyCard .tablewrap { max-height: none; }

/* ── 목록 열 ── 폭을 정해 두지 않으면 공고명이 화면을 다 먹는다 */
/* 목록은 열이 많다. 좌우 셀 여백을 표 기본(12)보다 좁혀(8) 9열이 좁은 화면에도 들어오게 한다.
   양끝(첫·마지막 열)은 테두리와 붙지 않게 기본 여백을 남긴다. */
#noticeTable th, #noticeTable td { padding-left: var(--s-2); padding-right: var(--s-2); }
#noticeTable th:first-child, #noticeTable td:first-child { padding-left: var(--s-3); }
#noticeTable th:last-child,  #noticeTable td:last-child  { padding-right: var(--s-3); }
/* 계산 버튼(맨 오른쪽 열)을 오른쪽에 고정한다. 표가 좁아 넘쳐도 이 액션 버튼만은 절대 안 잘린다.
   넓어서 안 넘칠 땐 제자리라 티가 안 나고, 좁을 때만 가운데 열들이 이 뒤로 스크롤된다.
   배경을 불투명하게 줘야 뒤 내용이 이 열 밑으로 비쳐 보이지 않는다. */
#noticeTable th:last-child, #noticeTable td:last-child {
  position: sticky; right: 0;
  box-shadow: -8px 0 8px -8px var(--c-line);   /* 뒤 내용이 지나갈 때만 옅게 보이는 왼쪽 그림자 */
}
#noticeTable td:last-child { background: var(--c-surface); z-index: 1; }
#noticeTable th:last-child { background: var(--c-surface-2); z-index: 2; }
#noticeTable tbody tr:hover td:last-child { background: var(--c-surface-2); }
#noticeTable td.col-name { width: 440px; max-width: 440px; line-height: 1.35; }
#noticeTable .name-row { display: flex; align-items: center; min-width: 0; }
#noticeTable .name-row a { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#noticeTable td.col-name .sub {
  margin-top: 2px;
  font-size: var(--f-note);
  color: var(--c-text-3);
  font-variant-numeric: tabular-nums;
}
#noticeTable td.col-agency { max-width: 150px; overflow: hidden; text-overflow: ellipsis; }
#noticeTable td.col-region { max-width: 92px;  overflow: hidden; text-overflow: ellipsis; }
#noticeTable td.col-kind   { max-width: 108px; overflow: hidden; text-overflow: ellipsis;
                             color: var(--c-text-2); }
#noticeTable td, #noticeTable th { vertical-align: middle; }
/* 2줄 구성이라 행이 44~48px 안에 들어와야 한 화면에 훑을 만큼 담긴다 */
#noticeTable tbody td { padding-top: var(--s-1); padding-bottom: var(--s-1); }
#noticeTable .dim { color: var(--c-text-3); font-weight: 400; }

/* 개찰 전 공고는 사정률이 원래 비어 있다. 열린 공고만 볼 때는 열째로 접는다. */
#listCard.hide-rate .col-rate { display: none; }

/* 열을 숨기지 않는다. main 을 넓혀(--w-content 1760) 조건을 펼쳐도 9열이 다 들어간다.
   그래도 좁은 화면이면 열을 지우는 대신 표가 자기 안에서 가로로 스크롤한다(.tablewrap overflow-x).
   — 열이 사라지는 것보다 스크롤이 낫다. 공고명은 남는 자리를 먹되 너무 길어지지 않게 상한만 둔다. */
#searchSplit:not(.is-collapsed) #noticeTable td.col-name { width: auto; max-width: 300px; }

/* ── 투찰마감 ── 이 표에서 가장 중요한 날짜다.
   색만으로 뜻을 전하지 않는다. D 표기가 항상 같이 붙는다. */
.due { font-variant-numeric: tabular-nums; }
.due b { font-weight: 600; margin-left: var(--s-1); }
.due.is-today { color: var(--c-bad-text); font-weight: 700; }
.due.is-soon  { color: var(--c-warn-text); font-weight: 600; }
.due.is-near  { font-weight: 700; }
.due.is-past  { color: var(--c-text-3); }

/* 계산 탭에서 목록으로 돌아가는 길 */
.backlink {
  display: inline-block;
  margin: 0 0 var(--s-3);
  font-size: var(--f-label);
  color: var(--c-accent);
  cursor: pointer;
}
.backlink:hover { text-decoration: underline; }

/* ── 목록 배지 ── E단계 배지 규격.
   높이 20px · 좌우 8px · 12px 글자 · radius 6px · 낮은 채도 배경 + 진한 글자.
   '공고/폴백/직접'(.src)과 뜻이 다르므로 클래스를 합치지 않는다. 규격만 같다.

   색은 손댈 거리가 있는 것에만 쓴다.
     is-alert 하한율이 범위를 벗어남 / is-rate 흔한 값이 아님 / is-ord 정정 이력
   나머지(수의·제한·A값·기초·공동도급)는 사실 서술이라 중립색이다.
   의미 하나에 색 하나를 고정한다 — 화면마다 다른 색이면 안 된다. */
.badges { display: inline-flex; gap: var(--s-1); margin-left: var(--s-2); flex: 0 0 auto; }
.badge {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 var(--s-2);
  border-radius: var(--r-1);
  font-size: var(--f-note);
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.badge.is-alert { background: var(--c-bad-bg);     color: var(--c-bad-text); }
.badge.is-rate  { background: var(--c-warn-bg);    color: var(--c-warn-text); }
.badge.is-ord   { background: var(--c-accent-soft); color: var(--c-accent); }
/* 출처 배지 — 나라장터가 아닌 소스(LH 등). 중립 표면색으로, 경고가 아니라 사실 표시.
   .src 계열과 뜻이 같지만(어디서 왔나) 목록 배지라 .badge 규격을 따른다. */
.badge.is-src   { background: var(--c-surface-2); color: var(--c-text-2); border: 1px solid var(--c-line-2); font-weight: 600; }
.badge.is-plain { background: var(--c-surface-2);  color: var(--c-text-2); }
/* 열린 공고의 64%에 붙는다. 면을 빼고 실선만 남겨 한 단계 뒤로 물린다. */
.badge.is-weak  { background: transparent; color: var(--c-text-3);
                  box-shadow: inset 0 0 0 1px var(--c-line); font-weight: 400; }
.badge.is-more  { background: transparent; color: var(--c-text-3); padding: 0 var(--s-1); cursor: default; }
/* 공고명으로 건진 '추정' — 오탐이 섞여 있으니 사람이 보라는 표시다.
   업종 조건이 걸렸을 때만 나타나고, 그때는 이 배지가 가장 중요한 신호다. */
.badge.is-guess { background: var(--c-warn-bg); color: var(--c-warn-text); }

/* 계산이 되지 않는 공고의 버튼 — 숨기지 않고 흐리게 둔다.
   숨기면 열이 들쭉날쭉해 보이고, 왜 못 누르는지도 알 수 없다.
   이유는 title 로 붙어 있다. */
button.mini[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}
button.mini[disabled]:hover {
  border-color: var(--c-line-2);
  color: var(--c-text-2);
  background: var(--c-surface);
}
.btn[disabled] { opacity: 0.4; cursor: not-allowed; }

/* ── 예가범위별 분포 ──
   사정률을 가르는 것은 발주기관이 아니라 예가범위다. 무리별로 칸을 나눠 세운다.

   무리를 세로로 쌓으면 카드가 화면 두세 개 높이가 된다.
   구간이 0.1%씩이라 ±3% 무리 하나가 60줄이기 때문이다(±2% 는 40줄).
   ±2% 와 ±3% 는 나란히 놓고 비교하는 것이 이 카드의 목적이므로 가로로 세운다. */
#rangeGroups {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-5) var(--s-6);
  /* 무리마다 구간 수가 달라 키가 다르다. 늘려 맞추지 않고 위를 맞춘다. */
  align-items: start;
}
/* 미상 무리는 그래프를 그리지 않아(renderRanges) 통계만 남는다. 한 줄을 통째로 쓴다.
   byPriceRange 가 미상을 항상 끝으로 정렬하므로 마지막 줄에 온다. */
#rangeGroups .rangegroup.is-unknown { grid-column: 1 / -1; }

.rangegroup h3 { margin: 0 0 var(--s-3); font-size: var(--f-body); color: var(--c-text); }
.rangegroup .summary { margin-bottom: var(--s-3); }
/* 범위 미상 무리는 서로 다른 범위가 섞인 것이라 판단에 쓰면 안 된다. 눈에서 한 단계 물린다. */
.rangegroup.is-unknown { opacity: 0.72; }

/* 가로로 둘을 세우려면 줄 높이를 글자가 정하게 두면 안 된다.
   line-height 를 1 로 묶어야 한 줄이 글자 크기 그대로가 된다 —
   기본 1.55 로 두면 10px 글자가 16px 줄이 되어 60줄에서 360px 가 그냥 새어 나간다.
   .hist.mini 를 직접 고치지 않는 이유: 공고 상세의 #dRateHist 가 같은 클래스를 쓴다.
   거기는 한 무리뿐이라 좁힐 이유가 없다. */
/* 틈은 어느 표에서나 2px 로 같아야 한다. 여기만 좁히면 같은 막대가 자리마다 다르게 읽힌다.
   60줄 × 1px 이 늘어 무리마다 60px 쯤 길어지지만, 규격을 지키는 값이다. */
#rangeGroups .hist { --lab-w: 46px; --cnt-w: 28px; }
#rangeGroups .histbody { gap: 2px; }
#rangeGroups .hist .bar {
  font-size: 10px;
  line-height: 1;
}
#rangeGroups .hist .fill { height: 9px; }

/* 전체 평균과 구분되지 않는 기관 행 — 지우지 않되 앞으로 나서지도 않게 */
#agencyTable tr.is-muted td { color: var(--c-text-2); }

/* ── 무효 확률 ──
   금액을 넣으면 확률이 나오는 쪽. 이 카드에서 가장 먼저 읽혀야 하는 숫자다.
   색만으로 뜻을 전하지 않는다 — 숫자와 문장이 늘 같이 붙는다. */
.bidrow { margin-bottom: var(--s-4); }
.risk { margin: 0 0 var(--s-4); }
.risk-main { font-size: 36px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.2; }
.risk-main b { font-weight: 700; }
.risk-main.is-ok   { color: var(--c-ok-text); }
.risk-main.is-warn { color: var(--c-warn-text); }
.risk-main.is-bad  { color: var(--c-bad-text); }
.risk-sub { margin-top: var(--s-2); font-size: var(--f-label); color: var(--c-text-2); }
.risk-sub b { color: var(--c-text); font-variant-numeric: tabular-nums; }
.risk-sub .dim { color: var(--c-text-3); }

/* 하한이 나올 수 있는 폭 위에 내 금액이 어디쯤인지 */
.posbar { margin: 0 0 var(--s-4); }
.posbar-track {
  position: relative;
  height: 8px;
  border-radius: 4px;
  background: var(--c-surface-2);
  overflow: visible;
}
.posbar-fill { position: absolute; inset: 0 auto 0 0; border-radius: 4px; background: var(--c-accent-soft); }
.posbar-mark {
  position: absolute; top: -4px; width: 2px; height: 16px;
  background: var(--c-accent); transform: translateX(-1px);
}
.posbar-ends {
  display: flex; justify-content: space-between;
  margin-top: var(--s-2);
  font-size: var(--f-note); color: var(--c-text-3);
  font-variant-numeric: tabular-nums;
}

/* ── 무효 확률 슬라이더 ──
   0.1% 단위로 직접 움직이는 것이 주 조작이고, 프리셋 버튼은 빠른 선택이다.
   현재 값을 슬라이더 바로 위에 크게 둔다 — 이 카드에서 가장 먼저 읽히는 숫자다. */
.riskslider { margin: 0 0 var(--s-4); }
.riskslider .slider-head b {
  font-size: 36px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.riskslider .slider-head b.is-ok   { color: var(--c-ok-text); }
.riskslider .slider-head b.is-warn { color: var(--c-warn-text); }
.riskslider .slider-head b.is-bad  { color: var(--c-bad-text); }
.riskslider input[type=range] { height: 24px; }
/* 표본이 얇아 계단이 굵을 때. 고장이 아니라는 것을 알려주는 자리다. */
.riskslider .note.is-warn { color: var(--c-warn-text); }
.riskslider .note b { color: inherit; font-weight: 600; }

/* 막힌 계산 버튼은 이유가 글자로 보인다. 폭이 들쭉날쭉하지 않게 최소 폭을 준다. */
#noticeTable button.mini { min-width: 74px; }
#noticeTable button.mini[disabled] { font-weight: 400; }

/* ── 밝기 전환 버튼 ── 헤더 우측. 조용한 보조 버튼이라 강조색을 쓰지 않는다. */
.theme-toggle {
  margin-left: auto; align-self: center;
  font: inherit; font-size: var(--f-label); cursor: pointer;
  padding: 6px 12px; border-radius: var(--r-1);
  border: 1px solid var(--c-line-2); background: var(--c-surface); color: var(--c-text-2);
}
.theme-toggle:hover { background: var(--c-surface-2); color: var(--c-text); }

/* ═══ 2단 배치 — 조건 사이드바 + 목록/분석 본문 ═══
   세로로 쌓으면 오른쪽이 늘 비었다. 조건을 왼쪽 고정 폭으로 세우고
   목록·분석을 오른쪽이 채우게 해서 가로를 다 쓴다. */
/* 사이드바 폭·간격을 변수로 둔다. 기본 300(계산 탭 입력열이 이 값을 공유한다). */
.split { display: grid; grid-template-columns: var(--rail-w, 300px) minmax(0, 1fr); gap: var(--rail-gap, var(--s-5)); align-items: start; }
/* 목록만 사이드바를 좁혀 표에 자리를 준다. 변수만 바꾸므로 좁은 화면에서 세로로 쌓는
   @media .split 규칙(grid-template-columns 를 직접 지정)이 그대로 이겨 모바일 스택이 안 깨진다.
   이게 없으면 계산 탭 입력열까지 224 로 좁아져 공고 정보 글자가 뭉갠다. */
#searchSplit { --rail-w: 224px; --rail-gap: var(--s-4); }
/* 계산 탭 입력열은 조금 넓혀 예가범위 시작 (%)+출처 배지가 옆 칸(예가범위 끝)과 안 겹치게 한다.
   그만큼 오른쪽 결과 열이 줄지만 결과 표는 넉넉해 문제없다. */
[data-panel="calc"] .split { --rail-w: 344px; }
.rail {
  position: sticky;
  top: calc(var(--s-6) + 40px);
  /* 조건이 화면보다 길면 sticky 가 아래를 잘라 끝까지 스크롤이 안 된다(2026-07-28 실측:
     rail 1002px > 뷰포트 673px). 제 높이를 뷰포트로 묶고 넘치면 자기 안에서 스크롤한다. */
  max-height: calc(100vh - var(--s-6) - 40px - var(--s-4));
  overflow-y: auto;
}
.rail .card { margin-bottom: 0; }
.colmain > .card { margin-top: 0; }
.colmain > .card + .card { margin-top: var(--s-5); }
.rail .filtergrid { grid-template-columns: 1fr; }

#dataStatus { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
#dataStatus .line { display: block; padding: var(--s-2) var(--s-4);
  border-bottom: 0; border-left: 1px solid var(--c-line); }
#dataStatus .line:first-child { border-left: 0; padding-left: 0; }
#dataStatus .k { display: block; margin-bottom: var(--s-1); }
#dataStatus .v { display: block; text-align: left; font-family: var(--font-num);
  font-size: var(--f-stat); font-variant-numeric: normal; line-height: 1.2; }

/* ── 조건 사이드바 접기 ──
   2단은 조건·목록을 같이 보여 좋지만 목록이 좁아진다.
   접으면 사이드바가 사라지고 목록이 전체폭이 돼 표가 다 보인다. */
/* minmax(0,1fr) — 그냥 1fr 이면 min-content 가 auto 라 넓은 표가 칸을 밀어
   페이지가 통째로 가로로 넘친다(2026-07-27 실측: 1280폭에서 문서 1524px).
   0 으로 클램프해야 표가 제 overflow-x 안에서 스크롤된다(펼침과 같은 방식). */
#searchSplit.is-collapsed { grid-template-columns: minmax(0, 1fr); }
#searchSplit.is-collapsed .rail { display: none; }

/* ── 좁은 화면: 2단을 세로로 쌓는다 ──
   창을 줄이거나 작은 화면에서 열면 사이드바+본문 2단이 눌려 표가 카드 밖으로 잘린다
   (뱃지가 반쯤 잘림). 일정 폭 아래에서는 한 열로 쌓아 본문이 전체폭을 쓰게 한다. */
@media (max-width: 860px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  /* 쌓이면 sticky·자체 스크롤이 되레 방해된다. 자연스럽게 흐르게 푼다. */
  .rail { position: static; max-height: none; overflow: visible; }
  .split-main { position: static; }
  #dataStatus { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #dataStatus .line { border-left: 0; padding-left: 0; }
}
@media (max-width: 520px) {
  #dataStatus { grid-template-columns: 1fr; }
}
.card-head { display: flex; align-items: center; gap: var(--s-3); }
.rail-toggle {
  margin-left: auto; font: inherit; font-size: var(--f-label); cursor: pointer;
  padding: 5px 12px; border-radius: var(--r-1);
  border: 1px solid var(--c-line-2); background: var(--c-surface); color: var(--c-text-2);
}
.rail-toggle:hover { background: var(--c-surface-2); color: var(--c-text); }
