/* 레이아웃과 공통 컴포넌트. 출처는 docs/mockups/mockup-b-workspace.html.
   화면별 규칙은 해당 화면을 만드는 태스크가 이 파일 끝에 이어 붙인다. */
*{box-sizing:border-box;margin:0;padding:0}
body{
  background:var(--bg);color:var(--text);
  font-family:system-ui,-apple-system,"Segoe UI","Malgun Gothic","Apple SD Gothic Neo",sans-serif;
  font-size:15px;line-height:1.6;font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;
  /* 푸터가 짧은 화면에서도 바닥에 붙도록 세로 flex (2026-08-23). .wrap이 남는 높이를 먹는다.
     100dvh는 iOS 툴바를 뺀 높이 — 없으면 스플릿 아래에 --bg 띠가 생긴다(품질 검토 L-4). */
  min-height:100vh;min-height:100dvh;display:flex;flex-direction:column
}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:0;cursor:pointer}
a{color:var(--brand)}
:focus-visible{outline:0;box-shadow:var(--focus);border-radius:6px}
/* `hidden` 속성은 UA 규칙이라 작성자의 display(grid·flex)가 이긴다 — 어디서든 hidden은 정말로 숨긴다
   (2026-08-23 품질 검토 M-1: 비밀번호 토글이 JS 없이도 그려졌다). */
[hidden]{display:none!important}
/* 고대비 모드는 box-shadow를 지운다. 그림자만 쓰면 포커스 표시가 사라진다. */
@media (forced-colors:active){:focus-visible{outline:2px solid CanvasText}}
svg{display:block}
.icon{width:20px;height:20px;stroke:currentColor;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
.icon-sm{width:16px;height:16px}
.icon-lg{width:26px;height:26px}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* 글로벌 헤더 */
.gnb{position:sticky;top:0;z-index:30;background:var(--surface);border-bottom:1px solid var(--border)}
.gnb-inner{max-width:1240px;margin:0 auto;padding:11px 24px;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:9px;text-decoration:none;color:var(--text);min-height:44px}
/* 홈 버튼 = NTB 로고 + 이름 (2026-08-23). 이름은 제목 글꼴(tokens.css의 서브셋), 폰 폭에서는 숨긴다. */
.brand-logo{width:48px;height:38px;object-fit:contain;display:block}
.brand-name{font-family:"Do Hyeon",system-ui,"Malgun Gothic","Apple SD Gothic Neo",sans-serif;font-weight:400;font-size:1.15rem;letter-spacing:.02em;white-space:nowrap}
.gnb-nav{display:flex;gap:3px}
.gnb-item{padding:9px 14px;border-radius:var(--radius-sm);font-weight:650;font-size:.88rem;color:var(--muted);min-height:44px;display:flex;align-items:center;text-decoration:none;transition:background .18s ease,color .18s ease}
.gnb-item:hover{background:var(--surface-3);color:var(--text)}
.gnb-item[aria-current="page"]{background:var(--brand-soft);color:var(--brand)}
.gnb-right{margin-left:auto;display:flex;gap:8px;align-items:center}
/* 로그인한 회원 (2026-08-22). 이름은 한 줄, 역할은 칩. */
.gnb-user{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--text)}
.gnb-user-name{font-weight:650;max-width:9em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.role-chip{font-size:.72rem;font-weight:700;padding:3px 9px;border-radius:999px;background:var(--brand-soft);color:var(--brand);border:1px solid var(--brand-line);white-space:nowrap}
.gnb-user-link{display:flex;align-items:center;gap:8px;text-decoration:none;color:var(--text);padding:6px 8px;border-radius:var(--radius-sm);min-height:44px}
.gnb-user-link:hover{background:var(--surface-3)}
/* 좁은 화면에서는 숨긴다 — 규칙은 아래 640px 미디어쿼리 블록 안에 있다. **여기에 따로
   `@media`를 만들지 말 것**: `tests/test_lineup_reorder.py`가 파일의 **첫** 640px 블록에서
   `.slot-tail`을 찾는다. */
.gnb-user-hint{font-size:.74rem;color:var(--muted);white-space:nowrap}
.gnb-logout{display:flex;margin:0}
.gnb-logout .btn{padding:8px 12px}

/* ── 로그인 · 가입 · 대기 · 내 계정 (2026-08-22) ── */
/* `.auth-wrap`·`.auth-brand`는 2026-08-23 로그인 화면 개편으로 빠졌다 — 로그인 전 화면은 auth.css의 스플릿. */
.auth-fields{display:flex;flex-direction:column;gap:13px}
.auth-actions{flex-direction:column}
.btn-block{width:100%;justify-content:center}
.auth-foot{margin-top:14px}
.account-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px;align-items:start}

/* ── 읽기 전용 게이팅 (2026-08-22 로그인·역할) ── */
/* 참석 그리드의 정적 카드. 토글 버튼과 같은 모양이되 눌리지 않는다. */
.att-static{cursor:default}
.att-static .mini{margin-left:auto;flex:none}
.lineup-readonly-note{display:inline-flex;align-items:center;gap:6px;font-size:.82rem;color:var(--muted);min-height:44px}
/* `inert`는 상호작용만 끈다 — 눈에도 잠겼다고 말해 준다. */
.lineup-grid[inert]{opacity:.85}
.lineup-grid[inert] .slot-x,.lineup-grid[inert] .slot-move,.lineup-grid[inert] .plan-add-btn,.lineup-grid[inert] .pool-p{display:none}
.readonly-info dl{display:grid;grid-template-columns:max-content 1fr;gap:8px 16px;margin:0}
.readonly-info dt{color:var(--muted);font-size:.82rem;font-weight:650}
.readonly-info dd{margin:0}

/* ── 설정 관리 블록 · 회원 관리 · 감사 로그 (2026-08-22) ── */
.admin-links{display:flex;flex-direction:column;gap:6px}
.admin-link{display:flex;align-items:center;gap:14px;padding:12px 14px;border-radius:var(--radius-sm);text-decoration:none;color:var(--text);min-height:48px}
.admin-link:hover{background:var(--surface-3)}
.admin-link>span{display:flex;flex-direction:column;gap:2px}
.admin-link-desc{font-size:.78rem;color:var(--muted)}
.admin-link .mini{margin-left:auto}
.member-actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.member-approve{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
/* ── 역할 여럿 고르기 (2026-08-24 사용자 결정) ──────────────────────────────
   `<select>` 하나였던 자리다. `fieldset`은 기본 테두리·여백을 갖고 있어 표 칸
   안에서 상자가 두 겹으로 보이므로 전부 지운다 — 묶는 것은 낭독기를 위한
   것이지 그림을 위한 것이 아니다(`legend`가 `.visually-hidden`인 이유).

   **라벨이 곧 히트 영역이다.** 체크 글리프는 18px이지만 `<label>`이 44px를
   지키므로 손가락은 이름 어디를 눌러도 닿는다 — 선수단 필터 칩이 쓰는 것과
   같은 구조이고, `scripts/mobile_check.py`가 `label` 안의 `<input>`을 44px
   검사에서 빼 두는 근거이기도 하다. */
.role-picks{display:flex;gap:4px;flex-wrap:wrap;border:0;margin:0;padding:0;min-width:0}
.role-pick{
  display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:0 10px;
  border:1px solid var(--border);border-radius:999px;background:var(--surface);
  font-size:.8rem;font-weight:700;color:var(--muted);cursor:pointer;white-space:nowrap;
  transition:background .15s ease,border-color .15s ease,color .15s ease
}
.role-pick:hover{border-color:var(--brand);color:var(--text)}
/* 고른 역할은 알약이 채워진다. `:has()`를 못 쓰는 브라우저에서도 체크 글리프
   자체는 보이므로 상태가 사라지지는 않는다 — 색은 보조 신호다. */
.role-pick:has(input:checked){background:var(--brand-soft);border-color:var(--brand-line);color:var(--brand)}
.role-pick:has(input:focus-visible){outline:none;box-shadow:var(--focus)}
.role-pick input{width:17px;height:17px;accent-color:var(--brand);flex:none}
.log-table select{min-height:44px;padding:6px 8px;border-radius:var(--radius-sm);border:1px solid var(--border-strong);background:var(--surface)}
tr.is-disabled .log-name{color:var(--faint)}
.temp-password{font-size:1.1rem;font-weight:800;letter-spacing:.08em;-webkit-user-select:all;user-select:all;background:var(--surface);padding:2px 8px;border-radius:6px;border:1px solid var(--border-strong)}
.audit-table td{font-size:.82rem;vertical-align:top}
.audit-at,.audit-ip{white-space:nowrap;color:var(--muted)}
.audit-action{font-size:.78rem}

/* ── 화면 맨 위 기본 팀 칩 (2026-08-18 사용자 요청 ①) ────────────────────
   브랜드 옆이라 어느 화면에서도 "지금 어느 팀인가"가 보인다. 드롭다운은 팀이
   둘 이상일 때만 그려진다(`gnb_team.html`).

   `[x-cloak]`이 여기 있는 이유: 이 칩은 `base.html`의 GNB에 있어 **모든**
   화면에서 Alpine이 붙기 전 한 프레임 동안 목록이 펼쳐진 채 보인다. 워크스페이스
   페이지에만 있던 인라인 규칙(`game_workspace.html`)은 다른 화면에 닿지 않는다. */
[x-cloak]{display:none!important}

.gnb-team{display:flex;align-items:center}
.team-switch{position:relative}
/* 44px 터치 타깃 (설계 스펙 §13). 이름이 길면 줄이지 않고 잘라 낸다 — 헤더가
   두 줄로 늘어나면 그 아래 모든 화면의 첫 화면 높이가 달라진다. */
.team-chip{
  display:inline-flex;align-items:center;gap:7px;min-height:44px;padding:0 12px;
  border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--surface-2);color:var(--text);font-weight:750;font-size:.86rem;
  cursor:pointer;transition:background .16s ease,border-color .16s ease,color .16s ease
}
.team-chip:hover{background:var(--brand-soft);border-color:var(--brand);color:var(--brand)}
.team-chip:focus-visible{outline:none;box-shadow:var(--focus)}
.team-chip-name{max-width:11em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 바꿀 곳이 없는 칩은 버튼처럼 보이지 않아야 한다 — 눌러도 아무 일이 없는
   컨트롤로 보이는 것이 이 앱이 되풀이해 고쳐 온 결함이다. */
.team-chip.is-static{cursor:default;background:transparent}
.team-chip.is-static:hover{background:transparent;border-color:var(--border);color:var(--text)}
.team-chip.is-empty{text-decoration:none;color:var(--muted);border-style:dashed}
.team-chip.is-empty:hover{color:var(--brand)}
.team-caret{color:var(--faint);flex:none}
.team-chip:hover .team-caret{color:var(--brand)}

/* 메뉴는 칩보다 넓을 수 있다. 왼쪽에 매다는 것은 칩이 헤더 **왼쪽**에 있어서다
   — 시간 팝오버(`.time-pop`)가 오른쪽에 매달린 것과 반대이고, 이유도 반대다. */
/* **숨김이 기본값이다.** `x-show`(인라인 `display:none`)로 숨기면 htmx가 OOB
   조각의 id 같은 요소를 settle하면서 그 인라인 스타일을 지워, 새로 온 메뉴가
   펼쳐진 채 화면을 덮는다(브라우저 실측 — `gnb_team.html`의 주석에 사정이 있다).
   여는 쪽을 클래스로 두면 무엇이 지워지든 닫힌 쪽으로 실패한다. */
.team-menu{
  position:absolute;z-index:40;top:calc(100% + 6px);left:0;min-width:100%;max-width:280px;
  padding:6px;border:1px solid var(--border-strong);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow);display:none;flex-direction:column;gap:2px
}
.team-menu.is-open{display:flex}
.team-opt{
  display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:0 10px;
  border:0;border-radius:var(--radius-sm);background:transparent;color:var(--text);
  font-size:.86rem;font-weight:650;text-align:left;cursor:pointer;
  transition:background .16s ease,color .16s ease
}
.team-opt:hover{background:var(--surface-3)}
.team-opt:focus-visible{outline:none;box-shadow:var(--focus)}
/* 지금 기본인 팀은 `<span>`이라 커서를 바꾸지 않는다. 체크 표시와 빈 자리의
   폭이 같아야 목록이 좌우로 흔들리지 않는다. */
.team-opt.on{color:var(--brand);font-weight:800;cursor:default}
.team-opt.on:hover{background:transparent}
.team-opt-tick{width:16px;height:16px;flex:none}
.team-menu-foot{
  display:flex;align-items:center;gap:8px;min-height:44px;padding:0 10px;margin-top:4px;
  border-top:1px solid var(--border);border-radius:0 0 var(--radius-sm) var(--radius-sm);
  color:var(--muted);font-size:.82rem;font-weight:650;text-decoration:none
}
.team-menu-foot:hover{color:var(--brand)}
.icon-btn{width:44px;height:44px;border-radius:10px;display:grid;place-items:center;border:1px solid var(--border);background:var(--surface-2);color:var(--muted);transition:background .18s ease,color .18s ease}
.icon-btn:hover{background:var(--surface-3);color:var(--text)}

/* 페이지 골격 */
.wrap{max-width:1240px;width:100%;margin:0 auto;padding:24px;display:flex;flex-direction:column;gap:20px;flex:1 0 auto}

/* 하단 푸터 (2026-08-23). 조각은 partials/footer.html. 인쇄 오더지는 base.html을 안 쓰지만 한 번 더 막는다. */
.site-footer{border-top:1px solid var(--border);background:var(--surface-2);color:var(--faint);font-size:.78rem}
.site-footer-in{max-width:1240px;margin:0 auto;padding:14px 24px;display:flex;justify-content:space-between;align-items:center;gap:6px 16px;flex-wrap:wrap}
.site-footer-copy{font-variant-numeric:tabular-nums}
@media (max-width:720px){ .site-footer-in{justify-content:center;text-align:center;padding:12px 16px} }
@media print{.site-footer{display:none}}
.page-title{font-size:1.5rem;font-weight:800;letter-spacing:-.02em}
.page-desc{color:var(--muted);font-size:.88rem;margin-top:3px}
.page-head{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap}
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow-sm)}
.card-head{display:flex;align-items:center;gap:10px;padding:16px 18px;border-bottom:1px solid var(--border);flex-wrap:wrap}
.card-head h2{font-size:1rem;font-weight:750}
.card-head .sub{font-size:.8rem;color:var(--muted)}
.card-body{padding:18px}

/* 버튼
   `justify-content:center`가 **base에** 있어야 한다. `.btn`은 글자 크기로 서므로
   보통은 아무 차이가 없지만, 상자가 내용보다 넓어지는 순간(`min-width`·`width:100%`·
   grid/flex 늘어남) 글자가 왼쪽 모서리에 붙는다 — 기본값이 `flex-start`라서다.
   2026-08-24 실측: `.gc-edit`(76px 상자에 글자 28.2px)이 중앙에서 **9.8px 왼쪽**,
   ③ 라인업 저장 막대의 `오더지 확정`(311px)이 **205px 왼쪽**, `초기화`(151px)가
   **77px 왼쪽**이었다. 사용자가 "센터도 안 맞는다"고 말한 것이 이것이다.
   `.btn-block`·`.form-actions .btn`·`.log-table … .btn` 셋이 이미 각자
   `justify-content:center`를 덧붙이고 있었다 — 같은 결함을 세 번 손으로 때운
   자국이고, 넷째(`.gc-edit`)에서 새어 나왔다. 그래서 자리에서 고친다. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:9px 15px;border-radius:var(--radius-sm);font-weight:650;font-size:.86rem;min-height:44px;white-space:nowrap;border:1px solid transparent;text-decoration:none;transition:background .18s ease,border-color .18s ease,color .18s ease}
.btn-primary{background:var(--brand);color:var(--brand-ink)}
.btn-primary:hover{filter:brightness(1.08)}
.btn-ghost{background:var(--surface-2);border-color:var(--border-strong);color:var(--text)}
.btn-ghost:hover{background:var(--surface-3)}
.btn-accent{background:var(--accent-soft);color:var(--accent)}
.btn-danger{background:var(--danger-soft);color:var(--danger)}
.btn-lg{padding:12px 20px;min-height:48px;font-size:.92rem}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* 폼. 래퍼 이름은 `.form-field`다 — `.field`는 Task 9의 수비 배치도가 쓴다.
   같은 이름을 쓰면 폼 래퍼가 aspect-ratio·overflow:hidden을 뒤집어쓰고,
   `.field select`(0,1,1)가 배치도의 `.fn-pick`(0,1,0)을 눌러 버린다. */
.form-field{display:flex;flex-direction:column;gap:5px}
.form-field label{font-size:.8rem;font-weight:650;color:var(--muted)}
.form-field input,.form-field select,.form-field textarea{
  padding:9px 11px;border-radius:var(--radius-sm);border:1px solid var(--border-strong);
  background:var(--surface);min-height:44px;width:100%
}
.form-field textarea{min-height:88px;resize:vertical}
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:13px}
.form-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:16px}

/* 배너 */
.alert{display:flex;gap:11px;align-items:flex-start;padding:13px 16px;border-radius:var(--radius-sm);font-size:.85rem;border:1px solid transparent}
.alert-info{background:var(--brand-soft);border-color:var(--brand-line)}
.alert-warn{background:var(--warn-soft);border-color:color-mix(in srgb,var(--warn) 35%,transparent)}
.alert-danger{background:var(--danger-soft);border-color:color-mix(in srgb,var(--danger) 35%,transparent)}
.note{font-size:.78rem;color:var(--faint);margin-top:11px;line-height:1.55}
.empty{padding:40px 24px;text-align:center;color:var(--faint);font-size:.88rem}
/* `position:relative`가 없으면 안쪽의 `.visually-hidden`이 이 상자를 빠져나간다.
   그것들은 `position:absolute`인데 이 상자가 static이면 컨테이닝 블록이 바깥이
   되어 **overflow 클리핑을 받지 않는다.** 표는 min-width 때문에 640px이고,
   마지막 `<th>`의 숨은 '관리' 글자가 그 끝에 놓이므로 문서 폭이 그만큼 늘어난다
   — 375px에서 본문 전체가 가로로 158px(선수단은 247px) 밀렸다. 표만 자기 상자
   안에서 스크롤되어야 한다.

   브라우저에서 `window.scrollTo(400,0)` 뒤 `window.scrollX`를 재서 찾았다.
   `overflow-x:auto`가 걸려 있으니 됐다고 읽기 쉬운데, 클리핑은 컨테이닝 블록이
   이 상자일 때만 듣는다. */
