/* 최적화 도구 **안쪽**의 3단 틀 (변수 · 평면 · 3D).

   ## 어디에 쓰나

   `/tools` 은 그대로 **격자**다. 격자에서 항목 하나를 열면 그 안이
   이 틀이다 — 지금은 `mgt-read.html` 하나가 쓴다. 격자 자체의 스타일은
   `index.css` 에 있다. **둘을 한 파일에 두지 않는다** — 고르는 화면과
   일하는 화면은 결이 다르다.

   **틀을 먼저 못 박고 안을 채운다.** 도구를 하나씩 만들면서 틀을 같이
   고치면, 세 번째 도구쯤에서 첫째와 둘째가 서로 다른 화면이 된다 — 도구
   장 아홉이 `chrome.css` 한 벌을 쓰는 이유와 같다. 새 최적화 도구는 이
   파일을 물고 자기 것만 더한다.

   ## 왜 이 셋이고 왜 이 차례인가

       변수        평면              3D
       (고정 폭)   (잡아 끈다)  ↔    (잡아 끈다)

   왼쪽에서 값을 바꾸면 가운데 평면이 먼저 답하고, 오른쪽 3D 가 그것을
   세워 보여준다. **손이 가는 곳에서 눈이 가는 곳으로 흐른다.**

   칸은 셋에서 안 는다. 기능이 늘어도 **왼쪽 변수 칸 안**에서 는다 —
   칸을 넷으로 만드는 순간 하나하나가 좁아져 아무것도 제대로 안 보인다.

   ## 폭 규칙

   **왼쪽은 고정이다.** 입력칸은 넓어져도 값을 안 한다(도구 장에서 값 칸
   84px 이 오히려 선택지를 자른 것과 반대 방향의 같은 이야기다 — 폭은
   내용이 정하지 화면이 정하지 않는다).

   **가운데와 오른쪽은 사용자가 잡아 끈다.** 평면을 크게 볼 때와 3D 를
   크게 볼 때가 다르다. 비율은 `--split` 하나에 담고 JS 가 그 값만 바꾼다 —
   폭을 두 곳에 적으면 갈라진다. */

@media(prefers-reduced-motion:reduce){*{transition:none!important}}

/* ── 틀 ──────────────────────────────────────────────────────────────
   화면 높이를 꽉 쓴다. 이 장은 읽는 장이 아니라 **일하는 화면**이라
   스크롤이 생기면 평면과 3D 가 동시에 안 보인다 — 그러면 3단인 뜻이
   없다. 그래서 바깥 스크롤을 막고 칸 안에서만 구르게 한다. */
body.opt{overflow:hidden}
.opt3{
  /* `--split` 은 평면 칸의 몫이다. **단위까지 이 값에 담는다** — `calc()`
     안에서는 `fr` 을 곱할 수 없어서(`calc(1.35*1fr)` 은 무효라 선언이 통째로
     버려진다, 2026-09-10 에 그래서 격자가 안 잡혔다) JS 가 `1.35fr` 처럼
     단위째 넣는다. 3D 쪽은 늘 `1fr` 이라 비율이 이 값 하나로 정해진다. */
  --vars:280px;
  /* **평면과 3D 는 같은 폭으로 시작한다**(2026-09-11, 정현님). 처음에는
     평면을 1.35 로 크게 잡았는데 그럴 이유가 없었다 — 둘 중 어느 쪽을
     크게 볼지는 사용자가 끌어서 정한다. `shell.js` 의 DEF 와 짝이다. */
  --split:1fr;
  display:grid;
  grid-template-columns:var(--vars) minmax(220px,var(--split)) 6px minmax(220px,1fr);
  height:calc(100vh - var(--navh,64px) - var(--toph,0px));
  background:var(--bg-2);
  border-top:1px solid var(--line);
}
/* **네 자식이 적힌 차례대로 한 줄에 선다.** 열을 손으로 지정하지 않는다 —
   2026-09-10 에 손잡이를 HTML 에서는 변수 칸 뒤에 두고 열만 3번으로 줬더니,
   자동 배치가 평면을 **둘째 줄로 밀어** 칸 머리가 서로 다른 높이에 섰다.
   보이는 차례와 적는 차례를 같게 두면 그런 자리가 안 생긴다(탭 차례도
   화면과 같아진다). 손잡이는 평면과 3D 사이에만 있다 — 왼쪽은 고정이라
   끌 것이 없다. */

.opane{display:flex;flex-direction:column;min-width:0;min-height:0;
  background:var(--bg);border-right:1px solid var(--line)}
.opane:last-child{border-right:0}

/* ── 칸 머리 ─────────────────────────────────────────────────────────
   칸 이름은 늘 보인다. 셋 다 비어 있을 때 **어느 칸이 무엇인지**를
   화면이 스스로 말해야 한다 — 빈 화면에서 이름까지 없으면 고장으로 읽힌다. */
.oph{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:11px 14px;border-bottom:1px solid var(--line);background:var(--bg-2);
  font-family:var(--mono);font-size:11px;letter-spacing:.13em;color:var(--ink-3);
  white-space:nowrap}
/* 칸 이름은 **굵은 산세리프**다(2026-09-10, 안 D). 오른쪽 요약은 모노로
   남는다 — 이름은 읽는 것이고 요약은 세는 것이라 서체가 갈려야 한다.
   자간도 0 으로 되돌린다. 모노 자간(.13em)이 한글에 붙으면 글자가 흩어진다. */
