/* /graph 카드 캔버스 전용 스타일.
   ISS-939 로 index.html.erb 에서 분리했다 — 내용은 한 글자도 바꾸지 않았다.
   레이아웃의 :app 번들과 별개로 graph 뷰에서만 읽는다. */
    :root {
      --brand:#1e3a8a;
      --brand2:#1e40af;
      --brand-soft:#eef2ff;
      --accent:#4e79a7;
      --cta:#ff385c;
      --success:#59a14f;
      --warn:#edc948;
      --danger:#e15759;
      --ink:#172b4d;
      --ink2:#42526e;
      --muted:#6b778c;
      --faint:#97a0af;
      --bg:#f4f5f7;
      --surface:#ffffff;
      --panel:#fafbfc;
      --line:#dfe1e6;
      --line2:#ebecf0;
      --shadow-sm:0 1px 1px rgba(9,30,66,.06);
      --shadow:0 1px 3px rgba(9,30,66,.1),0 0 1px rgba(9,30,66,.08);
      /* WorkFlow 라벨: top 8px + 높이 약 24px + 아래 여유 8px */
      --pane-label-h:40px;
    }

    * {
      box-sizing: border-box;
    }

    html,
    body {
      width: 100%;
      height: 100%;
      margin: 0;
      overflow: hidden;
    }

    body,
    button,
    input {
      font-family: 'IBM Plex Sans KR', system-ui, sans-serif;
    }

    button,
    input {
      font-size: inherit;
    }

    button {
      color: inherit;
    }

    .graph-app {
      display: grid;
      grid-template-rows: 44px minmax(0, 1fr);
      width: 100%;
      height: 100vh;
      color: var(--ink);
      background: var(--bg);
      font-size: 12px;
    }

    /* 아래 .graph-layout 과 열을 맞춘다 — 로고는 서랍 폭(260px),
       프로젝트 입력창부터는 판(중앙) 좌측선에서 시작한다. */
    .topbar {
      display: grid;
      grid-template-columns: 260px minmax(0, 1fr);
      gap: 8px;
      align-items: center;
      min-width: 0;
      padding: 4px 8px;
      border-bottom: 1px solid var(--line);
      background: var(--surface);
    }

    .topbar-right {
      position: relative;
      display: flex;
      align-items: center;
      gap: 8px;
      min-width: 0;
    }

    /* 로고 + 카피 — 폭은 카드 서랍(260px)에 맞춘다 */
    .dashboard-link {
      display: grid;
      grid-template-columns: 18px minmax(0, 1fr);
      grid-template-rows: auto auto;
      gap: 0 6px;
      align-items: center;
      min-width: 0;
      color: var(--brand);
      text-decoration: none;
    }

    .dashboard-link svg {
      display: block;
      grid-row: 1 / 3;
    }

    .brand-name {
      font-size: 13px;
      font-weight: 800;
      letter-spacing: -0.01em;
      line-height: 1.2;
    }

    .brand-tagline {
      overflow: hidden;
      color: var(--muted);
      font-size: 9px;
      font-weight: 500;
      line-height: 1.25;
      letter-spacing: -0.02em;
      white-space: nowrap;
      text-overflow: ellipsis;
    }

    .dashboard-link:hover .brand-name {
      opacity: 0.75;
    }

    .project-select {
      flex: 0 0 auto;
      max-width: 200px;
      height: 28px;
      padding: 3px 6px;
      border: 1px solid var(--line);
      border-radius: 5px;
      outline: 0;
      color: var(--ink);
      background: var(--surface);
      font-size: 12px;
      font-weight: 700;
      cursor: pointer;
    }

    .project-select:focus {
      border-color: var(--brand);
    }

    .visually-hidden {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }

    .project-combo {
      position: relative;
      display: inline-flex;
    }

    .project-combo-input {
      flex: 0 0 auto;
      max-width: 220px;
      height: 28px;
      padding: 3px 8px;
      color: var(--ink);
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 5px;
      outline: 0;
      font-size: 12px;
      font-weight: 700;
    }

    .project-combo-input:focus { border-color: var(--brand); }

    .project-combo-pop {
      position: absolute;
      top: calc(100% + 6px);
      left: 0;
      min-width: 340px;
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 8px;
      box-shadow: 0 8px 24px rgba(15, 23, 42, 0.16);
      z-index: 30;
    }

    .project-combo-pop[hidden] { display: none; }
    .project-combo-list { max-height: 340px; overflow-y: auto; }
    .project-combo-row {
      display: flex;
      flex-direction: column;
      gap: 2px;
      width: 100%;
      padding: 7px 12px;
      color: var(--ink);
      background: transparent;
      border: 0;
      cursor: pointer;
      font: inherit;
      font-size: 12px;
      text-align: left;
    }
    .project-combo-row:hover,
    .project-combo-row[aria-selected="true"] { background: var(--brand-soft); }
    .project-combo-row-head { display: flex; align-items: center; gap: 5px; }
    .project-combo-name { min-width: 0; overflow: hidden; font-size: 12px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
    .project-combo-meta { color: var(--muted); font-size: 10px; }
    .project-combo-badge {
      flex: 0 0 auto;
      padding: 1px 5px;
      background: var(--brand-soft);
      border-radius: 4px;
      font-size: 10px;
    }
    .project-combo-dot { width:7px; height:7px; border-radius:50%; flex:0 0 auto; }
    .project-combo-dot[data-status="draft"]     { background:#8a8f98; }
    .project-combo-dot[data-status="active"]    { background:#3b82f6; }
    .project-combo-dot[data-status="delivered"] { background:#22c55e; }
    .project-combo-dot[data-status="archived"]  { background:#8a8f98; opacity:.35; }
    .project-combo-row[data-status="archived"] .project-combo-name { opacity:.55; }
    .project-combo-chips { display: flex; gap: 4px; padding: 7px 8px; overflow-x: auto; border-bottom: 1px solid var(--line); }
    .project-combo-chip {
      flex: 0 0 auto;
      padding: 2px 7px;
      color: var(--ink2);
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 999px;
      cursor: pointer;
      font-size: 10px;
    }
    .project-combo-chip[aria-pressed="true"] { color: var(--surface); background: var(--brand); border-color: var(--brand); }
    .project-combo-empty { padding: 14px 12px; color: var(--muted); font-size: 11px; text-align: center; }
    .project-combo-foot { padding: 6px 10px; color: var(--muted); border-top: 1px solid var(--line); font-size: 10px; }

    .project-action {
      flex: 0 0 auto;
      height: 28px;
      padding: 0 9px;
      border: 1px solid var(--line);
      border-radius: 5px;
      color: var(--ink2);
      background: var(--surface);
      font-size: 11px;
      font-weight: 700;
      white-space: nowrap;
      cursor: pointer;
    }

    .project-action:hover {
      border-color: var(--brand);
      color: var(--brand);
      background: var(--brand-soft);
    }

    .pane-toggle-group {
      display: inline-flex;
      flex: 0 0 auto;
    }

    .pane-toggle-group .pane-toggle {
      width: 28px;
      padding: 0;
      border-radius: 0;
      color: var(--muted);
    }

    .pane-toggle-group .pane-toggle:first-child { border-radius: 5px 0 0 5px; }
    .pane-toggle-group .pane-toggle:last-child { border-radius: 0 5px 5px 0; }
    .pane-toggle-group .pane-toggle + .pane-toggle { margin-left: -1px; }
    .pane-toggle-group .pane-toggle[aria-pressed="true"] {
      position: relative;
      border-color: var(--brand);
      color: var(--brand);
      background: var(--brand-soft);
    }

    /* 산출물 보기: 링크지만 보조 버튼과 같은 크기·정렬 (ISS-152) */
    .report-open {
      display: inline-flex;
      align-items: center;
      text-decoration: none;
    }

    /* display:inline-flex가 hidden 속성을 이기는 것 방지 (ISS-279 — 메뉴로 이사한 버튼 숨김) */
    .report-open[hidden] { display: none; }

    .save-state {
      flex: 0 0 auto;
      color: var(--success);
      font-size: 11px;
      font-weight: 700;
      white-space: nowrap;
    }

    .save-state[hidden] {
      display: none;
    }

    /* File 메뉴 (ISS-279) — 동사 5종을 드롭다운 하나로. 팝업 z-index 30 > 카드(2)·선택(3) */
    .bar-menu-wrap {
      position: relative;
      display: inline-flex;
    }

    .bar-menu {
      position: absolute;
      top: calc(100% + 6px);
      right: 0;
      min-width: 240px;
      padding: 4px;
      background: var(--surface, #fff);
      border: 1px solid var(--line);
      border-radius: 8px;
      box-shadow: 0 8px 24px rgba(15, 23, 42, 0.16);
      z-index: 30;
    }

    .bar-menu[hidden] { display: none; }

    .bar-menu-item {
      display: flex;
      flex-direction: column;
      width: 100%;
      padding: 7px 12px;
      color: var(--ink);
      background: transparent;
      border: 0;
      border-radius: 6px;
      cursor: pointer;
      font: inherit;
      font-size: 12px;
      text-align: left;
      text-decoration: none;
    }

    .bar-menu-item:hover,
    .bar-menu-item:focus-visible {
      background: var(--brand-soft, #eef2ff);
    }

    .bar-menu-item .sub {
      font-size: 10px;
      color: var(--muted);
    }

    .bar-menu-item.is-danger { color: #c9372c; }
    .bar-menu-row { display:flex; align-items:center; gap:8px; padding:6px 12px; }
    .bar-menu-item-label { font-size:12px; color:var(--muted, #8a8f98); }
    .project-status-select { flex:1; font-size:12px; padding:3px 6px; }
    .project-status-nudge { font-size:11px; padding:2px 8px; border-radius:10px;
      border:1px solid #22c55e; color:#22c55e; background:transparent; cursor:pointer; }
    .secret-menu-panel { width: 320px; padding: 8px 12px; }
    .secret-menu-row { padding: 8px 0; border-top: 1px solid var(--line); }
    .secret-menu-head { display: flex; align-items: center; gap: 6px; }
    .secret-menu-name { flex: 1; font-size: 12px; font-weight: 700; }
    .secret-menu-value { font-size: 11px; color: var(--muted); }
    .secret-menu-actions { display: inline-flex; gap: 4px; }
    .secret-menu-actions button { padding: 2px 6px; font: inherit; font-size: 10px; }
    .secret-menu-form { display: flex; gap: 4px; margin-top: 6px; }
    .secret-menu-form input { min-width: 0; flex: 1; padding: 5px 7px; }
    .secret-menu-note { margin-top: 4px; color: #b42318; font-size: 10px; }
    .engine-dot {
      display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; height: 28px;
      padding: 0 9px; border: 1px solid var(--line); border-radius: 5px;
      color: var(--ink2); background: var(--surface); font-size: 11px; font-weight: 700;
      white-space: nowrap; cursor: pointer;
    }
    .engine-dot-led { width: 8px; height: 8px; border-radius: 50%; background: #8993a4; }
    .engine-diag-panel {
      position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
      width: 320px; padding: 8px 12px; border: 1px solid var(--line);
      border-radius: 8px; color: var(--ink); background: var(--surface);
      box-shadow: 0 8px 24px rgba(15, 23, 42, 0.16);
    }
    .engine-diag-panel[hidden] { display: none; }
    .engine-diag-section { padding: 8px 0; }
    .engine-diag-section + .engine-diag-section { border-top: 1px solid var(--line); }
    .engine-diag-title { margin-bottom: 6px; font-weight: 700; }
    .engine-diag-row { margin-top: 3px; overflow-wrap: anywhere; }
    .engine-diag-warn { color: #b42318; }
    .pack-import-result {
      position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center;
      background: rgba(15, 23, 42, .55); padding: 24px;
    }
    .pack-import-result[hidden] { display: none; }
    .pack-import-panel {
      width: min(760px, 100%); max-height: 80vh; overflow: auto; background: #fff;
      border-radius: 12px; padding: 20px; box-shadow: 0 18px 50px rgba(0,0,0,.28);
    }
    .pack-import-table { width: 100%; border-collapse: collapse; margin-top: 12px; }
    .pack-import-table th, .pack-import-table td { padding: 8px; border-bottom: 1px solid #e5e7eb; text-align: left; }
    .pack-import-table tr.is-warning { background: #fff1f2; color: #9f1239; font-weight: 700; }

    .bar-menu-sep {
      height: 1px;
      margin: 4px 0;
      background: var(--line);
    }

    /* 5분위 구간 체크 (ISS-293) — 항목이 온전한 단위로, 라벨 중간 줄바꿈 금지 */
    .q-check-grid {
      display: flex;
      flex-direction: column;
      gap: 4px;
      margin-top: 6px;
    }
    .q-check {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 5px 8px;
      border: 1px dashed var(--line);
      border-radius: 6px;
      font-size: 11px;
      white-space: nowrap;
      cursor: pointer;
    }
    .q-check:hover { background: var(--brand-soft, #eef2ff); }

    /* 5P 능동 안내 (ISS-282) — 상태 문장 / 제안·확정 구분 / 여기요 행 배지 */
    .p5-status {
      margin: 8px 0 6px;
      padding: 7px 10px;
      border: 1px dashed var(--line);
      border-radius: 6px;
      background: var(--brand-soft, #eef2ff);
      font-size: 11px;
      line-height: 1.5;
      word-break: keep-all;
    }

    tr.p5-proposed td { opacity: 0.78; }          /* 제안 = 연하게 (점선 어휘의 행 버전) */
    tr.p5-proposed select { border-style: dashed; }
    tr.p5-confirmed td { opacity: 1; }            /* 확정 = 또렷하게 */
    tr.p5-confirmed select { border-style: solid; border-color: var(--brand); }

    .p5-here {
      display: inline-block;
      margin-left: 4px;
      padding: 1px 6px;
      border-radius: 99px;
      background: var(--brand);
      color: #fff;
      font-size: 9px;
      font-weight: 700;
      animation: p5here 1.2s ease-in-out infinite;
    }
    @keyframes p5here {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(-2px); }
    }

    /* 역할 라벨 (ISS-273) — 콤보=열기 / 입력칸=이름 */
    .bar-role-label {
      flex: 0 0 auto;
      font-size: 10px;
      font-weight: 700;
      color: var(--muted);
      white-space: nowrap;
      display: inline-flex;
      align-items: center;
      gap: 2px;
    }

    /* 좁은 화면 방어 (ISS-274) — 상단 바는 원래도 1200px에서 넘쳤다(사전 존재).
       새 요소가 그걸 키우지 않게, 좁으면 보조 표식부터 접는다. */
    @media (max-width: 1440px) {
      .bar-role-label { display: none; }
      .save-badge { max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
    }

    /* 저장 상시 배지 (ISS-272) — 플래시와 달리 사라지지 않는다 */
    .save-badge {
      flex: 0 0 auto;
      font-size: 11px;
      white-space: nowrap;
      color: var(--muted);
    }

    [data-run-plan-reason] {
      flex-shrink: 1;
      min-width: 0;
      max-width: 200px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .save-badge[data-state="saving"] { color: var(--muted); }
    .save-badge[data-state="saved"]  { color: var(--success); }
    .save-badge[data-state="error"]  { color: #c9372c; font-weight: 700; }

    .project-name {
      width: min(300px, 26vw);
      min-width: 140px;
      height: 28px;
      padding: 4px 9px;
      border: 1px solid var(--line);
      border-radius: 5px;
      outline: 0;
      color: var(--ink);
      background: var(--panel);
    }

    .project-name:focus {
      border-color: var(--brand);
      background: var(--surface);
    }

    .domain-badge,
    .graph-counter {
      display: inline-flex;
      align-items: center;
      min-height: 24px;
      padding: 3px 8px;
      border: 1px solid var(--line);
      border-radius: 999px;
      white-space: nowrap;
      font-size: 11px;
      font-weight: 700;
    }

    .domain-badge {
      color: var(--brand);
      background: var(--brand-soft);
    }

    /* 공간 이름 — 윗쪽 WorkFlow / 아래 FreeView */
    .pane-label {
      position: absolute;
      left: 10px;
      top: 8px;
      z-index: 6;
      padding: 2px 8px;
      border: 1px solid var(--line);
      border-radius: 999px;
      color: var(--muted);
      background: rgba(255, 255, 255, 0.9);
      font-size: 10px;
      font-weight: 800;
      letter-spacing: 0.02em;
      pointer-events: none;
    }

    .pane-label-inline {
      position: static;
      background: var(--surface);
    }

    /* 지금 해야 할 한 가지 — WorkFlow 위쪽의 독립된 안내 띠 */
    .next-action {
      display: flex;
      align-items: center;
      gap: 8px;
      width: 100%;
      min-width: 0;
      padding: 6px 10px;
      border-bottom: 1px solid rgba(59, 130, 246, 0.45);
      background: var(--surface);
      box-sizing: border-box;
    }

    .next-action:not(:has(.canvas-alert:not([hidden]), .next-action-step:not(:empty), .next-action-text:not(:empty), .next-action-btn:not(:empty))) {
      display: none;
    }

    .next-action:has(.canvas-alert:not([hidden])) {
      border-bottom-color: var(--warn);
      color: var(--ink);
      background: var(--brand-soft);
    }

    .next-action:has(.canvas-alert:not([hidden])) > :not(.canvas-alert) {
      display: none;
    }

    .next-action-step {
      flex: 0 0 auto;
      color: var(--brand);
      font-size: 13px;
      font-weight: 800;
    }

    .next-action-text {
      min-width: 0;
      overflow: hidden;
      color: var(--ink2);
      font-size: 12.5px;
      font-weight: 700;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .next-action-btn {
      flex: 0 0 auto;
      margin-left: auto;
      height: 27px;
      padding: 0 10px;
      border: 1px solid var(--brand);
      border-radius: 5px;
      color: var(--surface);
      background: var(--brand);
      font-size: 11px;
      font-weight: 800;
      white-space: nowrap;
      cursor: pointer;
    }

    .next-action-btn:hover { background: var(--brand2); }
    .next-action-btn[hidden] { display: none; }

    /* 줌 컨트롤 — 스크롤과 안내 띠에 영향받지 않고 판 위에 떠 있다 */
    .zoom-controls {
      position: absolute;
      right: 12px;
      top: 8px;
      z-index: 6;
      display: flex;
      gap: 3px;
      padding: 3px;
      border: 1px solid var(--line);
      border-radius: 7px;
      background: var(--surface);
      box-shadow: var(--shadow);
    }

    .zoom-btn {
      min-width: 26px;
      height: 24px;
      padding: 0 7px;
      border: 0;
      border-radius: 4px;
      color: var(--ink2);
      background: transparent;
      font-size: 12px;
      font-weight: 700;
      cursor: pointer;
    }

    .zoom-btn:hover {
      color: var(--brand);
      background: var(--brand-soft);
    }

    .zoom-now {
      /* 폭 고정 — min-width 면 "100%"가 넘쳐 버튼이 밀리고 컨트롤 전체가 흔들린다 (ZOOM-002) */
      width: 46px;
      min-width: 46px;
      padding-inline: 0;
      color: var(--muted);
      font-size: 10px;
      font-variant-numeric: tabular-nums;
    }

    .zoom-fit {
      color: var(--brand);
      font-size: 10px;
    }

    .zoom-badge {
      flex: 0 0 auto;
      padding: 3px 8px;
      border: 1px solid var(--line);
      border-radius: 999px;
      color: var(--muted);
      background: var(--panel);
      font-size: 11px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
      cursor: pointer;
    }

    .zoom-badge[hidden] { display: none; }
    .zoom-badge:hover { border-color: var(--brand); color: var(--brand); }

    .graph-counter {
      flex: 0 0 180px;
      justify-content: center;
      color: var(--muted);
      background: var(--panel);
      font-variant-numeric: tabular-nums;
    }

    /* 1600px 실측에서 넘치는 180px 카운터를 1680px 이하에서 잔해 없이 접는다. */
    @media (max-width: 1680px) {
      .graph-counter { display: none; }
    }

    /* 파일을 보고 판을 짓는다 */
    .auto-build {
      margin-left: auto;
      padding: 4px 11px;
      border: 1px solid var(--brand);
      border-radius: 5px;
      color: var(--surface);
      background: var(--brand);
      font-size: 11px;
      font-weight: 700;
      white-space: nowrap;
      cursor: pointer;
    }

    .auto-build:hover { background: var(--brand2); }

    /* 판을 진짜로 돌린다 — 파일 → 학습 → 5분위 → What-if */
    .run-plan {
      padding: 4px 11px;
      border: 1px solid var(--success);
      border-radius: 5px;
      color: var(--surface);
      background: var(--success);
      font-size: 11px;
      font-weight: 700;
      white-space: nowrap;
      cursor: pointer;
    }

    .run-plan:hover { opacity: 0.85; }

    @media (max-width: 1360px) {
      .auto-build,
      .run-plan[data-play] {
        padding: 4px 6px;
        font-size: 0;
      }

      .auto-build > i,
      .run-plan[data-play] > i {
        font-size: 13px;
      }

      .run-plan[data-run-plan] { padding: 4px 6px; }
      [data-dup-badge] { display: none; }
    }

    /* 차트 — SVG 직접 (외부 라이브러리 금지) */
    .chart-box {
      margin-top: 10px;
      padding: 8px;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: var(--surface);
    }

    .chart-title {
      margin-bottom: 4px;
      color: var(--ink);
      font-size: 11px;
      font-weight: 700;
    }

    .chart-svg {
      display: block;
      width: 100%;
      height: auto;
      max-height: 220px;
    }

    /* 지식그래프는 세로가 있어야 노드 라벨이 읽힌다.
       막대차트용 220px 상한에 걸리면 640x340이 짜부라진다 (KG-002). */
    .chart-svg.is-graph {
      max-height: 460px;
      margin-inline: auto;
      max-width: 860px;
    }

    .chart-box.is-kg {
      border-color: #24324a;
      background: #0a0e1a;
      box-shadow: inset 0 0 28px rgba(56, 189, 248, 0.05);
    }

    .chart-box.is-kg .chart-title { color: #cbd5e1; }

    .chart-svg.is-graph.is-starlight { border-radius: 5px; }

    /* 카드 결과 표 */
    .mini-table {
      width: 100%;
      margin-top: 10px;
      border-collapse: collapse;
      font-size: 11px;
      font-variant-numeric: tabular-nums;
    }

    .mini-table th,
    .mini-table td {
      padding: 4px 6px;
      border-bottom: 1px solid var(--line2);
    }

    .mini-table th {
      color: var(--muted);
      background: var(--panel);
      font-size: 10px;
      text-align: left;
    }


    .graph-layout {
      display: grid;
      grid-template-columns: 260px minmax(520px, 1fr) 340px;
      gap: 8px;
      min-width: 0;
      min-height: 0;
      padding: 8px;
    }

    /* 세 영역은 각각 접을 수 있다. 플레이는 세 영역을 모두 접는 상태로 진입한다. */
    .pane-off-drawer .graph-layout { grid-template-columns: minmax(0, 1fr) 340px; }
    .pane-off-detail .graph-layout { grid-template-columns: 260px minmax(0, 1fr); }
    .pane-off-drawer.pane-off-detail .graph-layout { grid-template-columns: minmax(0, 1fr); }
    .pane-off-drawer aside.graph-drawer,
    .pane-off-detail aside.graph-detail { display: none; }
    .is-playing.detail-peek .graph-layout {
      grid-template-columns: minmax(0, 1fr) 340px;
    }
    .is-playing.detail-peek aside.graph-detail { display: block; }
    .pane-off-dock .graph-center .split-bar,
    .pane-off-dock .graph-center .result-dock { display: none; }

    .graph-drawer,
    .graph-center,
    .graph-detail {
      min-width: 0;
      min-height: 0;
      border: 1px solid var(--line);
      border-radius: 7px;
      background: var(--surface);
      box-shadow: var(--shadow-sm);
    }

    .graph-detail {
      overflow-y: auto;
      padding: 10px;
    }

    /* 서랍 — 제목/필터는 고정, 카드만 스크롤 */
    .graph-drawer {
      display: grid;
      grid-template-rows: auto auto minmax(0, auto) minmax(0, 1fr);
      overflow: hidden;
      padding: 10px;
    }

    .drawer-cards {
      min-height: 0;
      overflow-y: auto;
      padding-right: 2px;
    }

    .drawer-heading {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      margin-bottom: 8px;
    }

    .drawer-heading h1 {
      margin: 0;
      font-size: 15px;
    }

    .drawer-count {
      padding: 2px 7px;
      border: 1px solid var(--line);
      border-radius: 999px;
      color: var(--muted);
      background: var(--panel);
      font-size: 10px;
      font-weight: 700;
    }

    .group-filters {
      display: flex;
      flex-wrap: wrap;
      gap: 5px;
      margin-bottom: 8px;
    }

    /* 그룹 필터 — 4개 계열을 아이보리 계열 저채도 컬러카드로 구분한다.
       채도를 낮게 유지해 카드(--surface #fff)보다 뒤로 물러나 보이게. */
    .filter-chip {
      padding: 4px 10px;
      border: 1px solid var(--line);
      border-radius: 5px;
      color: var(--ink2);
      background: #faf8f3;
      font-size: 11px;
      font-weight: 700;
      cursor: pointer;
    }

    .filter-chip[data-group-filter="시작"]        { background: #f7f4ec; }
    /* 조작 표 (PARAM-001) — 정본은 변수마다 위젯 4개를 반복한다.
       대표님 CLAUDE.md §4: "조정값 한 벌 × 변수 개수 — 표 하나면 된다." */
    .param-table select,
    .param-table input {
      width: 100%;
      padding: 2px 4px;
      border: 1px solid var(--line);
      border-radius: 3px;
      font-size: 10.5px;
      background: var(--surface);
    }
    .param-table input { text-align: right; }
    .param-table select:focus,
    .param-table input:focus { outline: none; border-color: var(--brand); }
    /* 통제불가로 정한 줄 — 무엇이 잠겼는지 눈에 보이게 */
    .param-table tr.is-locked { opacity: 0.55; }
    .param-table tr.is-locked td:first-child { text-decoration: line-through; }

    /* 카드 검색 (SEARCH-001) — 31장이 되니 눈으로 훑기 어렵다 */
    .card-search {
      width: 100%;
      margin-bottom: 6px;
      padding: 5px 8px;
      border: 1px solid var(--line);
      border-radius: 5px;
      font-size: 11.5px;
      background: var(--surface);
    }
    .card-search:focus {
      outline: none;
      border-color: var(--brand);
      box-shadow: 0 0 0 2px var(--brand-soft, #eef2f8);
    }

    .filter-chip[data-group-filter="탐색"]        { background: #eef3f6; }
    .filter-chip[data-group-filter="텍스트 계열"]  { background: #f3f2ea; }
    .filter-chip[data-group-filter="수치 인과"]    { background: #f1f3ef; }
    .filter-chip[data-group-filter="마감 계열"]    { background: #f5f1ea; }

    /* ── 계열 색점 범례 (ISS-124) — 칩에 계열색 점을 찍어 카드색이 뭘 뜻하는지 알린다 ── */
    .filter-chip[data-group-filter="시작"],
    .filter-chip[data-group-filter="탐색"],
    .filter-chip[data-group-filter="텍스트 계열"],
    .filter-chip[data-group-filter="수치 인과"],
    .filter-chip[data-group-filter="마감 계열"] {
      display: inline-flex;
      align-items: center;
      gap: 5px;
    }
    .filter-chip[data-group-filter="시작"]::before,
    .filter-chip[data-group-filter="탐색"]::before,
    .filter-chip[data-group-filter="텍스트 계열"]::before,
    .filter-chip[data-group-filter="수치 인과"]::before,
    .filter-chip[data-group-filter="마감 계열"]::before {
      content: "";
      width: 8px;
      height: 8px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .filter-chip[data-group-filter="시작"]::before        { background: #4a86d4; }
    .filter-chip[data-group-filter="탐색"]::before        { background: #3d9a9a; }
    .filter-chip[data-group-filter="텍스트 계열"]::before  { background: #5aa35a; }
    .filter-chip[data-group-filter="수치 인과"]::before    { background: #8a63be; }
    .filter-chip[data-group-filter="마감 계열"]::before    { background: #d18e33; }

    .filter-chip:hover {
      border-color: var(--accent);
    }

    .filter-chip.is-active {
      border-color: var(--brand);
      color: var(--brand);
      background: var(--brand-soft);
    }

    .drawer-cards {
      display: grid;
      /* 카드 수가 적어도 남은 공간을 나눠 먹지 않게 — 내용 높이만 쓴다 */
      grid-auto-rows: min-content;
      align-content: start;
      gap: 6px;
    }

    .drawer-saved { min-height: 0; max-height: 45vh; margin-bottom: 8px; padding-right: 6px; overflow-y: auto; }
    .saved-group-head {
      display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 4px;
      border: 0; color: var(--ink); background: var(--surface); font-size: 11px;
      font-weight: 700; text-align: left; cursor: pointer;
    }
    .saved-group-head.is-sub { padding-top: 7px; color: var(--muted); }
    .saved-group-head span { margin-left: auto; }
    .saved-list { display: grid; gap: 7px; padding: 0 6px 4px 2px; }
    .saved-group-head[hidden], .saved-list[hidden], .saved-card[hidden] { display: none; }
    .saved-card {
      position: relative; display: grid; grid-template-columns: 14px minmax(0, 1fr);
      gap: 2px 7px; width: 100%; padding: 7px 8px; border: 1px solid var(--line);
      border-radius: 5px; color: var(--ink); background: var(--surface); text-align: left;
      cursor: pointer;
      box-shadow: 2px 2px 0 0 var(--surface), 2px 2px 0 1px var(--line),
                  4px 4px 0 0 var(--surface), 4px 4px 0 1px var(--line);
    }
    .saved-card > i { grid-row: 1 / span 2; align-self: center; }
    .saved-card-name {
      min-width: 0; overflow: hidden; font-size: 11px; font-weight: 700;
      text-overflow: ellipsis; white-space: nowrap;
    }
    .saved-card-meta { color: var(--muted); font-size: 10px; }
    .saved-card:hover { border-color: var(--ink); }

    /* hidden 속성이 display:grid 에 밀리지 않게 — 필터가 카드를 숨긴다 */
    .drawer-card[hidden] {
      display: none;
    }

    .drawer-card {
      display: grid;
      grid-template-columns: 8px minmax(0, 1fr);
      gap: 8px;
      width: 100%;
      padding: 9px;
      border: 1px solid var(--line);
      border-radius: 6px;
      text-align: left;
      color: var(--ink);
      background: var(--surface);
      box-shadow: var(--shadow-sm);
      cursor: pointer;
    }

    .drawer-card:hover {
      border-color: var(--accent);
      box-shadow: var(--shadow);
    }

    /* 서랍 카드를 판으로 끌어다 놓을 수 있다 */
    .drawer-card {
      cursor: grab;
    }

    .drawer-card:active {
      cursor: grabbing;
    }

    .drawer-card.is-dragging {
      opacity: 0.45;
      border-color: var(--brand);
    }

    /* 판이 카드를 받을 준비가 됐다 */
    .graph-canvas.is-pannable { cursor: grab; }
    .graph-canvas.is-panning { cursor: grabbing; }

    .graph-canvas.is-drop-target {
      background-color: var(--brand-soft);
      box-shadow: inset 0 0 0 2px var(--brand);
    }

    .drawer-card.is-selected {
      border-color: var(--brand);
      background: var(--brand-soft);
    }

    /* 서랍도 판과 같은 계열색 — 색이 다르면 "이 카드가 저 계열"이 안 읽힌다 (COLOR-001) */
    .drawer-card[data-card-group="시작"]        { background: #bcd8f5; }
    .drawer-card[data-card-group="탐색"]        { background: #c3e8e8; }
    .drawer-card[data-card-group="텍스트 계열"]  { background: #c3e8c3; }
    .drawer-card[data-card-group="수치 인과"]    { background: #dcc9ee; }
    .drawer-card[data-card-group="마감 계열"]    { background: #f8d9a6; }
    .drawer-card[data-card-group="IoT"]          { background: #d9e7f7; }

    .card-dot {
      width: 8px;
      height: 8px;
      margin-top: 4px;
      border-radius: 50%;
      background: var(--accent);
    }

    /* 아이콘이 들어간 서랍 카드 (ICON-004) — 점 자리를 아이콘이 차지한다 */
    .card-dot.is-icon {
      width: 18px;
      height: 18px;
      margin-top: 1px;
      border-radius: 0;
      background: none;
      color: var(--brand);
      opacity: 0.85;
    }
    .card-dot.is-icon svg { width: 18px; height: 18px; }
    .drawer-card:has(.card-dot.is-icon) { grid-template-columns: 18px minmax(0, 1fr); }

    .drawer-card[data-card-id="file_upload"] .card-dot {
      background: var(--brand);
    }

    .drawer-card[data-card-id="record_lookup"] .card-dot {
      background: var(--success);
    }

    .drawer-card[data-card-id="x_control_rule"] .card-dot {
      background: var(--warn);
    }

    .drawer-card-name {
      display: block;
      margin-bottom: 3px;
      font-size: 13px;
      font-weight: 700;
      line-height: 1.35;
    }

    .drawer-card-description {
      display: block;
      color: var(--muted);
      font-size: 11px;
      line-height: 1.4;
    }

    .drawer-empty {
      padding: 20px 10px;
      border: 1px dashed var(--line);
      border-radius: 6px;
      text-align: center;
      color: var(--muted);
      background: var(--panel);
      font-size: 12px;
    }

    /* 안내 / 판 / 결과 영역.
       결과 도크가 닫혀 있으면 판이 세로를 다 쓴다 (dock-open 클래스로 전환). */
    /* 자식 5개(안내/판/타임라인/스플릿바/결과)에 맞춰 5행.
       도크가 닫혀 있으면 스플릿바·결과 행이 0이라 판이 세로를 다 쓴다. */
    .graph-center {
      position: relative;
      display: grid;
      /* 도크가 닫혀도 스플릿바는 보인다 — 끌어올리면 결과가 열린다 */
      grid-template-rows: auto minmax(0, 1fr) 0 8px 0;
      overflow: hidden;
      background: var(--bg);
    }

    /* hidden 자식이 있어도 행이 밀리지 않게 각자 자기 행을 명시한다 */
    .next-action    { grid-row: 1; }
    .canvas-frame   { grid-row: 2; }
    .play-timeline  { grid-row: 3; }
    .split-bar      { grid-row: 4; }
    .result-dock    { grid-row: 5; }

    /* 판 / 스플릿바 / 결과. --dock-h 를 스플릿바가 조정한다. */
    .graph-center.dock-open {
      grid-template-rows: auto minmax(120px, 1fr) 0 8px var(--dock-h, 180px);
    }

    .is-playing .graph-center {
      grid-template-rows: auto minmax(0, 1fr) 28px 0 0;
    }

    .play-timeline {
      display: flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
      padding: 2px 8px;
      border-top: 1px solid var(--line);
      color: var(--muted);
      background: var(--surface);
    }

    .play-timeline[hidden] { display: none; }
    .play-timeline button {
      width: 22px;
      height: 22px;
      padding: 0;
      border: 1px solid var(--line);
      border-radius: 4px;
      color: var(--muted);
      background: var(--surface);
      cursor: pointer;
    }
    .play-timeline button:hover { border-color: var(--brand); color: var(--brand); }
    .play-timeline input { flex: 1 1 auto; min-width: 40px; accent-color: var(--brand); }
    .play-timeline span { min-width: 46px; text-align: center; font-variant-numeric: tabular-nums; }

    /* 스플릿바 — 경계가 확실히 보여야 한다 (대표님 지시) */
    .split-bar {
      display: grid;
      place-items: center;
      background: var(--brand);
      cursor: row-resize;
      touch-action: none;
      user-select: none;
    }

    .split-bar[hidden] { display: none; }

    .split-bar:hover,
    .split-bar.is-dragging {
      background: var(--brand2);
    }

    .split-grip {
      width: 44px;
      height: 3px;
      border-radius: 2px;
      background: rgba(255, 255, 255, 0.65);
    }

    /* 도크가 닫혀 있을 땐 "끌어올리면 열립니다"를 알려준다 */
    .graph-center:not(.dock-open) .split-grip::after {
      content: "▲ 결과";
      position: absolute;
      margin: -13px 0 0 -12px;
      color: #fff;
      font-size: 9px;
      font-weight: 700;
      letter-spacing: -0.02em;
      white-space: nowrap;
    }

    .split-bar:hover .split-grip,
    .split-bar.is-dragging .split-grip {
      background: #fff;
    }

    .result-dock {
      display: grid;
      grid-template-rows: auto minmax(0, 1fr);
      min-height: 0;
      background: var(--surface);
    }

    .result-dock[hidden] { display: none; }

    .dock-bar {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 5px 10px;
      border-bottom: 1px solid var(--line2);
      background: var(--panel);
    }

    .dock-title {
      color: var(--ink);
      font-size: 12px;
      font-weight: 800;
    }

    .dock-hint {
      color: var(--faint);
      font-size: 10px;
    }

    .dock-close {
      margin-left: auto;
      padding: 1px 7px;
      border: 1px solid var(--line);
      border-radius: 4px;
      color: var(--muted);
      background: var(--surface);
      font-size: 11px;
      cursor: pointer;
    }

    .dock-close:hover {
      border-color: var(--danger);
      color: var(--danger);
    }

    .dock-body {
      min-height: 0;
      padding: 12px;
      overflow: auto;
      font-size: 12px;
    }

    .dock-empty {
      display: grid;
      place-items: center;
      height: 100%;
      color: var(--faint);
      font-size: 12px;
    }

    .canvas-alert {
      font-size: 12px;
      font-weight: 700;
    }

    .canvas-alert[hidden] {
      display: none;
    }

    /* 스크롤되지 않는 판 프레임 — 라벨과 줌 컨트롤의 고정 기준 */
    .canvas-frame {
      position: relative;
      min-width: 0;
      min-height: 0;
      overflow: hidden;
    }

    .canvas-viewport {
      /* transform 된 graph-canvas와 그 카드들을 이 낮은 stacking context 안에 가둔다.
         프레임 직속 라벨·줌 컨트롤·미니맵(z-index 5+)은 항상 그 위에 놓인다. */
      position: relative;
      z-index: 0;
      width: 100%;
      height: 100%;
      min-height: 0;
      padding: 0;
      overflow: auto;          /* 팬/줌 — 판이 화면보다 커질 수 있다 */
      overscroll-behavior: contain;
      /* 카드 뭉치가 뷰포트보다 작을 때 뒤에 회색(--bg)이 비쳐 여백처럼 보였다.
         그리드와 같은 배경 + 격자로 채워 여백을 없앤다 (대표님 2026-07-17). */
      background-color: var(--surface);
      background-image:
        linear-gradient(var(--line2) 1px, transparent 1px),
        linear-gradient(90deg, var(--line2) 1px, transparent 1px);
      background-size: 24px 24px;
    }

    /* 줌 래퍼 — 스케일된 크기를 갖고 스크롤 범위를 만든다 */
    .canvas-scaler {
      width: 2376px;
      height: 1680px;
    }

    .graph-canvas {
      position: relative;
      /* ★ A4 가로(297x210mm = 1123x794px @96dpi, 비율 1.414) 비율.
         판 자체가 산출물이 된다 — 보고서에 넣거나 인쇄할 때 잘리지 않게.
         1728 x 1200 = A4 가로의 1.54배, 둘 다 격자(24px) 배수.
         전엔 2376x1680(2.1배)이라 전체뷰가 40%까지 축소돼 글씨가 떡이 됐다 (대표님 2026-07-17).
         캔버스를 줄이면 같은 x-ratio라도 물리 간격이 좁아지고 fit 줌이 ~58%로 올라 판독된다.
         정본 표준: 리포트는 A4 세로(794px), 판은 A4 가로. (docs/replit.md) */
      width: 1728px;
      height: 1200px;
      overflow: hidden;
      background-color: var(--surface);
      background-image:
        linear-gradient(var(--line2) 1px, transparent 1px),
        linear-gradient(90deg, var(--line2) 1px, transparent 1px);
      background-size: 24px 24px;
      touch-action: none;
      user-select: none;
    }

    /* A4 가로 페이지 경계 — 인쇄/보고서에서 어디까지 들어가는지 (1123x794 @96dpi) */
    /* ⚠️ 연결선과 헷갈리면 안 된다 (ISS-397, 대표님이 "두 카드가 점선으로 이어진 이유"를
       물으셨다 — 인쇄 경계를 연결선으로 읽으신 것). 연결선(.graph-line)은
       2.5px 진한 회색 + 화살표다. 경계선은 그 반대로 간다:
         · 점선이 아니라 촘촘한 도트 → 선이 아니라 '자국'으로 보이게
         · 더 얇고 더 연하게 → 판의 요소가 아니라 배경임을 말한다
         · 라벨은 오히려 키워서 읽히게 → 정체를 스스로 밝힌다 */
    .page-guide {
      position: absolute;
      z-index: 0;
      pointer-events: none;
      border-right: 1px dotted #dfe6f0;
      border-bottom: 1px dotted #dfe6f0;
    }

    /* 라벨은 판 축소(zoom)를 되돌려 항상 같은 크기로 읽히게 한다.
       41%로 줄이면 9px 라벨이 사실상 사라져 정체를 못 밝혔다 (ISS-397). */
    .page-guide::after {
      content: "📄 A4 가로 — 인쇄 경계";
      position: absolute;
      right: 6px;
      bottom: 5px;
      transform: scale(calc(1 / var(--canvas-zoom, 1)));
      transform-origin: 100% 100%;
      color: #8c9bb3;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 0.2px;
      background: rgba(255, 255, 255, 0.92);
      border: 1px solid #e3e9f2;
      border-radius: 5px;
      padding: 2px 7px;
      white-space: nowrap;
    }

    .graph-lines {
      position: absolute;
      inset: 0;
      z-index: 1;
      width: 100%;
      height: 100%;
      overflow: visible;
      pointer-events: none;
    }

    .graph-line {
      fill: none;
      stroke: var(--ink2);
      stroke-width: 2.5;
      marker-end: url(#graph-arrow);
      pointer-events: stroke;
      cursor: pointer;
    }

    /* 줄 바뀜 연결 — 흐릿하게 뒤로 */
    .graph-line.is-wrap {
      stroke: var(--faint);
      stroke-width: 1.5;
      stroke-dasharray: 4 5;
      opacity: 0.5;
    }

    /* ③ 계열을 넘는 선 = 큰 줄기. 시작→텍스트→수치→마감의 뼈대다.
       계열 안의 선(자잘한 배선)보다 굵고 진하게 해서 눈이 줄기를 먼저 잡게 한다. */
    .graph-line.is-trunk {
      stroke: var(--brand);
      stroke-width: 4;
      opacity: 0.95;
    }

    /* 줄기가 줄바꿈 자리에 있어도 줄기다 — 흐림보다 우선한다.
       단 되돌아가는 길이라 점선은 남긴다 (구조적 우회임을 계속 말해준다). */
    .graph-line.is-trunk.is-wrap {
      stroke: var(--brand);
      stroke-width: 3;
      stroke-dasharray: 7 4;
      opacity: 0.8;
    }

    /* ② 계열 이름표 — 카드 왼쪽에 세로로 세운다. 판을 안 먹고 흐름도 안 가린다.
       선(z-index:1)·카드(2)보다 뒤에 둔다 — 읽히되 주인공은 아니다. */
    .group-band {
      position: absolute;
      z-index: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 26px;
      padding: 6px 0;
      border-radius: 5px;
      font-size: 12px;
      font-weight: 800;
      letter-spacing: 0.06em;
      white-space: nowrap;
      writing-mode: vertical-rl;
      pointer-events: none;
      user-select: none;
    }
    .group-band[data-card-group="시작"]        { color:#2f6ab5; background:#e8f1fc; }
    .group-band[data-card-group="탐색"]        { color:#2f7070; background:#e6f4f4; }
    .group-band[data-card-group="텍스트 계열"]  { color:#3d7f3d; background:#e9f6e9; }
    .group-band[data-card-group="수치 인과"]    { color:#6b4b9a; background:#f0e9f9; }
    .group-band[data-card-group="마감 계열"]    { color:#a86c1f; background:#fdf1de; }

    .graph-line.is-dashed {
      stroke: var(--accent);
      stroke-dasharray: 7 6;
    }

    /* 선택된 선 — 굵기·색·그림자로 확실히 (대표님 지시) */
    .graph-line.is-selected {
      stroke: var(--cta);
      stroke-width: 4;
      stroke-dasharray: none;
      marker-end: url(#graph-arrow-selected);
      opacity: 1;
      filter: drop-shadow(0 0 3px rgba(255, 56, 92, 0.45));
    }

    /* 흐릿한 wrap 선도 선택하면 진하게 */
    .graph-line.is-wrap.is-selected {
      stroke: var(--cta);
      stroke-width: 4;
      opacity: 1;
    }

    .graph-line:hover {
      stroke-width: 3.5;
    }

    /* ── 실행 상태 ─────────────────────────────────────────────
       색상은 정본 components/pipeline_runner.py:209-213 을 그대로 이식.
       pending #6B7280 / running #3B82F6 / success #10B981 / error #EF4444 / skipped #F59E0B */
    .graph-line.is-running {
      stroke: #3B82F6;
      stroke-width: 2.5;
      stroke-dasharray: 8 6;
      animation: edge-flow 0.6s linear infinite;
    }

    .graph-line.is-success { stroke: #10B981; }
    .graph-line.is-error   { stroke: #EF4444; }

    @keyframes edge-flow {
      to { stroke-dashoffset: -14; }
    }

    @keyframes kg-twinkle {
      0%, 100% { opacity: 0.18; }
      50%      { opacity: 0.85; }
    }

    .chart-svg.is-starlight .kg-star {
      animation: kg-twinkle 3.2s ease-in-out infinite;
    }

    /* 노드 glow 은은한 맥동 — 과하지 않게 */
    @keyframes kg-node-breathe {
      0%, 100% { opacity: 0.9; }
      50%      { opacity: 1; }
    }

    /* 카드가 도는 동안 테두리가 붕붕거린다 */
    .graph-node.is-running {
      border-color: #3B82F6;
      animation: node-pulse 1.1s ease-in-out infinite;
    }

    @keyframes node-pulse {
      0%, 100% {
        box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.45), var(--shadow);
        border-color: #3B82F6;
      }
      50% {
        box-shadow: 0 0 0 6px rgba(59, 130, 246, 0), var(--shadow);
        border-color: #60A5FA;
      }
    }

    .graph-node.is-success { border-color: #10B981; }
    .graph-node.is-error   { border-color: #EF4444; }
    .graph-node.is-skipped { border-color: #F59E0B; }

    /* 상태 배지 */
    .node-status.is-running {
      color: #1e40af;
      background: #dbeafe;
      animation: badge-blink 1.1s ease-in-out infinite;
    }

    .node-runtime-guide {
      grid-column: 1 / -1;
      color: var(--muted);
      font-size: 10px;
      line-height: 1.35;
    }

    /* ★ 안 도는 카드는 판에서 한눈에 보여야 한다 (STATE-002).
       대표님: "돌고있는 카드와 아닌카드가 시각적으로 표현되어야 하고
                아닌카드를 집중적으로 작업할수 있다고 생각해."
       → 점선 테두리 + 채도를 뺀다. 안 도는 카드가 곧 남은 작업 목록이다.
         (CLAUDE.md §5-C "점선 = 이 시스템의 정식 상태"의 확장) */
    .graph-node:has(.node-status.is-noengine) {
      border-style: dashed;
      border-width: 1.5px;
      filter: saturate(0.35);
      opacity: 0.72;
    }
    /* 고르면 또렷하게 — 작업하려고 고른 카드까지 흐리면 안 된다 */
    .graph-node.is-selected:has(.node-status.is-noengine) {
      filter: none;
      opacity: 1;
      border-style: dashed;
    }

    /* 손댈 게 있는 카드 (ACT-001) — 대표님: "별다른 조작이 필요없는 카드와,
       적극적인 액션이 필요한 카드도 나눠서 사용자 행동을 유도해야 할것 같아."
       카드 우상단 작은 배지. 볼 것만 있는 카드엔 없다. */
    .node-act {
      position: absolute;
      top: 6px;
      right: 8px;
      padding: 1px 5px;
      border: 1px solid var(--cta, #b45309);
      border-radius: 3px;
      color: var(--cta, #b45309);
      font-size: 11px;
      font-weight: 800;
      letter-spacing: 0.02em;
      background: #fff7ed;
      pointer-events: none;
    }

    /* 엔진이 없어 안 도는 카드 — "대기"처럼 보이면 대표님이 뭘 누를지 찾게 된다.
       흐리게 눌러 "이건 아직 없다"를 말한다 (STATE-001). */
    .node-status.is-noengine {
      color: #5b6472;
      background: #eef1f5;
      border: 1px dashed #9aa3b0;
    }
    .node-status.is-success { color: #065f46; background: #d1fae5; }
    .node-status.is-error   { color: #991b1b; background: #fee2e2; }
    .node-status.is-skipped { color: #92400e; background: #fef3c7; }

    @keyframes badge-blink {
      0%, 100% { opacity: 1; }
      50%      { opacity: 0.55; }
    }

    @media (prefers-reduced-motion: reduce) {
      .graph-line.is-running,
      .graph-node.is-running,
      .node-status.is-running { animation: none; }
      .chart-svg.is-starlight .kg-star { animation: none; }
    }

    /* 카드 우측 귀퉁이 — 처리 시간 */
    .node-elapsed {
      position: absolute;
      right: 8px;
      bottom: 7px;
      color: var(--faint);
      font-size: 10px;
      font-variant-numeric: tabular-nums;
      letter-spacing: -0.02em;
    }

    .node-elapsed.is-done { color: var(--muted); }

    .rubber-line {
      fill: none;
      stroke: var(--brand2);
      stroke-width: 2;
      stroke-dasharray: 5 4;
      pointer-events: none;
    }

    .connection-delete {
      position: absolute;
      z-index: 5;
      display: none;
      width: 22px;
      height: 22px;
      padding: 0;
      border: 1px solid var(--danger);
      border-radius: 50%;
      color: var(--danger);
      background: var(--surface);
      font-size: 14px;
      font-weight: 700;
      line-height: 19px;
      cursor: pointer;
      box-shadow: var(--shadow);
    }

    .connection-delete.is-visible {
      display: block;
    }

    .graph-node {
      position: absolute;
      z-index: 2;
      /* 격자(24px)에 맞춘 크기 — 7칸 x 5칸 고정.
         높이가 제각각이면 포트(세로 50%)가 어긋나 선이 꼬인다. */
      display: grid;
      /* 아이콘 | 이름  → 아래 상태 (ICON-001)
         변수명을 뺀 자리를 아이콘 32px과 이름 2줄에 준다. */
      grid-template-columns: 32px minmax(0, 1fr);
      grid-template-rows: minmax(0, 1fr) auto auto;
      column-gap: 9px;
      align-items: center;
      width: 188px;
      height: 132px;
      padding: 10px 12px;
      border: 1px solid var(--line);
      border-radius: 7px;
      color: var(--ink);
      background: var(--bg);
      box-shadow: var(--shadow);
      cursor: grab;
    }

    .graph-node:active {
      cursor: grabbing;
    }

    /* 계열별 색 — 어디가 시작이고 어디가 마무리인지 한눈에.
       왼쪽 띠 + 옅은 배경. 필터 칩(아이보리)과 같은 계열을 쓴다. */
    /* 계열별 카드 바탕 — 채도를 낮게. 어디가 시작이고 어디가 마무리인지 한눈에. */
    /* 계열 4색 — 채도를 올려 변별력을 준다 (COLOR-001).
       ⚠️ 전엔 시작(#eef2f8)과 수치인과(#edf1f8)가 둘 다 옅은 파랑이라
          사실상 같은 색이었다. 대표님: "너무 흐려서 변별력이 없어보여."
          이제 파랑(시작) / 초록(텍스트) / 보라(수치인과) / 주황(마감)으로 색상환을 벌린다. */
    .graph-node[data-card-group="시작"] {
      border-color: #4a86d4;
      background: #bcd8f5;
    }

    /* 탐색 계열 (EXPLORE-001) — 청록. 기존 4색(파랑215°/초록120°/보라267°/주황35°)과
       색상환에서 벌린 자리(180°). Y 없이도 도는 카드들이라 계열을 따로 뒀다. */
    .graph-node[data-card-group="탐색"] {
      border-color: #3d9a9a;
      background: #c3e8e8;
    }

    .graph-node[data-card-group="텍스트 계열"] {
      border-color: #5aa35a;
      background: #c3e8c3;
    }

    .graph-node[data-card-group="수치 인과"] {
      border-color: #8a63be;
      background: #dcc9ee;
    }

    .graph-node[data-card-group="마감 계열"] {
      border-color: #d18e33;
      background: #f8d9a6;
    }

    /* 그룹 접기 (ISS-117) — 대표 카드에 그룹임을 표시 */
    .graph-node.is-group-head { border-color: #1e3a8a; box-shadow: 0 0 0 2px rgba(30,58,138,.18); }
    .graph-node.is-group-fail { border-color: #dc2626; box-shadow: 0 0 0 2px rgba(220,38,38,.2); }
    .graph-node.is-group-head::after {
      content: attr(data-group-badge);
      position: absolute; left: 0; right: 0; bottom: -16px;
      font-size: 9px; font-weight: 700; color: #1e3a8a; text-align: center;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }

    .graph-node:hover {
      border-color: var(--accent);
    }

    /* 선택된 카드 — 판에서 한눈에 찾을 수 있어야 한다 (SELECT-001).
       대표님: "선택된 카드가 WorkFlow에서는 도드라져 보이지 않음.
                선택이 되었으면 태두리라도 굵게 일시적으로 표현 합시다."
       ⚠️ background:brand-soft는 계열 4색([data-card-group])이 더 구체적이라
          덮어써서 아무 효과가 없었다. 테두리·그림자로 띄운다(계열색은 살린다). */
    .graph-node.is-selected {
      /* 2차 상향 (ISS-276) — SELECT-001의 강조가 축소 줌(38% 등)에서는 안 보였다.
         링을 두껍게(4→8px)·진하게, 바깥 발광 추가, scale 1.04→1.07. */
      border: 3px solid var(--brand);
      box-shadow:
        0 0 0 8px rgba(30, 58, 138, 0.28),
        0 0 24px 6px rgba(30, 58, 138, 0.35),
        0 8px 20px rgba(23, 43, 77, 0.28);
      transform: translateZ(0) scale(1.07);
      z-index: 3;                     /* 이웃 카드 위로 */
    }

    .graph-node.is-multi {
      outline: 2px solid var(--brand, #1e3a8a);
      outline-offset: 1px;
    }

    @media (prefers-reduced-motion: no-preference) {
      .graph-node { transition: box-shadow 0.15s ease, transform 0.15s ease; }
    }

    /* ── "여기요!" 다음 카드 추천 (HERE-001) ──────────────────
       대표님: "다음단계 연결이 안된 카드를 선택한 상태에서 연결이 될만한
       다음카드가 '여기요!'를 한다면 사용자에게 편리한 ui라고 할수 있을까?"
       ⚠️ 권하기만 한다. 추천 아닌 카드를 못 잇게 막지 않는다 — FLOW는 실측이라
          틀린 규칙이 맞는 연결을 막으면 안 된다 (CLAUDE.md §3). */
    .graph-node.is-suggested {
      border: 2px dashed var(--brand);
      box-shadow: 0 0 0 4px rgba(30, 58, 138, 0.10);
    }

    .node-here {
      position: absolute;
      top: -11px; right: -8px;
      z-index: 4;                     /* is-selected(3) 위로 */
      display: inline-flex;
      align-items: center;
      gap: 3px;
      padding: 2px 7px;
      border: 0;
      border-radius: 9px;
      background: var(--brand);
      color: #fff;
      font-size: 10px;
      font-weight: 700;
      line-height: 1.5;
      cursor: pointer;
      white-space: nowrap;
      box-shadow: 0 2px 6px rgba(23, 43, 77, 0.28);
    }

    .node-here:hover { filter: brightness(1.12); }
    .node-here:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

    /* 서랍에만 있는 카드 — 누르면 판에 얹으면서 잇는다 */
    .node-here.is-spawn::after { content: "+"; font-weight: 800; }

    @media (prefers-reduced-motion: no-preference) {
      .node-here { animation: here-pop 1.6s ease-in-out infinite; }
      @keyframes here-pop {
        0%, 100% { transform: translateY(0); }
        50%      { transform: translateY(-2px); }
      }
    }

    .graph-node.is-placeholder {
      /* 격자 6칸 x 5칸 — 일반 카드와 같은 높이 */
      width: 144px;
      height: 120px;
      border: 2px dashed var(--faint);
      text-align: center;
      color: var(--muted);
      background: var(--panel);
    }

    /* 아이콘 — 축소해도 형태가 남는다. 계열색 위에서 진하게. */
    /* 아이콘 — 이름 2줄 높이만큼 크게. 축소해도 형태가 남는 게 값이다.
       대표님: "아이콘은 2줄 크기로 키우고" */
    .node-icon {
      grid-column: 1;
      grid-row: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      color: var(--brand);
      opacity: 0.92;
    }
    .node-icon svg { width: 42px; height: 42px; }

    /* 이름 — 카드에서 제일 중요하다. 2줄까지 쓰고 넘치면 …
       ⚠️ 전엔 13px 1줄(18px)인데 변수명이 47px을 먹었다. 뒤집혔던 것. */
    .node-title {
      grid-column: 2;
      grid-row: 1;
      /* 대표님: "타이틀이 길면 '...'이 아니라 2줄로 표시하고"
         → line-clamp(…)를 쓰지 않는다. 줄바꿈해서 다 보여준다. */
      display: block;
      overflow-wrap: break-word;
      font-size: 15px;
      font-weight: 700;
      line-height: 1.28;
      word-break: keep-all;
    }

    /* ── 시맨틱 줌 (ISS-118) — 많이 축소되면 상세를 접고 제목만 크게 ── */
    .is-zoomed-out .node-flow { display: none; }
    .is-zoomed-out .node-title {
      font-size: 26px; line-height: 1.15;
      grid-row: 1 / span 2; align-self: center;
    }
    .is-zoomed-out .node-status { font-size: 13px; }

    /* 상태는 카드 아래 한 줄을 통째로 */
    .graph-node .node-status { grid-column: 1 / -1; grid-row: 3; }

    .node-monitor {
      grid-column: 1 / -1;
      grid-row: 4;
      overflow: hidden;
      color: var(--brand);
      font-size: 10px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* 대기 카드가 왜 멈췄는지 — 우측 Params 게이트의 첫 문장을 그대로 비춘다 */
    .node-gate-reason {
      position: absolute;
      right: 9px;
      bottom: 9px;
      left: 62px;
      overflow: hidden;
      color: #92400e;
      font-size: 9px;
      font-weight: 700;
      line-height: 18px;
      text-align: right;
      text-overflow: ellipsis;
      white-space: nowrap;
      pointer-events: none;
    }

    .is-zoomed-out .node-gate-reason { display: none; }

    .node-preview {
      grid-column: 1 / -1;
      grid-row: 2;
      display: block;
      max-width: 100%;
      overflow: hidden;
      color: var(--muted);
      font-size: 10.5px;
      line-height: 1.3;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .node-preview.has-visual {
      text-overflow: clip;
      white-space: normal;
    }

    .node-preview.has-visual svg {
      display: block;
      width: 100%;
    }

    .node-flow {
      grid-column: 1 / -1;
      grid-row: 2;
      display: flex;
      align-items: center;
      gap: 5px;
      color: var(--muted);
      font-size: 10px;
      line-height: 1.35;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    /* 붙임쪽지 (ISS-116) */
    .memo {
      position: absolute;
      z-index: 2;
      min-width: 160px;
      min-height: 100px;
      max-width: 240px;
      background: #fff9c4;
      border: 1px solid #f0e68c;
      border-radius: 4px;
      box-shadow: 0 2px 8px rgba(23, 43, 77, .14);
      display: flex;
      flex-direction: column;
    }
    .memo-header {
      display: flex;
      justify-content: flex-end;
      height: 18px;
      cursor: move;
      background: rgba(0, 0, 0, .04);
      border-radius: 4px 4px 0 0;
    }
    .memo-delete {
      width: 18px;
      height: 18px;
      border: 0;
      background: transparent;
      color: var(--muted);
      font-size: 13px;
      line-height: 1;
      cursor: pointer;
    }
    .memo-delete:hover { color: #b42318; }
    .memo-text {
      flex: 1;
      padding: 7px 9px;
      font-size: 12px;
      line-height: 1.5;
      color: #5c4b00;
      outline: none;
      overflow-wrap: anywhere;
    }
    .memo-text:empty::before {
      content: "메모 입력…";
      color: #b0a56a;
    }
    /* 미니맵 (ISS-110) — 판이 뷰포트보다 클 때만 */
    .minimap{position:absolute;right:10px;bottom:10px;width:160px;height:120px;background:rgba(255,255,255,.92);border:1px solid var(--line);border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.15);z-index:5;overflow:hidden}
    .minimap.is-collapsed{height:24px;width:24px}
    .mm-layer{position:absolute;inset:0}
    .mm-node{position:absolute;background:var(--brand-soft, #eef2ff);border:1px solid var(--brand)}
    .mm-viewport{position:absolute;border:1.5px solid var(--brand);background:rgba(30,58,138,.08);pointer-events:none}
    .mm-toggle{position:absolute;right:2px;top:2px;width:18px;height:18px;border:0;background:transparent;cursor:pointer;z-index:6;font-size:12px}
    /* 트랙패드 대응 카드 삭제 버튼 (ISS-170) — 우클릭 못 쓸 때 클릭으로 삭제 */
    .card-del-btn {
      position: absolute;
      width: 20px;
      height: 20px;
      border-radius: 50%;
      border: 1px solid #d94040;
      background: #fff;
      color: #d94040;
      font-size: 14px;
      font-weight: bold;
      line-height: 16px;
      text-align: center;
      cursor: pointer;
      z-index: 4;
      opacity: 0;
      pointer-events: none;
    }
    .card-del-btn.is-visible { opacity: 1; pointer-events: auto; }
    .card-del-btn:hover { background: #d94040; color: #fff; }

    /* 유색 카드 배경 위 보조 텍스트 대비 보정 (ISS-123) — 전역 muted는 안 건드리고 여기서만 */
    .graph-node .node-flow,
    .graph-node .node-preview {
      color: #4a5567;   /* muted(#6b778c)보다 진하게 — 유색 배경 위 대비 확보 */
    }

    .node-arrow {
      color: var(--faint);
    }

    .node-status,
    .placeholder-label {
      display: inline-flex;
      margin-top: 8px;
      padding: 2px 6px;
      border: 1px solid var(--line);
      border-radius: 999px;
      color: var(--muted);
      background: var(--panel);
      font-size: 10px;
      font-weight: 700;
    }

    .node-status { color: var(--line); }

    .port {
      position: absolute;
      top: 50%;
      z-index: 4;
      width: 12px;
      height: 12px;
      padding: 0;
      border: 2px solid var(--surface);
      border-radius: 50%;
      background: var(--accent);
      box-shadow: 0 0 0 1px var(--brand);
      transform: translateY(-50%);
      cursor: crosshair;
    }

    .port-input {
      left: -7px;
    }

    .port-output {
      right: -7px;
    }

    .port.is-target {
      background: var(--success);
      box-shadow: 0 0 0 3px var(--brand-soft);
    }

    .graph-detail {
      font-size: 12px;
    }

    .detail-card {
      display: none;
    }

    .detail-card.is-active {
      display: block;
    }

    .detail-eyebrow {
      margin-bottom: 4px;
      color: var(--muted);
      font-size: 10px;
      font-weight: 700;
    }

    /* 스텝 진행 바 (STEP-001) — 판의 실행 순서를 우측에서 짚어간다 */
    .step-bar {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 10px;
      padding: 6px 8px;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: var(--surface);
    }
    .step-bar[hidden] { display: none; }
    .step-nav {
      flex: none;
      width: 22px;
      height: 22px;
      padding: 0;
      border: 1px solid var(--line);
      border-radius: 4px;
      color: var(--ink2);
      font-size: 13px;
      line-height: 1;
      background: var(--panel);
      cursor: pointer;
    }
    .step-nav:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }
    .step-nav:disabled { opacity: 0.35; cursor: default; }
    .step-info { flex: 1; min-width: 0; }
    .step-count {
      display: block;
      margin-bottom: 4px;
      color: var(--ink2);
      font-size: 10.5px;
      font-weight: 700;
    }
    .step-track {
      height: 3px;
      border-radius: 2px;
      background: var(--faint);
      overflow: hidden;
    }
    .step-fill {
      height: 100%;
      border-radius: 2px;
      background: var(--brand);
      transition: width 0.2s ease;
    }

    /* 우측 제목 아이콘 — 카드와 같은 그림 (ICON-003) */
    .detail-icon {
      display: inline-flex;
      vertical-align: -3px;
      margin-right: 5px;
      color: var(--brand);
    }
    .detail-icon svg { width: 17px; height: 17px; }

    /* 원문 제목이 하는 일을 가릴 때만 붙이는 부제 (TITLE-001).
       원문은 기획자와 대조하는 열쇠라 못 건드린다 — 뜻은 여기에 적는다. */
    .detail-subtitle {
      margin: -4px 0 10px;
      padding: 6px 8px;
      border-left: 2px solid var(--brand);
      color: var(--ink2);
      font-size: 11px;
      line-height: 1.45;
      background: var(--brand-soft, #eef2f8);
      border-radius: 0 4px 4px 0;
    }

    .detail-title {
      margin: 0 0 10px;
      font-size: 15px;
      line-height: 1.4;
    }

    /* 카드 이력 버전 배지 — 제목 옆 작은 pill (정본대조/수정 시 +0.001) */
    .card-version-badge {
      font-size: 11px;
      padding: 1px 7px;
      border-radius: 999px;
      background: #eef1f6;
      color: #5a6b8c;
      margin-left: 8px;
      font-variant-numeric: tabular-nums;
      vertical-align: middle;
    }

    .mode-toggle {
      display: inline-flex;
      padding: 2px;
      margin-bottom: 8px;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: var(--panel);
    }

    .mode-button {
      padding: 3px 9px;
      border: 0;
      border-radius: 4px;
      color: var(--muted);
      background: transparent;
      font-size: 11px;
      font-weight: 700;
      cursor: pointer;
    }

    .mode-button.is-active {
      color: var(--brand);
      background: var(--surface);
      box-shadow: var(--shadow-sm);
    }

    .detail-description {
      margin-bottom: 10px;
      padding: 10px;
      border: 1px solid var(--line2);
      border-radius: 6px;
      color: var(--ink2);
      background: var(--panel);
      font-size: 12px;
      line-height: 1.55;
    }

    .detail-description p {
      margin: 0;
    }

    .detail-description p + p {
      margin-top: 5px;
    }

    .detail-description ul {
      margin: 0;
      padding-left: 17px;
    }

    .detail-description li + li {
      margin-top: 4px;
    }

    .mode-description {
      display: none;
    }

    .mode-description.is-active {
      display: block;
    }

    .flow-summary {
      display: grid;
      grid-template-columns: 1fr;
      gap: 6px;
      margin-bottom: 10px;
    }

    .flow-item {
      padding: 8px;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: var(--surface);
    }

    .flow-label {
      display: block;
      margin-bottom: 3px;
      color: var(--muted);
      font-size: 10px;
      font-weight: 700;
    }

    .flow-value {
      font-size: 11px;
      font-weight: 500;
      color: var(--muted);
      line-height: 1.35;
      overflow-wrap: anywhere; /* pp_wda_integrated_df 같은 긴 변수명 */
                              /* 한글 단어는 보존하고 긴 변수명만 필요한 곳에서 줄바꿈한다. */
    }

    /* 변수명 아래 한 줄 우리말 (FLOW-004).
       대표님: "원문 변수명 두고 아래 한 줄로 우리말 설명 붙이자"
       → 변수명은 정본 대조의 열쇠라 그대로 두고, 뜻은 아래에 적는다.
         df 같은 프로그래머 약어만 봐서는 무슨 데이터인지 알 수 없다. */
    .flow-note {
      display: block;
      margin-top: 3px;
      color: var(--ink);
      font-size: 12px;
      font-weight: 600;
      line-height: 1.35;
      word-break: keep-all;
    }

    /* 시작 카드의 "앞이 없음"은 결핍이 아니라 설계다 (ISS-278) — 회색·보통 굵기로 */
    .flow-note.is-origin {
      color: var(--muted);
      font-weight: 400;
    }

    .detail-tabs {
      display: flex;
      border-bottom: 1px solid var(--line);
    }

    .detail-tab {
      padding: 6px 10px;
      border: 0;
      border-bottom: 2px solid transparent;
      color: var(--muted);
      background: transparent;
      font-size: 11px;
      font-weight: 700;
      cursor: pointer;
    }

    .detail-tab.is-active {
      border-bottom-color: var(--brand);
      color: var(--brand);
    }

    .tab-panel {
      display: none;
    }

    .tab-panel.is-active {
      display: block;
    }

    .tree-subtabs {
      display: flex;
      gap: 4px;
      margin: 8px 0 6px;
      border-bottom: 1px solid var(--line);
    }

    .tree-subtab {
      padding: 5px 10px;
      border: 0;
      border-bottom: 2px solid transparent;
      color: var(--muted);
      background: none;
      font-size: 12px;
      cursor: pointer;
    }

    .tree-subtab.is-active {
      border-bottom-color: var(--brand);
      color: var(--ink);
      font-weight: 600;
    }

    .tree-subpanel {
      display: none;
    }

    .tree-subpanel.is-active {
      display: block;
    }

    [data-detail-id="algo_train"] [data-tab-panel="result"] {
      overflow: auto;
    }

    .tree-svg-result {
      min-width: max-content;
      margin-top: 12px;
      overflow: auto;
    }

    .tree-svg-result svg {
      display: block;
      max-width: none;
    }

    /* 영역 맞춤(fit) — 트리 SVG를 FreeView 너비에 맞춰 축소한다 (ISS-360, 대표님 요청).
       기본이 fit이라 스크롤 없이 전체 트리가 한눈에 보인다. [원본 크기] 토글로 해제. */
    .tree-svg-result.is-fit {
      min-width: 0;
      width: 100% !important;
      overflow: hidden;
    }
    .tree-svg-result.is-fit svg {
      width: 100%;
      max-width: 100%;
      height: auto;
    }
    .tree-fit-toggle {
      margin: 8px 0 2px;
      padding: 3px 10px;
      font-size: 11px;
      border: 1px solid var(--line);
      border-radius: 5px;
      background: var(--panel, #fafbfc);
      color: var(--brand);
      cursor: pointer;
    }

    .tree-view { margin-top: 10px; }
    .tree-view-toolbar { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }
    .tree-view-toolbar button {
      padding: 2px 8px;
      font-size: 12px;
      border: 1px solid var(--line);
      border-radius: 5px;
      background: var(--panel, #fafbfc);
      color: var(--brand);
      cursor: pointer;
    }
    .tree-zoom-label { font-size: 11px; color: var(--muted); }
    .tree-view-port {
      position: relative;
      overflow: hidden;
      height: 52vh;
      border: 1px solid var(--line);
      border-radius: 8px;
      background: #fff;
      cursor: grab;
      touch-action: none;
    }
    .tree-view-port.is-panning { cursor: grabbing; }
    .tree-view-canvas { transform-origin: 0 0; will-change: transform; }
    .tree-view-canvas svg { display: block; max-width: none; }

    .erd-result {
      margin-top: 10px;
      overflow: auto;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: #f8fafc;
    }

    .erd-result svg { display: block; min-width: 100%; height: auto; }

    .mission-sql-code {
      margin: 10px 0;
      padding: 10px;
      overflow: auto;
      border-radius: 6px;
      color: #e2e8f0;
      background: #1e293b;
      font: 11px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
      white-space: pre;
    }

    .mission-preview { width: 100%; border-collapse: collapse; font-size: 10px; }
    .mission-preview th, .mission-preview td {
      padding: 5px 7px; border: 1px solid var(--line); text-align: left; white-space: nowrap;
    }
    .mission-preview th { background: var(--panel); }
    .mission-validated { color: #166534; font-weight: 700; }
    .mission-validated.is-invalid { color: #b91c1c; }

    /* in/out 설명이 길어도 카드가 안 늘어나게 */
    .graph-node .node-flow {
      display: -webkit-box;
      overflow: hidden;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      text-overflow: ellipsis;
    }

    /* ⚠️ 여기 있던 nowrap + ellipsis가 카드 이름을 …로 잘랐다.
       선택자가 더 구체적이라 .node-title의 2줄 설정을 눌렀다 (ICON-002).
       대표님: "타이틀이 길면 '...'이 아니라 2줄로 표시하고." */
    .graph-node .node-title {
      overflow: visible;
      white-space: normal;
      text-overflow: clip;
    }

    .params-empty {
      margin-top: 10px;
      padding: 18px 10px;
      border: 1px dashed var(--line);
      border-radius: 6px;
      text-align: center;
      color: var(--muted);
      background: var(--panel);
      font-size: 11px;
    }

    /* 파일 업로드 — 판의 시작점 */
    .upload-drop {
      display: grid;
      gap: 3px;
      margin-top: 10px;
      padding: 16px 10px;
      border: 1.5px dashed var(--accent);
      border-radius: 6px;
      text-align: center;
      background: var(--panel);
      cursor: pointer;
    }

    .upload-drop:hover,
    .upload-drop.is-over {
      border-color: var(--brand);
      background: var(--brand-soft);
    }

    /* 브라우저 기본 file input 버튼이 줌된 카드 밖에 별도 레이어처럼 노출되지 않게 한다. */
    .upload-drop input[type="file"][hidden] { display: none; }

    .upload-icon { font-size: 20px; }

    .upload-text {
      color: var(--ink);
      font-size: 12px;
      font-weight: 700;
    }

    .upload-hint {
      color: var(--faint);
      font-size: 10px;
    }

    .upload-result {
      margin-top: 8px;
      padding: 8px 10px;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: var(--surface);
      font-size: 11px;
      line-height: 1.6;
    }

    .upload-result[hidden] { display: none; }

    .upload-result.is-error {
      border-color: var(--danger);
      color: var(--danger);
    }

    .file-head {
      display: flex;
      align-items: baseline;
      gap: 6px;
      flex-wrap: wrap;
    }

    .file-meta {
      color: var(--muted);
      font-size: 10px;
    }

    .preview-label {
      margin: 8px 0 2px;
      color: var(--muted);
      font-size: 10px;
      font-weight: 700;
    }

    .col-list {
      display: flex;
      flex-wrap: wrap;
      gap: 3px;
      margin-top: 6px;
    }

    .col-tag {
      padding: 1px 6px;
      border: 1px solid var(--line);
      border-radius: 3px;
      background: var(--panel);
      font-size: 10px;
    }

    .col-tag.is-usable {
      border-color: var(--success);
      color: #065f46;
      background: #f0fdf4;
    }

    .col-tag.is-y {
      border-color: var(--brand);
      color: var(--brand);
      background: var(--brand-soft);
      font-weight: 700;
    }

    /* 이 파일로는 못 도는 카드 — 점선으로 끄고 이유를 적는다 (CLAUDE.md §5-C)
       대표님(2026-07-22): "테두리가 약해서 없어 보여. 기존 점선표현이 직관적" —
       faint+0.55는 점선 자체가 안 보였다. 점선을 선명하게, 흐림은 내용에만. */
    .graph-node.is-gated,
    .drawer-card.is-gated {
      border-style: dashed;
      border-width: 1.5px;
      border-color: var(--muted);
      background: var(--panel);
      opacity: 0.75;
    }

    /* ── 이미 판에 있는 카드 (DRAG-001) ────────────────────────
       대표님: "방금전까지 되던 카드 WorkFlow에 끌어다 놓기, 지금은 왜 안될까?"
       → 고장이 아니라 자동구현이 32장 중 25장을 이미 깔아놔서
         드롭이 "이미 판에 있습니다"로 조용히 무시된 것이었다.
       ⚠️ is-gated(점선=못 쓰는 카드)와 다르다. 이건 "이미 쓴 카드"라
          덜 흐리게 하고 ✓ 로 구분한다. 끌어보기 전에 눈으로 알아야 한다. */
    .drawer-card.is-onboard {
      opacity: 0.68;
      cursor: default;
    }

    .drawer-card.is-onboard .drawer-card-name::after {
      content: " ✓";
      color: var(--success);
      font-weight: 800;
    }

    .drawer-card.is-onboard:hover {
      opacity: 0.72;   /* 눌러서 판의 그 카드로 갈 수는 있다 */
    }

    /* ── 검색으로 판에서 찾기 (FIND-001) ──────────────────────
       대표님: "전체 화면에서 5p를 찾기가 힘들어"
       → 검색창은 서랍만 걸렀다. 판에 25장이 깔리면 그 카드가 어디 있는지
         눈으로 찾아야 했다. 이제 판이 따라간다. */
    .graph-node.is-found {
      border: 3px solid #e8a33d;
      box-shadow: 0 0 0 6px rgba(232, 163, 61, 0.5);
      z-index: 4;
    }

    /* ⚠️ 처음엔 box-shadow 만 2번 흔들었더니 대표님: "빤짝임이 약해."
       스크롤되는 사이 이미 끝나 있었다. 배경까지 물들이고 계속 돈다 —
       검색어를 지우거나 다른 걸 찾으면 그때 멈춘다(is-found 제거). */
    @media (prefers-reduced-motion: no-preference) {
      .graph-node.is-found { animation: found-pulse 1.1s ease-in-out infinite; }
      @keyframes found-pulse {
        0%, 100% {
          box-shadow: 0 0 0 6px rgba(232, 163, 61, 0.5);
          background: #fff7e6;
          transform: translateZ(0) scale(1.03);
        }
        50% {
          box-shadow: 0 0 0 18px rgba(232, 163, 61, 0);
          background: var(--surface);
          transform: translateZ(0) scale(1);
        }
      }
    }

    .graph-node.is-gated .node-status,
    .graph-node.is-gated .node-elapsed {
      visibility: hidden;
    }

    /* 중립 게이트 (ISS-278) — "이 도메인엔 해당 없음"용. 경고(params-gate)와 분리해
       정상 상태가 경고로 오독되는 재발을 막는다. */
    .params-idle {
      margin-top: 10px;
      padding: 10px;
      border: 1px dashed var(--line);
      border-radius: 6px;
      color: var(--muted);
      background: var(--surface-alt, #f8f9fb);
      font-size: 11px;
      line-height: 1.6;
      word-break: keep-all;
    }

    .params-gate {
      margin-top: 10px;
      padding: 10px;
      border: 1px dashed var(--warn);
      border-radius: 6px;
      color: var(--ink2);
      background: #fffbeb;
      font-size: 11px;
      line-height: 1.6;
    }

    /* 허수 카드 — 근거가 부족해 점선으로 둔 것 */
    .drawer-card.is-dashed {
      border-style: dashed;
      border-color: var(--warn);
      background: #fffdf7;
    }

    .ask-badge {
      display: inline-block;
      margin-left: 4px;
      padding: 0 4px;
      border-radius: 3px;
      color: #92400e;
      background: #fef3c7;
      font-size: 9px;
      font-weight: 700;
      vertical-align: middle;
    }

    /* 점선 카드의 질문 — 기획자에게 물을 것 */
    .params-ask {
      margin-top: 8px;
      padding: 10px;
      border: 1px solid var(--brand);
      border-radius: 6px;
      color: var(--brand);
      background: var(--brand-soft);
      font-size: 11px;
      line-height: 1.6;
    }

    .params-gate code,
    .params-ask code {
      padding: 1px 4px;
      border-radius: 3px;
      background: rgba(9, 30, 66, 0.06);
      font-size: 10px;
    }

    /* 중첩 탭 시각화 — 정본 t9 구조 (미확정, 대표님 판단용) */
    .nest-demo {
      margin-top: 10px;
      padding: 8px;
      border: 1px dashed var(--brand);
      border-radius: 6px;
      background: var(--panel);
    }

    .nest-label {
      margin-bottom: 6px;
      color: var(--brand);
      font-size: 10px;
      font-weight: 800;
    }

    .nest-tabs {
      display: flex;
      gap: 3px;
      margin-bottom: 6px;
    }

    .nest-tab {
      flex: 1;
      padding: 4px 6px;
      border: 1px solid var(--line);
      border-radius: 4px;
      color: var(--muted);
      background: var(--surface);
      font-size: 10px;
      font-weight: 700;
      cursor: pointer;
    }

    .nest-tab.is-on {
      border-color: var(--brand);
      color: var(--brand);
      background: var(--brand-soft);
    }

    .nest-hint {
      margin-top: 6px;
      color: var(--muted);
      font-size: 10px;
      line-height: 1.6;
    }

    /* Params 자리에 들어갈 것을 미리 적어두는 메모 */
    /* 카드 하나만 돌리는 버튼 (STEP-002).
       대표님: "카드별 실행을 두는것이 더 직관적일것 같아."
       ⚠️ 상단 [▶ 다시 실행]은 판을 통째로 돈다. 이건 이 카드만. */
    .card-run {
      width: 100%;
      margin: 10px 0 2px;
      padding: 7px 12px;
      border: 0;
      border-radius: 6px;
      background: var(--brand);
      color: #fff;
      font-size: 12px;
      font-weight: 700;
      cursor: pointer;
    }

    .card-run:hover { filter: brightness(1.1); }
    .card-run:disabled { opacity: 0.5; cursor: default; filter: none; }
    .card-run:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

    /* 빈 Params 에서 "그럼 뭘 눌러?" 에 답하는 버튼 (GOTO-001).
       대표님: "앞 카드에서 뭘 눌러야 하는거지?" */
    .params-goto {
      margin: 8px 0 6px;
      padding: 6px 12px;
      border: 1px solid var(--brand);
      border-radius: 6px;
      background: var(--surface);
      color: var(--brand);
      font-size: 11px;
      font-weight: 700;
      cursor: pointer;
    }

    .params-goto:hover { background: var(--brand); color: #fff; }
    .params-goto:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

    .params-note {
      margin-top: 10px;
      padding: 10px;
      border: 1px dashed var(--accent);
      border-radius: 6px;
      color: var(--ink2);
      background: var(--panel);
      font-size: 11px;
      line-height: 1.6;
    }

    .freeview-note {
      margin-top: 16px;
      padding: 12px 12px 8px;
      border-top: 1px solid var(--line);
      border-left: 3px solid var(--brand);
      border-radius: 4px;
      background: var(--panel, #fafbfc);
      font-size: 12px;
    }

    .freeview-note-label {
      margin-bottom: 4px;
      color: var(--brand);
      font-size: 11px;
      font-weight: 600;
    }

    .freeview-note .detail-description p { margin: 3px 0; }

    .freeview-note .params-note {
      margin: 3px 0;
      padding: 3px 0;
      background: none;
    }

    /* ── XimTier 5P Framework (P5-001) ────────────────────────
       기획자가 만든 개념. 출처: 드라이브 HTML
       "2026-06-28_AI분석보고서_AI_5P_변수선정가이드_*.html"
       원문: "데이터 생성 방법론 · 인과추론 5단계 논리 사슬"
       ⚠️ 이름과 설명은 기획자 원문 그대로. 한 글자도 바꾸지 말 것 (CLAUDE.md §5).
       Replit zip 에는 없다 — 기획자가 코드 밖에서 만든 산출물이다. */
    .p5-table {
      margin-top: 10px;
      border: 1px solid var(--line);
      border-radius: 6px;
      overflow: hidden;
    }

    .p5-row {
      display: grid;
      grid-template-columns: 34px 1fr;
      gap: 8px;
      align-items: center;
      padding: 7px 8px;
      border-top: 1px solid var(--line);
      font-size: 11px;
    }

    .p5-row:first-child { border-top: 0; }

    .p5-axis {
      justify-self: center;
      padding: 2px 0;
      border-radius: 4px;
      color: #fff;
      font-size: 9px;
      font-weight: 800;
      text-align: center;
      width: 100%;
    }

    /* 5P 색 — 기획자 가이드의 역할 순서를 따른다 */
    .p5-row[data-p5="Y"]  .p5-axis { background: #b91c1c; }   /* Performance — 최종 목표 */
    .p5-row[data-p5="X1"] .p5-axis { background: #1d4ed8; }   /* Process     — 내부 운영 */
    .p5-row[data-p5="X2"] .p5-axis { background: #c2410c; }   /* Promotion   — 성과 증폭 */
    .p5-row[data-p5="X3"] .p5-axis { background: #6d28d9; }   /* Predictor   — 예측 지표 */
    .p5-row[data-p5="X4"] .p5-axis { background: #047857; }   /* Platform    — 기반 환경 */

    .p5-name { font-weight: 700; color: var(--ink); }
    .p5-en   { color: var(--ink2); font-weight: 600; }
    .p5-desc { color: var(--muted); font-size: 10px; line-height: 1.5; }

    .p5-sheets {
      margin-top: 3px;
      color: var(--ink2);
      font-size: 10px;
      word-break: break-all;
    }

    .p5-none { color: var(--muted); font-style: italic; }

    .params-note code {
      padding: 1px 4px;
      border-radius: 3px;
      background: var(--line2);
      font-size: 10px;
    }

    @media (max-width: 1120px) {
      .graph-layout {
        grid-template-columns: 260px 620px 340px;
        overflow-x: auto;
      }
    }

/* Ontology evidence console — scoped to the FreeView relation map. */
.onto-console { color:#dce4ee; background:#0c1118; font:12px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; border:1px solid #293442; text-align:left; }
.onto-console * { box-sizing:border-box; }
.onto-console button { font:inherit; cursor:pointer; }
.onto-console button:focus-visible { outline:2px solid #a5c5ee; outline-offset:3px; }
.onto-console-head { display:flex; align-items:center; justify-content:space-between; gap:20px; padding:23px 26px; background:#111923; border-bottom:1px solid #2c3847; }
.onto-kicker,.onto-section-label { display:block; font:10px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace; letter-spacing:1.5px; color:#91a3b9; }
.onto-identity h2 { margin:7px 0 0; font-size:23px; line-height:1.3; font-weight:600; letter-spacing:-.7px; color:#eef3f8; }
.onto-header-meta { display:flex; align-items:center; gap:18px; }
.onto-state { font-size:11px; color:#d1b482; border:1px solid #625437; background:#272319; padding:5px 10px; border-radius:3px; }
.onto-state:before { content:""; display:inline-block; width:5px; height:5px; background:currentColor; margin-right:8px; border-radius:50%; }
.onto-state.is-ready { color:#90c3ab; border-color:#34584b; background:#162923; }
.onto-contract { color:#8799af; font:11px ui-monospace,monospace; }
.onto-stats { display:grid; grid-template-columns:1.35fr repeat(4,1fr); border-bottom:1px solid #293442; background:#101720; }
.onto-stat { padding:15px 24px; border-right:1px solid #293442; min-width:0; }
.onto-stat:last-child { border:0; }
.onto-stat>span { display:block; color:#8a9bb0; font:9px/1.5 ui-monospace,monospace; letter-spacing:1px; }
.onto-stat>strong { display:block; margin-top:7px; font-size:21px; line-height:1.3; font-weight:500; font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.onto-toolbar { display:flex; align-items:center; justify-content:space-between; padding:11px 20px; border-bottom:1px solid #26313e; }
.onto-controls { display:flex; gap:6px; }
.onto-control { color:#bfccdc; background:#18222e; border:1px solid #354356; border-radius:3px; padding:5px 10px; }
.onto-control:hover { background:#243448; color:white; }
.onto-workspace { display:grid; grid-template-columns:minmax(0,1fr) 254px; }
.onto-map-scroll { overflow:auto; min-width:0; max-height:760px; scrollbar-color:#3d4f65 #111923; }
.onto-map { position:relative; min-width:780px; background-color:#0c1118; background-image:radial-gradient(ellipse at center,rgba(43,66,93,.16),transparent 65%),radial-gradient(#263343 0.6px,transparent 0.6px); background-size:auto,20px 20px; }
.onto-connections { position:absolute; inset:0; width:100%; height:100%; pointer-events:none; }
.onto-edge { fill:none; opacity:.62; vector-effect:non-scaling-stroke; transition:opacity .15s; }
.onto-edge.is-unknown { stroke-dasharray:4 5; opacity:.33; }
.onto-map.has-selection .onto-edge { opacity:.1; }
.onto-map.has-selection .onto-edge.is-selected { opacity:1; filter:drop-shadow(0 0 3px #adc9e84d); }
.onto-flow { fill:none; opacity:0; stroke-width:2.5px; stroke-dasharray:3 97; vector-effect:non-scaling-stroke; pointer-events:none; }
.onto-console.is-processing .onto-flow:not(.is-unknown) { opacity:.85; animation:onto-request-flow 1.8s ease-in-out infinite alternate; }
.onto-console.is-processing .onto-map.has-selection .onto-flow { opacity:0; }
.onto-console.is-processing .onto-map.has-selection .onto-flow.is-selected:not(.is-unknown) { opacity:1; }
.onto-activity { align-self:center; font-size:10px; color:#94a7bc; margin-right:8px; }
.onto-console.is-processing .onto-activity { color:#b8d6f5; }
.onto-control:disabled { opacity:.5; cursor:wait; }
@keyframes onto-request-flow { from { stroke-dashoffset:0; } to { stroke-dashoffset:-97; } }
.onto-axis-label { position:absolute; width:24%; border-top:1px solid #2b394a; padding-top:6px; color:#aebbd0; font:9px/1.5 ui-monospace,monospace; letter-spacing:.7px; }
.onto-axis-label:before { content:""; display:inline-block; width:5px; height:5px; background:var(--axis); margin-right:7px; }
.onto-variable { position:absolute; width:24%; height:51px; padding:8px 11px; color:#dce4ee; background:#151e29; border:1px solid #39485a; border-left:3px solid var(--axis); border-radius:3px; text-align:left; box-shadow:0 3px 12px #0003; }
.onto-variable.is-unanswered { border-top-style:dashed; border-right-style:dashed; border-bottom-style:dashed; }
.onto-variable:hover,.onto-variable.is-selected { background:#223247; border-color:var(--axis); box-shadow:0 0 0 1px var(--axis),0 4px 15px #0004; }
.onto-variable-top { display:flex; justify-content:space-between; align-items:center; gap:8px; }
.onto-variable-top strong { overflow:hidden; white-space:nowrap; text-overflow:ellipsis; font-size:12px; font-weight:500; }
.onto-score { color:#a9c1dc; font:11px ui-monospace,monospace; }
.onto-variable-meta { display:block; color:#92a4ba; font-size:9px; margin-top:3px; }
.onto-root { position:absolute; left:39.8%; width:20.4%; min-height:128px; padding:19px 12px; text-align:center; color:#eef3fc; background:linear-gradient(145deg,#263449,#172233); border:1px solid #7f9ec4; border-top:3px solid #bdcfe7; border-radius:3px; box-shadow:0 0 0 7px #17233577,0 12px 40px #0006; }
.onto-root .onto-kicker { font-size:8px; letter-spacing:1px; color:#b1c7e1; }
.onto-root strong { display:block; font-size:22px; font-weight:600; line-height:1.3; margin:13px 0 9px; overflow-wrap:anywhere; }
.onto-root-note { color:#9db1c9; font-size:10px; }
.onto-inspector { background:#111923; border-left:1px solid #2c3847; padding:23px 19px; overflow-wrap:anywhere; }
.onto-inspector h3 { font-size:22px; line-height:1.4; font-weight:500; color:#edf2f8; margin:15px 0 7px; }
.onto-inspector-intro { color:#99a9bd; font-size:11px; margin:0 0 22px; }
.onto-metric { border-top:1px solid #354252; border-bottom:1px solid #354252; padding:17px 0; }
.onto-metric strong { display:block; color:#d2e4fa; font:36px/1.4 ui-monospace,monospace; letter-spacing:-2px; }
.onto-metric-note { color:#8fa1b8; font-size:10px; }
.onto-facts { display:grid; grid-template-columns:70px minmax(0,1fr); gap:12px 8px; padding:9px 0 20px; margin:18px 0 0; font-size:11px; }
.onto-facts dt { color:#889bb2; }
.onto-facts dd { margin:0; color:#c1cfdf; }
.onto-evidence-note { padding:13px; border:1px solid #3f4650; border-left:2px solid #ad9471; background:#1c2027; }
.onto-evidence-note p { font-size:11px; color:#b7bdc7; margin:9px 0 0; }
.onto-inspector-foot { white-space:pre-line; color:#869bb5; font-size:10px; line-height:2; margin-top:24px; }
.onto-footer { display:flex; justify-content:space-between; gap:12px; padding:10px 20px; border-top:1px solid #293442; color:#71879f; font:9px/1.5 ui-monospace,monospace; letter-spacing:1px; }
.onto-console.is-expanded { position:fixed; inset:14px; z-index:10000; display:flex; flex-direction:column; box-shadow:0 0 0 20px #080c12df; overflow:auto; }
.onto-console.is-expanded .onto-workspace { flex:1; min-height:0; }
.onto-console.is-expanded .onto-map-scroll { max-height:none; }
@media(max-width:1000px) { .onto-workspace { grid-template-columns:minmax(0,1fr); } .onto-inspector { border-left:0; border-top:1px solid #293442; } .onto-header-meta { flex-direction:column; align-items:flex-end; gap:6px; } .onto-stat { padding:12px; } }
@media(prefers-reduced-motion:reduce) { .onto-edge { transition:none; } .onto-console.is-processing .onto-flow { animation:none!important; opacity:0!important; } }
.onto-decision-bar { display:flex; flex-wrap:wrap; align-items:center; gap:12px; padding:12px 20px; border-bottom:1px solid #354252; background:#141f2d; }
.onto-decision-summary { flex:1; min-width:220px; color:#b5c8e0; font-size:11px; }
.onto-variable { height:58px; padding:6px 11px; }
.onto-variable-values { display:block; font:10px/1.4 ui-monospace,monospace; color:#bad1eb; margin-top:2px; }