.table-scroll{overflow-x:auto;position:relative}

@media (max-width:720px){
  .wrap{padding:16px}
  .gnb-inner{padding:10px 16px;gap:10px}
  .gnb-nav{order:3;width:100%;overflow-x:auto}
  .brand-name{display:none}  /* 폰 폭에서는 로고만 — 링크의 aria-label이 이름을 맡는다 */
}
@media (prefers-reduced-motion:reduce){ *{transition:none!important} }

/* ── 경기 목록 보드 (Task 3) ── */
.game-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:14px}
.game-card{
  position:relative;
  text-align:left;width:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  padding:18px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:13px;text-decoration:none;color:inherit;
  transition:border-color .18s ease,box-shadow .18s ease,background .18s ease
}
.game-card:hover{border-color:var(--brand);box-shadow:var(--shadow)}
.game-card.next{border-color:var(--brand);background:linear-gradient(180deg,var(--brand-soft),transparent 55%)}
/* 취소된 경기 (2026-08-18 요청). 지난 경기처럼 뒤로 물리되 **테두리를 파선으로**
   바꿔 색 말고도 신호를 남긴다 — 취소는 '지나간 것'과 다른 상태이고, `.past`와
   배경만 같으면 흑백 인쇄나 색각 이상에서 구분되지 않는다(스킬 규칙: Color is
   not the only indicator). `.sp-pick.is-warn`이 같은 처방을 쓴다.
   `opacity`는 여기서도 쓰지 않는다 — 바로 아래 `.past` 주석의 실측이 이 카드에
   그대로 적용된다. 실제 신호는 위쪽의 '취소' 칩이다. */
.game-card.cancelled{background:var(--surface-2);border-style:dashed;border-color:var(--border-strong)}
.game-card.cancelled:not(:hover){box-shadow:none}
/* 지난 경기는 배경을 한 단 낮추고 그림자를 지워 뒤로 물린다 — 다가올 카드는
   흰(어두운 테마에선 밝은) 면이 떠 있고, 지난 카드는 페이지 쪽으로 가라앉은
   평평한 면이다.
   `opacity`(와 `filter`)를 쓰지 말 것. 그 두 속성은 카드 **하위 트리 전체**를
   페이지 배경과 합성해서 글자만 배경 쪽으로 끌어당긴다. 칩 배경은 이미
   --bg에 가까워 거의 움직이지 않으므로 대비만 무너진다. opacity:.86일 때
   실측으로 라이트에서 .mini 3.52 / .mini.ok 3.63 / .mini.warn 3.70 /
   .gc-cta 3.94로 스펙 §13의 본문 4.5:1 아래였다. 이관된 21경기가 전부
   지난 경기라 이건 예외가 아니라 보드의 기본 화면이다. */
.game-card.past{background:var(--surface-2)}
.game-card.past:not(:hover){box-shadow:none}
.gc-top{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.gc-date{font-size:1.12rem;font-weight:800;letter-spacing:-.02em}
.gc-dday{font-size:.72rem;font-weight:800;padding:3px 9px;border-radius:999px;background:var(--brand);color:var(--brand-ink)}
.gc-opp{font-size:.95rem;font-weight:700}
.gc-meta{font-size:.8rem;color:var(--muted);display:flex;gap:12px;flex-wrap:wrap}
.gc-steps{display:flex;gap:6px;flex-wrap:wrap}
/* `.mini`·`.mini.ok`·`.mini.warn`은 전역 이름이다. 뒤 태스크가 다른 화면의
   작은 배지를 만들 때 이 이름을 다시 정의하면 보드의 준비 상태 칩 색이 전부
   조용히 바뀐다 — 이 카드에서 초록(`ok`)은 '된 것'이라는 뜻을 갖고 있어서
   색이 바뀌면 의미가 무너진다. 새 배지는 새 이름을 쓸 것.
   (`.form-field` vs `.field`와 같은 종류의 충돌이다. 위 폼 주석 참조.) */
.mini{display:flex;align-items:center;gap:5px;font-size:.73rem;font-weight:700;padding:4px 9px;border-radius:999px;background:var(--surface-3);color:var(--faint)}
.mini.ok{background:var(--brand-soft);color:var(--brand)}
.mini.warn{background:var(--accent-soft);color:var(--accent)}
/* CTA 줄에는 '카드 열기' 링크와 '수정' 버튼이 나란히 선다.
   카드를 `<a>`로 감싸지 않는 이유는 game_card.html 맨 위 주석에 있다 —
   중첩 인터랙티브 요소는 무효한 마크업이고 클릭이 바깥 링크로 새어 나간다.
   그래서 링크는 이 한 줄에만 두고, `::after`로 카드 전체를 덮어 "카드 아무
   데나 눌러도 열린다"를 유지한다(stretched link). */
.gc-cta{display:flex;align-items:center;gap:8px;margin-top:auto}
.gc-open{display:inline-flex;align-items:center;gap:6px;font-size:.85rem;font-weight:700;color:var(--brand);text-decoration:none}
.gc-open::after{content:"";position:absolute;inset:0;z-index:1;border-radius:var(--radius)}
/* 덮개가 카드를 가리므로 포커스 링도 덮개에 그린다 — 링크만 반짝이면
   무엇이 선택됐는지 보이지 않는다 (스펙 §13). */
.gc-open:focus-visible{outline:none}
.gc-open:focus-visible::after{outline:2px solid var(--brand);outline-offset:-2px}
/* 덮개(z-index:1)보다 위에 있어야 자기 클릭을 자기가 받는다.
   ── 2026-08-24 사용자 요청 ① 뒷부분: *"경기 화면에서 수정 버튼을 좀 더 크게"* ──
   375px에서 실측 53.6×44였다. 높이는 이미 44를 지켰지만 **폭이 문제였다** —
   카드 전체가 눌리는 `.gc-open` 덮개 위에 얹힌 53px짜리 표적이라, 빗나가면
   수정이 아니라 워크스페이스가 열린다. 그 오작동은 조용하다(엉뚱한 화면이
   열릴 뿐 오류가 안 난다). 그래서 폭을 키우는 것이 이 요청의 핵심이다.
   `min-width`로 잡는 이유: `padding`만 키우면 글자 두 자에 맞춰 다시 좁아진다.
   ── 2026-08-24 사용자 요청: *"수정 버튼이 센터도 안 맞고 어색하다"* ──
   그때 넓힌 `min-width:76px`가 **글자를 왼쪽에 남겨 둔 채로** 상자만 키웠다
   (실측 왼쪽 여백 19px · 오른쪽 28.8px). 가운데 맞춤은 `.btn` base에서 고쳤고
   (위 '버튼' 주석), 여기서는 **상자를 내용으로 채운다** — 연필 아이콘을 넣어
   16+6+28.2+28(패딩)+2(테두리) = 80.2px가 자연 폭이 되게 하고 `min-width`를
   거기에 맞춘다. 글자 두 자가 빈 상자 안에서 떠 있던 것이 "어색해"의 나머지
   절반이다. 76 → 80은 앞 요청의 하한(76)을 지킨다. */
.gc-edit{
  position:relative;z-index:2;margin-left:auto;
  min-width:80px;min-height:48px;padding:0 14px;gap:6px;font-size:.88rem;font-weight:700;
  /* 채움을 카드보다 **밝은** 쪽으로 뒤집는다. `.btn-ghost`의 --surface-2는 흰
     카드에서는 거의 안 보이고, `.past`·`.cancelled` 카드에서는 카드 배경과
     **같은 값**이라 테두리만 남으며, `.next` 카드(brand-soft 그라디언트) 위에서는
     초록 면에 붙은 회색 헝겊이 된다 — 같은 버튼이 카드마다 다르게 보이는 것이
     "어색해"의 절반이다. --surface는 셋 모두에서 카드 위로 뜬다. */
  background:var(--surface);border-color:var(--border-strong);color:var(--muted)
}
/* 아이콘이 줄어들면 연필이 찌그러진다. `.btn{white-space:nowrap}`은 글자만 지킨다. */
.gc-edit .icon{flex:none}
/* `.btn-ghost:hover`(--surface-3)를 덮는다. 명시도가 같으므로 **이 파일에서 뒤에
   있는 것**이 이긴다 — 위로 옮기면 조용히 진다. */
.gc-edit:hover{background:var(--brand-soft);border-color:var(--brand-line);color:var(--brand)}
/* 날짜를 읽을 수 없어 워크스페이스를 열 수 없는 경기. '수정'만 달고 목록 맨
   위에 선다 — 이 카드가 그 경기를 고치는 유일한 문이다(broken_game_card.html).
   `.gc-open` 덮개가 없으므로 `.gc-edit`의 `margin-left:auto`가 버튼을 오른쪽
   끝으로 보내지 않도록 왼쪽에 붙인다. */
.game-card.broken{border-color:var(--danger)}
.game-card.broken .gc-edit{margin-left:0}
.gc-broken-value{font-size:.73rem;font-weight:700;padding:4px 9px;border-radius:999px;background:var(--surface-3);color:var(--muted)}

/* ── 경기 폼 (Task 4) ── */
#game-form-slot:empty{display:none}
/* 오류 상자는 화면마다 다른 id를 쓴다(`hx-error-target`이 인자다). 간격 규칙을
   id에 걸면 새 화면이 상자를 만들 때마다 배너가 카드에 딱 붙어 나온다 —
   워크스페이스의 `#info-error`가 실제로 그랬다. 그래서 클래스로 건다.
   `:not(:empty)`인 이유: 비어 있는 상자가 자리를 차지하면 안 된다. */
.form-error:not(:empty){margin-top:13px}

/* base.html의 오류 훅이 오류 상자를 찾지 못했을 때 쓰는 마지막 수단.
   원래 hx-target(성공 조각 자리)에 배너를 넣을 수는 없으므로 화면 위에
   띄운다. 조용히 아무것도 하지 않으면 사용자는 버튼이 죽은 줄 안다. */
#error-flash{
  position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:60;
  max-width:min(520px,calc(100vw - 32px));box-shadow:var(--shadow)
}
#error-flash .alert{background:var(--surface);border-color:var(--danger)}

/* ── 워크스페이스 셸 (Task 5) ── */
.ws-head{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-sm)}
.ws-top{padding:18px 20px;display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap;border-bottom:1px solid var(--border)}
.ws-back{display:flex;align-items:center;gap:6px;font-size:.82rem;color:var(--muted);font-weight:650;margin-bottom:6px;text-decoration:none}
.ws-back:hover{color:var(--brand)}
.ws-title{font-size:1.42rem;font-weight:800;letter-spacing:-.02em;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.ws-badge{font-size:.72rem;font-weight:800;padding:4px 10px;border-radius:999px;background:var(--brand-soft);color:var(--brand);border:1px solid var(--brand-line)}
/* 이미 지나간 경기의 상태 배지. 브랜드색은 '지금 챙길 경기'를 뜻하므로
   (목업의 `D-2 · 준비 중`) 끝났거나 지나간 경기까지 같은 색으로 칠하면 그
   신호가 사라진다. 새 토큰을 만들지 않고 있는 것으로 낮춘다. */
.ws-badge.muted{background:var(--surface-3);color:var(--muted);border-color:var(--border)}
.ws-meta{color:var(--muted);font-size:.86rem;margin-top:5px;display:flex;gap:14px;flex-wrap:wrap}
/* Task 14가 '경기 종료' 버튼을 여기 넣는다. 지금은 비어 있어 폭을 차지하지 않는다. */
.ws-actions{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}

.stepper{display:flex;overflow-x:auto;background:var(--surface-2)}
.step-tab{
  flex:1;min-width:150px;min-height:44px;display:flex;align-items:center;gap:10px;padding:14px 16px;
  border-right:1px solid var(--border);color:var(--muted);text-align:left;position:relative;
  transition:background .18s ease,color .18s ease
}
.step-tab:last-child{border-right:0}
.step-tab:hover:not([aria-disabled="true"]){background:var(--surface-3);color:var(--text)}
.step-tab[aria-selected="true"]{background:var(--surface);color:var(--text)}
.step-tab[aria-selected="true"]::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;background:var(--brand)}
/* 비활성 탭에 `opacity`(와 `filter`)를 쓰지 말 것 — 하위 트리 전체를 배경과
   합성해서 글자만 배경 쪽으로 끌어당긴다. `.game-card.past{opacity:.86}`이
   Task 3에서 보드 전체를 대비 미달로 만든 것과 같은 결함이고, ⑤ 탭은 끝나지
   않은 모든 경기에서 비활성이라 예외가 아니라 기본 화면이다. 실측으로
   opacity:.5일 때 --muted가 라이트 2.30 / 다크 3.06, `.st-sub`의 --faint가
   2.00 / 2.31로 스펙 §13의 4.5:1을 크게 밑돌았다 (0.7까지 낮춰도 미달이다).
   그래서 흐리게 하는 대신 글자를 한 단 옅은 **토큰**으로 바꾸고(--faint,
   4.95 / 5.61) 번호 고리의 테두리만 낮춘다. 마우스 신호는 not-allowed와
   hover 없음이 맡는다. */
.step-tab[aria-disabled="true"]{cursor:not-allowed;color:var(--faint)}
.step-tab[aria-disabled="true"] .st-no{border-color:var(--border)}
.st-no{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font-size:.76rem;font-weight:800;border:2px solid var(--border-strong);color:var(--faint);flex:none}
.step-tab.done .st-no{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.step-tab[aria-selected="true"] .st-no{border-color:var(--brand);color:var(--brand)}
.st-txt{min-width:0}
.st-name{display:block;font-size:.86rem;font-weight:700;white-space:nowrap}
.st-sub{display:block;font-size:.73rem;color:var(--faint);white-space:nowrap;line-height:1.3}
#ws-panels{display:flex;flex-direction:column;gap:20px}

@media (max-width:720px){ .ws-actions{margin-left:0;width:100%} }

/* ── 참석 관리 (Task 6) ── */
.att-bulk{display:flex;gap:8px;margin-left:auto;flex-wrap:wrap}
.att-summary{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px}
.att-stat{flex:1;min-width:130px;background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:13px 15px}
.att-stat .k{font-size:.76rem;color:var(--muted);font-weight:650}
.att-stat .v{font-size:1.55rem;font-weight:800;letter-spacing:-.02em;line-height:1.2;margin-top:3px}
.att-stat.ok .v{color:var(--brand)}
.att-stat.warn .v{color:var(--accent)}
.att-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:8px}
.att-item{
  display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--border);
  border-radius:var(--radius-sm);background:var(--surface-2);min-height:56px;
  transition:border-color .16s ease,background .16s ease
}
.att-item:hover{border-color:var(--border-strong)}
.att-item.on{border-color:var(--brand-line);background:var(--brand-soft)}
.att-toggle{display:flex;align-items:center;gap:10px;flex:1;min-width:0;text-align:left;min-height:44px}
/* 테두리가 '아직 안 찍힌 것'을 알아보는 **유일한** 시각 단서다. 목업의
   --border-strong는 --surface-2 위에서 라이트 1.42:1 · 다크 1.67:1로 WCAG
   1.4.11(비텍스트 3:1)에 한참 못 미친다. --muted는 7.24 / 8.67이다. */