.oph > span:first-child{font-family:var(--sans);font-size:12.5px;font-weight:600;
  letter-spacing:0;color:var(--ink)}
.ophr{font-size:10.5px;letter-spacing:.06em;color:var(--ink-3);
  overflow:hidden;text-overflow:ellipsis}

.opbody{flex:1;min-height:0;overflow:auto;scrollbar-width:none}
/* 스크롤바 — **안 보인다**(2026-09-14, 정현님: "도구 템플릿에서 변수 탭에 스크롤이
   없을 텐데"). 칸이 넘치면 휠·트랙패드·키보드로는 그대로 구르고, **막대만 안 그린다.**
   막대를 없애면 넘친다는 표시가 사라지는데(2026-09-11 에는 그래서 6px 로 가늘게만
   했다), 정현님이 막대 없는 쪽을 고르셨다. 틀 한 벌이라 모든 도구가 같이 바뀐다.
   `overflow` 까지 막으면 아래 절에 **아예 못 간다** — 그래서 구르기는 남긴다. */
.opbody::-webkit-scrollbar{display:none;width:0;height:0}

/* ── 칸 발 (2026-09-11) ──────────────────────────────────────────────
   **단위는 변수 칸 오른쪽 아래**다. 칸 머리에 두면 무엇을 보는지와 한
   줄에 섞이고, 요약 표 안에 두면 접을 때 같이 사라진다 — 단위는 모든
   숫자에 걸리는 것이라 늘 보여야 한다. 칸이 굴러도 안 따라간다. */
.opfoot{flex:none;display:flex;justify-content:flex-end;align-items:baseline;gap:6px;
  padding:6px 14px;border-top:1px solid var(--line-2);background:var(--bg-2);
  font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;color:var(--ink-3);
  white-space:nowrap}
.opfoot[hidden]{display:none}
.opfoot b{font-weight:500;color:var(--ink-2)}
.opfoot b.warn{color:var(--warn)}
/* 캔버스 칸은 **안 구른다.** 그림은 칸에 맞춰 그리는 것이지 넘쳐서
   구르는 것이 아니다 — 구르게 두면 확대·축소와 스크롤이 서로 싸운다. */
.opbody.ocanvas{overflow:hidden;position:relative;display:block}
.ocanvas canvas{display:block;width:100%;height:100%}
/* 캔버스 위에서는 끌기가 글자 선택으로 새면 안 된다. `touch-action` 을
   끄지 않으면 화면이 손가락 끌기를 스크롤로 가져간다. mgt 읽기에 있던 것을
   둘째 도구(모델 보기)가 같은 캔버스를 끌게 되어 여기로 올렸다(2026-09-11). */
.ocanvas canvas{cursor:grab;touch-action:none;user-select:none}
.ocanvas canvas.grabbing{cursor:grabbing}

/* 아직 아무것도 없을 때. **"고장" 과 "빈 것" 을 가른다.** */
.oempty{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;margin:0;pointer-events:none;
  font-size:12.5px;color:var(--ink-3);text-align:center;padding:0 20px}
.ocanvas.has .oempty{display:none}

/* ── 잡아 끄는 손잡이 ────────────────────────────────────────────────
   6px 은 눈에는 선이고 손에는 좁다. 그래서 **잡히는 자리를 넓힌다** —
   보이는 것은 그대로 두고 `::before` 로 앞뒤 5px 씩 더 받는다. */
.ogrip{position:relative;cursor:col-resize;background:var(--line);
  border:0;padding:0;touch-action:none}
.ogrip::before{content:'';position:absolute;top:0;bottom:0;left:-5px;right:-5px}
.ogrip:hover,.ogrip:focus-visible{background:var(--brand)}
.ogrip:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* 끄는 동안은 칸 안의 글자가 잡히지 않게 한다 — 안 그러면 드래그가
   글자 선택으로 새 버린다. */
body.dragging{cursor:col-resize;user-select:none}

/* ── 변수 칸 ─────────────────────────────────────────────────────────
   지금은 비어 있다. 입력이 생기면 도구 장의 `#inputs` 와 같은 결을
   쓴다 — 거기서 이미 라벨·값·단위 세 칸으로 자리를 잡았고, 긴 선택칸을
   제 줄로 내리는 규칙(`registry.js` 의 fitSelects)도 그 결에 붙어 있다. */
#vars{padding:16px 14px}
.ovar-empty{margin:0;font-size:12.5px;line-height:1.85;color:var(--ink-3)}
.ovar-empty b{display:block;color:var(--ink-2);font-weight:500;
  font-family:var(--mono);font-size:11px;letter-spacing:.12em;margin-bottom:8px}
.ovar-empty code{font-family:var(--mono);font-size:11px;color:var(--ink-2)}

/* ── 좁은 화면 ───────────────────────────────────────────────────────
   3단은 가로가 있어야 성립한다. 1100 아래에서는 **세로로 쌓는다** —
   억지로 세 칸을 유지하면 칸마다 200px 이 안 되어 셋 다 못 쓴다.
   그때는 바깥 스크롤을 돌려준다. */
@media(max-width:1100px){
  body.opt{overflow:auto}
  /* 띠는 세로로 쌓여도 맨 위에 그대로 있다 — 되돌아갈 길은 폭과 무관하다.
     다만 파일명은 자리를 많이 먹으므로 접는다. */
  .otop{height:40px}
  .otop .onow{display:none}
  .opt3{grid-template-columns:1fr;height:auto}
  .opt3 > #pane-vars,.opt3 > #pane-plan,.opt3 > #grip,.opt3 > #pane-3d{grid-column:1}
  .opane{border-right:0;border-bottom:1px solid var(--line)}
  .opane#pane-plan,.opane#pane-3d{height:min(64vh,560px)}
  /* 세로로 쌓으면 좌우로 끌 것이 없다. */
  .ogrip{display:none}
}

