        * { box-sizing: border-box; }
        :root {
            --accent: #1a237e;
            --accent-soft: #e8eaf6;
            --line: #e0e6ed;
        }
        body {
            margin: 0; padding: 0.6rem 1rem 0;
            background-color: #f4f7f6;
            font-family: 'Malgun Gothic', sans-serif;
            color: #263238;
        }

        /* ===== 상단 바 ===== */
        /* ⚠️ **`flex-wrap: wrap` 을 빼지 않는다.** 없으면 좁은 창에서 이 바가 여덟 칸을
           한 줄에 밀어 넣으려 하고, 칸들이 글자 폭 아래로 찌부러져 **제목과 단추
           글자가 세로로 한 글자씩 쪼개진다.**

           2026-09-22 실측: **로그인 상태에서 창 폭 701px** 이면 이미 「자리 배치」가
           자/리/배/치 로 접히고, 오른쪽 칸들은 화면 밖으로 밀려 **가로로 스크롤해야**
           닿았다. 내용의 최소 폭이 로그인 시 ~670px · 무로그인 시 ~560px 라 그렇다 —
           폰만의 이야기가 아니라 **노트북에서 창을 반으로 줄여도** 걸린다.

           ⚠️ 바가 두 줄이 되어도 좌석 크기 계산은 안 깨진다 — `seat.js` 의 `fitSeats`
              가 `stage.getBoundingClientRect().top` 으로 **실제로 재기** 때문이다.
              바가 높아진 만큼 좌석이 그만큼 작아질 뿐이다. */
        .appbar {
            display: flex; align-items: center; gap: 1rem;
            flex-wrap: wrap; row-gap: 0.5rem;
            background: white; border-radius: 12px;
            box-shadow: 0 4px 10px rgba(0,0,0,0.05);
            padding: 0.55rem 1.1rem; margin-bottom: 0.6rem;
        }
        .class-tag {
            background-color: var(--accent); color: white;
            padding: 0.5rem 1.2rem; border-radius: 50px;
            font-size: 1.3rem; font-weight: bold; cursor: pointer;
            white-space: nowrap; text-decoration: none;
        }
        .class-tag:hover { background-color: #283593; }
        /* `white-space: nowrap` 이 글자가 세로로 쪼개지는 것을 막는다. `flex: 1` 은
           밑변(flex-basis)이 0 이라, 이것이 없으면 제목이 한 글자 폭까지 찌부러진다. */
        .appbar h1 { flex: 1; margin: 0; font-size: 1.5rem; color: var(--accent); white-space: nowrap; }
        /* 🔴 **저장 상태는 이 글자에만 나온다** (2026-09-22). 예전에는 `#save-btn` 도 같이
           물들었는데, 그 단추가 하는 일은 자동 저장이 아니라 **배치를 목록에 보관하는
           것**이라(`📌 이 배치 보관`) 주황이 「보관이 안 됐다」로 읽혔다.
           단추 색을 잃은 만큼 글자를 굵게 해서 메운다. */
        /* ⚠️ `min-width` — 「저장됨 ✓」이 4초 뒤 사라져도(2026-09-23, [D114]) **칸은 남는다.**
              안 그러면 글자가 생기고 없어질 때마다 옆 단추 셋이 좌우로 밀린다. */
        .save-state { font-size: 0.92rem; font-weight: bold; color: #90a4ae; white-space: nowrap; min-width: 4.8em; }
        .save-state.dirty { color: #ef6c00; }
        .save-state.saved { color: #2e7d32; }

        /* ===== 본문 레이아웃 =====
           왼쪽(넓게): 학생용 칠판+좌석표 — 화면 위쪽부터 크게, 아래는 여백(뒷자리 학생 배려)
           오른쪽 패널(.side): 교사용 정보 — 배정 상태 / '아직 자리 없는 번호' / 안내
           아래 고정 막대(.toolbar): 교사용 버튼 — 칠판 앞 교사 손에 닿게
           설정은 ⚙️ 설정 버튼을 누르면 뜨는 팝업(dialog) 안에 있음 */
        .wrap { display: flex; gap: 1rem; align-items: flex-start; }
        .col-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
        .side {
            width: 270px; flex: none;
            position: sticky; top: 0.5rem;
            height: calc(100vh - 8.5rem);          /* 하단 고정 버튼 막대 자리 확보 */
            display: flex; flex-direction: column; justify-content: flex-end; gap: 0.6rem;
            overflow-y: auto;
        }
        /* 🔴 **세로로 설 때는 `align-items: stretch` 로 되돌린다** (2026-09-24, UI 점검 11).
              위의 `flex-start` 가 세로 방향에서는 「폭을 채우지 마라」가 되어, 자리표 칸이
              화면 폭이 아니라 **좌석 폭만큼** 늘었다. 휴대폰(390)에서는 그 폭(496px)에 맞춰
              브라우저가 화면 전체를 넓혀 가로 스크롤이 생겼고, `fitSeats` 는 넓어진 폭을
              재서 좌석을 다시 크게 만들었다 — 스스로를 굳히는 고리였다. */
        @media (max-width: 900px) {
            .wrap { flex-direction: column; align-items: stretch; }
            .side { width: auto; position: static; height: auto; justify-content: flex-start; }
        }

        /* ===== 설정 · 출력 팝업 ===== */
        #settings-dialog, #output-dialog, #pick-dialog, #ask-dialog {
            width: min(560px, 94vw); max-height: 88vh;
            border: none; border-radius: 14px; padding: 0; overflow: hidden;
            box-shadow: 0 12px 40px rgba(0,0,0,0.25);
        }
        #settings-dialog[open], #output-dialog[open], #pick-dialog[open], #ask-dialog[open] { display: flex; flex-direction: column; }
        #settings-dialog::backdrop, #output-dialog::backdrop,
        #pick-dialog::backdrop, #ask-dialog::backdrop { background: rgba(0,0,0,0.45); }
        .dialog-head {
            flex: none;
            display: flex; align-items: center; justify-content: space-between;
            padding: 1rem 1.2rem 0.7rem; background: #fff;
            border-bottom: 1px solid var(--line);
        }
        .dialog-head strong { font-size: 1.2rem; color: var(--accent); }
        .panel { flex: 1 1 auto; overflow-y: auto; padding: 1rem 1.2rem 1.2rem; }

        .panel .sect {
            border: 1px solid var(--line); border-radius: 10px;
            padding: 0.6rem 0.9rem; margin-bottom: 0.7rem;
        }
        .panel .sect > summary {
            cursor: pointer; font-weight: bold; font-size: 1.02rem; color: var(--accent);
            list-style: none; padding: 0.2rem 0;
        }
        .panel .sect > summary::-webkit-details-marker { display: none; }
        .panel .sect > summary::before { content: "▸ "; color: #90a4ae; }
        .panel .sect[open] > summary::before { content: "▾ "; }
        .panel .sect[open] > summary { margin-bottom: 0.5rem; border-bottom: 1px solid var(--accent-soft); }
        .panel label { display: block; font-size: 0.9rem; margin: 0.5rem 0 0.2rem; color: #546e7a; }
        .panel input[type=number], .panel input[type=text], .panel select, .panel textarea {
            width: 100%; padding: 0.45rem 0.6rem; font-size: 1rem;
            border: 1px solid #cfd8dc; border-radius: 8px; font-family: inherit;
        }
        .panel textarea { resize: vertical; line-height: 1.5; }
        .row-inline { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
        .row-inline input[type=text], .row-inline input[type=number] { flex: 1; }

        /* 보관한 배치 목록 */
        .snap-row {
            display: flex; align-items: center; gap: 0.4rem;
            padding: 0.45rem 0.55rem; border: 1px solid var(--line); border-radius: 8px;
            margin-bottom: 0.35rem;
        }
        .snap-row .snap-meta { flex: 1; min-width: 0; }
        .snap-row .snap-name { font-weight: bold; font-size: 0.92rem; color: #37474f;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .snap-row .snap-time { font-size: 0.78rem; color: #90a4ae; }
        .snap-row .btn { padding: 0.3rem 0.6rem; font-size: 0.85rem; }
        .snap-row .btn.del { color: #c62828; border-color: #ef9a9a; }
        .hint { font-size: 0.8rem; color: #90a4ae; margin-top: 0.3rem; line-height: 1.4; }
        .summary {
            margin-top: 0.6rem; padding: 0.5rem 0.7rem; border-radius: 8px;
            background: #f1f8e9; font-size: 0.88rem; line-height: 1.5;
        }
        .summary.warn { background: #fff3e0; }
        .summary.error { background: #ffebee; color: #c62828; }

        .btn {
            display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem;
            padding: 0.5rem 0.9rem; font-size: 0.95rem; font-weight: bold;
            border: 1px solid #cfd8dc; background: #fff; color: #37474f;
            border-radius: 8px; cursor: pointer; font-family: inherit;
        }
        .btn:hover { background: #eceff1; }
        .btn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
        .btn.primary:hover { background: #283593; }
        .btn.active { background: #ffd600; border-color: #f9a825; color: #000; }
        .btn.wide { width: 100%; margin-top: 0.4rem; }
        .btn:disabled { opacity: 0.45; cursor: default; }

        /* 맨 위 띠의 홈 · 로그인/로그아웃 (`partials/topbar-buttons.blade.php`).
           바로 위 `.btn` 들과 나란히 서므로 크기를 그쪽에 맞춘다. */
        .appbar .app-topbar__btn { padding: 0.5rem 0.9rem; font-size: 0.95rem; border-radius: 8px; }
        .appbar .app-topbar__logout { margin-left: 0.3rem; }

        /* 아주 좁은 화면(폰)에서는 제목을 아예 뺀다. 반 이름표(`1학년 3반`)가 이미 어느
           화면인지 말해 주므로, 한 줄을 통째로 아끼는 편이 낫다. */
        @media (max-width: 600px) {
            .appbar h1 { display: none; }
        }

        .rule-line { display: flex; align-items: center; gap: 0.3rem; margin-bottom: 0.4rem; font-size: 0.9rem; }
        .rule-line input { width: 3.2rem; padding: 0.3rem; text-align: center; border: 1px solid #cfd8dc; border-radius: 6px; }
        .rule-line .btn { padding: 0.25rem 0.5rem; }
        .check-line { display: flex; align-items: center; gap: 0.4rem; margin: 0.4rem 0; font-size: 0.9rem; }
        details > summary { cursor: pointer; font-weight: bold; color: #546e7a; margin-bottom: 0.5rem; }

        /* ===== 번호 고르기 격자 (2026-09-22) =====
           🔴 **칠판 앞에서 손가락으로 누르는 단추다.** 높이를 3.5rem 로 잡는다 — 이보다
              작으면 터치가 옆 번호로 빗나간다. 좌석 칸(`.seat`)과 달리 여기는 화면
              크기에 안 맞춘다. 목록이 길어지면 창이 스크롤되면 그만이다. */
        .pick-grid {
            display: grid; gap: 0.4rem;
            grid-template-columns: repeat(auto-fill, minmax(4.2rem, 1fr));
        }
        .pick-num {
            min-height: 3.5rem; padding: 0.3rem 0.2rem;
            display: flex; flex-direction: column; align-items: center; justify-content: center;
            border: 2px solid #cfd8dc; background: #fff; color: #263238;
            border-radius: 10px; cursor: pointer; font-family: inherit;
        }
        .pick-num:hover { background: #eceff1; }
        .pick-num .pick-n { font-size: 1.35rem; font-weight: 800; line-height: 1.1; }
        .pick-num .pick-name {
            font-size: 0.72rem; font-weight: 700; color: #607d8b; line-height: 1.2;
            max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        /* 이미 어딘가 앉아 있는 번호. ⚠️ **막지 않는다** — 누르면 원래 자리에서 옮겨 온다.
           점선은 "이미 어딘가 있다" 를 알리는 표시이지 금지 표시가 아니다.

           🔴 **글자를 흐리게 하지 않는다.** 2026-09-22 실측: 랜덤 배치를 하고 나면 명단이
              전원 배치돼 있어 **스무 칸이 전부 이 상태**가 된다. 색을 빼면 창 전체가
              「눌리지 않는 것」처럼 보여서, 표시가 정보가 아니라 방해가 된다.
              테두리만으로 말한다. */
        .pick-num.seated { background: #fafafa; border-style: dashed; border-color: #b0bec5; }
        .pick-num.current { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
        /* ⚠️ `.btn` 이 `display: inline-flex` 라 `[hidden]` 을 이긴다. 이 줄이 없으면
              「이 자리 비우기」가 고정 모드에서도 보인다. */
        #pick-clear[hidden] { display: none; }

        /* ===== 확인창 ===== */
        .ask-body { font-size: 1rem; line-height: 1.6; white-space: pre-line; }
        .ask-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.1rem; }
        .ask-actions .btn { min-width: 5.5rem; }
        .btn.danger { background: #c62828; border-color: #c62828; color: #fff; }
        .btn.danger:hover { background: #b71c1c; }

        /* ===== 도구 막대 — 화면 하단 고정, 스크롤해도 항상 보임 ===== */
        .toolbar {
            position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
            display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem;
            background: rgba(255,255,255,0.97);
            border-top: 1px solid var(--line);
            box-shadow: 0 -3px 14px rgba(0,0,0,0.08);
            padding: 0.6rem 0.9rem;
        }
        .toolbar .btn { flex: none; }
        .toolbar .sep { width: 1px; height: 1.7rem; align-self: center; background: var(--line); margin: 0 0.2rem; }

        /* ===== 휴대폰 (600px 이하) — 진님 결정 2026-09-24, UI 점검 11 =====
           🔴 **여기 규칙은 전부 600px 아래에서만 먹는다.** 칠판·교사 PC·태블릿은 그대로다.
           ① 도구 막대 — 🎲 랜덤 배치 · ↩️ 되돌리기 · ⋯ 더보기 셋만. 나머지(`tb-extra`)는
              더보기를 누르면 펼쳐진다(`.toolbar.is-open`). 예전에는 네 줄(152px)이었다.
           ② 자리표 칸 안쪽 여백을 줄여 좌석에 폭을 돌려준다. 좌석 최소 크기는 `seat.js` 의
              `fitSeats` 가 이 폭에서만 40 → 26px 로 낮춘다(`PHONE_MQ` — 이 숫자 600 과 짝).
           ⚠️ `#btn-more` 는 넓은 화면에서 아예 안 보인다. `.btn` 이 `display` 를 갖고 있어
              `[hidden]` 대신 여기서 끈다. */
        #btn-more { display: none; }
        @media (max-width: 600px) {
            body { padding: 0.5rem 0.5rem 0; }
            #btn-more { display: inline-flex; }
            .toolbar { gap: 0.4rem; padding: 0.5rem; }
            .toolbar:not(.is-open) .tb-extra { display: none; }
            .toolbar .sep { display: none; }   /* 펼치면 여러 줄이라 가름선이 줄 끝에 홀로 남는다 */
            .board-stage { padding: 0.7rem; }
        }

        /* ===== 화면 보기 팝오버 (버튼 위로 목록이 뜸) ===== */
        .pop-wrap { position: relative; display: inline-flex; }
        .pop {
            position: absolute; right: 0; bottom: calc(100% + 0.6rem);
            background: #fff; border: 1px solid var(--line); border-radius: 10px;
            box-shadow: 0 8px 26px rgba(0,0,0,0.20);
            padding: 0.35rem; min-width: 15rem; max-width: 92vw; z-index: 40;
            display: flex; flex-direction: column; gap: 0.15rem;
        }
        .pop[hidden] { display: none; }
        .pop::after {
            content: ""; position: absolute; top: 100%; right: 1.2rem;
            border: 8px solid transparent; border-top-color: #fff;
            filter: drop-shadow(0 1px 0 var(--line));
        }
        .pop-item {
            display: flex; align-items: center; gap: 0.45rem; width: 100%;
            padding: 0.6rem 0.7rem; border: none; background: none; border-radius: 8px;
            font: inherit; font-weight: bold; color: #37474f; cursor: pointer; text-align: left;
        }
        .pop-item:hover { background: #eceff1; }
        .pop-item small { display: block; font-weight: normal; font-size: 0.8rem; color: #78909c; }
        .pop-item .tick { color: var(--accent); font-weight: 900; visibility: hidden; }
        .pop-item.on { background: #e8eaf6; }
        .pop-item.on .tick { visibility: visible; }
        .mode-hint {
            font-size: 0.95rem; font-weight: bold; color: #ef6c00; line-height: 1.4;
            background: #fff3e0; border-radius: 10px; padding: 0.5rem 0.7rem;
        }
        .mode-hint:empty { display: none; }

        /* ===== 좌석 무대 ===== */
        .board-stage {
            background: white; border: 1px solid var(--line); border-radius: 12px;
            padding: 1.4rem; display: flex; flex-direction: column; gap: 1rem;
            overflow-x: auto;
        }
        .board-stage.flip-v { flex-direction: column-reverse; }
        .board {
            background: #263238; color: #fff; text-align: center;
            padding: 0.6rem; border-radius: 8px; font-weight: bold; letter-spacing: 0.3em;
            min-width: 12rem; align-self: center; width: 60%;
        }
        .grid {
            display: flex; flex-direction: column; gap: 0.7rem; align-items: center;
            /* --seat 등은 JS(fitSeats)가 화면 폭에 맞춰 덮어씀. 아래는 기본값. */
            --seat: 3.3rem; --seat-gap: 3px; --aisle: 1.6rem;
        }
        /* 분단(-로 구분한 묶음)을 세로로 줄 맞춤: 각 줄이 같은 분단 열 구조를 갖는다 */
        .seat-row { display: grid; grid-auto-flow: column; gap: var(--aisle); }
        .seat-bundle { display: grid; gap: var(--seat-gap); }
        .seat, .seat-spacer { width: var(--seat); height: var(--seat); }
        .seat-spacer { visibility: hidden; }
        /* 🔴 **좌석은 `<div>` 가 아니라 진짜 `<button>` 이다** (2026-09-22, [D109]).
              그래야 Tab 으로 닿고 Enter·Space 로 눌린다 — 흉내(`role="button"`)로는
              브라우저마다 갈래가 갈린다.

           ⚠️ 단추는 **브라우저가 제 모양을 얹는다.** 아래 다섯 줄(`appearance`·`font`·
              `padding`·`margin`·`color`)이 그것을 걷어내는 자리다. 이 줄을 지우면
              칸마다 회색 배경과 작은 글씨가 되살아난다. */
        .seat {
            appearance: none; -webkit-appearance: none;
            font-family: inherit; color: inherit;
            padding: 0; margin: 0; text-align: center;
            border-radius: 8px;
            border: 2px solid #b0bec5; background: #fafafa;
            display: flex; flex-direction: column; align-items: center; justify-content: center;
            font-size: calc(var(--seat) * 0.42); font-weight: 800; cursor: pointer;
            user-select: none; position: relative; overflow: hidden;
        }
        .s-num { font-size: calc(var(--seat) * 0.4); font-weight: 800; line-height: 1.05; }
        .s-name {
            font-size: calc(var(--seat) * 0.16); font-weight: 700; line-height: 1.1; color: #455a64;
            max-width: 94%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        .seat.disabled { border-style: dashed; background: #f0f0f0; opacity: 0.5; cursor: pointer; }
        .seat.fixed { border-color: #f9a825; background: #fff8e1; }
        .seat.fixed::after {
            content: "🔒"; position: absolute; top: 1px; right: 2px; font-size: 0.65rem;
        }
        .seat.selected { outline: 3px solid #29b6f6; outline-offset: 1px; }
        .seat.swap-pick { outline: 3px solid #ab47bc; outline-offset: 1px; }
        .seat.empty-num { color: #cfd8dc; }

        /* ── 「지금 여기」 — 키보드로 짚은 칸 (2026-09-22, [D109]) ───────────────
           이 화면에는 이미 색으로 말하는 것이 셋 있다 — **주황=고정 · 하늘=재추첨
           선택 · 보라=맞바꾸기.** 그래서 넷째 색이 필요했고 진님이 **남색 + 번짐**을
           보기에서 골랐다.

           🔴 **`:focus-visible` 이지 `:focus` 가 아니다.** `:focus` 로 하면 **마우스로
              누른 칸에도** 남색 테두리가 남는다 — 칠판을 손가락으로 누르는 화면에서
              누른 자리마다 테두리가 붙어 다니면 「선택된 자리」로 읽힌다.
              `:focus-visible` 은 키보드로 짚었을 때만 켜진다.

           ⚠️ **`z-index` 가 필요하다.** 번짐(`box-shadow`)은 칸 **바깥**으로 6px 나가는데,
              좌석은 순서대로 그려지므로 **뒤에 그려진 옆 칸이 그 위를 덮는다.**
              빼면 오른쪽·아래쪽 번짐만 잘려 테두리가 한쪽만 두꺼워 보인다.

           ⚠️ **`.chip.unplaced.armed:focus-visible` 를 같이 적어 둔다.** 집어 든 칩은
              `.armed` 가 **하늘색 테두리**를 거는데, 그 줄이 이 규칙보다 **아래**에 있어
              셋 중 하나만 적으면 집어 든 칩에서만 남색이 하늘색에 진다. 세 번째 줄이
              그 한 경우를 이긴다. */
        .seat:focus-visible,
        .chip.unplaced:focus-visible,
        .chip.unplaced.armed:focus-visible {
            outline: 3px solid #1a237e;
            outline-offset: 2px;
            box-shadow: 0 0 0 6px rgba(26, 35, 126, 0.18);
            z-index: 2;
        }
        /* 미사용(빈자리) 칸은 `opacity: 0.5` 라 테두리까지 흐려진다 — 짚었을 때만
           또렷하게 돌린다. 안 그러면 빈자리 위에서는 「지금 여기」가 안 보인다. */
        .seat.disabled:focus-visible { opacity: 1; }

        .status {
            background: white; border: 1px solid var(--line);
            border-radius: 12px; padding: 0.7rem 0.9rem; font-size: 0.92rem; line-height: 1.6;
        }
        .chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.4rem; align-items: center; }
        .chip {
            background: #eceff1; border-radius: 50px; padding: 0.15rem 0.6rem; font-size: 0.85rem;
        }
        .chip.excluded { background: #ffcdd2; text-decoration: line-through; color: #b71c1c; }
        /* 이 칩도 **진짜 단추다** (2026-09-22, [D109]). 좌석만 단추로 바꾸면
           「번호를 집어 → 자리에 놓는다」가 키보드로는 **시작조차 안 된다** —
           집는 자리가 여기이기 때문이다. 단추 기본 모양 되돌림은 `.seat` 와 같다. */
        .chip.unplaced {
            appearance: none; -webkit-appearance: none;
            font-family: inherit; border: 0; line-height: inherit;
            background: #ffe0b2; cursor: pointer; font-size: 1.05rem; font-weight: 800;
            padding: 0.25rem 0.75rem;
        }
        .chip.unplaced:hover { background: #ffcc80; }
        .chip.unplaced.armed {
            background: #1565c0; color: #fff; outline: 3px solid #90caf9; outline-offset: 1px;
        }

        .legend { font-size: 0.82rem; color: #78909c; margin-top: 0.4rem; }
        /* 키보드 안내 (2026-09-22, [D109]).
           ⚠️ **좁은 화면에서는 통째로 숨긴다.** 900px 아래에서는 `.side` 가 자리표 **밑**으로
              내려오는데(`.wrap` 이 세로로 선다), `fitSeats` 는 그 높이를 안 보고 좌석 크기를
              잡는다 — 줄이 늘수록 아래가 밀린다. 게다가 그 폭은 칠판 터치·태블릿이라
              **키보드가 아예 없는** 화면이다. 숨겨서 잃는 것이 없다. */
        .legend-kbd kbd {
            display: inline-block; background: #fff; border: 1px solid #b0bec5;
            border-bottom-width: 2px; border-radius: 4px; padding: 0 0.28rem;
            font-family: inherit; font-size: 0.78rem; font-weight: 800; color: #455a64;
        }
        @media (max-width: 900px) { .legend-kbd { display: none; } }

        /* ===== 토스트 =====
           🔴 **본부 알림과 같은 모양·같은 자리** (2026-09-23, [D114] ← 본부 [D113]).
              그 전에는 아래 가운데(`bottom: 2rem`)라 **도구막대 단추 글자를 덮었다.**
           ⚠️ `top`·`right` 는 JS 가 잰다 (`seat.js` 의 `placeToast`). 여기 값은 JS 가 돌기 전 자리다.
           ⚠️ 숨길 때 `opacity` 만 내린다 (D109, 화면 읽기). 그래서 **숨은 동안은 누름을 통과**시킨다 —
              안 그러면 보이지 않는 상자가 오른쪽 위 명단 칸의 누름을 먹는다.
           색은 왼쪽 띠로만 가른다: 초록(됐다) · 회색(안내) · 빨강(안 됐다). */
        #toast {
            position: fixed; top: 16px; right: 16px; z-index: 50;
            width: max-content; max-width: min(400px, calc(100vw - 32px));
            padding: 0.7rem 2.6rem 0.7rem 1rem;          /* 오른쪽은 ✕ 자리 */
            background: #fff; color: #263238; border-left: 6px solid #546e7a; border-radius: 10px;
            box-shadow: 0 6px 18px rgba(0,0,0,0.25);
            font-size: 0.95rem; line-height: 1.5; word-break: keep-all; overflow-wrap: anywhere;
            cursor: pointer; opacity: 0; transition: opacity 0.25s;
        }
        #toast:not(.show) { pointer-events: none; }
        #toast.show { opacity: 1; }
        #toast.ok { border-left-color: #2e7d32; }
        #toast.error { border-left-color: #d32f2f; font-weight: bold; }
        #toast .toast-close {
            position: absolute; top: 0.4rem; right: 0.4rem; width: 1.75rem; height: 1.75rem;
            padding: 0; border: none; border-radius: 50%; background: transparent;
            color: #78909c; font-family: inherit; font-size: 0.95rem; font-weight: bold; line-height: 1;
            cursor: pointer;
        }
        #toast .toast-close:hover { background: #eceff1; color: #263238; }

        /* ===== 인쇄용 머리글 (평소엔 숨김) ===== */
        .print-head { display: none; }

        @media print {
            @page { margin: 12mm; }
            body { background: #fff; padding: 0 !important; }
            .appbar, #settings-btn, #settings-dialog, #output-dialog, .toolbar, .side, #toast { display: none !important; }
            .wrap { display: block; }
            .col-main { display: block; }
            .board-stage { border: none; padding: 0; gap: 0.4cm; }

            .print-head { display: block; text-align: center; margin-bottom: 0.4cm; }
            .print-head .t { font-size: 13pt; font-weight: 800; }
            .print-head .d { font-size: 9pt; color: #555; }

            /* 좌석/빈칸은 화면과 똑같이 var(--seat) 를 쓴다(분단 정렬 유지).
               enterPrintMode()가 --seat 를 작은 mm 값으로 바꿔 표만 작아진다. */
            .grid { gap: calc(var(--seat) * 0.14); }
            .seat { border: 1px solid #333; border-radius: 3px; overflow: hidden; }
            /* 🔴 **짚어 둔 칸의 남색 테두리는 인쇄물에 안 나간다** (2026-09-22, [D109]).
                  키보드로 자리를 고치다 그대로 인쇄하면 **그 한 칸만 굵은 테두리**로
                  찍혀, 교탁에 붙은 종이에서 「이 자리가 뭔가 다르다」로 읽힌다. */
            .seat:focus-visible, .chip.unplaced:focus-visible {
                outline: none !important; box-shadow: none !important;
            }
            .seat.disabled { border: 1px dashed #bbb; }
            .seat.fixed { background: #fff; border-color: #333; }
            .seat.fixed::after { content: ""; }
            .s-num { font-size: calc(var(--seat) * 0.36); font-weight: 800; }
            .s-name { font-size: calc(var(--seat) * 0.16); font-weight: 600; color: #000; }

            /* 배경 그래픽 인쇄가 꺼져 있어도 보이도록: 흰 배경 + 검은 글씨 + 테두리 */
            .board {
                width: auto !important; align-self: center;
                background: #fff !important; color: #000 !important;
                border: 2px solid #333; border-radius: 3px;
                font-size: 10pt; font-weight: 800; letter-spacing: 0.3em; padding: 2pt 14pt;
            }
        }

/* ── 잠금 (ROADMAP 「아」, DECISIONS D59) ─────────────────────────────
   칠판에 띄운 화면에는 **표시만** 있고 푸는 단추가 없다. 푸는 자리는
   /seat/locks 다 — 그래야 칠판 앞에서 로그인할 이유 자체가 안 생긴다.

   ⚠️ 여기서 막는 것은 **화면**이다. 진짜 방어는 서버에 있다
      (SeatController::save 가 423 으로 거부한다). 두 겹이다. */
.lock-badge {
    display: inline-flex; align-items: center; gap: 0.3rem;
    background: #fff3e0; color: #e65100;
    border: 1px solid #ffb74d; border-radius: 999px;
    padding: 0.15rem 0.7rem; font-size: 0.85rem; font-weight: bold;
    white-space: nowrap;
}
.lock-badge[hidden] { display: none; }

/* 잠긴 반: **자료를 바꾸는 것**만 흐려진다.
   ⚠️ 보기와 인쇄는 잠겨도 된다. 잠금은 "고치지 못하게" 하는 것이지 "보지 못하게"
      하는 것이 아니다 — 잠긴 반의 자리표를 칠판에 띄우고 인쇄하는 것이 오히려
      잠금을 건 목적에 맞는다. 그래서 `#btn-view` 와 출력 팝업은 그대로 둔다.
      🔴 **`#snap-export`(⬇ 파일로 내보내기)도 같은 줄에 선다** (2026-09-22, [D108]).
         내보내기는 지금 화면을 파일로 뜨는 것이라 **아무것도 바꾸지 않는다.** 흐려 두면
         "잠기면 백업도 못 뜬다" 가 되는데, 그것은 잠금이 막으려던 것이 아니다.

   🔴 **`pointer-events: none` 을 쓰지 않는다** (2026-09-22, [D108]). 그것을 걸면 클릭이
      핸들러까지 가지 못해 **안내까지 같이 죽는다.** 잠긴 반에서 툴바를 누르면 토스트도
      안 떠서 「눌러도 아무 일이 없는 고장」으로 읽혔다. 지금은 눌리되 `seat.js` 의
      `guardLocked` 가 가로채 안내를 띄운다.

   ⚠️ **이 목록은 `seat.js` 의 `LOCKED_CONTROLS` 와 짝이다.** 한쪽만 고치면 「흐린데 눌리는
      칸」이나 「멀쩡해 보이는데 말없이 막힌 칸」이 생긴다 — 둘을 같이 고친다. */
.is-locked .toolbar .btn:not(#btn-view):not(#btn-more),
.is-locked #settings-dialog input,
.is-locked #settings-dialog textarea,
.is-locked #settings-dialog select,
.is-locked #settings-dialog .btn:not(#settings-close):not(#snap-export) {
    opacity: 0.45;
    /* 흐린 것만으로는 "왜 안 되지" 가 남는다. 커서가 먼저 말해 준다. */
    cursor: not-allowed;
}
.is-locked .seat { cursor: default; }