.att-check{width:22px;height:22px;border-radius:6px;border:2px solid var(--muted);flex:none;display:grid;place-items:center;color:transparent}
.att-item.on .att-check{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.att-txt{min-width:0}
.att-name{display:block;font-weight:700;font-size:.88rem}
.att-sub{display:block;font-size:.74rem;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 체크된 카드의 배경은 --brand-soft다. 거기서 --faint는 다크 4.25:1로 스펙 §13의
   4.5:1에 못 미친다(0.74rem이라 큰 글씨 예외도 없다). 참석은 예외 상태가 아니라
   이 화면의 목표 상태이므로 미달인 채로 둘 수 없다 — 한 단계 진한 --muted로
   올린다(라이트 6.90 · 다크 6.56). tests/test_layout.py의 배경 목록 참조. */
.att-item.on .att-sub{color:var(--muted)}
/* 불참·부상. 미응답과 완전히 같은 카드였어서 드롭다운 글자를 읽어야만 구분됐다 —
   23장을 훑는 조작에서는 사실상 안 보인다. 배경은 --surface-3을 쓴다(이 배경의
   본문 대비는 tests/test_layout.py가 이미 재고 있다: --faint 4.61 / 4.82,
   --muted 6.74 / 7.46). 체크 칸의 붉은 테두리가 '빠짐'을 말한다 — --danger는
   --surface-3 위에서 5.76 / 5.68로 비텍스트 3:1을 넘는다. */
.att-item.out{background:var(--surface-3)}
.att-item.out .att-check{border-color:var(--danger);background:var(--danger-soft)}
.att-item.out .att-name{color:var(--muted)}
/* 최소 44px — 설계 스펙 §13. 목업과 계획서의 36px은 그 아래였다. */
.att-state{font-size:.75rem;padding:5px 7px;border-radius:8px;border:1px solid var(--border-strong);background:var(--surface);min-height:44px;flex:none}

/* ── 표 (Task 16에서 앞당겨 옴) ─────────────────────────────────────────────
   선수 목록(Task 17)과 선수별 기록(Task 16)이 함께 쓴다. Task 17을 Task 7
   다음으로 당기면서 이쪽이 먼저 필요해졌다 — 계획서 §3의 재배치 참조.
   min-width 때문에 좁은 화면에서 표가 넘치므로 반드시 .table-scroll 안에 둔다. */
.log-table{width:100%;border-collapse:collapse;font-size:.85rem;min-width:640px}
.log-table th,.log-table td{padding:9px 11px;border-bottom:1px solid var(--border);text-align:left;vertical-align:middle}
/* 머리말과 이름은 접지 않는다. 컬럼이 하나 늘자(장단점 · 특이사항, 2026-08-18
   요청 ③) 375px에서 `min-width:640px`를 6열이 나눠 갖게 되어 '권용현'이 두
   줄로, '등번호'가 세 줄로 접혔다. 표는 `.table-scroll` 안에서 가로로
   스크롤되므로 넓어져도 문서가 밀리지 않는다 — 접히는 쪽이 손해다. */
.log-table th{font-size:.76rem;font-weight:700;color:var(--muted);background:var(--surface-2);white-space:nowrap}
.log-name{font-weight:700;white-space:nowrap}

/* ── 소제목 (Task 8에서 앞당겨 옴 — 라인업 편집기와 선수 폼이 함께 쓴다) ── */
.sub-head{font-size:.82rem;font-weight:750;color:var(--muted);margin:18px 0 9px}

/* ── 선수단 (Task 17) ────────────────────────────────────────────────────── */
/* 비활동 선수 행. `opacity`를 쓰지 말 것 — 이 파일의 `.game-card.past` 위
   주석에 실측이 있다. opacity는 행 하위 트리 전체를 페이지 배경과 합성해서
   글자만 배경 쪽으로 끌어당긴다. `.86`에서 이미 3.5:1대로 떨어졌으니 계획서
   초안의 `.55`는 본문이 읽히지 않는다. 지난 경기 카드와 같은 처방을 쓴다 —
   배경을 한 단 낮추고, 흐림은 이름 색으로만 낸다(--muted는 --surface-2 위에서
   라이트 7.24 · 다크 8.67). 실제 신호는 옆 칸의 '비활동' 칩이다. */
.log-table tr.inactive{background:var(--surface-2)}
.log-table tr.inactive .log-name{color:var(--muted);font-weight:650}

/* 검색 · 소화 포지션 필터 (2026-08-18 사용자 요청).
   **두 줄이다** — 위는 검색칸 + '초기화'(`.filter-line`), 아래는 포지션 칩
   열 개(`.filter-chips`). 칩을 검색칸과 한 줄에 세우면 1280px에서도 줄이
   넘치고, 넘친 칩이 '초기화'를 밀어낸다.
   `flex-wrap`이 요점이다 — 375px에서 한 줄에 서면 카드가 넘치는데, 표와 달리
   이 줄은 `.table-scroll` 바깥이라 **문서 전체가 가로로 밀린다.**
   `.table-scroll` 위 주석의 사고와 같은 자리다.
   높이·테두리·모서리는 `.form-field input`이 이미 정한다(44px, 스펙 §13).
   여기서 다시 쓰지 않는다 — 두 벌이 되면 폼의 터치 타깃을 고칠 때 이 줄만
   조용히 뒤처진다.
   `min-width:0`이 없으면 flex 항목의 기본 `min-width:auto` 때문에 검색칸이
   200px 아래로 줄지 못해 위의 넘침이 그대로 난다.
   `max-width`가 없으면 1280px에서 검색칸이 880px까지 늘어난다 — 이름 두세
   글자를 받는 칸이 화면 폭을 다 먹고 '초기화'만 오른쪽 끝으로 밀려 한 줄이
   텅 빈다. 375px에서는 flex-basis 200px가 먼저 걸려 이 값이 닿지 않는다. */
.filter-bar .form-field{flex:1 1 200px;min-width:0;max-width:360px}
/* '초기화'는 OOB로 갈아 끼워지는 상자 안에 산다. `display:contents`라 링크가
   flex 항목으로 직접 서고, 필터가 없어 상자가 비었을 때 gap을 만들지 않는다
   (`#player-form-slot:empty`과 같은 목적이되 Jinja가 남기는 공백에 걸리지
   않는다 — `:empty`는 공백 하나에도 깨진다). */
.filter-reset-slot{display:contents}
.filter-reset{flex:none}
/* --faint on --surface: 라이트 5.03 · 다크 4.79 (tests/test_layout.py의 행렬). */
.roster-count{font-size:.82rem;color:var(--faint);margin:0 0 10px}
.roster-count b{color:var(--text);font-weight:750}
/* 결과가 없을 때의 '전체 명단 보기'. `.empty`가 가운데 정렬이라 버튼이 글에
   붙어 나오는 것을 띄운다. */
.empty .btn{margin-top:12px}

/* ── 포지션 필터 칩 (2026-08-18 요청 ⓪) ───────────────────────────────────
   `.pos-chip`(선수 폼의 체크박스 칩)과 **일부러 따로 둔다.** 저쪽은 진짜
   체크박스를 품고 있어 `accent-color`가 상태를 말하지만, 이쪽은 라디오를
   숨기고 칩 자체가 상태를 그린다 — 같은 규칙을 공유하면 한쪽을 고칠 때
   다른 쪽의 상태 표시가 조용히 사라진다.

   라디오를 `display:none`으로 감추지 말 것. 그러면 포커스를 받을 수 없어
   화살표 키 이동과 포커스 링이 통째로 죽는다. 화면에서만 지우고(`clip-path`
   대신 `position:absolute`+`opacity:0`) 포커스는 살려 둔 뒤, 링은 칩에
   `:has(:focus-visible)`로 그린다. */
.filter-bar{display:flex;flex-direction:column;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.filter-line{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap}
.filter-chips{display:flex;gap:8px;flex-wrap:wrap;align-items:center;border:0;padding:0;margin:0;min-width:0}
.filter-legend{font-size:.8rem;font-weight:650;color:var(--muted);padding:0;margin-right:4px}
.filter-chip{
  display:inline-flex;align-items:center;position:relative;padding:8px 14px;border-radius:999px;
  border:1px solid var(--border-strong);background:var(--surface-2);font-size:.83rem;
  font-weight:650;cursor:pointer;min-height:44px;
  transition:background .18s ease,border-color .18s ease,color .18s ease
}
.filter-chip input{position:absolute;width:1px;height:1px;opacity:0;margin:0}
.filter-chip:hover{background:var(--surface-3);border-color:var(--brand)}
/* 고른 칩은 색만으로 말하지 않는다 — 테두리 굵기와 글자 두께도 함께 바뀐다
   (스킬 규칙: Color is not the only indicator). 낭독기에는 라디오의
   `checked`가 그대로 읽힌다. */
.filter-chip:has(input:checked){background:var(--brand-soft);border-color:var(--brand);color:var(--brand);font-weight:750}
/* **선수단의 칩 띠만 화면에서 지운다** (2026-08-27 A안 Task 7, 스펙 §5-4).
   입력 수단은 이제 폰의 접힌 보드(`.pos-board`)와 PC의 왼쪽 사이드바
   (`.pos-side`)다 — 같은 화면에 셋을 다 두면 한 화면이 같은 일을 세 번 한다.
   실측에서 이 띠는 폰에서 `scrollWidth 742` vs `clientWidth 356`으로 열 중
   다섯이 화면 밖이었고, 밀 수 있다는 표시조차 없었다(스펙 §2).

   **`<fieldset>` 자체는 지우지 않는다 — 그것이 폼 값의 주인이다.** htmx는
   `#roster-filter`의 값을 `form.elements`로 걷으므로 `display:none`이어도 라디오
   값은 그대로 실리고, 선수 등록 폼의 `hx-include="#roster-filter"`도 같은 자리를
   읽는다. 지우면 '투수'로 좁혀 놓고 한 명을 고치는 순간 전체 명단이 돌아온다.

   **id 선택자다.** `.filter-chips`는 경기 목록의 연도 칩(`#games-years`)이 함께
   쓰는 클래스라, 클래스로 지우면 그 화면의 필터가 통째로 사라진다. */
#roster-positions{display:none}
.filter-chip:has(input:focus-visible){outline:none;box-shadow:var(--focus)}

/* ── 경기 목록 도구 막대 (2026-08-24 요청: 연도 칩 · 검색) ──────────────────
   칩·검색·초기화는 위의 로스터 필터(.filter-bar 계열)를 그대로 입는다.
   여기는 경기 화면에만 있는 둘 — 라벨 없는 검색칸 폭과 칩 속 개수 — 만 더한다. */
.games-search{flex:1 1 200px;min-width:0;max-width:300px}
/* 칩 속 개수. 12px 하한(scripts/mobile_check의 TEXT_FLOOR)이라 .7rem대를 쓰지
   않는다. 고른 칩에서는 칩의 브랜드색을 그대로 물려받는다. */
.filter-chip .chip-count{font-size:.75rem;font-weight:700;color:var(--faint);margin-left:2px}
.filter-chip:has(input:checked) .chip-count{color:inherit}

/* ── 보기 전환 · 리스트 보기 (2026-08-24 요청 ④) ────────────────────────────
   카드 DOM 그대로, 래퍼(#board-view)의 is-rows 아래에서만 CSS로 한 줄로
   눕힌다 — 템플릿 두 벌 금지(games_board.js의 주석). 여기 grid는 PC 전용
   배치다: ≤1024px는 mobile.css가 압축 카드(flex column)로 덮는다. */
.view-seg{display:inline-flex;border:1px solid var(--border-strong);border-radius:999px;background:var(--surface);padding:3px;gap:2px}
.view-seg button{display:inline-flex;align-items:center;gap:6px;min-height:44px;min-width:44px;padding:8px 15px;border:0;border-radius:999px;background:none;font-size:.8rem;font-weight:650;color:var(--muted);cursor:pointer;transition:background .18s ease,color .18s ease}
.view-seg button:hover{color:var(--text)}
.view-seg button[aria-pressed="true"]{background:var(--brand);color:var(--brand-ink)}
.view-seg button:focus-visible{outline:none;box-shadow:var(--focus)}
.board-view.is-rows .game-list{display:flex;flex-direction:column;gap:0;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow-sm);overflow:hidden}
/* 상태 배경(.past .cancelled .next)은 카드 보기의 규칙이 그대로 온다 — 두
   보기가 같은 경기를 다른 톤으로 말하면 안 된다. 여기서는 배치만 바꾼다. */
.board-view.is-rows .game-card{display:grid;grid-template-columns:minmax(120px,150px) minmax(180px,1.6fr) auto auto;align-items:center;gap:3px 16px;padding:12px 18px;border:0;border-bottom:1px solid var(--border);border-radius:0;box-shadow:none}
.board-view.is-rows .game-card:last-child{border-bottom:0}
.board-view.is-rows .game-card:hover{background:var(--surface-2)}
.board-view.is-rows .game-card.next{background:linear-gradient(90deg,var(--brand-soft),transparent 60%);box-shadow:inset 3px 0 0 var(--brand)}
.board-view.is-rows .gc-top{flex-direction:column;align-items:flex-start;gap:4px}
.board-view.is-rows .gc-steps{justify-content:flex-end}
.board-view.is-rows .gc-cta{margin-top:0}

/* ── 상대팀 콤보박스 팝오버 (2026-08-24 요청 ②) ─────────────────────────────
   z-index:30은 시각·날짜 팝오버(.time-pop·.date-pop)와 같은 층이다. 앵커는
   left:0;right:0 — 자기 칸 폭 그대로라 폼 밖으로 넘치지 않는다(날짜 팝오버가
   싸운 넘침이 여기는 처음부터 없다). */
.opp-field{position:relative}
.opp-pop{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:30;background:var(--surface);border:1px solid var(--border-strong);border-radius:var(--radius-sm);box-shadow:var(--shadow);max-height:296px;overflow-y:auto}
.opp-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:10px 14px;min-height:44px;font-size:.86rem;background:none;border:0;cursor:pointer;transition:background .12s ease}
.opp-item:hover,.opp-item:focus-visible{background:var(--brand-soft);outline:none}
/* 긴 팀명은 말줄임 — 눈확인에서 8자 이름이 전적 옆에서 두 줄로 꺾였다.
   이름 전체는 고른 뒤 입력칸에서 어차피 다 보인다. */
.opp-name{font-weight:700;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}
/* --faint 12px — mobile_check TEXT_FLOOR에 맞춰 .75rem 아래로 내리지 않는다. */
.opp-rec{margin-left:auto;font-size:.75rem;color:var(--faint);white-space:nowrap;font-variant-numeric:tabular-nums}
.opp-item:hover .opp-rec,.opp-item:focus-visible .opp-rec{color:var(--brand)}
.opp-note{padding:9px 14px;margin:0;font-size:.75rem;color:var(--faint);border-top:1px solid var(--border)}

/* ── 선수 메모 (2026-08-18 요청 ③) ────────────────────────────────────────
   표에는 한 줄 요약만 선다. `max-width`가 없으면 긴 문장 하나가 표의
   `min-width`를 밀어 올려 `.table-scroll` 안에서도 가로 스크롤이 길어진다. */
.log-memo{max-width:22em}
.memo-line{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted)}
.memo-tag{
  display:inline-block;min-width:1.5em;text-align:center;border-radius:5px;padding:1px 4px;
  margin-right:4px;font-size:.7rem;font-weight:750;background:var(--surface-3);color:var(--faint)
}
.memo-tag.ok{background:var(--brand-soft);color:var(--brand)}
.memo-tag.warn{background:var(--accent-soft);color:var(--accent)}
/* 세 칸 모두 문장이라 `.form-grid`의 200px 열에 넣으면 두 글자마다 줄이 바뀐다. */
.memo-grid{display:flex;flex-direction:column;gap:13px}
.memo-grid textarea{min-height:60px}

/* ── 경기 취소 (2026-08-18 요청) ──────────────────────────────────────────
   ①의 취소 카드. 이미 취소된 상태에서는 테두리를 경고색 파선으로 바꿔, 이
   카드가 '지금 그런 상태다'를 말하게 한다 — 버튼 색만으로는 스크롤해 내려온
   사람이 취소 전인지 후인지 구분하지 못한다. `.game-card.cancelled`와 같은
   처방(파선)을 써서 두 화면이 같은 언어를 쓴다. */
.cancel-card.is-cancelled{border-style:dashed;border-color:var(--warn)}
.cancel-reason{margin:0 0 10px;font-size:.88rem;line-height:1.6}
.cancel-reason b{color:var(--muted);font-size:.8rem;font-weight:650;margin-right:8px}
/* --faint on --surface: 라이트 5.03 · 다크 4.79 (tests/test_layout.py 행렬). */
.field-optional{color:var(--faint);font-weight:500}

/* 두 라벨이 한 줄에 선다 — `.field-label`("상태")과 `.check-line`("활동 중").
   `.check-line`에 font-size를 걸면 `.form-field label`(명시도 0,1,1)이
   `.check-line`(0,1,0)을 눌러 조용히 무시된다. 그래서 `.field-label`을
   `.form-field label`과 같은 값으로 맞추고, 크기는 그쪽이 정하게 둔다. */
.field-label{font-size:.8rem;font-weight:650;color:var(--muted)}
.check-line{display:flex;align-items:center;gap:8px;min-height:44px;cursor:pointer}
.check-line input{width:18px;height:18px;accent-color:var(--brand);cursor:pointer}

.pos-grid{display:flex;flex-wrap:wrap;gap:8px}
/* min-height는 44px이다 — 설계 스펙 §13의 터치 타깃. 이 저장소에서 40px/36px로
   내려갔다가 되돌린 것이 Task 2·6에서 두 번 있었고 계획서 초안이 다시 40px였다.
   `.btn`·`.gnb-item`·`.att-toggle`·`.att-state`가 전부 44다.
   테두리는 --border-strong를 쓴다. `.att-check`가 --muted로 올라간 것과 달라
   보이지만 다른 경우다 — 저쪽은 네이티브 체크박스를 대신 그린 것이라 테두리
   자체가 상태 표시라서 WCAG 1.4.11(3:1)이 걸린다. 이 칩은 진짜 체크박스를
   안에 품고 있어(accent-color가 상태를 말한다) 테두리는 컨테이너 경계다 —
   `.btn-ghost`·`.att-state`와 같은 부류다.
   선택된 칩의 글자는 --brand on --brand-soft로 라이트 4.57 · 다크 7.20이다.
   여유가 얇으니 tests/test_layout.py의 대비 행렬이 이 쌍을 지킨다. */
.pos-chip{
  display:inline-flex;align-items:center;gap:7px;padding:8px 13px;border-radius:999px;
  border:1px solid var(--border-strong);background:var(--surface-2);font-size:.83rem;
  font-weight:650;cursor:pointer;min-height:44px
}
.pos-chip input{width:16px;height:16px;accent-color:var(--brand);cursor:pointer}
.pos-chip:has(input:checked){border-color:var(--brand);background:var(--brand-soft);color:var(--brand)}
#player-form-slot:empty{display:none}

/* ── 라인업 편집기 (Task 8) ───────────────────────────────────────────────
   터치 타깃은 전부 44px이다 — 설계 스펙 §13. 목업과 계획서는 `.slot-x` 29,
   `.slot-pos-sel` 34, `.pool-p` 42였다. **스펙과 목업이 어긋나면 스펙이
   이긴다**(2026-08-16 사용자 결정). Task 2·6·17에서 세 번 되풀이된 자리다. */
/* 두 열의 **아래끝이 맞아야 한다.** `align-items:start`로 두면 짧은 쪽 아래에
   빈 자리가 남는데, 오더지가 비어 있을 때는 왼쪽(타순)이 짧고 다 채우면 왼쪽이
   길어서 어느 쪽이 남을지도 그때그때 달라진다(2026-08-17 사용자 지적).
   `stretch`로 두 열을 같은 높이로 만들고, 각 열의 마지막 카드가 남는 높이를
   흡수한다. */
.lineup-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:18px;align-items:stretch;margin-top:14px}
.lineup-side{display:flex;flex-direction:column;gap:18px}
/* 카드가 늘어나도 머리말은 위에, 본문이 남는 높이를 가져간다. */
.lineup-grid > .card,.lineup-side > .card{display:flex;flex-direction:column}
.lineup-grid > .card > .card-body,.lineup-side > .card > .card-body{flex:1;display:flex;flex-direction:column;min-height:0}
/* 오른쪽 열에서 늘어나는 것은 '남은 참석자'다. 배치도는 `aspect-ratio`로 비율이
   정해져 있어 늘리면 좌표가 어긋난다(Task 9의 기하 테스트가 그 비율에 걸려 있다). */