/* ── 도구 띠 — 3단 위의 한 줄 ────────────────────────────────────────

   ## 왜 생겼나 (2026-09-10)

   두 가지가 한 자리에서 풀렸다.

   **되돌아갈 길이 없었다.** 3단은 바깥 스크롤을 막고 화면을 꽉 써서,
   격자로 돌아갈 길이 배너의 `도구` 하나뿐이었다 — 그런데 그것은 지금
   자리를 가리키는 표시(`.here`)라 눌러도 돌아온 것 같지 않다. 그래서
   화살표를 변수 칸 머리 안(`변수` 앞)에 뒀었는데, 정현님이 **어색하다**고
   짚으셨다. 맞는 지적이다 — 그 화살표는 변수 칸에 딸린 것이 아닌데 변수
   칸 안에 있었다.

   **도구 이름이 화면 어디에도 없었다.** 재보니 정말 없었다(배너 제목에만
   있었다). 어느 도구를 쓰고 있는지 화면이 말하지 않았고, 그래서 전체가
   허전했다.

   띠 하나가 둘을 같이 푼다. 화살표는 제 자리(왼쪽 위)를 얻고, 이름은
   설 자리를 얻는다.

   ## 두 줄이 하는 일이 다르다

       띠      --bg      어디에 있나  (되돌아가기 · 도구 이름 · 지금 파일)
       칸 머리  --bg-2    어느 칸인가  (칸 이름 · 그 칸의 조작 · 요약)

   색이 갈려야 두 줄이 각자 다른 층으로 읽힌다.

   ## 화살표만 둔다

   `← 도구` 처럼 글자를 붙이지 않는다(2026-09-10, 정현님). 옆에 도구 이름이
   바로 붙어 있어서 글자를 더하면 한 줄에 이름이 둘이 된다. */
.otop{display:flex;align-items:center;gap:14px;padding:0 16px;height:44px;
  background:var(--bg);border-top:1px solid var(--line)}
.otop .oback{font-size:15px;line-height:1;color:var(--ink-3);
  text-decoration:none;padding:2px 4px;border-radius:2px}