.lineup-side > .card:last-child{flex:1}
/* 왼쪽 카드에서 남는 높이는 교체 후보 목록이 가져간다. 타순 9칸을 늘리면 빈 칸이
   비정상적으로 커진다. */
.lineup-sub-fill{flex:1;display:flex;flex-direction:column}
.dh-toggle{margin-left:auto;display:flex;align-items:center;gap:7px;min-height:44px;font-size:.84rem;font-weight:650;cursor:pointer}
.dh-toggle input{width:17px;height:17px;accent-color:var(--brand);cursor:pointer}
/* 소제목 규칙은 여기 있었으나 Task 17(먼저 실행됨)이 이미 넣었다 — 계획서 §3의
   재배치 참조. 다시 쓰면 같은 선택자가 파일에 두 번 생긴다. 값이 같아 화면은
   안 깨지고 그래서 아무도 눈치채지 못한다. 계획서는 `grep -c` 한 줄로 세라고
   했는데, 그 문구를 주석에 그대로 옮기면 주석 자신이 두 번째 결과로 잡혀
   확인이 늘 실패한다. 실제로 그랬다 — 세는 것은 아래 테스트가 한다. */

/* 가운데 칸은 minmax(0,1fr)이다. 기본값 `1fr`은 최소 크기가 콘텐츠라 긴 이름이
   들어오면 칸이 줄지 않고 행이 컨테이너를 밀어낸다(375px에서 가로 스크롤). */
.slot-list{display:flex;flex-direction:column;gap:7px}
/* 타순 행 + 그 아래 열리는 수비 위치 목록 (2026-08-18 사용자 요청 ②).
   `.slot-list`의 flex 항목이 이제 이 셀이다 — 행에 직접 gap을 걸면 목록과 행
   사이가 7px로 벌어져 둘이 한 덩어리로 보이지 않는다. */
.slot-cell{display:flex;flex-direction:column;gap:6px}
.slot{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:11px;align-items:center;padding:9px 12px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-2);min-height:54px}
.slot.empty{border-style:dashed;background:transparent}
.slot.conflict{border-color:var(--danger);background:var(--danger-soft)}
/* 44px인 이유는 미관이 아니다 — 타순 칸의 이 배지가 **드래그 손잡이**다
   (설계 스펙 §13). 손잡이를 분리한 사정은 `lineup_editor.html`의 주석에 있다.
   교체 후보·선발투수 칸의 배지는 끌 수 없지만 같은 크기로 둔다: 한 화면에서
   같은 모양의 배지가 두 크기로 나오면 어느 쪽이 손잡이인지 알 수 없다. */
.slot-no{min-width:44px;min-height:44px;padding:0 5px;border-radius:8px;background:var(--surface-3);color:var(--muted);display:grid;place-items:center;font-weight:800;font-size:.8rem}
/* 손잡이는 **터치에서도 손잡이다** (2026-08-24).
   HTML5 드래그앤드롭은 터치에서 아예 발동하지 않아(`dragstart`가 한 번도 나오지
   않는다) `lineup.js`의 포인터 경로가 그 자리를 대신하는데, `touch-action`을
   그대로 두면 세로로 끄는 순간 브라우저가 그 제스처를 페이지 스크롤로 가져가
   `pointermove`가 끊긴다 — 끌기가 시작도 못 한다. 그래서 **손잡이 44px만**
   스크롤을 포기한다. 행의 나머지(이름·드롭다운·▲▼·X)는 평소대로 스크롤하고,
   손가락을 배지 위에 정확히 얹고 페이지를 미는 것만 안 된다.

   **`mobile.css`가 아니라 여기 있는 이유**: `touch-action`은 마우스에 아무
   효과가 없으므로 PC에 두어도 PC가 바뀌지 않고, ≤1024px 밖에도 터치 화면이
   있다(윈도우 태블릿·가로로 놓은 큰 iPad). `mobile.css`는 ≤1024px 전용이라
   (`test_pc_is_untouched`) 거기 두면 그 폭들에서 손잡이가 조용히 죽는다.

   `user-select`는 길게 눌렀을 때 뜨는 글자 선택 말풍선을 막는다. */
.slot-no[draggable="true"]{cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none;align-content:center;gap:3px}
/* 손잡이라는 것을 **손가락에게도** 말한다 (2026-08-24 사용자 결정).
   커서가 있는 화면에서는 바로 위 `cursor:grab`이 끌 수 있는 배지와 못 끄는
   배지를 갈랐다. 손가락에는 커서가 없다 — 폰 왼쪽 끝에 똑같이 생긴 44px 배지가
   열둘 늘어서는데 아홉은 타순을 다시 쓰고 셋(선발투수 · 교체 후보)은 그냥
   스크롤한다. 그 셋이 같은 크기인 이유는 위 주석에 있고 그것은 그대로 옳다:
   갈라야 하는 것은 크기가 아니라 **손잡이라는 표시**다.

   그래서 번호 **아래**에 점 여섯(3×2)을 찍는다. 옆에 붙이지 않는 이유는
   `.slot`의 첫 열이 44px로 못 박혀 있어서다 — 옆으로 늘리면 열을 넘친다.

   지키는 것 넷:
     · 44×44 그대로다. 점 격자는 12×8이고 번호(.8rem = 12.8px)와 합쳐도
       세로 24px 남짓이라 `min-height:44px` 안에서 논다.
     · 색은 `currentColor`다 — 번호와 **같은 색**이라 대비가 번호를 따라간다
       (--brand 위 --brand-ink · --surface-3 위 --muted · --danger 위 --surface).
       리터럴을 박으면 그 셋 중 하나에서 조용히 미달한다.
     · `opacity`로 말하지 않는다(파일 곳곳의 그 규칙 그대로).
     · 움직이지 않는다 — `prefers-reduced-motion`으로 감쌀 것이 없다.

   `[draggable="true"]`가 **범위**다. 빈 칸은 `draggable="false"`라 안 나오고
   (끌 것이 없다), 선발투수 칸과 교체 후보 행의 배지는 이 속성 자체가 없다. */
.slot-no[draggable="true"]::after{content:"";width:12px;height:8px;background:radial-gradient(circle at 2px 2px,currentColor 1.1px,transparent 1.4px) 0 0/4px 4px}
.slot.filled .slot-no{background:var(--brand);color:var(--brand-ink)}
/* `#fff`를 박지 말 것. 다크의 --danger(#f87171)·--accent(#fb923c)는 **밝은**
   색이라 흰 글자가 2.77:1 · 2.26:1이 된다(목업과 계획서가 둘 다 #fff였다).
   --brand 배지가 --brand-ink를 쓰는 이유와 같다. --surface는 danger 위에서
   라이트 6.47 · 다크 6.30, accent 위에서 라이트 5.18 · 다크 7.70이다.
   실제 수치는 tests/test_layout.py의 TEXT_ON_SURFACES가 `--surface` 행으로
   잰다 — 여기에 리터럴을 되돌려 놓으면 그 행렬은 아무것도 못 잡는다. */
.slot.conflict .slot-no{background:var(--danger);color:var(--surface)}
.slot-name{font-weight:700;font-size:.91rem}
.slot-sub{font-size:.76rem;color:var(--muted)}
/* '소화 포지션 아님'(설계 스펙 §8). 한 가지 뜻에 한 가지 색이다 — 행 상태에
   따라 색을 바꾸면 같은 문구가 두 색으로 나온다. 초안은 충돌 행에서 --danger로
   갈아탔는데, 그때 --warn이 --danger-soft 위에서 4.03이었기 때문이다.
   그 미달은 토큰 쪽에서 고쳤다(tokens.css의 --warn 주석 참조) — 지금은
   --surface-2 5.94 · --danger-soft 4.73 · --brand-soft 5.26이다. */
.slot-warn{color:var(--warn);font-weight:700}
.slot-empty-text{color:var(--faint);font-size:.85rem}
/* ── 타순 줄의 컨트롤 셋 — 포지션 셀렉트 · ▲▼ · X (2026-08-24 사용자 요청 ③) ──
   원문: *"수비 포지션 선택하는 박스, 타순 바꾸는 버튼, X 버튼을 카카오뱅크처럼
   버튼이 모바일 환경에서 및 PC에서 큼지막하게 누르기 편하게"*.

   **이 요청은 PC까지 포함한다** — 모바일 셸 라운드의 "PC는 그대로 둔다"
   경계(2026-08-23)를 사용자가 여기서 직접 열었다. 그래서 이 규칙들은 mobile.css가
   아니라 여기 최상위에 있다: mobile.css는 `test_pc_is_untouched`가 모든 @media를
   ≤1024px로 닫아 두므로 PC에 닿을 수 없다.

   **크기는 리터럴 px로 적는다.** `var(--slot-ctl)` 같은 사용자 정의 속성으로
   빼면 `test_no_later_rule_shrinks_an_editor_touch_target`이 "px로 끝나는 선언"만
   훑으므로 44px 하한 감시가 통째로 눈이 먼다 — 그 테스트가 잡으라고 만들어진
   결함(좁은 화면 블록에서만 타깃을 줄이는 것)이 다시 조용히 지나간다.

   48px는 44px(iOS)와 48dp(Android) 중 **큰 쪽**이다. 폰에서는 아래 640px 블록이
   52px로 한 단 더 올리고 셀렉트를 줄 전체로 편다. */
.slot-tail{display:flex;gap:8px;align-items:center}
/* 아이콘 버튼 둘(▲▼·X)은 **면을 갖는다**. 예전에는 배경도 테두리도 없는 회색
   글리프라 "여기가 버튼이다"를 색 하나로만 말했는데, 그것이 카카오뱅크식 큼직한
   버튼과 갈리는 지점이다. 면이 생기면 히트 영역이 눈에 보인다. */
/* **`.slot-x`와 `.slot-move`를 한 규칙으로 합치지 말 것.** 둘의 44px 하한을
   지키는 `test_editor_controls_meet_the_44px_touch_target`은 `_rule()`로 **줄
   맨 앞에 선언된** `.slot-x{`를 찾는다 — `.slot-move,.slot-x{`로 묶으면 그
   앵커가 안 맞아 "규칙을 찾지 못했다"로 터진다. 값이 같아도 따로 적는다. */
.slot-x{
  min-width:48px;min-height:48px;border-radius:14px;display:grid;place-items:center;
  border:1px solid var(--border);background:var(--surface);color:var(--muted);flex:none;
  transition:background .15s ease,color .15s ease,border-color .15s ease
}
/* 16px 글리프가 48px 면 안에서 길을 잃는다. 아이콘만 키운다 — 히트 영역은
   위 규칙이 이미 정했고 여기서 바뀌는 것은 보이는 크기뿐이다. */
.slot-move .icon,.slot-x .icon{width:22px;height:22px;stroke-width:2}
.slot-x:hover{background:var(--danger-soft);border-color:var(--danger);color:var(--danger)}
/* 타순 재정렬 ▲▼ (Task 19). 화살표는 SVG 아이콘이라 적용되는 것은 텍스트용
   1.4.3이 아니라 **1.4.11 Non-text Contrast**(3:1)이고, 그 조항의 예외 문구가
   "except for inactive components"다 — 비활성 화살표의 --border-strong
   (라이트 1.35)이 낮은 것은 그 예외에 해당한다. 활성 화살표는 --muted로
   --surface-2 위 6.90 · 8.67이다. */
.slot-move{
  min-width:48px;min-height:48px;border-radius:14px;display:grid;place-items:center;
  border:1px solid var(--border);background:var(--surface);color:var(--muted);flex:none;
  transition:background .15s ease,color .15s ease,border-color .15s ease
}
.slot-move:hover:not(:disabled){background:var(--surface-3);border-color:var(--border-strong);color:var(--text)}
/* 비활성 화살표는 면도 함께 가라앉힌다. 위 규칙이 준 흰 면만 남으면 "누를 수
   있어 보이는데 안 눌리는" 버튼이 된다. */
.slot-move:disabled{background:transparent;border-color:var(--border);color:var(--border-strong);cursor:not-allowed}
/* 끌고 있는 행에 `opacity`를 쓰지 말 것. 하위 트리를 배경 위로 합성해 글자만
   배경 쪽으로 끌어당긴다 — `.game-card.past`(Task 3)와 `.log-table tr.inactive`
   (Task 17)에서 두 번 되돌린 처방이다. 테두리로 말하되 **--border-strong으로는
   안 된다**: --surface-2 위에서 1.42:1이라 점선 전환 말고는 신호가 없다시피
   하다(품질 검토 실측). --brand 점선이면 3:1을 넘고 놓을 자리(--brand 실선 +
   --brand-soft 배경)와도 구분된다. */
.slot.dragging{cursor:grabbing;border-color:var(--brand);border-style:dashed}
/* 목업의 `.slot.is-target`과 같은 표시다(mockup-b-workspace.html:180). */
.slot.drag-over{border-color:var(--brand);background:var(--brand-soft)}
/* 빈 칸도 놓을 자리가 된다. `--faint`는 `--brand-soft` 위에서 다크 4.25:1로
   미달이다(0.85rem이라 큰 글씨 예외 없음). Task 6의 `.att-item.on .att-sub`,
   Task 8의 `.pool-pos`와 같은 조합이고 대비 행렬은 토큰 쌍만 보므로 못 잡는다. */
.slot.drag-over .slot-empty-text{color:var(--muted)}
/* 셀렉트도 같은 키다. 글자를 .77rem(12.3px)에서 .88rem(14.1px)으로 올린 것이
   실제로 누르기 편해지는 부분이다 — 상자만 키우고 글자를 두면 커진 만큼 빈
   여백이 늘 뿐이다. `padding`을 세로 0으로 두는 이유: `min-height`와 세로 패딩이
   함께 걸리면 셀렉트가 48px보다 커져 ▲▼·X와 높이가 어긋난다. */
.slot-pos-sel{font-size:.88rem;font-weight:700;padding:0 12px;border-radius:14px;border:1px solid var(--border-strong);background:var(--surface);color:var(--text);cursor:pointer;min-height:48px}
.slot-pos-sel.unset{border-color:var(--warn);background:var(--warn-soft);color:var(--warn)}

.pitcher-slot{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:11px;align-items:center;padding:9px 12px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-2);min-height:54px;margin-bottom:11px}
.pitcher-slot.empty{border-style:dashed;background:transparent}
.pitcher-slot .slot-no{background:var(--accent-soft);color:var(--accent)}
.pitcher-slot.filled .slot-no{background:var(--accent);color:var(--surface)}
/* 설계 스펙 §7은 "라인업에 있는 선수가 참석 취소됨 → 해당 슬롯 빨간 강조"라고
   한다. 타순 칸만의 규칙이 아니다 — 목업은 선발투수 칸을 글자로만 알렸다. */
.pitcher-slot.conflict{border-color:var(--danger);background:var(--danger-soft)}
.pitcher-slot.conflict .slot-no{background:var(--danger);color:var(--surface)}

.pool-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:12px}
.pool-count{font-size:.82rem;color:var(--muted)}
.pool-count b{color:var(--brand);font-size:1.05rem}
/* `flex:1`이라 카드가 늘어나면 목록도 함께 늘어난다 — 오더지를 다 채워 왼쪽이
   길어졌을 때 오른쪽에 빈 칸 대신 후보가 더 보인다. `max-height`는 반대 경우의
   상한이다(오더지가 비어 있으면 이 목록이 열 높이를 정한다). `min-height:0`이
   없으면 flex 항목이 내용만큼 부풀어 `overflow-y`가 듣지 않는다. */
.pool{display:flex;flex-direction:column;gap:6px;flex:1;min-height:0;max-height:420px;overflow-y:auto;padding-right:3px}
.pool-row{display:flex;gap:6px;align-items:stretch}
.pool-item{display:flex;align-items:center;gap:11px;padding:9px 12px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--surface);flex:1;min-width:0;text-align:left;min-height:52px;transition:border-color .16s ease,background .16s ease}
.pool-item:hover{border-color:var(--brand);background:var(--brand-soft)}
/* 끌 수 있다는 유일한 시각 신호다 (2026-08-18 요청 ②). 이 버튼은 누르는 것도
   되므로 `grab`이 `pointer`를 대신한다 — 둘 다 "여기서 뭔가 일어난다"를
   말하고, 이쪽만 드래그를 함께 알린다. */
.pool-item{cursor:grab}
.pool-item:active{cursor:grabbing}
.pool-num{width:31px;height:31px;border-radius:9px;background:var(--surface-3);color:var(--muted);display:grid;place-items:center;font-weight:800;font-size:.79rem;flex:none}
.pool-name{display:block;font-weight:700;font-size:.88rem}
/* --muted다. `.pool-item:hover`가 배경을 --brand-soft로 바꾸는데 거기서
   --faint는 다크 4.25:1로 스펙 §13의 4.5:1에 못 미친다(0.75rem이라 큰 글씨
   예외도 없다). --muted는 라이트 6.90 · 다크 6.56. Task 6의
   `.att-item.on .att-sub`와 **완전히 같은 조합**이고, tests/test_layout.py의
   TEXT_ON_SURFACES는 토큰 쌍 행렬이라 이 조합을 잡지 못한다. */
.pool-pos{display:block;font-size:.75rem;color:var(--muted)}
.pool-add{margin-left:auto;color:var(--brand)}
.pool-p{min-width:44px;min-height:44px;border-radius:var(--radius-sm);border:1px solid var(--border-strong);background:var(--surface-2);color:var(--muted);font-weight:800;font-size:.76rem;flex:none;transition:background .16s ease,color .16s ease,border-color .16s ease}
.pool-p:hover{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.pool-empty{padding:24px;text-align:center;color:var(--faint);font-size:.85rem}

/* ── 수비 위치 고르기 (2026-08-18 사용자 요청 ②) ─────────────────────────
   타순 칸 **아래**에 열린다. 배치도 목록(`.fn-picker`)과 같은 자리 고르기라
   생김새를 맞췄지만 뜨지 않는다(`position:static`) — 타순 목록은 세로로 길고
   스크롤되는 카드 안이라, 띄우면 스크롤할 때 행과 목록이 따로 논다.

   버튼은 전부 44px다 (설계 스펙 §13). 자리 이름이 짧아 칩처럼 만들고 싶어지는
   자리인데, Task 2·6·8·17에서 네 번 미달이 났던 것이 정확히 그 유혹이다. */
.slot-picker{
  padding:12px;border:1px solid var(--brand);border-radius:var(--radius-sm);
  background:var(--brand-soft);display:flex;flex-direction:column;gap:10px
}
.slot-picker:focus-visible{outline:none;box-shadow:var(--focus)}
.sp-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.sp-title{font-size:.85rem;font-weight:750;color:var(--text)}
.sp-close{min-width:44px;min-height:44px;border:0;border-radius:10px;background:transparent;color:var(--muted);display:grid;place-items:center;cursor:pointer;flex:none}
.sp-close:hover{background:var(--surface);color:var(--text)}
.sp-close:focus-visible{outline:none;box-shadow:var(--focus)}
.sp-list{display:flex;flex-wrap:wrap;gap:7px}
/* 세로 flex인 것은 '⇄ …와 교대'가 이름 아래 두 번째 줄로 서기 때문이다
   (2026-08-18 요청 ②). `min-height:44px`는 그대로 지켜진다. */
.sp-pick{
  min-height:44px;padding:0 14px;border:1px solid var(--border-strong);border-radius:var(--radius-sm);
  background:var(--surface);color:var(--text);font-size:.85rem;font-weight:700;cursor:pointer;
  display:inline-flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:1px;
  transition:background .16s ease,border-color .16s ease,color .16s ease
}
.sp-pick:hover{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.sp-pick:focus-visible{outline:none;box-shadow:var(--focus)}
/* 이미 누가 맡은 자리라는 것과 누르면 맞바꾼다는 것을 **글자로** 말한다
   (2026-08-18 요청 ②). 색만으로는 낭독기에 아무 말도 하지 못한다.
   hover에서 부모가 `--brand-ink`로 갈아타면 이 줄도 함께 가야 한다. `--faint`인
   채로 두면 초록 배경 위 회색 글자가 되어 대비가 무너진다. */
.sp-swap{font-size:.72rem;font-weight:650;color:var(--faint)}
.sp-pick:hover .sp-swap{color:inherit}
.sp-pick.is-warn .sp-swap{color:var(--warn)}
.sp-pick.is-warn:hover .sp-swap{color:inherit}
/* 소화 못 하는 자리. **색만으로 말하지 않는다** (설계 스펙 §13) — 숨긴 글로
   '소화 포지션 아님'이 함께 나가고, 테두리 모양(파선)이 색과 별개의 신호다. */
.sp-pick.is-warn{border-color:var(--warn);border-style:dashed;color:var(--warn)}
.sp-pick.is-warn:hover{background:var(--warn);border-color:var(--warn);color:var(--surface)}
.sp-empty{margin:0;font-size:.8rem;color:var(--muted);line-height:1.55}
.sp-toggle{
  min-height:44px;padding:0 12px;border:1px dashed var(--border-strong);border-radius:var(--radius-sm);
  background:transparent;color:var(--muted);font-size:.8rem;font-weight:700;cursor:pointer;
  display:flex;align-items:center;gap:7px;align-self:flex-start
}
.sp-toggle:hover{color:var(--text);border-color:var(--text)}
.sp-toggle:focus-visible{outline:none;box-shadow:var(--focus)}
.sp-toggle-note{font-weight:600;color:var(--faint)}
.sp-later{
  min-height:44px;padding:0 12px;border:0;border-radius:var(--radius-sm);background:transparent;
  color:var(--muted);font-size:.8rem;font-weight:700;cursor:pointer;align-self:flex-start
}
.sp-later:hover{color:var(--text)}
.sp-later:focus-visible{outline:none;box-shadow:var(--focus)}

@media (max-width:960px){ .lineup-grid{grid-template-columns:1fr} }
/* 컨트롤이 넷이다 — 포지션 셀렉트 · ▲ · ▼ · X. 44px를 지키면 375px에서 한 줄에
   들어가지 않으므로 **줄을 통째로 아래로 내린다.** 타깃을 줄여서 맞추지 말 것:
   Task 2·6·17·8에서 네 번 되풀이된 자리이고, 좁은 화면에서만 무너지면
   데스크톱에서 보는 사람은 영원히 모른다. `test_lineup_view.py`의
   `test_no_later_rule_shrinks_an_editor_touch_target`가 이 블록을 훑는다. */
@media (max-width:640px){
  .slot,.pitcher-slot{grid-template-columns:44px minmax(0,1fr);gap:8px;padding:9px 10px}
  .slot-tail{grid-column:1 / -1;justify-content:flex-end;gap:8px}
  /* GNB '내 계정' 힌트 글자 — 좁은 화면에서는 이름·칩만 남긴다 (2026-08-22). */
  .gnb-user-hint{display:none}
  /* 빈 칸에도 `.slot-tail`이 있다(안이 전부 `x-if="entry"`라 비어 있을 뿐).
     그대로 두면 높이 0짜리 2행과 간격이 생겨 빈 칸만 3px 높아진다. */
  .slot-tail:empty{display:none}
  .pitcher-slot > .slot-x{grid-column:1 / -1;justify-self:end}
  /* ── 2026-08-24 사용자 요청 ③ — 폰에서 한 단 더 크게 ────────────────────
     이 블록은 예전에 셀렉트를 **줄이던** 자리다(`padding:5px 6px;font-size:.74rem`).
     줄이 통째로 아래로 내려와 폭이 남아도는데도 글자를 11.8px까지 눌렀던 것은,
     타깃을 줄이지 말라는 바로 위 주석과 정반대였다. 이제 남는 폭을 셀렉트가
     쓴다 — `flex:1`이 그것이고, `min-width:0`이 없으면 flex 항목의 기본
     최소 크기(min-content)가 줄을 넘긴다. */
  .slot-pos-sel{flex:1;min-width:0;min-height:52px;font-size:.9rem;padding:0 14px}
  .slot-move,.slot-x{min-width:52px;min-height:52px}
  .slot-move .icon,.slot-x .icon{width:24px;height:24px}
}

/* ── 수비 배치도 (Task 9, 설계 스펙 §9) ─────────────────────────────────────
   **`.field`는 이 배치도의 이름이다. 바꾸지 말 것.** 폼 래퍼는 Task 2에서
   `.form-field`로 옮겼다 — 둘이 같은 이름을 쓰면 모든 폼 래퍼가
   `aspect-ratio:4/3`을 뒤집어쓴다. `.field-lines`·`.field-node`·`.field-legend`·
   `.fn-*`도 마찬가지다. */
.field{
  position:relative;width:100%;aspect-ratio:4/3;border-radius:12px;
  background:
    radial-gradient(ellipse 78% 62% at 50% 92%, color-mix(in srgb,var(--brand) 16%,transparent), transparent 70%),
    var(--surface-2);
  border:1px solid var(--border)
}
.field-lines{position:absolute;inset:0;width:100%;height:100%}
/* 노드 자체가 버튼이다(2026-08-17 사용자 결정) — 안에 44px 컨트롤을 넣으면
   아홉 노드가 겹치므로, 누르면 배치도 아래에 목록이 열린다. 버튼이므로 스펙
   §13의 44px가 적용된다.

   **상자 크기를 고정한다(88×44).** 이름 길이에 따라 늘어나면 겹침을 계산할 수
   없다 — 그래서 `.fn-name`이 `nowrap` + 말줄임이다. 노드는 좌표에 **가운데**를
   맞추므로 상자가 크면 서로 먹는다: 초안은 배지·이름·자리 이름을 세로로 쌓아
   78×67이었고, 375px에서 '투수' 글자를 눌렀는데 **포수 목록이 열렸다**
   (품질 검토가 `elementFromPoint`로 실측하고 실제 클릭으로 재현했다).

   좌표상 가장 가까운 두 자리는 유격수(36,44)·3루수(23,62)이고 세로 간격이
   폭의 13.5%다. 44px를 지키려면 배치도 폭이 326px 이상이어야 하며, 그 아래는
   아래 미디어쿼리가 격자 목록으로 떨어뜨린다. 같은 계산이 위아래 끝 노드가
   상자 밖으로 나가지 않는 것도 보장한다(포수 y=91% → 아래 여유 0.0675×폭).
   `tests/test_lineup_field.py`가 좌표와 이 값들로 겹침·삐져나감을 직접 잰다. */
.field-node{position:absolute;transform:translate(-50%,-50%);display:flex;align-items:center;gap:5px;width:88px;min-width:44px;height:44px;min-height:44px;padding:0 4px;border-radius:999px;text-align:left}
/* 빈 자리의 점선 원이 '여기가 비었다'를 나르는 유일한 표시다. --border-strong은
   --surface 위에서 라이트 1.35 · 다크 1.59라 WCAG 1.4.11(비텍스트 3:1)에 한참
   못 미친다 — Task 6이 미선택 체크 칸을 같은 이유로 --muted(7.24/8.67)로 올렸다.
   여기서는 비활성 예외(Task 19의 ▲▼)를 쓸 수 없다: 이 배지는 누를 수 있다. */
.fn-badge{width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font-size:.7rem;font-weight:800;background:var(--surface);border:2px dashed var(--muted);color:var(--muted);transition:background .18s ease,border-color .18s ease,color .18s ease}
.field-node:hover .fn-badge{border-color:var(--brand);color:var(--brand)}
.field-node.on .fn-badge{background:var(--brand);border-style:solid;border-color:var(--brand);color:var(--brand-ink)}
/* 목업은 --faint였다. 이 글자들은 **혼합된 배경** 위에 있다 — `.field`가
   --surface-2에 --brand를 16% 얹은 그라디언트라 --faint가 라이트 3.99 · 다크
   4.14로 스펙 §13의 4.5에 못 미친다(0.73/0.64rem이라 큰 글씨 예외도 없다).
   --muted는 5.83 / 6.40이다. `TEXT_ON_SURFACES`는 **토큰 쌍** 행렬이라 이렇게
   섞인 배경을 보지 못하므로 `tests/test_lineup_field.py`가 직접 계산한다 —
   그 테스트는 그라디언트의 16%까지 CSS에서 읽어 온다. */
.fn-name{font-size:.73rem;font-weight:750;line-height:1.2;color:var(--muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.field-node.on .fn-name{color:var(--text)}

.fn-picker{margin-top:12px;padding:10px;border:1px solid var(--brand-line);border-radius:var(--radius-sm);background:var(--surface-2)}
.fn-picker-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:8px}
.fn-picker-title{font-size:.86rem;font-weight:800}
.fn-current{font-size:.78rem;color:var(--muted)}
.fn-close{min-width:44px;min-height:44px;margin-left:auto;border-radius:10px;display:grid;place-items:center;color:var(--muted);flex:none;transition:background .15s ease,color .15s ease}
.fn-close:hover{background:var(--surface-3);color:var(--text)}
.fn-list{display:flex;flex-direction:column;gap:6px;max-height:264px;overflow-y:auto}
.fn-pick{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:6px 10px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);text-align:left;font-size:.85rem;font-weight:700;transition:border-color .16s ease,background .16s ease,color .16s ease}
.fn-pick:hover{border-color:var(--brand);background:var(--brand-soft)}
.fn-pick.clear{border-style:dashed;font-weight:650;color:var(--muted)}
.fn-pick.clear:hover{border-color:var(--danger);background:var(--danger-soft);color:var(--danger)}
.fn-num{width:31px;height:31px;border-radius:9px;background:var(--surface-3);color:var(--muted);display:grid;place-items:center;font-weight:800;font-size:.79rem;flex:none}
.fn-at{font-size:.74rem;font-weight:650;color:var(--muted)}
.fn-tags{margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}
/* --warn·--danger는 --surface(기본)와 --brand-soft(hover) 양쪽에 올라간다.
   --warn은 둘 다 TEXT_ON_SURFACES에 있고(5.26 이상), --danger는 --surface에만
   있어 --brand-soft 행을 함께 넣었다 — 상태 배경을 새로 쓰면 그 위의 잉크를
   전부 등록하는 것이 순서다(계획서 §3-1 규칙 4). */
.fn-warn{font-size:.74rem;font-weight:700;color:var(--warn)}
.fn-out{font-size:.74rem;font-weight:700;color:var(--danger)}
.fn-empty{padding:14px;text-align:center;color:var(--muted);font-size:.82rem}

.field-legend{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;font-size:.76rem;color:var(--muted);align-items:center}
.legend-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:999px;background:var(--surface-3);font-weight:650}
.legend-chip.miss{background:var(--warn-soft);color:var(--warn)}
.legend-chip.ok{background:var(--brand-soft);color:var(--brand)}

/* ── 폰에서도 야구장 그림을 본다 (2026-08-24 사용자 요청 ④) ────────────────
   원문: *"라인업화면에서 결과적으로 수비포지션에 대한 야구장포지션 화면도
   모바일화면에서 볼 수 있도록 해줘. 현재 PC화면에서는 보여."*

   예전에는 520px 아래가 통째로 격자 목록이었다. 그 판단 자체는 옳았다 —
   88×44 노드로는 폭 326px 아래에서 히트 영역이 실제로 겹쳤고(유격수 36,44와
   3루수 23,62의 세로 간격이 폭의 13.5%뿐이다), **타깃을 줄여서 맞추는 것은
   Task 2·6·17·8에 이어 다섯 번째로 금지된 처방**이다.

   그래서 타깃이 아니라 **상자를 바꾼다.** 두 가지를 함께 돌린다:

   ① 노드를 가로 칩(88×44)에서 **세로 칩(60×54)** 으로 세운다. 배지가 위,
      이름이 아래다. 가로가 28px 줄어 이제 가로로 가장 가까운 쌍(투수 50,65 ↔
      3루수 23,62 — 폭의 27%)이 여유를 갖는다.
   ② 배치도를 4/3에서 **4/5**로 세운다. 세로 간격은 전부 *높이*의 백분율이라,
      높이를 키우면 13.5%였던 유격수↔3루수 간격이 픽셀로 벌어진다.

   360px 뷰포트(배치도 폭 298px · 높이 372px)에서 잰 여유:
     · 유격수↔3루수 세로 = .18 × 372 = 67px  ≥ 노드 높이 54  → 13px 남는다
     · 투수↔3루수  가로 = .27 × 298 = 80px  ≥ 노드 폭  60  → 20px 남는다
     · 포수(y=91%) 아래 = 372 − (.91 × 372 + 27) = 6px      → 범례를 안 덮는다
   그 아래(≤359px)는 여전히 격자 목록이다. 계산이 아니라 실제 기기 폭의 문제다 —
   360px는 갤럭시, 375px는 아이폰이고, 320px는 2016년 SE뿐이다.
   `tests/test_lineup_field.py`가 이 두 구간의 기하를 각각 계산한다.

   `.field-lines`는 `preserveAspectRatio="none"`이라 4/5 상자에서 세로로 늘어난다.
   **그래도 노드와 어긋나지 않는다** — 선의 좌표도 노드의 좌표도 전부 상자에 대한
   백분율이라 같은 비율로 함께 늘어난다. 마름모가 위아래로 길어질 뿐이고, 그것은
   홈플레이트 뒤에서 본 실제 구장에 오히려 가깝다. */
@media (min-width:360px) and (max-width:520px){
  .field{aspect-ratio:4/5}
  /* 세로 칩. `width`·`height`를 **고정**으로 두는 것이 겹침 계산의 전제다
     (`test_the_node_name_cannot_grow_the_box`와 같은 이유) — 이름 길이에 따라
     상자가 변하면 위에 적은 여유가 전부 거짓이 된다. */
  .field-node{
    width:60px;height:54px;flex-direction:column;justify-content:center;gap:2px;
    padding:3px 4px;text-align:center;border-radius:16px;
    background:color-mix(in srgb,var(--surface) 88%,transparent);border:1px solid var(--border)
  }
  .field-node.on{border-color:var(--brand)}
  .fn-badge{width:26px;height:26px}
  /* 세로로 쌓았으므로 이름은 칩 폭 전체를 쓴다. 말줄임은 최상위 규칙 그대로다. */
  .fn-name{max-width:100%;line-height:1.1}
}
/* 320px급 화면에서는 그림을 포기하고 격자 목록으로 떨어뜨린다. 같은 버튼·같은
   JS를 그대로 쓰고 **배치만** 바꾼다. */
@media (max-width:359px){
  .field{aspect-ratio:auto;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:8px}
  .field-lines{display:none}
  .field-node{position:static;transform:none;width:auto;justify-content:center;background:var(--surface-2);border:1px solid var(--border)}
}

/* ── 라인업 저장 막대 (Task 11) ── */
/* 팀명 칸이 설정으로 빠지면서(2026-08-17) 비고가 폭 전체를 쓴다. */
.lineup-meta{display:grid;grid-template-columns:1fr;gap:14px;margin-top:18px}
.field-note{margin:7px 0 0;font-size:.78rem;color:var(--muted);line-height:1.55}
.field-note b{color:var(--text)}

/* 자동 저장 상태. 글자가 바뀌어도 옆 버튼이 밀리지 않게 자리를 잡아 둔다
   (레이아웃이 튀면 누르려던 버튼이 손가락 아래에서 움직인다). */
.lineup-save-state{
  display:inline-flex;align-items:center;gap:7px;min-width:15em;
  font-size:.8rem;font-weight:650;color:var(--muted)
}
.lineup-save-dot{width:8px;height:8px;border-radius:50%;background:var(--muted);flex:none}
.lineup-save-state.is-pending{color:var(--accent)}
.lineup-save-state.is-pending .lineup-save-dot{background:var(--accent)}
.lineup-save-state.is-saving{color:var(--muted)}
.lineup-save-state.is-saving .lineup-save-dot{background:var(--muted);animation:save-pulse 1s ease-in-out infinite}
.lineup-save-state.is-saved{color:var(--brand)}
.lineup-save-state.is-saved .lineup-save-dot{background:var(--brand)}
.lineup-save-state.is-error{color:var(--danger)}
.lineup-save-state.is-error .lineup-save-dot{background:var(--danger)}
@keyframes save-pulse{0%,100%{opacity:1}50%{opacity:.3}}
/* 움직임을 줄여 달라고 한 사용자에게는 깜빡이지 않는다 (스펙 §13). */
@media (prefers-reduced-motion:reduce){
  .lineup-save-state.is-saving .lineup-save-dot{animation:none}
}
/* 2026-08-18 사용자 요청 ③으로 패널 **맨 위**로 올라갔다. `margin-bottom`이
   여기 있는 이유: 바로 아래에 오는 것이 `.alert`인데 그 조각은 앱 곳곳에서
   쓰여 여백을 그쪽에 주면 관계없는 화면이 함께 벌어진다. 미는 쪽은 이 막대다. */
.lineup-actions{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:14px;margin-bottom:14px;
  padding:13px 16px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)
}
/* ── 막대 속의 상자 둘 (2026-08-24 사용자 결정, `lineup_editor.html`의 주석) ──
   폰에서는 '저장 상태 + 오더지 확정'만 탭 바 위에 고정한다. `order`는 **같은
   컨테이너의 형제**에서만 들으므로 그 둘이 따로 상자에 들어가야 하는데, 여기
   `display:contents`가 그 상자를 **PC·태블릿에서만** 걷어낸다 — 일곱이 예전처럼
   `.lineup-actions`의 flex 항목이 되어 한 줄로 선다. 폰에서는 `mobile.css`가
   같은 명시도로 `display:grid`를 다시 선언해(나중 파일이 이긴다) 상자를 되살린다.

   **이 두 줄이 `mobile.css`가 아니라 여기 있는 이유**는 하나다: `mobile.css`는
   ≤1024px 전용이고(`test_pc_is_untouched`가 전수로 지킨다) 이것은 >720px에서
   살아야 하는 규칙이다.

   `order:1`은 마크업 이동을 되돌리는 값이다. '오더지 확정'이 `.lineup-primary`로
   올라가면서 마크업에서 버튼 무리보다 **앞**에 서게 됐는데, 나머지가 전부
   `order:0`이라 이 한 줄이 확정 버튼을 다시 줄 맨 끝으로 보낸다 — 지우면 PC의
   버튼 순서가 2026-08-23 이전과 달라진다. 폰에서도 같은 값이 `.lineup-primary`
   안에서 저장 상태 **아래**를 뜻한다. */