.otop .oback:hover{color:var(--brand)}
.otop .oback:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.otop b{font-size:14px;font-weight:600;letter-spacing:-.01em;color:var(--ink)}
/* 지금 무엇을 열어 두었나. 없으면 비운다 — 자리는 남는다. */
.otop .onow{margin-left:auto;font-family:var(--mono);font-size:11px;
  color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ── 배너를 화면 끝까지 (2026-09-11, 정현님) ─────────────────────────
   사이트 배너(`chrome.css` 의 `.nav .wrap`)는 1600px 에서 가운데로 모인다.
   읽는 장에서는 맞지만, 도구는 3단이 화면 끝까지 차서 **배너만 안쪽에
   떠 있었다.** 도구 장에서만 풀어 로고는 왼쪽 끝, `시작하기` 는 오른쪽
   끝에 둔다. 옆 여백은 띠(`.otop`)와 같은 16px — 로고와 `←` 가 한 줄에
   선다. `chrome.css` 는 안 고친다 — 다른 장이 같이 바뀐다. */
body.opt .nav .wrap{max-width:none;padding:0 16px}

/* ═══════════════════════════════════════════════════════════════════
   부품 — 도구가 갖다 쓰는 것 (2026-09-10)
   ═══════════════════════════════════════════════════════════════════

   ## 왜 여기 모으나

   틀만 한 벌이고 **안에 들어가는 것은 도구마다 새로 그리고 있었다.**
   `mgt-read.css` 의 `.mr-sum` 주석이 그것을 스스로 적어 두었다 —
   *"OPT-06 왼쪽 요약(`member-list.css` 의 `.sum`)과 같은 결이다"*. 같은
   결인데 두 파일에 두 벌이고, 셋째 도구가 오면 세 벌이 된다. 이 파일
   머리말이 경고한 그 자리다.

   그래서 **여러 도구가 쓸 것만** 여기 둔다. 한 도구에서만 쓰는 것은
   그 도구 파일에 남긴다 — 여기 올리는 기준은 "둘째 도구가 곧 필요할까".

   ## 결이지 내용물이 아니다 (2026-09-10 에 한 번 틀렸다)

   처음에 참고 화면(어느 최적화 도구)의 **부품을 그대로** 옮겼다. 분절
   선택(ㅁ|ㄴ|ㄷ), 목적함수 슬라이더의 양끝말, 토글, 값 묶음 칩. 정현님이
   짚으셨다 — *"컨셉을 따라갔어야 하는데 내용물을 따라갔다."*

   도구는 최적화·뷰어·파서로 갈리는데 **목적함수는 최적화에만 있다.**
   뷰어를 만드는 사람이 여기서 `.oseg` 를 보면 쓸 자리를 찾게 되고, 그건
   없는 구조를 본이 주장하는 것이다.

   그래서 넷을 걷었다(`.oseg` · `.orange-ends` · `.oswitch` · `.ochips`).
   **필요해지면 그 도구가 자기 파일에 만들고, 둘째 도구가 같은 것을 원할
   때 여기로 올린다.**

   남긴 것은 도구 종류와 무관한 결이다.

       한 화면에서 끝난다        `.opt3` `.opane` `.oph`
       수마다 이름과 단위가 붙는다  `.ofield` `.okpi` `.obadge`
       화면이 조작법을 말한다      `.ohint` `.ofit` `.ophc`
       빈 것과 고장을 가른다       `.oempty` `.oalert`

   ## JS 를 안 쓴다

   접는 절은 `<details>`, 토글은 `<input type=checkbox>`, 슬라이더는
   `<input type=range>` 다. 전부 브라우저가 이미 하는 일이라 **CSP
   (`script-src 'self'`) 아래서도 그냥 돈다.** 스크립트로 흉내 내면 이
   장에 JS 가 생기고, 그러면 접근성(키보드·스크린리더)을 우리가 다시
   만들어야 한다.

   ## `[hidden]` 을 매번 옆에 붙인다

   `display:` 를 준 요소에 `hidden` 을 걸면 **작가 규칙이 UA 의
   `display:none` 을 이긴다.** 이 저장소에서 네 번 겪었다. 그래서 아래
   부품마다 `[hidden]{display:none}` 이 짝으로 붙어 있다.

   ## 색은 토큰만 쓴다

   `--ok`·`--warn`·`--ng` 는 `DESIGN.md` 의 판정 임계값과 짝이다. 여기서
   새 색을 만들면 화면이 계산과 다른 말을 하게 된다.                    */

/* ── 도구 머리 ───────────────────────────────────────────────────────
   변수 칸 맨 위. 이름과, 그 아래 모노 한 줄(무엇을 먹고 무엇을 내놓나). */
.ovh{margin:0 0 16px;padding-bottom:12px;border-bottom:1px solid var(--line)}
.ovh h1{margin:0;font-size:15px;font-weight:600;letter-spacing:-.01em}
.ovh p{margin:5px 0 0;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.09em;color:var(--ink-3);text-transform:uppercase}

/* ── 번호 붙은 절 ────────────────────────────────────────────────────
   `<details>` 라 JS 없이 접힌다. 머리 오른쪽에 **지금 값 요약**을 둔다 —
   접어 둔 채로도 무엇이 들어 있는지 보이게. */
.osec{border-bottom:1px solid var(--line-2)}
.osec[hidden]{display:none}
.osec > summary{display:flex;align-items:center;gap:9px;padding:12px 0;
  cursor:pointer;list-style:none;font-size:13.5px;font-weight:500}
.osec > summary::-webkit-details-marker{display:none}
.osec > summary:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.osec-n{flex:none;width:17px;height:17px;display:grid;place-items:center;
  background:var(--ink);color:#fff;border-radius:2px;
  font-family:var(--mono);font-size:10.5px;font-weight:500}
/* **제목은 줄지 않고 요약이 준다**(2026-09-11). 전에는 제목이 `min-width:0`
   이고 요약이 `nowrap` 이라, 요약에 긴 파일 이름(`동교동 173-7_구조해석_
   260910.mgt`)이 들어가자 제목 폭이 0 이 되어 "mgt 불러오기" 가 한 글자씩
   세로로 쌓였다(정현님이 짚으셨다). 요약은 남는 폭만 쓰고 넘치면 `…` —
   다 보려면 가리킨다(JS 가 `title` 을 단다). */
.osec-t{flex:1 0 auto;white-space:nowrap}
.osec-sum{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;
  color:var(--ink-3);white-space:nowrap}
/* 열림 표시. `▾` 를 글자로 두지 않는다 — 글꼴마다 크기가 튄다. */
.osec > summary::after{content:'';flex:none;width:7px;height:7px;
  border-right:1.5px solid var(--ink-3);border-bottom:1.5px solid var(--ink-3);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .15s ease}
.osec[open] > summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.osec-body{padding:2px 0 16px;display:flex;flex-direction:column;gap:13px}

/* ── 라벨 + 값 한 줄 ─────────────────────────────────────────────────
   도구 장의 `.f`(라벨 · 값 · 단위)와 같은 세 칸이다. 단위가 없으면 비운다. */
.ofield{display:grid;grid-template-columns:1fr auto auto;align-items:center;
  gap:8px;font-size:13px}
.ofield[hidden]{display:none}
.ofield > label{color:var(--ink-2);min-width:0}
.ofield input,.ofield select{font-family:var(--mono);font-size:13px;
  border:1px solid var(--line);border-radius:3px;padding:5px 7px;
  background:var(--bg);color:var(--ink);min-width:0;width:110px}
.ofield select{width:120px}
.ofield input:focus,.ofield select:focus{outline:2px solid var(--brand);
  outline-offset:-1px;border-color:var(--brand)}
.ofield .u{font-family:var(--mono);font-size:11px;color:var(--ink-3)}

/* **입력칸이 여럿 설 때는 한 벌로 묶는다** (2026-09-12, 정현님이 부력 검토에서
   짚으셨다). `.ofield` 는 줄마다 **제 격자**라, 세 칸의 폭을 그 줄의 내용이
   혼자 정한다 — 단위가 `m²`·`개층`·`kN/m³`·빈 칸으로 제각각이면 마지막 칸
   폭이 줄마다 달라지고, 그만큼 **값 칸이 좌우로 흔들린다.**

   묶는 자리(`.ofields`)에 격자를 두고 줄은 `display:contents` 로 사라지게
   하면, 세 칸이 **모든 줄에 걸쳐 하나**가 되어 값 칸이 한 줄로 선다.
   입력칸을 쌓는 도구는 이 클래스를 담는 자리에 건다.

   ⚠ `display:contents` 인 줄에는 `hidden` 이 안 듣는다 — 줄을 감춰야 하면
     묶지 말고 `.ofield` 를 따로 세운다. */
.ofields{display:grid;grid-template-columns:1fr auto auto;align-items:center;
  gap:9px 8px}
.ofields > .ofield{display:contents}
/* 묶음 안의 소제목 — 명세의 `group` 이 바뀌는 자리. 줄 전체를 쓴다. */
.ofields > .g{grid-column:1/-1;margin:6px 0 0;
  font-family:var(--mono);font-size:10px;letter-spacing:.12em;
  color:var(--ink-3);text-transform:uppercase}
.ofields > .g:first-child{margin-top:0}

/* ── 슬라이더 ────────────────────────────────────────────────────────
   라벨과 **지금 값**이 한 줄에 서고 트랙이 그 아래. 값을 안 보이면
   슬라이더는 "대충 이쯤" 이 되고, 구조 값은 대충이면 안 된다. */
.orange{display:block}
.orange[hidden]{display:none}
.orange > .h{display:flex;justify-content:space-between;align-items:baseline;
  gap:10px;margin-bottom:6px;font-size:13px}
.orange > .h span{color:var(--ink-2);min-width:0}
.orange > .h b{font-family:var(--mono);font-size:12.5px;font-weight:500;
  color:var(--ink);white-space:nowrap}
.orange input[type=range]{width:100%;margin:0;accent-color:var(--brand)}
.orange input[type=range]:focus-visible{outline:2px solid var(--brand);
  outline-offset:3px}

/* ── 단추 ────────────────────────────────────────────────────────────
   `.btn`(chrome.css)은 채운 단추다. 여기 것은 **되돌리기·다시 하기**처럼
   되돌릴 수 있는 일에 쓰는 테두리 단추다. */
.obtn{display:block;width:100%;padding:7px 12px;cursor:pointer;
  border:1px solid var(--line);border-radius:3px;background:var(--bg);
  font-family:inherit;font-size:12.5px;color:var(--ink-2);
  transition:border-color .15s ease,color .15s ease}
.obtn:hover{border-color:var(--ink-3);color:var(--ink)}
.obtn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.obtn[hidden]{display:none}

/* ── 파일 단추 (2026-09-12) ──────────────────────────────────────────
   **도구가 파일을 받는 자리다.** 계산기와 도구를 가르는 것이 "파일을
   받는가" 하나이므로(CLAUDE.md), 결국 거의 모든 도구가 이 단추를 갖는다.

   2026-09-12 까지 같은 모양이 **세 벌**이었다 — `mgt-read.css` 의
   `.mr-pick` · `optra-view.css` 의 `.ov-pick`, 그리고 새 도구 셋이 또
   하나씩 만들려던 참이었다. 여기로 올린다. 끌어 놓을 때의 강조
   (`body.*-dragover`)만 도구에 남는다 — 무엇을 받는지가 도구마다 다르다.

   `.btn` 은 `chrome.css` 것을 그대로 쓴다(배너의 "시작하기" 와 같은 단추).
   변수 칸이 280px 라 폭을 꽉 채운다 — 옆에 둘 것이 없다. */
.opick{display:flex;flex-direction:column;gap:8px}
.opick[hidden]{display:none}
.opick .btn{text-align:center;padding:8px 14px;font-size:13.5px}
.opick .btn:focus-within{outline:2px solid var(--brand);outline-offset:2px}
.opick .name{font-family:var(--mono);font-size:11.5px;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ── 막힌 것 ─────────────────────────────────────────────────────────
   **빈 것과 고장을 가른다.** 캔버스에 적으면 둘이 섞인다 — 그래서 변수
   칸, 원인을 만든 조작 바로 아래에 띄운다. */
.oalert{margin:0;padding:9px 11px;border:1px solid var(--ng);border-radius:3px;
  font-size:12.5px;line-height:1.7;color:var(--ng);word-break:keep-all}
.oalert[hidden]{display:none}
.oalert.warn{border-color:var(--warn);color:var(--warn)}

/* ── 요약 / 판정 ─────────────────────────────────────────────────────
   라벨과 값이 한 줄씩. 위에 이름과 **판정 배지**를 둘 수 있다.
   `mgt-read.css` 의 `.mr-sum` 과 `member-list.css` 의 `.sum` 이 각자
   그리던 것을 여기로 모은다. */
.okpi{font-family:var(--mono);font-size:12.5px}
.okpi[hidden]{display:none}
.okpi > .h{display:flex;align-items:center;justify-content:space-between;
  gap:10px;margin-bottom:7px;font-size:10.5px;letter-spacing:.12em;
  color:var(--ink-3);text-transform:uppercase}
.okpi .r{display:flex;justify-content:space-between;gap:10px;padding:4px 0;
  border-bottom:1px solid var(--line-2)}
.okpi .r span:first-child{color:var(--ink-3);white-space:nowrap}
.okpi .r b{font-weight:500;color:var(--ink);text-align:right}
.okpi .r b.ok{color:var(--ok)}
.okpi .r b.warn{color:var(--warn)}
.okpi .r b.ng{color:var(--ng)}
/* 배지. 글자는 도구가 넣는다 — `O.K.`·`N.G.`·`검토 전`. */
.obadge{flex:none;padding:1px 7px;border-radius:2px;font-size:10px;
  letter-spacing:.1em;font-weight:500;color:#fff;background:var(--ink-3)}
.obadge.ok{background:var(--ok)}
.obadge.warn{background:var(--warn)}
.obadge.ng{background:var(--ng)}

/* ── 부재 색 (2026-09-11) ────────────────────────────────────────────
   **도구가 구조 모델을 그릴 때 부재 종류마다 쓰는 색이다.** 한 도구 안에
   두지 않고 본(이 파일)에 둔다 — 다음 도구도 같은 색으로 그려야 도구를
   오갈 때 같은 그림으로 읽힌다.

   그 전(mgt 읽기 첫 판)에는 보 `--ink-2`·벽 `--ink-3` 이 **둘 다 푸른
   회색**이라 톤온톤으로 안 갈렸고, 기둥은 `--brand` 라 **하중 화살표와
   같은 색**이었다.

       보        파랑    선            --m-beam
       기둥      보라    네모 · 상자    --m-col
       플레이트  자홍    반투명 면      --m-plate
       벽        빨강    굵은 띠 · 면   --m-wall

       지점      먹      네모 · 다리    --m-sup   ┐ 부재 위에 겹치는 표시
       강조      먹      흰 후광 + 선   --m-hi    ┘ 둘 다 `--ink` 를 따른다

   **넷이 서로 색만으로 갈린다.** 모든 짝을 적록 색각이상 흉내까지 넣어
   쟀다(OKLab ΔE — 정상 시각 16.3 이상 보↔기둥, 색각이상 13.0 이상
   보↔플레이트).

   **벽은 빨강이다(2026-09-11).** 처음엔 돌회색(#7a746c)으로 바탕에 깔았는데
   넷 중 혼자 색이 없어 어색했다. 빨강으로 가려면 **지점·강조를 빨강에서
   떼어야 했다** — 둘이 판정 빨강(`--ng` #B2342A)을 빌려 쓰고 있어 빨간 벽과
   ΔE 3.1(색각이상 2.6), 곧 **같은 색**이었다. 그래서 둘을 먹으로 옮겼다.
   먹은 부재 넷·하중 어느 것과도 정상 27.3, 색각이상 25.3 넘게 떨어진다.

   **초록은 안 쓴다.** 적록 색각이상에서 바닥하중 주황(`--warn`)과 ΔE 2.2 로
   같은 색이 된다 — 부재 바로 위에 그려진다.

   ⚠ **하중과는 색이 아니라 모양으로 가른다.** 부재 넷에 겹치는 표시까지
     모두를 색 하나로 가르는 것은 불가능하다(모든 짝이 서로 붙는 그림에서는
     세 색이 상한이다). 가장 가까운 짝은 **벽↔바닥하중**(정상 11.5, 색각이상
     3.7)이다 — 바닥하중은 빗금 면, 벽은 굵은 띠라 모양으로 갈린다.

   ⚠ **`--m-plate` 는 흰 바탕 대비 2.7:1** 이라 가는 선에 쓰지 않는다 —
     면을 칠할 때만 쓴다.

   **값을 바꾸면 모든 짝을 다시 잰다.** 눈으로 고르지 않는다. */
:root{
  /* ⚠ 시험 중(2026-09-11): 차분한 벌. 색상은 그대로, 채도만 절반으로
     (OKLCH C 합 0.60 → 0.29). 부재 짝 최소 정상 18.3 · 색각이상 11.9.
     되돌리면 보 #2a78d6 · 기둥 #4a3aa7 · 플레이트 #e87ba4 · 벽 #c0392b —
     머리말의 수치는 그 벌의 것이다. */
  --m-beam:  #7595bd;
  --m-col:   #545378;
  --m-plate: #e0b7c6;
  --m-wall:  #b25f54;
  --m-sup:   var(--ink);
  --m-hi:    var(--ink);
  /* 부재력 덧그림(2026-09-11) — 모멘트 보라 · 전단 갈색. **부재 위에 겹치는
     선·면**이라 부재 넷·하중 둘 어느 것과도 갈려야 한다. OKLab ΔE, 정상/
     적록 색각이상 흉내(Machado 2009) — ⚠ 아래 수치는 **되돌리기 전의 진한
     부재 벌**에 잰 것이다. 차분한 벌(위)에서는 다시 재야 한다:
         모멘트 #7a3fbf   보 21.6/18.9 · 기둥 14.3/11.7 · 벽 22.9/24.4 ·
                          바닥하중 29.2/29.4 · 절점하중 22.1/13.4
         전단   #6e3b1e   보 29.2/27.3 · 기둥 13.5/12.9 · 벽 17.5/17.1 ·
                          바닥하중 17.7/18.0 · 절점하중 17.3/11.7
     둘은 한 캔버스에 같이 안 뜬다(성분을 하나 고른다) — 서로 갈릴 필요는
     없고, 무엇을 보고 있는지 한눈에 다르게 읽히면 된다(한색↔난색).
     자두색(#8a2f7d)은 색각이상에서 기둥과 1.3 이라 버렸다. **판정 색을 부호에
     빌리지 않는다** — 정·부모멘트는 좋고 나쁨이 아니다. 흰 바탕 대비 6.4·9.1. */
  --m-mom:   #7a3fbf;
  --m-shr:   #6e3b1e;
  /* 고른 부재(2026-09-11). 가리킨 것(`--m-hi`, 먹)과 갈려야 해서 색을
     쓴다 — 청록은 부재 색 넷에 없어 종류 색과 안 싸운다. 캔버스와 왼쪽
     "개체 정보" 가 같은 부재를 가리킨다는 표시다. */
  --m-sel:   var(--brand);
}

/* ── 범례 ────────────────────────────────────────────────────────────
   **캔버스에 글자를 적지 않는다** — 배율마다 흐려지고 두 캔버스에 두 번
   그려야 한다. 색은 JS 가 그리는 색과 **같은 토큰**이어야 한다. */
.olegend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--ink-2)}
.olegend[hidden]{display:none}
.olegend i{display:inline-block;width:14px;height:0;vertical-align:middle;
  margin-right:6px;border-top:2px solid var(--ink-2)}
.olegend i.sq{width:9px;height:9px;border:0;background:var(--brand)}
.olegend i.thick{border-top-width:5px;border-color:var(--ink-3);opacity:.7}
.olegend i.dash{border-top-style:dashed}
/* 부재 범례 — 캔버스가 그리는 모양을 따른다: 보는 선, 기둥은 네모, 벽은
   굵은 띠, 플레이트는 반투명 면. 색은 위 `--m-*` 와 **같은 토큰**이다. */
.olegend i.beam{border-color:var(--m-beam)}
.olegend i.col{width:9px;height:9px;border:0;background:var(--m-col)}
.olegend i.wall{border-top-width:5px;border-color:var(--m-wall);opacity:.7}
.olegend i.plate{width:12px;height:9px;border:0;background:var(--m-plate);opacity:.45}
/* 부재력 덧그림 — 캔버스가 그리는 것과 같이 옅은 면 위에 선. */
.olegend i.mom,.olegend i.shr{height:7px;border-top-width:1.5px;border-radius:0 0 3px 3px}
.olegend i.mom{border-color:var(--m-mom);background:rgba(122,63,191,.13)}
.olegend i.shr{border-color:var(--m-shr);background:rgba(110,59,30,.13)}

/* ── 잔글씨 ──────────────────────────────────────────────────────────
   출처·가정·"브라우저 안에서만 읽는다" 같은 한 줄. 변수 칸 맨 아래. */
.ofoot{margin:16px 0 0;padding-top:12px;border-top:1px solid var(--line-2);
  font-size:12px;line-height:1.8;color:var(--ink-3);word-break:keep-all}
.ofoot b{font-weight:500;color:var(--ink-2)}
.ofoot kbd{font-family:var(--mono);font-size:11px;color:var(--ink-2);
  border:1px solid var(--line);border-radius:2px;padding:0 4px}

/* ── 칸 머리 안의 조작 ───────────────────────────────────────────────
   **여기가 인터렉티브가 자라는 자리다** (2026-09-10).

   지금 도구는 파일을 읽어 보여 주기만 하지만, 앞으로는 칸마다 제 조작이
   붙는다 — 3D 를 분해해 보기, 층만 보기, 애니메이션 재생. 그것을 변수
   칸에 두면 **어느 칸에 듣는 조작인지 모르게 된다.** 그 칸 머리에 두면
   자리가 곧 대상이다.

   칸 이름과 오른쪽 요약(`.ophr`) 사이에 들어간다. 좁아지면 요약이 먼저
   줄고(`.ophr` 이 말줄임) 조작은 남는다 — 읽는 것보다 누르는 것이 앞선다. */
.ophc{display:flex;align-items:center;gap:8px;flex:none;margin-right:auto}
/* **제목 옆에 붙는다.** `.oph` 가 `space-between` 이라 그냥 두면 조작이
   칸 한가운데로 밀려 제목과도 요약과도 안 붙는다 — 어디에 딸린 것인지
   안 읽힌다. `margin-right:auto` 하나로 제목 쪽에 붙고 요약만 오른쪽 끝에
   남는다(2026-09-10, 재서 고쳤다: 조작이 l=560 으로 가운데 있었다). */
.ophc[hidden]{display:none}
.ophc button{padding:2px 8px;cursor:pointer;border:1px solid var(--line);
  border-radius:2px;background:var(--bg);font-family:var(--mono);
  font-size:10.5px;letter-spacing:.06em;color:var(--ink-2);white-space:nowrap}
.ophc button:hover{border-color:var(--ink-3);color:var(--ink)}
.ophc button:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.ophc button[aria-pressed=true]{background:var(--ink);border-color:var(--ink);color:#fff}
.ophc input[type=range]{width:76px;margin:0;accent-color:var(--brand)}

/* ── 캔버스 바닥의 조작 안내 ─────────────────────────────────────────
   **끌 수 있다는 것을 화면이 말한다.** 처음 여는 사람은 캔버스가 그림인지
   조작할 수 있는 것인지 모른다. 캔버스 위에 얹되 손은 통과시킨다. */
.ohint{position:absolute;left:12px;bottom:10px;margin:0;pointer-events:none;
  font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  color:var(--ink-3);text-transform:uppercase;user-select:none}
.ohint[hidden]{display:none}
/* 오른쪽 아래는 **누르는 자리**다(맞춤·처음으로). 여기만 손을 받는다. */
.ofit{position:absolute;right:12px;bottom:10px;padding:3px 9px;cursor:pointer;
  border:1px solid var(--line);border-radius:2px;background:var(--bg);
  font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;color:var(--ink-2)}
.ofit:hover{border-color:var(--ink-3);color:var(--ink)}
.ofit:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.ofit[hidden]{display:none}
/* 절 **안**에 넣는 잔글씨. 위 선과 여백이 이미 절에 있으므로 뺀다. */
.ofoot.bare{margin:0;padding:0;border:0}

/* ── 캔버스 위의 작은 창 (2026-09-11) ────────────────────────────────
   **무엇 위에 있나를 그 자리에서 말한다.** 부재를 눌러 왼쪽 칸에 띄우면
   손이 화면을 가로지르고, 그 사이 무엇을 가리켰는지 잊는다.

   캔버스에 글자를 그리지 않는다 — 배율마다 흐려지고 두 캔버스에 두 번
   그려야 한다(범례를 왼쪽에 둔 것과 같은 이유). DOM 으로 얹는다.

   손을 통과시킨다(`pointer-events:none`). 창이 마우스를 먹으면 그 밑의
   부재에서 커서가 나간 것이 되어 **창이 자기를 지우는** 깜빡임이 난다. */
.otip{position:absolute;z-index:3;pointer-events:none;
  max-width:250px;padding:8px 10px;
  background:var(--bg);border:1px solid var(--line);border-radius:3px;
  box-shadow:0 2px 4px rgba(11,26,33,.06),0 10px 24px -12px rgba(11,26,33,.3);
  font-size:12px;line-height:1.6;color:var(--ink-2)}
.otip[hidden]{display:none}
/* 머리 — 무엇인가(부재 번호·종류). 아래 줄들과 서체를 가른다. */
.otip b{display:block;margin-bottom:4px;font-family:var(--mono);font-size:11px;
  letter-spacing:.06em;font-weight:600;color:var(--ink)}
.otip .r{display:flex;justify-content:space-between;gap:12px}
.otip .r span{color:var(--ink-3);white-space:nowrap}
.otip .r em{font-style:normal;font-family:var(--mono);color:var(--ink)}

/* ── 칸 안의 조작 줄 (2026-09-11) ────────────────────────────────────
   **칸 머리 아래 한 줄.** 토글이 셋을 넘으면 머리가 붐빈다 — 이름과 요약이
   조작에 밀려 잘린다(`.ophr` 이 먼저 줄어드는데, 그러면 몇 개인지 못 읽는다).

   `.ophc`(머리 안)와 갈라 쓴다.

       .ophc    한둘. 이 칸의 성격을 바꾸는 것 (보기 방식)
       .otools  셋 이상. 무엇을 겹쳐 볼지 고르는 것 (레이어)

   머리와 같은 바탕(`--bg-2`)이라 두 줄이 한 덩어리로 읽히고, 아래 선으로
   캔버스와 갈린다. */
.otools{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  padding:6px 12px;background:var(--bg-2);border-bottom:1px solid var(--line)}
.otools[hidden]{display:none}
.otools button{padding:3px 9px;cursor:pointer;border:1px solid var(--line);
  border-radius:2px;background:var(--bg);font-family:var(--mono);
  font-size:10.5px;letter-spacing:.06em;color:var(--ink-2);white-space:nowrap}
.otools button:hover{border-color:var(--ink-3);color:var(--ink)}
.otools button:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.otools button[aria-pressed=true]{background:var(--ink);border-color:var(--ink);color:#fff}
/* 조작 사이를 가르는 세로 선. 성격이 다른 묶음 사이에만 둔다. */
.otools i{width:1px;height:14px;background:var(--line);margin:0 3px}
/* 조작 줄 안의 고르기. 단추와 높이를 맞춘다 — 한 줄에 섞이므로 밑선이
   어긋나면 줄이 두 겹으로 읽힌다. */
.otools .osel{display:flex;align-items:center;gap:6px;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;color:var(--ink-3)}
.otools .osel[hidden]{display:none}
.otools .osel select{font-family:var(--mono);font-size:11px;padding:2px 4px;
  border:1px solid var(--line);border-radius:2px;background:var(--bg);color:var(--ink)}
.otools .osel select:focus{outline:2px solid var(--brand);outline-offset:-1px}
/* **레이어 안에서 고르는 것은 그 레이어 단추 바로 옆**(2026-09-11) — 하중
   케이스처럼. 켰을 때만 보이고(`hidden`), 칸마다 따로 든다. 변수 칸에 두면
   어느 칸에 듣는지 모르고, 켜는 손과 고르는 손이 떨어진다. */

/* ── 캔버스 위 알림 (2026-09-11) ─────────────────────────────────────
   **그림이 비어 보이는 까닭을 그림 위에서 말한다.** 예: 풍·지진 하중
   케이스는 파일에 힘이 없어 화살표가 안 뜬다 — 말없이 비어 있으면 고장으로
   읽힌다. 캔버스에 글자를 굽지 않고 글자 요소로 얹는다(범례와 같은 이유).
   손을 막지 않는다(`pointer-events:none`) — 그 밑에서 끌고 확대한다. */
.onote{position:absolute;left:10px;top:10px;max-width:min(380px,calc(100% - 20px));
  margin:0;padding:6px 9px;background:var(--bg);border:1px solid var(--line);
  border-radius:2px;font-size:11.5px;line-height:1.55;color:var(--ink-2);
  pointer-events:none}
.onote[hidden]{display:none}
.onote b{color:var(--ink);font-weight:600}
.onote code{font-family:var(--mono);font-size:10.5px;word-break:break-all}
/* 알릴 것이 둘이면(풍·지진 방향 + 덧그림 없음) 문단으로 가른다. */
.onote p{margin:0}
.onote p+p{margin-top:6px;padding-top:6px;border-top:1px solid var(--line-2)}