.lineup-primary,.lineup-secondary{display:contents}
.lineup-actions .btn-primary{order:1}
/* 상자가 둘 뜨는 날(참석 취소 충돌 + 인원 부족)이 드물지 않다. 붙어 있으면
   테두리 두 개가 한 상자처럼 보여 두 번째 경고를 읽지 않는다. */
.lineup-actions ~ .alert{margin-bottom:14px}
.lineup-state{display:inline-flex;align-items:center;gap:5px;font-size:.8rem;font-weight:750;color:var(--brand)}
/* `.lineup-dirty`가 2026-08-24까지 여기 있었다. **어느 템플릿에도 없는**
   클래스였다(품질 검토 B-6) — 자동 저장으로 바뀌면서(2026-08-17) 그 자리를
   `.lineup-save-state`가 대신했는데 규칙만 남았다. `mobile.css`의 짝과
   `tests/test_mobile_shell.py`의 이름 셋을 한 번에 걷어냈다. */
.lineup-spacer{margin-left:auto}
.lineup-message{margin-top:12px}
/* 저장 막대의 버튼은 터치 타깃 44px를 지킨다 (설계 스펙 §13). `.btn`의 기본
   패딩은 min-height를 보장하지 않아 Task 2·6·8·17에서 같은 자리가 반복해서
   미달이었다. */
.lineup-actions .btn{min-height:44px}
#ws-refresher{display:none}

@media (max-width:520px){
  /* 팀명·비고를 한 줄에 두면 두 칸 다 못 쓰게 좁아진다. */
  .lineup-meta{grid-template-columns:1fr}
  /* 여기 `.lineup-spacer{margin-left:0;flex-basis:100%;height:0}`가 있었다 —
     버튼 넷이 한 줄에 안 들어가던 시절 왼쪽 상태 문구 다음에서 줄을 바꾸던
     규칙이다. **2026-08-24부터 닿지 않는다**(품질 검토 B-6): 520px는 폰
     구간(≤720px) 안이고 거기서 `mobile.css`가 같은 명시도(0,1,0)로
     `.lineup-spacer{display:none}`을 **나중에** 선언하므로 상자 자체가
     그려지지 않는다 — margin·flex-basis·height는 없는 상자의 값이었다.
     `.lineup-spacer`를 폰에서 되살리는 날에는 이 규칙이 아니라 격자 배치를
     다시 볼 것(그 상자는 격자에서 칸 하나를 먹는다). */
}
/* 인쇄 버튼이 잠겨 있는 이유를 옆에 적는다 (Task 11-P). `--muted`는 `--surface`
   위에서 7.24:1이다 — 잠긴 버튼의 짝 문구라 본문 대비를 지켜야 한다. */
.lineup-hint{font-size:.78rem;font-weight:650;color:var(--muted)}

/* ── 시간 선택 팝오버 (2026-08-17 2차 요청 ③(시간 선택)) ─────────────────
   격자 칸은 44px다(스펙 §13). Task 2·6·17·8에서 네 번 놓친 자리라 열여섯
   칸을 6열에 넣으면서도 줄이지 않는다.

   비활성 칸에 opacity를 쓰지 않는다. 대비 실측은 조상에 opacity·filter가
   없을 때만 뜻이 있어서, 색 토큰으로 낮춘다.

   **팝오버는 자기 필드보다 넓다.** 6열 × 44px + 5 × 6px + 안쪽 여백 24px +
   테두리 2px = 320px인데, `.form-grid`의 열은 200px까지 좁아진다. 그래서
   `left:0`이 아니라 `right:0`으로 매단다 — 왼쪽 기준으로 붙이면 격자가
   2열이 되는 폭(뷰포트 483~690px)에서 팝오버가 뷰포트를 최대 85px 넘어
   가로 스크롤이 생긴다. 실측 표는 `tests/test_game_time.py`의
   `test_the_popover_hangs_from_the_right_edge_of_its_field`에 있다.
   (`left`를 함께 주면 폭이 필드에 묶여 격자가 눌린다. 둘 중 하나만.) */
.time-field{position:relative}
.time-trigger{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface);color:var(--text);font-size:.95rem;font-weight:600;cursor:pointer}
.time-trigger:focus-visible{outline:none;box-shadow:var(--focus)}
.time-pop{position:absolute;z-index:30;top:calc(100% + 6px);right:0;min-width:100%;padding:12px;border:1px solid var(--border-strong);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow)}
.time-pop-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px;font-size:.85rem;font-weight:700;color:var(--muted)}
.time-clear{min-height:44px;padding:0 10px;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--brand);font-size:.85rem;font-weight:700;cursor:pointer}
.time-grid{display:grid;grid-template-columns:repeat(6,minmax(44px,1fr));gap:6px}
.time-cell{min-width:44px;min-height:44px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-2);color:var(--text);font-size:.9rem;font-weight:700;cursor:pointer}
.time-cell:focus-visible{outline:none;box-shadow:var(--focus)}
.time-cell.on{border-color:var(--brand);background:var(--brand);color:var(--brand-ink)}
.time-cell:disabled{background:var(--surface-3);color:var(--faint);cursor:not-allowed}
.time-min{display:flex;align-items:center;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}
.time-min-label{min-width:28px;font-size:.85rem;font-weight:700;color:var(--muted)}

/* ── 날짜 선택 팝오버 (2026-08-24 사용자 요청 ①) ────────────────────────────
   `<input type="date">`를 걷어낸 자리다. 사용자가 본 "오른쪽으로 삐져 나온
   리스트 박스"는 그 컨트롤의 **네이티브 달력**이었고 CSS가 닿지 않는 곳이었다.

   **팝오버는 자기 필드보다 넓다** — 7열 × 44px + 6 × 4px + 안쪽 여백 24px +
   테두리 2px = 358px인데 `.form-grid`의 열은 200px까지 좁아진다. 넘치는 쪽을
   어디로 보낼지가 문제이고, **답이 시각 팝오버와 반대다.**

   시각 팝오버는 `right:0`이다. `.form-grid`가 2열이 되는 폭(483px 이상)에서
   `시작 시각`은 **오른쪽 칸**이라, 왼쪽으로 넘쳐야 화면 안에 남는다.
   `경기 날짜`는 그 격자의 **첫 칸**이다 — 늘 왼쪽 칸이므로 정확히 반대로,
   오른쪽으로 넘쳐야 한다. `right:0`을 그대로 베껴 왔더니 483·690·1280px에서
   팝오버 왼쪽 끝이 각각 -38 · -126 · -75px으로 **화면 밖으로 나갔다**(실측).
   같은 처방이 자리에 따라 반대 결과를 낸다.

   폰(≤482px)은 또 다르다. 1열이라 필드가 카드 폭 전체인데 팝오버가 그보다도
   넓어서, 어느 쪽 끝에 매달아도 반대편이 나간다 — 아래 폰 블록이 화면 가운데에
   맞춘다. `max-width`로 뷰포트에 묶는 것은 그 위에 덧대는 마지막 안전선이다.
   `left`와 `right`를 **함께** 주지 말 것 — 폭이 필드에 묶여 격자가 눌린다. */
.date-field{position:relative}
/* 높이는 **44px이다 — `.time-trigger`·`.form-field input`과 같은 값.** 여기만
   48로 키우면 격자가 2열이 되는 폭(483px 이상)에서 `경기 날짜`(왼쪽 칸)와
   `시작 시각`(오른쪽 칸)이 나란히 서면서 4px 어긋난다. 이 라운드가 키우기로 한
   것은 ③ 라인업의 컨트롤 셋과 목록의 '수정' 버튼이지 폼 입력칸이 아니다. */
.date-trigger{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface);color:var(--text);font-size:.95rem;font-weight:600;cursor:pointer}
.date-trigger:focus-visible{outline:none;box-shadow:var(--focus)}
.date-trigger .icon{color:var(--muted);flex:none}
.date-pop{
  position:absolute;z-index:30;top:calc(100% + 6px);left:0;min-width:100%;
  max-width:calc(100vw - 32px);
  padding:12px;border:1px solid var(--border-strong);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow)
}
.date-pop-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;font-size:.9rem;font-weight:800}
/* 달 넘기기 버튼도 44px다. 팝오버 안이라고 예외가 되지 않는다 — 스펙 §13의
   예외는 본문 안 인라인 링크뿐이다. */
.date-nav{min-width:44px;min-height:44px;border-radius:10px;display:grid;place-items:center;border:1px solid var(--border);background:var(--surface);color:var(--muted);cursor:pointer;flex:none}
.date-nav:hover{background:var(--surface-3);color:var(--text)}
.date-nav:focus-visible{outline:none;box-shadow:var(--focus)}
/* **`minmax(0,1fr)`이 아니라 `minmax(44px,1fr)`이다.** 0으로 두면 격자의
   최소 폭이 0이라, 폭이 정해지지 않은(shrink-to-fit) 절대 배치 팝오버가 자기
   필드 폭(200px까지 좁아진다)에 맞춰 서고 **44px 칸 일곱은 그 상자 밖으로
   흘러나간다** — 브라우저로 483·520·690·1280px에서 실측한 결함이다. 흰 배경
   밖에 날짜가 떠 있어도 문서 넘침은 0이라 기하 스윕도 못 잡았다.
   `.time-grid`가 같은 이유로 `minmax(44px,1fr)`을 쓰고 있다. */
.date-week,.date-grid{display:grid;grid-template-columns:repeat(7,minmax(44px,1fr));gap:4px}
.date-wd{text-align:center;font-size:.74rem;font-weight:800;color:var(--muted);padding-bottom:4px}
/* 주말 색. **색만으로 뜻을 나르지 않는다** — 요일 머리글이 글자로 이미 '일'·'토'를
   말하고, 날짜 버튼의 접근 가능한 이름도 요일을 통째로 읽는다(`aria-label`).
   여기서 색은 훑는 속도를 돕는 보조 신호다. */
.date-wd.sun,.date-cell.sun{color:var(--danger)}
.date-wd.sat,.date-cell.sat{color:var(--brand)}
.date-cell{
  min-width:44px;min-height:44px;display:grid;place-items:center;
  border:1px solid transparent;border-radius:12px;background:transparent;
  color:var(--text);font-size:.9rem;font-weight:700;cursor:pointer;
  transition:background .15s ease,border-color .15s ease,color .15s ease
}
.date-cell:hover{background:var(--surface-3)}
.date-cell:focus-visible{outline:none;box-shadow:var(--focus)}
/* 이번 달 밖의 날. `opacity`를 쓰지 말 것 — 하위 트리를 배경 위로 합성해
   대비만 무너뜨린다(`.game-card.past`·`.log-table tr.inactive`에서 두 번
   되돌린 처방이다). 색 토큰으로 낮춘다. */
.date-cell.outside{color:var(--faint);font-weight:600}
/* '오늘'은 테두리, '고른 날'은 채움. 둘이 같은 날일 수 있으므로 서로 다른
   축으로 말한다 — 색 하나로 겹치면 오늘을 고른 순간 표시가 사라진다. */
.date-cell.today{border-color:var(--border-strong)}
.date-cell.on{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.date-cell.on.sun,.date-cell.on.sat{color:var(--brand-ink)}
.date-pop-foot{display:flex;justify-content:flex-end;margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}
.date-today{min-height:44px;padding:0 14px;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--brand);font-size:.85rem;font-weight:700;cursor:pointer}
.date-today:hover{background:var(--brand-soft)}
.date-today:focus-visible{outline:none;box-shadow:var(--focus)}

/* 폰에서는 **필드가 아니라 화면 가운데**에 매단다.
   격자가 최소 332px(7 × 44 + 6 × 4)인데 360px 화면의 필드는 298px뿐이다 —
   팝오버가 자기 필드보다 넓으므로 왼쪽 끝에 매달면 오른쪽이, 오른쪽 끝에
   매달면 왼쪽이 나간다. 어느 쪽 앵커도 답이 아니다.

   **상한이 482px인 것이 중요하다.** `.form-grid`는 `minmax(200px,1fr)` 두 칸이
   들어가는 483px부터 2열이 되고, 그때 `경기 날짜`는 화면 가운데가 아니라
   **왼쪽 칸**에 선다. 520px까지 이 규칙을 걸었더니 483·520px에서 팝오버가
   각각 -38 · -29px으로 화면 밖으로 나갔다(실측). 1열일 때만 "필드 가운데 =
   화면 가운데"가 참이다.

   `right:auto`가 짝이다 — `left`와 `right`를 함께 두면 폭이 다시 필드에 묶인다. */
@media (max-width:482px){
  .date-pop{left:50%;right:auto;transform:translateX(-50%);max-width:calc(100vw - 16px);padding:10px}
  .date-week,.date-grid{gap:2px}
}
/* ── 320px급 화면의 알려진 한계 (2026-08-24) ───────────────────────────────
   44px 칸 일곱은 308px이고 여기에 안쪽 여백과 테두리가 더 붙는다 — **320px
   화면에는 물리적으로 들어가지 않는다.** 칸을 그대로 두면 격자가 화면을 26px
   흘리고(실측), 그것은 요청받은 결함과 같은 종류다.
   그래서 이 폭에서만 칸이 줄어든다(약 38px). 배치도가 같은 360px 경계에서
   격자 목록으로 떨어지는 것과 같은 판단이다 — 360px는 갤럭시, 375px는
   아이폰이고 320px는 2016년 SE뿐이다.
   **이 규칙이 360px 위로 번지면 안 된다.** `tests/test_game_date.py`의
   `test_the_44px_floor_is_only_relaxed_below_the_narrowest_real_phone`가
   그 경계를 지킨다 — 좁은 화면에서만 타깃을 줄이는 것은 이 저장소가 다섯 번
   되풀이한 결함이라, 한계를 두더라도 보이는 자리에 둔다. */
@media (max-width:359px){
  .date-week,.date-grid{grid-template-columns:repeat(7,minmax(0,1fr))}
  /* **트랙만 풀면 아무 일도 안 일어난다.** 칸 자신의 `min-width:44px`가 트랙과
     무관하게 버텨서, 320px에서 격자가 그대로 화면을 26px 흘렸다(실측). 한계를
     두기로 했으면 여기까지 풀어야 실제로 풀린다. */
  .date-cell{min-width:0}
}

/* ── 투수 교체 플랜 (Task 13) ── */
.plan-badge{display:inline-flex;align-items:center;gap:5px;font-size:.7rem;font-weight:800;padding:3px 9px;border-radius:999px;background:var(--accent-soft);color:var(--accent)}
.plan-list{display:flex;flex-direction:column;gap:7px}
.plan-row{display:grid;grid-template-columns:30px 1fr auto auto;gap:10px;align-items:center;padding:9px 12px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-2);min-height:52px}
.plan-seq{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;font-weight:800;font-size:.78rem;background:var(--surface-3);color:var(--muted)}
.plan-row.is-starter .plan-seq{background:var(--accent);color:#fff}
.plan-row.is-closer .plan-seq{background:var(--brand);color:var(--brand-ink)}
.plan-name{display:block;font-weight:700;font-size:.89rem}
.plan-note{display:block;font-size:.75rem;color:var(--faint)}
.plan-note-input{width:100%;font-size:.75rem;padding:3px 6px;border-radius:6px;border:1px solid var(--border);background:var(--surface);min-height:28px;margin-top:2px}
.plan-role{font-size:.74rem;font-weight:750;padding:4px 10px;border-radius:999px;background:var(--surface-3);color:var(--muted);white-space:nowrap}
.plan-row.is-starter .plan-role{background:var(--accent-soft);color:var(--accent)}
.plan-row.is-closer .plan-role{background:var(--brand-soft);color:var(--brand)}
.plan-empty{padding:18px;text-align:center;color:var(--faint);font-size:.83rem;border:1px dashed var(--border);border-radius:var(--radius-sm)}
.plan-add{display:flex;gap:6px;flex-wrap:wrap;margin-top:11px}
.plan-add-sel{font-size:.78rem;padding:6px 9px;border-radius:999px;border:1px solid var(--border-strong);background:var(--surface);min-height:36px;flex:1;min-width:140px}
.plan-add-btn{font-size:.77rem;font-weight:650;padding:7px 11px;border-radius:999px;min-height:36px;border:1px solid var(--border-strong);color:var(--muted);background:var(--surface);transition:border-color .16s ease,color .16s ease,background .16s ease}
.plan-add-btn:hover:not(:disabled){border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
.print-note{display:flex;gap:9px;align-items:flex-start;margin-top:12px;padding:10px 12px;border-radius:var(--radius-sm);background:var(--surface-2);border:1px solid var(--border);font-size:.77rem;color:var(--muted);line-height:1.5}

/* ── 결과 · 기록 (Task 14) ── */
.locked{display:flex;flex-direction:column;align-items:center;gap:12px;padding:56px 24px;text-align:center;color:var(--muted)}
.locked svg{color:var(--faint)}
.locked h3{font-size:1.05rem;font-weight:750;color:var(--text)}
.locked p{font-size:.86rem}
/* `.score-row` 세 줄과 옛 `.score-colon`은 2026-08-25 A안 Task 4가 걷어냈다 —
   스코어는 파일 끝의 `.score-card` 구획이 그린다(`.score-colon`도 거기서 새로 정의). */
/* 저장 알림. 라이브 영역이라 비어 있을 때는 자리를 차지하지 않아야 한다. */
.result-note:not(:empty){margin-top:13px;font-size:.85rem;font-weight:650;color:var(--brand)}

/* ── 선수별 기록 (Task 16) ── */
/* `.log-table` 세 줄과 `.log-name`은 Task 17(먼저 실행됨)이 선수 목록 표에
   쓰면서 이미 넣었다 — 여기서 다시 쓰면 같은 선택자가 파일에 두 번 생기는데
   값이 같아 화면은 안 깨지고 그래서 아무도 눈치채지 못한다.
   `test_the_log_table_rule_is_not_defined_twice`가 지킨다. */
.log-num{font-weight:400;color:var(--faint);font-size:.78rem}
.log-none{color:var(--faint)}
/* `.log-grid` 두 줄(Task 16의 인라인 폼 격자)은 2026-08-25 A안이 폼을 카드로
   바꾸면서 걷어냈다 — 쓰는 템플릿이 없다. */
/* 빈 슬롯은 그리드에서 빠져야 한다 — 지우면 `.record-split`이 1열로 접혔을 때도
   빈 슬롯이 암시적 둘째 칸을 만들어 gap 16px가 남는다(파일 끝 `.record-split`). */
#player-log-slot:empty{display:none}

/* ── 선수 카드 (2026-08-25 A안, 스펙 §4) ─────────────────────────────────── */
/* Task 5(카드 속) · Task 6(폰 규칙은 mobile.css의 기존 블록)이 이 구획에 이어 붙인다. */
/* 스코어 카드. 숫자는 <input>이라 자판으로도 넣는다 — 버튼은 그 값을 바꿀 뿐.
   숫자 글꼴은 대시보드 히어로(`.kpi .v`)와 같은 본문 글꼴 800이다 — Do Hyeon
   서브셋은 고정 문구용이고(CLAUDE.md), 큰 숫자의 관례가 이미 이쪽이다.
   높이 규칙이 없는 것은 의도다: line-height 1이라 글자 크기 3.2rem(48px)이 곧
   높이(≥44px, 스펙 §13) — 폰트를 줄이면 `test_the_score_inputs_keep_the_touch_
   target_floor`가 잡는다. */
.score-card{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:12px;padding:16px 18px 12px}
.score-team{display:flex;flex-direction:column;align-items:center;gap:4px}
.score-name{font-size:.8rem;font-weight:800;color:var(--muted);letter-spacing:.04em}
.score-digit{width:3ch;text-align:center;font-size:3.2rem;font-weight:800;line-height:1;border:0;background:transparent;padding:0;color:var(--text);-moz-appearance:textfield}
.score-digit::-webkit-outer-spin-button,.score-digit::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.score-digit::placeholder{color:var(--faint)}
.score-steps{display:flex;gap:8px}
.score-step{width:48px;height:48px;border-radius:12px;border:1px solid var(--border-strong);background:var(--surface-2);display:grid;place-items:center;transition:background .15s}
.score-step:hover{background:var(--surface-3)}
.score-mid{display:flex;flex-direction:column;align-items:center;gap:6px;min-width:56px}
.score-colon{font-size:2.2rem;font-weight:800;line-height:1;color:var(--faint)}
.score-verdict{font-size:.8rem;padding:4px 12px}
/* 결과가 없으면 배지는 **빈 채로 남는다** — OOB는 대상 id가 문서에 있어야
   갈아 끼우므로(`score_verdict.html`) 요소를 지우는 대신 여기서 감춘다. */
.score-verdict:empty{display:none}
/* 저장 중 점 — htmx가 요청 중에 폼에 `htmx-request`를 붙인다. */
.saving-dot{width:8px;height:8px;border-radius:50%;background:var(--brand);opacity:0;transition:opacity .15s}
.htmx-request .saving-dot{opacity:1}
.record-tally{display:flex;align-items:center;gap:12px;padding:10px 18px 14px;border-top:1px solid var(--border);font-size:.8rem;color:var(--muted);flex-wrap:wrap}
.record-tally .progress{flex:1;min-width:120px;height:8px;border-radius:999px;background:var(--surface-3);overflow:hidden}
.record-tally .progress i{display:block;height:100%;background:var(--brand);border-radius:999px;transition:width .25s}
.record-tally b{color:var(--text)}
.record-tally.ok .tally-check{color:var(--brand);font-weight:700}
.record-tally.warn .tally-check{color:var(--accent);font-weight:700}
.review-fold{border-top:1px solid var(--border)}
.review-fold summary{display:flex;align-items:center;gap:10px;min-height:48px;padding:0 18px;cursor:pointer;font-weight:750;font-size:.95rem;list-style:none}
.review-fold summary::-webkit-details-marker{display:none}
.review-fold summary::before{content:"";width:8px;height:8px;border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);transform:rotate(-45deg);flex:none;transition:transform .15s}
.review-fold[open] summary::before{transform:rotate(45deg)}
.review-peek{font-weight:600;font-size:.8rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.review-body{padding:0 18px 16px}
/* 폼에 card-body가 없어 오류 배너가 카드 모서리에 붙는다 — 배너가 있을 때만 띄운다. */
#result-error:not(:empty){padding:12px 18px 0}
.result-foot{display:flex;align-items:center;gap:12px;padding:10px 18px 14px;border-top:1px solid var(--border)}
/* .78rem(11.7px)이 아니라 .8rem — scripts/mobile_check.py의 12px 하한. */
.result-hint{font-size:.8rem;color:var(--muted);flex:1}
/* 작은 보조 버튼. min-height 44px(.btn)는 그대로다 — 패딩·글자만 줄인다(스펙 §13). */
.btn-sm{padding:6px 12px;font-size:.8rem}
/* 목록과 카드. **1열이 기본이다** — 문서 순서가 슬롯 먼저라 폰·태블릿에서 카드가
   목록 **위**에 온다(2026-08-25 검토 I-4). 2열을 기본으로 두면 721~1024px 태블릿이
   끼인다: `380px minmax(0,1fr)`가 768px에서 카드에 ~330px만 남겨 타석 칩 열둘과
   −/+ 여섯이 안 들어간다. 폭이 진짜 넉넉할 때만 옆에 세운다.
   덕분에 mobile.css는 `.record-split`을 손댈 일이 없다(Task 6). */
.record-split{display:grid;grid-template-columns:1fr;gap:16px;align-items:start}
.record-slot{min-width:0}
.record-list{min-width:0}
/* PC(태블릿 1024px **위**)에서만 목록 380px + 카드. 경계값은 모바일 셸이 태블릿을
   1024까지로 잡은 것과 한 벌이다 — mobile.css는 ≤1024px만 건드린다.
   `grid-column` 두 줄이 문서 순서를 되돌린다: 슬롯이 먼저 쓰였지만 오른쪽 칸이다.
   슬롯이 비면 여기서도 다시 1열 — `:has()`는 역할 체크박스가 이미 쓴다. */
@media (min-width:1024.02px){
  .record-split{grid-template-columns:380px minmax(0,1fr)}
  .record-split:has(#player-log-slot:empty){grid-template-columns:1fr}
  .record-slot{grid-column:2;grid-row:1}
  .record-list{grid-column:1;grid-row:1}
  /* 표를 380px 칸에 넣는 조정이라 2열일 때만 걸어야 한다. `:where()`로 명시도를
     (0,1,0)/(0,1,1)에 묶는다 — `.record-split .log-table`로 쓰면 (0,2,0)이 되어
     폰의 카드 행 규칙(mobile.css)을 도로 덮는다. */
  :where(.record-split) .log-table{min-width:0;font-size:.82rem}
  :where(.record-split) .log-table th,:where(.record-split) .log-table td{padding:8px 6px}
}
/* ── 선수 카드 (2026-08-25 A안, 스펙 §4-4·§4-6) ──
   폰·태블릿 규칙은 mobile.css의 **있는** 블록 안에 있다(Task 6) — 여기 새 @media를
   만들지 않는다. 누르는 것의 하한은 스펙 §13의 44px이고 두 테스트가 함께 지킨다
   (`test_record_card_ui`는 값을, `test_game_logs_ui`는 바닥을). */
.pcard-head{display:flex;align-items:center;gap:12px;padding:14px 18px;border-bottom:1px solid var(--border);flex-wrap:wrap}
.pcard-head .slot-no{width:40px;height:40px;font-size:.95rem}
.pcard-head h2{font-size:1.15rem;font-weight:800;letter-spacing:-.01em;min-width:0}
.pcard-head h2 .sub{font-size:.8rem;color:var(--muted);font-weight:600;word-break:keep-all}
.pcard-head h2:focus-visible{border-radius:6px}
.pcard-live{margin-left:auto;font-size:.8rem;font-weight:700;color:var(--brand);white-space:nowrap}
.pcard .sec{padding:14px 18px;border-bottom:1px solid var(--border)}
.pcard .sec-h{display:flex;align-items:baseline;gap:4px 8px;margin-bottom:10px;flex-wrap:wrap}
.pcard .sec-h h3{font-size:.86rem;font-weight:800}
.pcard .sec-h small{font-size:.76rem;color:var(--faint)}
.pcard .rebuilt{margin:10px 18px 0;color:var(--warn)}
/* 오류 배너가 카드 모서리에 붙지 않게 — `#result-error`와 같은 방식(배너가 있을 때만). */
#player-log-error:not(:empty){padding:12px 18px 0}
.pa-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.pa-chip{min-width:74px;min-height:56px;padding:0 10px;border-radius:12px;border:1.5px solid var(--border-strong);background:var(--surface);display:inline-flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.15;font-weight:800;font-size:.92rem;color:var(--text);transition:background .15s,border-color .15s,box-shadow .15s}
.pa-chip small{font-size:.8rem;font-weight:700;color:var(--faint);letter-spacing:.02em}
/* 첫 칩('1타석 / 결과 고르기')은 `<template x-if>`가 글자를 span 하나로 싸서 넣는다 —
   그 span도 세로로 쌓여야 채워진 칩과 같은 두 줄 모양이 된다(2026-08-25 눈확인:
   한 줄로 붙어 "1타석결과 고르기"였다). */
.pa-chip>span{display:flex;flex-direction:column;align-items:center}
.pa-chip.hit{background:var(--brand-soft);border-color:var(--brand-line);color:var(--brand)}
.pa-chip.hit small{color:var(--brand)}
.pa-chip.out{background:var(--surface-3);border-color:var(--surface-3);color:var(--muted)}
.pa-chip.ob{background:var(--surface-2);border-style:dashed}
.pa-chip.empty{border-style:dashed;color:var(--faint);font-weight:700;background:transparent}
.pa-chip.add{border-style:dashed;color:var(--brand);background:transparent;min-width:58px;font-size:.82rem}
.pa-chip[aria-pressed="true"]{box-shadow:var(--focus);border-color:var(--brand)}
/* 피커 — PC는 칩 행 아래 인라인. ≤720px는 mobile.css가 바텀시트로 세운다(dim·pane이 그때 쓰인다). */
.pa-picker{margin-top:12px}
.pa-picker-dim{display:none}
.pa-picker-pane{padding:12px;border:1px solid var(--brand-line);border-radius:12px;background:var(--surface-2)}
.pa-picker-h{display:flex;align-items:center;gap:8px;font-size:.8rem;font-weight:700;color:var(--muted);margin-bottom:10px}
.pa-picker-x{margin-left:auto;width:44px;height:44px;border-radius:9px;display:grid;place-items:center;color:var(--muted)}
.pa-picker-x:hover{background:var(--surface-3)}
.pa-picker-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px}
.pa-pick{min-height:52px;border-radius:10px;border:1.5px solid var(--border-strong);background:var(--surface);font-weight:800;font-size:.9rem;display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.1;transition:background .15s,border-color .15s}
.pa-pick small{font-size:.8rem;color:var(--faint);font-weight:700}
.pa-pick.hit{border-color:var(--brand-line);color:var(--brand)}
.pa-pick:hover{background:var(--brand-soft);border-color:var(--brand)}
.pa-pick[aria-pressed="true"]{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.pa-pick[aria-pressed="true"] small{color:var(--brand-ink)}
.pa-picker-foot{display:flex;gap:8px;margin-top:10px;align-items:center;font-size:.78rem;color:var(--faint)}
.pa-del{margin-left:auto;color:var(--danger);font-weight:700;min-height:44px;padding:0 10px;border-radius:8px}
/* 스테퍼. 이름이 `.stepper`가 아닌 것은 그 이름을 워크스페이스 단계 탭이 이미 쓰기 때문이다. */
.counters{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
/* 투수 칸 여섯. **한 줄에 여섯은 못 넣는다** — 한 칸에 −/+ 88px + 여백 + 숫자라 최소
   134px인데, PC 2열에서 카드 칸이 ~760px이라 6열이면 한 칸이 112px이다. 고정 6열로
   두었더니 버튼이 상자 밖으로 삐져나가 옆 칸에 덮여 **눌리지 않았다**(2026-08-25
   눈확인). 들어가는 만큼만 세우고 나머지는 다음 줄로 접는다 — 넘치는 일이 없다. */
.counters.six{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.counter{border:1px solid var(--border);border-radius:12px;padding:10px 12px 12px;background:var(--surface);display:flex;flex-direction:column;gap:6px}
.counter label{font-size:.8rem;font-weight:800;color:var(--muted)}
.counter-row{display:flex;align-items:center;gap:8px}
.counter output{font-size:1.9rem;line-height:1;min-width:2.2ch;text-align:center;flex:1}
.counter button{width:44px;height:44px;border-radius:12px;border:1px solid var(--border-strong);background:var(--surface-2);display:grid;place-items:center;flex:none}
.counter button:hover{background:var(--surface-3)}
.more-fold{margin-top:10px}
.more-fold summary{font-size:.8rem;color:var(--muted);cursor:pointer;min-height:44px;display:flex;align-items:center}
.more-fold .counters{margin-top:8px}
.decision{display:flex;gap:8px;margin-top:10px}
.decision .pa-chip{min-width:64px;min-height:44px}
.note-line input{width:100%;min-height:44px;padding:8px 12px;border:1px solid var(--border-strong);border-radius:10px;background:var(--surface);font-size:.9rem}
.note-line input::placeholder{color:var(--faint)}
.pcard-foot{display:flex;gap:10px;align-items:center;padding:12px 18px;background:var(--surface-2);border-top:1px solid var(--border);border-radius:0 0 var(--radius) var(--radius)}
.pcard-pos{flex:1;text-align:center;font-size:.78rem;color:var(--muted);line-height:1.25}
.pcard-pos b{display:block;color:var(--text);font-weight:700}
.pcard-foot .btn-primary{margin-left:auto}
/* prefers-reduced-motion: 이 구획의 전이(saving-dot·진행 막대·접힘 갈매기·칩)는 위
   전역 블록(`*{transition:none!important}`)이 이미 다 끈다 — 새 @media를 만들지 않는다. */

/* ── 선수단 명단 A안 (2026-08-27, 스펙 §5-1 · §6-1 · §6-2) ────────────────
 * 행마다 있던 '수정' 버튼 23개가 없어지고 **행 자체**가 상세를 연다. 포인터
 * 편의는 `<tr>`이 주고 키보드·낭독기의 의미는 이름 칸 안의 `.row-open` 버튼이
 * 갖는다 — `<tr>`에 `role="button"`을 주면 표의 의미가 망가진다.
 *
 * **PC까지 걸리는 것이 요청이다**(스펙 §1 ①: "폰·PC 양쪽에서" 사라진다) —
 * 그래서 mobile.css가 아니라 여기 있다. mobile.css는 ≤1024px로 닫혀 있다.
 */
.pick-row{cursor:pointer}
.pick-row:hover{background:var(--surface-2)}
/* 상세가 열려 있는 행 (스펙 §6-1). 표시를 놓는 것은 `roster.js`이고 여기서는
   칠하기만 한다 — 상태의 근원이 `aria-selected` 하나라 낭독기와 CSS가 같은
   것을 읽는다. **`:hover` 아래에 있어야 한다**: 둘 다 (0,2,0)이라 마우스를
   올린 순간 선택 표시가 사라지지 않으려면 순서로 이겨야 한다. */
.pick-row[aria-selected="true"]{background:var(--brand-soft)}
.row-open{border:0;background:none;padding:0;font:inherit;color:inherit;
  text-align:left;cursor:pointer}
.row-open:focus-visible{outline:none;box-shadow:var(--focus);border-radius:4px}

/* 폰 요약(`앞 둘 +n`)은 PC에서 숨는다. 서버는 둘 다 내려보내고 CSS가 고른다 —
   검색 조각(`GET /roster/rows`) 하나가 폰·PC를 함께 그려야 하기 때문이다. */
.pos-short{display:none}
.pos-short i{font-style:normal;color:var(--faint)}

/* 초성 레일과 그룹 머리는 **폰 전용**이다 (2026-08-27 A안 Task 5, 스펙 §5-2).
   PC 표에는 초성 줄이 끼면 안 되고(정렬된 표에서 이정표는 잡음이다), 레일은
   마우스에 아무 값이 없다 — 폭이 넓어 목록 전체가 한 화면에 든다.
   `mobile.css`의 `(max-width:720px)` 블록이 이 둘을 켠다. */
.cho-head{display:none}
.cho-rail{display:none}

/* ── 상태 요약 띠 (2026-08-27 A안 Task 6, 스펙 §5-3) ───────────────────────
   `활동 21 · 재활 1 · 비활동 1`. 값은 `.filter-chip`에서 그대로 가져온다 —
   같은 화면의 같은 갈래(필터를 거는 알약)라 둘이 다르게 생기면 하나만 필터로
   읽힌다. **44px은 PC에서도 유지한다**: `.filter-chip`이 이미 그렇고, 여기만
   36px로 두면 폰에서 mobile.css가 다시 올려야 하는데 그 규칙이 ≤720/≤1024 두
   블록의 충돌 감시(`test_no_later_block_takes_back_what_an_earlier_one_said`)에
   걸릴 자리를 하나 더 만든다.

   `flex-wrap`이 요점이다 — 375px에서 칩 셋이 한 줄에 못 서면 카드가 넘치는데,
   이 줄은 `.table-scroll` 바깥이라 **문서 전체가 가로로 밀린다.** */
.status-strip{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px}
.status-chip{
  display:inline-flex;align-items:center;gap:6px;padding:6px 13px;border-radius:999px;
  border:1px solid var(--border-strong);background:var(--surface-2);font-size:.82rem;
  font-weight:650;color:var(--text);text-decoration:none;min-height:44px;
  transition:background .18s ease,border-color .18s ease,color .18s ease
}
.status-chip b{font-weight:800;font-variant-numeric:tabular-nums}
.status-chip:hover{background:var(--surface-3);border-color:var(--brand)}
/* 걸린 칩은 색만으로 말하지 않는다 — 테두리와 글자 두께가 함께 바뀐다
   (스킬 규칙: Color is not the only indicator). 낭독기에는 `aria-current`가
   그대로 읽힌다. `.filter-chip:has(input:checked)`와 같은 값이다. */
.status-chip[aria-current]{background:var(--brand-soft);border-color:var(--brand);color:var(--brand);font-weight:750}

/* ── 포지션 보드는 **폰·태블릿 전용**이다 (2026-08-27 A안 Task 7, 스펙 §5-4) ──
   PC에는 왼쪽 사이드바(`.pos-side`)가 있어 같은 말을 두 번 하게 된다.
   `.cho-rail`·`.cho-head`와 같은 규약으로 여기서 끄고 mobile.css의
   `(max-width:1024px)` 블록이 켠다. */
.pos-board{display:none;border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--surface-2);margin:0 0 12px}
/* `list-style:none` + `::-webkit-details-marker`는 기본 삼각형을 지운다.
   대신 `::after`가 방향을 그린다 — 삼각형을 그냥 지우면 접히는 상자라는 것을
   눈으로 알 방법이 없어진다. */
.pos-board>summary{display:flex;align-items:center;gap:8px;min-height:44px;padding:8px 13px;
  cursor:pointer;font-size:.82rem;font-weight:650;color:var(--muted);list-style:none}
.pos-board>summary::-webkit-details-marker{display:none}
.pos-board>summary b{color:var(--text);font-weight:750}
.pos-board>summary::after{content:"▾";margin-left:auto;color:var(--faint)}
.pos-board[open]>summary::after{content:"▴"}
.pos-board>summary:focus-visible{outline:none;box-shadow:var(--focus);border-radius:var(--radius-sm)}
/* `minmax(0,1fr)`이라야 자동 최소 크기가 트랙 바닥을 안 정한다 — `1fr`만 쓰면
   320px에서 세 칸이 화면 밖으로 흐른다(`.stepper`가 이미 값을 치른 자리다). */
.pos-tiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:0 13px 13px}
.pos-tile{display:flex;flex-direction:column;justify-content:center;gap:2px;min-height:56px;
  padding:8px 10px;border-radius:var(--radius-sm);border:1px solid var(--border);
  background:var(--surface);text-decoration:none;color:var(--text);font-size:.82rem;
  transition:background .18s ease,border-color .18s ease}
.pos-tile:hover{background:var(--surface-3);border-color:var(--brand)}
.pos-tile .pos-nm{font-weight:650}
.pos-tile .pos-ct{color:var(--faint);font-size:.8rem}
.pos-tile .pos-ct b{color:var(--text);font-weight:800;font-variant-numeric:tabular-nums}
.pos-tile[aria-current]{background:var(--brand-soft);border-color:var(--brand);color:var(--brand);font-weight:750}
/* 얇은 자리는 글자('얇음')로도 말한다 — 여기 테두리는 그 글자의 보조다. */
.pos-tile.is-thin{border-color:var(--accent)}

/* ── 포지션 사이드바는 **PC 전용**이다 (스펙 §6-3) ──────────────────────────
   표 왼쪽 216px. `<details>`가 아니라 그냥 목록인 것은 세로 공간을 안 쓰기
   때문이다. mobile.css의 `(max-width:1024px)`가 이것을 끈다. */
.pos-side{width:216px;flex:none;position:sticky;top:16px;background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow-sm);padding:12px}
.pos-title{font-size:.78rem;font-weight:800;color:var(--faint);margin:2px 0 8px;padding:0 9px}
.pos-row{display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 9px;
  border-radius:var(--radius-sm);text-decoration:none;color:var(--text);font-size:.82rem;
  transition:background .18s ease}
.pos-row:hover{background:var(--surface-3)}
.pos-row[aria-current]{background:var(--brand-soft);color:var(--brand);font-weight:750}
.pos-n{margin-left:auto;color:var(--faint);font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}
.pos-row[aria-current] .pos-n{color:inherit}
.pos-row.is-thin .pos-n{color:var(--accent)}
.pos-all{font-weight:700}
.pos-rule{height:1px;background:var(--border);margin:8px 0}

/* 상세는 **비어 있으면 자리를 안 쓴다.** `:empty`가 그 판정이다 — JS로 클래스를
   토글하면 htmx 스왑 뒤에 되돌리기를 잊는 자리가 생긴다.
   첫 칸이 포지션 사이드바(PC 전용, `flex:none`)라 셋이 나란히 선다. */
.roster-main{display:flex;gap:16px;align-items:flex-start}
.roster-main>.card{flex:1;min-width:0}
.detail-host{width:340px;flex:none;position:sticky;top:16px}
.detail-host:empty{display:none}

/* PC에서 표가 좁아지는 것을 **포지션 칸이 뒤집어쓰지 않게** 한다 (2026-08-27).
   사이드바 216px과 상세 패널 340px이 붙으면서 1280px에서 표에 남는 폭이 ~600px가
   됐다. `.log-table{min-width:640px}`이 그 폭에 거의 딱 맞아 표가 가로로 스크롤되는
   대신 **칸끼리 나눠 갖는데**, `.log-memo{max-width:22em}`이 먼저 챙겨 가는 바람에
   포지션이 다섯 줄로 접히고 행이 200px가 됐다(실측).

   순서를 뒤집는다: 포지션은 이 화면에서 가장 자주 읽는 칸이라 최소 폭을 갖고,
   장단점은 어차피 한 줄 말줄임 요약이며 **전문이 상세 패널에 있다**. `mobile_check`는
   ≤1024px만 재므로 이 회귀를 잡지 못한다 — PC는 사람이 봐야 한다. */
/* `.log-table{min-width:640px}`은 **여섯 열** 기준으로 정한 바닥이다(2026-08-18에
   '장단점' 열이 늘면서 잡았다). 이제 '관리' 열이 빠져 다섯이고, 사이드바·상세
   패널이 붙어 표에 남는 폭이 1280px에서 ~604px다 — 640이 그보다 커서 **내용과
   무관하게** 가로로 넘치고, 맨 오른쪽 '상태' 칸이 스크롤 밖으로 밀렸다(실측 74px).
   열이 하나 줄었으니 바닥도 같이 줄인다. */
.roster-table{min-width:560px}
.roster-table td:nth-child(3),.roster-table th:nth-child(3){min-width:13em}
.roster-table .log-memo{max-width:11em}
/* 상태 칸이 좁아 '재활'이 '재/활' 두 줄로 접혔다 — 알약은 접히면 알약이 아니다
   (폰에서 `.log-table .mini`가 같은 이유로 `flex:none`을 갖는다). */
.roster-table .mini{white-space:nowrap}
.dsheet-dim{display:none}          /* 어둠막은 폰 전용 (Task 5) */
.dsheet-grab{display:none}         /* 손잡이도 폰 전용 (Task 5) */
.dsheet-pane{background:var(--surface);border:1px solid var(--border);
  border-radius:var(--radius);padding:16px;box-shadow:var(--shadow-sm)}
.dsheet-top{display:flex;align-items:center;gap:11px;margin-bottom:4px}
.dsheet-no{width:46px;height:46px;flex:none;border-radius:13px;
  background:var(--brand-soft);color:var(--brand);display:grid;place-items:center;
  font-weight:800;font-variant-numeric:tabular-nums}
.dsheet-who{min-width:0}
.dsheet-nm{font-size:1.2rem;font-weight:800;display:flex;align-items:center;gap:7px}
.dsheet-sub{font-size:.8rem;color:var(--faint)}
.dsheet-x{margin-left:auto;flex:none}
.dsheet-lbl{font-size:.78rem;font-weight:800;color:var(--faint);margin:16px 0 8px}
.dsheet-chips{display:flex;flex-wrap:wrap;gap:6px}
.dsheet-chip{font-size:.82rem;font-weight:650;padding:6px 11px;border-radius:999px;
  background:var(--surface-3);color:var(--text)}
.dsheet-chip.is-none{color:var(--faint);font-weight:400}
.dsheet-memo{font-size:.88rem;color:var(--muted);background:var(--surface-2);
  border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px}
.dsheet-memo p+p{margin-top:6px}
.dsheet-acts{display:flex;gap:8px;margin-top:18px}
.dsheet-acts .btn{flex:1;justify-content:center}

/* ── 선수 폼은 **오버레이**다 (2026-08-27 A안 Task 8, 스펙 §8-1) ─────────────
   여기 있는 것이 PC(>1024px) 기하이자 두 폭이 공유하는 몸이다. 폰·태블릿이
   되받는 것은 mobile.css의 `(max-width:1024px)` 블록 넉 줄뿐이다 — 폰 규칙을
   여기 적으면 `test_pc_is_untouched`가 못 보는 자리로 새고, PC 규칙을 저기
   적으면 그 테스트가 잡는다.

   **`:empty`가 자리 판정이다.** 상자(`#player-form-slot`)는 페이지에 늘
   있으므로 비었을 때 `position:fixed`인 채로 남으면 빈 오버레이가 화면 전체의
   클릭을 먹는다. JS로 클래스를 토글하지 않는 이유는 `.detail-host:empty`와
   같다 — htmx 스왑 뒤에 되돌리기를 잊는 자리가 생긴다.
   명시도 (1,1,0)이라 바로 위 `#player-form-slot`(1,0,0)을 순서와 무관하게 이긴다.

   **시트 기하 넷**은 ⑤ 기록 입력의 `.pa-picker`가 이미 값을 치른 것이다
   (mobile.css의 그 절 주석):
     ① `z-index:50` — 탭 바 30 위, `#error-flash` 60 아래.
     ② 판에 `max-height` + `overflow-y:auto` + `overscroll-behavior:contain`.
        없으면 667×375(작은 폰 가로)에서 판이 화면을 넘겨 ×가 밖으로 나가고,
        그때 판이 가장자리까지 덮으므로 어둠막까지 가려져 닫는 길 셋 중 둘이
        한꺼번에 죽는다.
     ③ 바닥에 `env(safe-area-inset-bottom)` (폰 규칙은 mobile.css).
     ④ 닫는 길 셋 — 어둠막 · × · Esc(`roster.js`). 넷째로 '취소'가 있다.

   판을 세로 flex로 두고 **몸만 스크롤시킨다.** 머리와 발이 `flex:none`이라
   판이 아무리 낮아도 ×와 저장 버튼이 화면 안에 남는다 — 폰 가로에서 실제로
   걸리는 자리가 그 둘이다. `.sheet-body`의 `min-height:0`이 빠지면 flex 항목의
   자동 최소 크기가 내용 높이가 되어 몸이 안 줄고 발이 화면 밖으로 밀린다. */
#player-form-slot{position:fixed;inset:0;z-index:50;display:grid;place-items:center;padding:26px}
#player-form-slot:empty{display:none}
.sheet-dim{position:absolute;inset:0;background:rgba(2,6,23,.45)}
.sheet-pane{position:relative;display:flex;flex-direction:column;
  width:620px;max-width:100%;max-height:100%;overflow-y:auto;overscroll-behavior:contain;
  background:var(--surface);border:1px solid var(--border);border-radius:16px;
  box-shadow:var(--shadow)}
.sheet-head{display:flex;align-items:center;gap:10px;flex:none;
  padding:12px 14px 12px 18px;border-bottom:1px solid var(--border)}
.sheet-head h2{font-size:1rem;font-weight:750}
.sheet-x{margin-left:auto;flex:none}
.sheet-body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:18px}
/* 발은 판의 마지막 flex 항목이라 PC에서는 저절로 판 아래에 앉는다. 폰에서
   `position:sticky`가 더 필요한 이유는 mobile.css 쪽에 적었다. */
.sheet-foot{display:flex;align-items:center;gap:8px;flex:none;
  padding:12px 18px;border-top:1px solid var(--border);background:var(--surface)}
/* 되돌릴 수 없는 삭제는 왼쪽 끝, 저장은 반대쪽 끝이다 — 둘이 붙어 있던 것이
   스펙 §2가 센 결함 일곱 중 하나다. `margin-left:auto`가 그 간격을 만든다. */
.sheet-foot-end{margin-left:auto;display:flex;gap:8px}

/* 이름 : 등번호. 등번호는 세 자리가 최대인 숫자 칸이라 고정 폭이다.
   `minmax(0,1fr)`이라야 첫 칸의 자동 최소 크기가 트랙 바닥을 안 정한다 —
   `1fr`만 쓰면 긴 이름이 들어간 순간 두 칸이 판 밖으로 흐른다. */
.pf-grid{display:grid;grid-template-columns:minmax(0,1fr) 130px;gap:13px}
.pf-status{border:0;padding:0;margin:16px 0 0;min-width:0}
.pf-status .field-label{display:block;padding:0;margin-bottom:5px}

/* ── 상태 분절 컨트롤 (스펙 §8-3) ──────────────────────────────────────────
   `.filter-chip`과 같은 처방이다: 라디오를 `display:none`으로 감추지 말 것 —
   포커스를 못 받아 화살표 키 이동과 포커스 링이 통째로 죽는다. 화면에서만
   지우고(`position:absolute` + `opacity:0`) 링은 칸에 `:has(:focus-visible)`로
   그린다. 고른 칸은 색만으로 말하지 않는다(글자 두께가 함께 바뀐다).
   칸마다 44px 하한은 스펙 §13의 터치 타깃이다. */
.seg-group{display:flex;border:1px solid var(--border-strong);
  border-radius:var(--radius-sm);background:var(--surface-2);overflow:hidden}
.seg-pick{position:relative;flex:1;display:flex;align-items:center;justify-content:center;
  min-height:44px;padding:0 10px;font-size:.83rem;font-weight:650;color:var(--muted);
  cursor:pointer;border-left:1px solid var(--border-strong);
  transition:background .18s ease,color .18s ease}
.seg-pick:first-child{border-left:0}
.seg-pick input{position:absolute;width:1px;height:1px;opacity:0;margin:0}
.seg-pick:hover{background:var(--surface-3)}
.seg-pick:has(input:checked){background:var(--brand-soft);color:var(--brand);font-weight:750}
.seg-pick:has(input:focus-visible){outline:none;box-shadow:var(--focus)}

/* 고른 포지션 수. 값은 `roster.js`가 되비친다 — 서버가 찍은 숫자는 칩을 누르는
   순간 거짓이 된다. */
.pf-count{display:inline-grid;place-items:center;min-width:22px;height:20px;padding:0 6px;
  margin-left:6px;border-radius:999px;background:var(--brand-soft);color:var(--brand);
  font-size:.78rem;font-weight:800;font-variant-numeric:tabular-nums}

/* 메모 셋은 접힌다(스펙 §8-2) — 23명 가운데 0명이 채웠는데 폼의 3할을 먹었다.
   `.pos-board`와 같은 처방으로 기본 삼각형을 지우고 `::after`가 방향을 그린다:
   삼각형을 그냥 지우면 접히는 상자라는 것을 눈으로 알 방법이 없어진다. */
.pf-memo{margin-top:18px;border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--surface-2)}
.pf-memo>summary{display:flex;align-items:center;gap:8px;min-height:44px;padding:8px 13px;
  cursor:pointer;font-size:.82rem;font-weight:750;color:var(--muted);list-style:none}
.pf-memo>summary::-webkit-details-marker{display:none}
.pf-memo>summary::after{content:"▾";margin-left:auto;color:var(--faint)}
.pf-memo[open]>summary::after{content:"▴"}
.pf-memo>summary:focus-visible{outline:none;box-shadow:var(--focus);border-radius:var(--radius-sm)}
.pf-memo-none{font-weight:400;color:var(--faint)}
.pf-memo .memo-grid{padding:0 13px 13px}
