/* =============================================================================
   더좋은 키즈 통합 신청·운영 프로토타입 — app.css
   -----------------------------------------------------------------------------
   1) base.css(양서경 원본 디자인 시스템) 뒤에 얹는 "보완 시트"다. 원본에는 없지만
      core.js(상태블록·토스트·모달·폼·선택묶음·규칙안내·상태배지)와 landing.js(진입화면)가
      실제로 쓰는 클래스만 정의한다.
   2) 색·간격·radius·그림자는 base.css `:root` 토큰을 참조한다. 토큰에 없는 값은
      base.css 안에서 이미 쓰이던 색을 그대로 가져와 아래 §톤 팔레트에서 이름만 붙였다.
   3) base.css의 규칙과 우선순위가 겹치는 자리(.form-body label / .form-body input 등)는
      요소 한정 선택자(label.form-field, input.form-input …)로 같은 특이성을 맞춰 이겼다.
   ============================================================================= */


/* === 톤 팔레트 ==============================================================
   base.css :root 에는 상태 톤 토큰이 없다. 아래 값은 전부 base.css 본문에서 이미
   쓰이던 색이고, 출처를 주석으로 남긴다. 대비(WCAG AA 4.5:1) 미달인 두 색만
   같은 색상 계열에서 명도를 낮춰 썼고 그 이유를 함께 적는다.
   ========================================================================== */

:root {
  /* 정상·완료 (base .admin-status-v2.done) — 대비 4.6:1 */
  --tone-ok-fg:      #167b60;
  --tone-ok-bg:      #e5f5ed;
  --tone-ok-bar:     var(--mint);

  /* 예정·정보 (base .queue-icon.blue / .dot.blue) — 원본 #4b83a8 은 tint 배경 위
     대비 3.6:1 로 AA 미달이라 같은 계열에서 명도만 낮췄다(5.1:1). */
  --tone-info-fg:    #36698c;
  --tone-info-bg:    #e8f1f7;
  --tone-info-bar:   #4b83a8;

  /* 주의 (base .success aside / .discount-note) — 대비 5.0:1 */
  --tone-warn-fg:    #806836;
  --tone-warn-bg:    #fff7df;
  --tone-warn-bar:   #e3a43b;

  /* 위험 (base .queue-icon.red / .danger-solid) — 원본 #b9514b 은 tint 배경 위
     대비 4.1:1 로 AA 미달이라 같은 계열에서 명도만 낮췄다(5.8:1). */
  --tone-danger-fg:  #9c3f3a;
  --tone-danger-bg:  #feecea;
  --tone-danger-bar: #d45d57;

  /* 종료·비활성 (base .admin-data-table td / .notice-list span) — 대비 5.2:1 */
  --tone-off-fg:     #52695f;
  --tone-off-bg:     #eef2f0;
  --tone-off-bar:    var(--line-field);

  /* 안내 박스 기본 톤 = 브랜드 민트 (base .notice-card) — 규칙 안내는 경고색이 아니라
     이 민트가 기본이다. 대비 4.8:1 */
  --note-bg:         #edf8f4;
  --note-bar:        #1ea980;
  --note-fg:         var(--green-text);
  --note-body:       #546d64;   /* base .cancel-policy p — 대비 5.2:1 */

  /* 솔리드 딥레드 (base .cancel-tag) — 흰 글자 대비 4.8:1 */
  --danger-solid-deep: #b9514b;

  /* base .primary 의 그림자값 그대로 (= var(--mint) 22% 알파) */
  --sh-btn-primary:  0 8px 18px #17a98038;
}


/* === 공통 상태 블록 (로딩 · 빈 · 에러) ======================================
   core.js loadingBlock / emptyBlock / errorBlock 이 만드는 구조.
   목록·폼은 이 3종을 반드시 갖춘다(DESIGN 7장).

   ⚠️ 에러는 모양이 둘이다(2026-08-21). 위 "3종"은 **어떤 상태를 갖추는가**를 세는 말이지
      **몇 가지 모양인가**가 아니다 — 화면·목록을 통째로 채우는 큰 상태 블록(.state-error)과
      폼·모달 안에 한 줄로 꽂히는 인라인 에러(.state-inline)가 따로 있다. 아래 인라인 에러
      절을 함께 읽을 것. 목록·폼이 갖춰야 할 상태가 셋이라는 규약 자체는 그대로다.
   ========================================================================== */

.state-block {
  text-align: center;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 13px;
  min-height: 180px;
  margin: 12px 0;
  padding: 40px 24px;
  display: flex;
}

.state-block p {
  color: var(--text-2);
  max-width: min(46ch, 100%);   /* 46ch 가 좁은 패널·모달 안에서 넘치지 않도록 100% 로 잘라둔다 */
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
}

/* 로딩 — 라우터가 화면 전환 때마다 outlet 전체에 깔기 때문에 높이를 조금 더 준다 */
.state-loading {
  gap: 15px;
  min-height: 220px;
}
.state-loading p { color: var(--text-3); }

.state-spinner {
  border: 3px solid var(--line);
  border-top-color: var(--mint);
  border-radius: var(--r-round);
  width: 30px;
  height: 30px;
  animation: state-spin .8s linear infinite;
  display: block;
}

@keyframes state-spin {
  to { transform: rotate(360deg); }
}

/* 빈 상태 */
.state-empty {
  background: var(--bg-subtle);
  border: 1px dashed var(--line-field);
  border-radius: var(--r-md);
}
.state-empty p { color: var(--text-3); }

/* 에러 — 서버가 준 한글 메시지를 그대로 보여주는 자리 */
.state-error {
  background: var(--tone-danger-bg);
  border: 1px solid #f4d5d1;   /* --tone-danger-bar 를 옅게 — 배경과 1단계만 차이 */
  border-radius: var(--r-md);
}
.state-error p {
  color: var(--tone-danger-fg);
  font-weight: 700;
}

/* 인라인 에러 — 폼·모달 안 짧은 검증/제출 실패 전용 (.state-error 에 덧붙이는 보조 클래스)
   core.js errorInline() / errorBlock(err, retry, {inline:true}) 이 붙인다.

   왜 따로 만들었나 — `.state-block` 은 "빈 화면·빈 목록을 채우는 자리"용이라 `min-height:180px`
   로 세로를 억지로 채운다. 그 상자가 로그인 카드(내용 폭 376px) 안에 그대로 들어가니
   한 줄짜리 문구가 상자의 14%만 쓰고 위아래로 77px씩 빈 공간이 남았다(2026-08-21 사장님 지적).
   padding 을 0 으로 해도 안 줄어든다 — 180px 을 만드는 것은 padding 이 아니라 min-height 하나다.

   그렇다고 `.state-block` 을 줄이면 안 된다 — 이 클래스를 쓰는 자리가 217곳이고, 그중
   부팅 실패·404 같은 전체화면 5곳과 점선 빈 상태 62곳은 지금의 180px 이 오히려 필요하다.
   그래서 기존 규칙은 한 줄도 안 건드리고 **그 자리에만 붙는 보조 클래스**를 새로 만든다.

   ⚠️ 선택자를 굳이 `.state-block.state-inline` 두 클래스로 묶은 이유 — 이 파일 아래쪽
      `@media (max-width: 720px)` 에 `.state-block { min-height:150px; padding:32px 18px }` 이
      있다. 특이성이 같으면 뒤가 이기므로, `.state-inline` 한 클래스로만 쓰면 좁은 화면에서
      큰 상자가 되살아난다. (0,0,2,0) 으로 올려야 구간을 건너도 유지된다. 아래 반응형 절에
      같은 두 클래스 선택자를 한 번 더 둔 것도 같은 이유다.

   무게는 `.form-error`(13px + "!" 배지)에 맞췄다 — 학부모·기관 로그인은 이미 그 표현을 쓰고
   있고 어드민 로그인만 큰 상자였다. 세 로그인이 같은 크기로 읽혀야 한다. */
.state-block.state-inline {
  text-align: left;
  flex-direction: row;
  justify-content: flex-start;
  align-items: flex-start;
  gap: 7px;
  min-height: 0;
  margin: 8px 0;
  padding: 9px 12px;
}

.state-block.state-inline p {
  max-width: none;   /* 46ch 로 자를 이유가 없다 — 이 상자는 폼 폭에 이미 갇혀 있다 */
  min-width: 0;      /* flex row 라 긴 한 덩어리 문구가 들어오면 부모를 넓힌다 */
  /* ⚠️ min-width:0 만으로는 부족하다(2026-08-21 실측) — flex item 이 shrink 는 되지만,
     안 끊기는 영문 토큰·URL 은 그 안에 줄바꿈 지점이 없어 shrink 된 상자 경계를 그대로
     넘어 화면 밖으로 삐져나간다(overflow 는 기본 visible). 한글은 브라우저가 글자
     단위로 이미 줄을 바꾸므로 문제가 없었고, 그래서 이 자리가 지금까지 안 걸렸다.
     `overflow-wrap: anywhere` 를 줘야 그런 토큰 내부에서도 강제로 줄을 바꿔 부모 폭
     (state-inline 상자) 안에 머문다. */
  overflow-wrap: anywhere;
  font-size: 13px;
  line-height: 1.6;
}

/* 색만으로 오류를 알리지 않도록 `.form-error::before` 와 같은 "!" 배지를 붙인다.
   ⚠️ `.form-error` 는 `align-items:center` 라 문구가 두 줄 이상이면 배지가 블록 세로
      가운데로 내려간다. 여기 문구는 두세 줄이 흔해서(예: 어드민 검토 해소의 "보호자 휴대폰
      번호를 정확히 입력해 주세요. 이 번호로 자동이체 동의 요청이 나갑니다.") 위쪽 정렬 +
      margin-top 으로 첫 줄 글자에 맞춰 고정했다. */
.state-block.state-inline::before {
  content: "!";
  color: var(--white);
  background: var(--tone-danger-bar);
  border-radius: var(--r-round);
  place-items: center;
  flex: 0 0 auto;
  margin-top: 2px;
  width: 15px;
  height: 15px;
  font-size: 11px;
  font-weight: 900;
  display: inline-grid;
}


/* === 규칙 안내 박스 =========================================================
   core.js ruleNote(title, lines, {tone}) — 뽀득에서 가져온 규칙(시작일 근거, 해지
   마감, 첫 달 반액, 정지 정산, 반 변경 권한)을 화면에 문장으로 드러내는 전용 박스다.
   기본 톤은 경고색이 아니라 브랜드 민트(base .notice-card 계열)로 둔다.
   ========================================================================== */

.rule-note {
  background: var(--note-bg);
  border-left: 4px solid var(--note-bar);
  border-radius: var(--r-md);
  margin: 18px 0;
  padding: 16px 20px;
}

.rule-note-title {
  color: var(--note-fg);
  font-size: 14px;
  font-weight: 900;
  display: block;
}

.rule-note ul {
  color: var(--note-body);
  margin: 9px 0 0;
  padding-left: 19px;
  font-size: 14px;
  line-height: 1.7;
  list-style: disc;   /* base 리셋이 list-style:none 이라 이 박스에서만 되살린다 */
}

.rule-note li { margin: 4px 0; }
.rule-note li::marker { color: var(--note-bar); }
.rule-note strong,
.rule-note b { color: var(--note-fg); }

.rule-note.tone-warn                { background: var(--tone-warn-bg); border-left-color: var(--tone-warn-bar); }
.rule-note.tone-warn .rule-note-title,
.rule-note.tone-warn strong,
.rule-note.tone-warn b              { color: var(--tone-warn-fg); }
.rule-note.tone-warn ul             { color: #6f5c33; }   /* --tone-warn-fg 를 살짝 낮춘 본문색 */
.rule-note.tone-warn li::marker     { color: var(--tone-warn-bar); }

.rule-note.tone-danger              { background: var(--tone-danger-bg); border-left-color: var(--tone-danger-bar); }
.rule-note.tone-danger .rule-note-title,
.rule-note.tone-danger strong,
.rule-note.tone-danger b            { color: var(--tone-danger-fg); }
.rule-note.tone-danger ul           { color: #6d4a47; }   /* --tone-danger-fg 를 살짝 낮춘 본문색 */
.rule-note.tone-danger li::marker   { color: var(--tone-danger-bar); }


/* === 폼 =====================================================================
   core.js field() / input() / select() / textarea() 가 만드는 구조.
   field() 는 <label class="form-field"> 를 돌려주는데, 위저드 안에서는 base 의
   `.form-body label`(특이성 0,0,1,1)과 겹친다 → 요소 한정으로 특이성을 맞춘다.
   ========================================================================== */

label.form-field,
.form-field {
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  margin: 0 0 16px;
  display: flex;
}

.form-label {
  color: var(--ink);
  align-items: center;
  gap: 4px;
  font-size: 14px;
  font-weight: 800;
  display: flex;
}

.req {
  color: var(--tone-danger-bar);
  font-size: 13px;
  font-style: normal;
  font-weight: 900;
}

input.form-input,
select.form-select,
textarea.form-textarea {
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--line-field);
  border-radius: var(--r-field);
  outline: none;
  width: 100%;
  min-width: 0;
  padding: 13px 14px;
  font-size: 15px;
  line-height: 1.5;
}

input.form-input::placeholder,
textarea.form-textarea::placeholder { color: var(--text-6); }

select.form-select {
  -webkit-appearance: none;
  appearance: none;
  /* 화살표 SVG — stroke 는 var(--muted)(#72847e) 값. data URI 라 토큰 참조가 안 된다. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%2372847e' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 38px;
}

textarea.form-textarea {
  resize: vertical;
  min-height: 96px;
  line-height: 1.7;
}

input.form-input:focus,
select.form-select:focus,
textarea.form-textarea:focus {
  border-color: var(--mint);
  box-shadow: var(--sh-focus);
}

input.form-input:disabled,
select.form-select:disabled,
textarea.form-textarea:disabled {
  color: var(--text-5);
  cursor: not-allowed;
  background: var(--bg-subtle);
}

.form-hint {
  color: var(--text-4);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.6;
}

.form-error {
  color: var(--tone-danger-fg);
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.6;
  display: flex;
}
/* ⚠️ 로그인·모달 화면들은 빈 <small class="form-error"> 를 자리만 잡아 두고 오류가 났을 때
      내용을 채운다. 그런데 아래 ::before 아이콘이 내용과 상관없이 항상 그려져서, 오류가
      없는 평상시에도 빨간 "!" 배지만 덩그러니 떠 있었다(학부모 로그인 화면에서 확인).
      내용이 비어 있으면 슬롯 자체를 숨긴다. */
.form-error:empty {
  display: none;
}

/* 색만으로 오류를 알리지 않도록 아이콘을 붙인다 */
.form-error::before {
  content: "!";
  color: var(--white);
  background: var(--tone-danger-bar);
  border-radius: var(--r-round);
  place-items: center;
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  font-size: 11px;
  font-weight: 900;
  display: inline-grid;
}

.form-field.has-error input.form-input,
.form-field.has-error select.form-select,
.form-field.has-error textarea.form-textarea {
  border-color: var(--tone-danger-bar);
  background: #fffbfa;   /* --tone-danger-bg 를 흰색에 섞은 아주 옅은 톤 */
}
.form-field.has-error input.form-input:focus,
.form-field.has-error select.form-select:focus,
.form-field.has-error textarea.form-textarea:focus {
  box-shadow: 0 0 0 3px var(--tone-danger-bg);
}

/* 2열 배치가 필요할 때 (base .form-grid 와 같은 결) */
.form-row {
  grid-template-columns: 1fr 1fr;
  gap: 0 18px;
  display: grid;
}


/* === 선택 묶음 (라디오 · 체크박스) ==========================================
   core.js choiceGroup() 구조. .choice-item 도 <label> 이라 base `.form-body label`
   (flex-direction:column)에 눌린다 → 요소 한정으로 맞춘다.
   ========================================================================== */

.choice-group {
  gap: 9px;
  display: grid;
}

label.choice-item,
.choice-item {
  background: var(--white);
  border: 1px solid var(--line-field);
  border-radius: var(--r-md);
  flex-direction: row;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  font-weight: 400;
  display: flex;
  position: relative;
}

/* base `.form-body input`(width:100%·padding 14px)이 라디오까지 늘려버리는 걸 되돌린다 */
.choice-item > input {
  accent-color: var(--mint);
  cursor: pointer;
  background: none;
  border: 0;
  border-radius: 0;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  box-shadow: none;
}

.choice-text {
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  display: flex;
}
.choice-text b     { font-size: 15px; font-weight: 800; }
.choice-text small { color: var(--text-4); font-size: 13px; line-height: 1.6; }

.choice-badge {
  color: var(--tone-off-fg);
  background: var(--tone-off-bg);
  border-radius: var(--r-pill);
  margin-left: auto;
  padding: 5px 10px;
  font-size: 12px;
  font-style: normal;
  font-weight: 800;
  white-space: nowrap;
}
.choice-badge.tone-ok     { color: var(--tone-ok-fg);     background: var(--tone-ok-bg); }
.choice-badge.tone-info   { color: var(--tone-info-fg);   background: var(--tone-info-bg); }
.choice-badge.tone-warn   { color: var(--tone-warn-fg);   background: var(--tone-warn-bg); }
.choice-badge.tone-danger { color: var(--tone-danger-fg); background: var(--tone-danger-bg); }

/* 선택됨 — 테두리를 2px 로 바꾸면 1px 만큼 내용이 밀리므로 inset 그림자로 굵혀 보이게 한다 */
.choice-item.is-selected {
  background: #f0fbf7;   /* base .center-options button.active */
  border-color: var(--mint);
  box-shadow: inset 0 0 0 1px var(--mint);
}

.choice-item.is-disabled {
  color: var(--text-6);
  cursor: not-allowed;
  background: #f2f4f3;   /* base .dish-options button:disabled */
  border-color: var(--line-inner);
}
.choice-item.is-disabled .choice-text b,
.choice-item.is-disabled .choice-text small { color: var(--text-6); }
.choice-item.is-disabled > input { cursor: not-allowed; }

.choice-item:focus-within { box-shadow: var(--sh-focus), inset 0 0 0 1px var(--mint); }

/* 가로로 늘어놓는 변형 (짧은 보기 3~4개) */
.choice-group.choice-inline {
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}


/* === 버튼 ===================================================================
   시각은 base 의 `.primary`(민트 솔리드) / `.card-actions button`(흰 아웃라인) /
   `.danger-solid`(레드 솔리드) 를 그대로 따른다 — 값도 원본 그대로 옮겼다.
   core.js modal() 과 main.js 의 <a class="btn-secondary"> 때문에 a 태그에도 얹힌다.
   ========================================================================== */

.btn-primary,
.btn-secondary,
.btn-danger,
.btn-ghost {
  text-align: center;
  cursor: pointer;
  justify-content: center;
  align-items: center;
  gap: 7px;
  font-weight: 800;
  line-height: 1.25;
  text-decoration: none;
  display: inline-flex;
}

/* base .primary 와 동일 */
.btn-primary {
  color: var(--white);
  background: var(--mint);
  border: 0;
  border-radius: 13px;
  padding: 14px 22px;
  box-shadow: var(--sh-btn-primary);
}
.btn-primary:hover { background: var(--mint-dark); }

/* base .card-actions button / .wizard-actions > button:not(.primary) 와 동일 */
.btn-secondary {
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--line-field);
  border-radius: 12px;
  padding: 13px 20px;
}
.btn-secondary:hover { background: var(--bg-subtle); border-color: var(--line); }

/* base .inline-cancel .danger-solid 와 동일 */
.btn-danger {
  color: var(--white);
  background: var(--tone-danger-bar);
  border: 0;
  border-radius: 13px;
  padding: 14px 22px;
}
.btn-danger:hover { background: var(--danger-solid-deep); }

.btn-ghost {
  color: var(--green-text);
  background: transparent;
  border: 0;
  border-radius: var(--r-chip);
  padding: 10px 12px;
  font-size: 14px;
}
.btn-ghost:hover { background: #edf5f1; }   /* base .admin-data-table td > button */

.btn-primary:disabled,
.btn-secondary:disabled,
.btn-danger:disabled,
.btn-ghost:disabled,
.btn-primary[aria-disabled="true"],
.btn-secondary[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: .45;
  box-shadow: none;
}

.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.btn-danger:focus-visible,
.btn-ghost:focus-visible {
  outline: 2px solid var(--mint-dark);
  outline-offset: 2px;
  box-shadow: var(--sh-focus);
}
.btn-danger:focus-visible { outline-color: var(--danger-solid-deep); }


/* === 모달 ===================================================================
   core.js modal() 구조. `.modal-backdrop` 은 base.css 에 있고(고정 오버레이 + 블러),
   닫기 버튼 `.close` 도 base 것을 그대로 쓴다 — `.close` 가 absolute 이므로
   `.modal-box` 를 position:relative 로 잡아 우상단에 붙게 한다.
   ========================================================================== */

body.modal-open { overflow: hidden; }

.modal-box {
  background: var(--white);
  border-radius: var(--r-modal);
  flex-direction: column;
  width: min(560px, 100%);
  max-height: min(88vh, 820px);
  display: flex;
  position: relative;
  overflow: hidden;
  box-shadow: var(--sh-modal);
}

.modal-wide { width: min(920px, 100%); }

.modal-head {
  border-bottom: 1px solid var(--line-inner);
  align-items: center;
  gap: 12px;
  padding: 24px 66px 18px 28px;   /* 오른쪽은 base .close(34px, right:22px) 자리 */
  display: flex;
}
.modal-head b {
  letter-spacing: -.03em;
  font-size: 20px;
  font-weight: 800;
}

.modal-body {
  flex: 1 1 auto;
  padding: 24px 28px;
  overflow: auto;
}
.modal-body > p {
  color: var(--text-1);
  margin: 0 0 12px;
  font-size: 15px;
  line-height: 1.75;
}
.modal-body > p:last-child { margin-bottom: 0; }
.modal-body .form-field:last-child { margin-bottom: 0; }

.modal-actions {
  background: var(--bg-subtle);
  border-top: 1px solid var(--line-inner);
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 9px;
  padding: 18px 28px;
  display: flex;
}


/* === 토스트 ================================================================
   `.toast` 자체는 base.css 에 있다(딥그린 알약). 여기서는 위치만 바꿔
   화면 하단 가운데에 쌓이게 한다 — base 의 fixed bottom/right 를 호스트가 흡수한다.
   ========================================================================== */

.toast-host {
  z-index: 200;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: min(430px, calc(100% - 32px));
  display: flex;
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%);
  pointer-events: none;
}

.toast-host > .toast {
  text-align: center;
  pointer-events: auto;
  width: 100%;
  font-size: 15px;
  animation: toast-in .22s ease-out;
  transition: opacity .24s ease, transform .24s ease;
  position: static;
  inset: auto;
}

.toast-host > .toast-out {
  opacity: 0;
  transform: translateY(10px);
}

.toast-ok    { background: #153f33; }              /* base .toast 기본색 (명시) */
.toast-error { background: var(--danger-solid-deep); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}


/* === 상태 배지 ==============================================================
   core.js statusBadge() — DESIGN 3.4 의 구독 상태 9종을 표현한다.
   이용중/예정/정지/해지/중지가 색만이 아니라 문구로도 구분되도록 텍스트는 항상 붙는다.
   ========================================================================== */

.status-badge {
  color: var(--tone-off-fg);
  background: var(--tone-off-bg);
  border-radius: var(--r-pill);
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 900;
  line-height: 1.3;
  white-space: nowrap;
  display: inline-flex;
}

.status-badge::before {
  content: "";
  background: currentColor;
  border-radius: var(--r-round);
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  display: block;
}

.status-badge.tone-ok     { color: var(--tone-ok-fg);     background: var(--tone-ok-bg); }      /* ACTIVE */
.status-badge.tone-info   { color: var(--tone-info-fg);   background: var(--tone-info-bg); }    /* SCHEDULED · TRANSFER_SCHEDULED */
.status-badge.tone-warn   { color: var(--tone-warn-fg);   background: var(--tone-warn-bg); }    /* PAUSE_SCHEDULED · PAUSED · CANCEL_SCHEDULED */
.status-badge.tone-danger { color: var(--tone-danger-fg); background: var(--tone-danger-bg); }  /* SUSPENDED */
.status-badge.tone-off    { color: var(--tone-off-fg);    background: var(--tone-off-bg); }     /* CANCELLED · CANCELLED_BEFORE_START */
.status-badge.tone-off::before { opacity: .55; }


/* === 공용 표 ================================================================
   base.css 에는 `.data-table` 이 없고 `.admin-data-table`(어드민 전용, min-width 980px)
   과 grid 기반 `.tr` 만 있다 → 세 역할 어디서나 쓸 최소 표를 여기에 둔다.
   ========================================================================== */

.simple-table-wrap { width: 100%; overflow-x: auto; }

.simple-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 14px;
  /* **한글은 기본값에서 아무 글자에서나 줄이 바뀐다.** 그래서 표의 최소 너비가 '한 글자'
     로 계산되고, 좁은 화면에서 표가 끝까지 찌부러진다 — 기관 `배송·박스 현황`에서
     날짜 칸의 `8월 19일(수)`가 16줄이 되고 수량 칸이 세로 막대가 됐다(한 행 높이 356px).
     래퍼에 `overflow-x: auto` 가 있어도 **표가 알아서 줄어드니 넘칠 일이 없어** 안 들었다.

     `keep-all` 은 어절 단위로만 줄을 바꾼다. 그러면 표의 최소 너비가 '가장 긴 어절' 로
     올라가고, 내용이 많은 표는 래퍼보다 넓어져 **비로소 래퍼가 가로로 스크롤한다.**
     내용이 짧은 표(학부모 청구 내역 등)는 그대로 100% 에 들어맞아 스크롤이 안 생긴다.

     표에 `min-width` 를 숫자로 박지 않은 이유가 이것이다 — 열 수로는 갈리지 않는다.
     학부모 5열 표는 멀쩡하고(행 높이 87px) 기관 5열 표만 깨졌다(356px). 차이는 열 수가
     아니라 칸 안의 문구 길이라, **폭은 내용이 정하게** 두는 편이 맞다. (1-1) 발견,
     2026-08-19. */
  word-break: keep-all;
}

/* 칸마다 긴 문구가 들어가는 표. `keep-all` 만으로는 어절 단위로 잘게 접혀서, 읽을 수는
   있어도 한 행이 200px 넘게 쌓인다(기관 배송 기록의 수량 칸 = 식기 5종 내역 한 줄).
   **필요한 폭을 이 표에만 못박고** 래퍼가 가로로 넘기게 한다.

   `.simple-table` 전체에 `min-width` 를 주지 않은 이유 — 이 클래스를 13개 화면이 쓰는데
   대부분은 지금도 잘 들어맞는다(학부모 청구 내역 5열 표의 한 행이 87px). 일괄로 주면
   멀쩡한 표까지 스크롤이 생겨 빈 칸을 옆으로 밀어 보게 된다. 2026-08-19. */
.simple-table-dense { min-width: 620px; }

.simple-table th {
  text-align: left;
  color: var(--text-3);
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--line-inner);
  padding: 11px 12px;
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
}

.simple-table td {
  vertical-align: middle;
  color: var(--ink);
  border-bottom: 1px solid var(--line-inner);
  padding: 14px 12px;
  line-height: 1.6;
}

.simple-table tbody tr:last-child td { border-bottom: 0; }
.simple-table td small { color: var(--text-4); font-size: 12px; display: block; margin-top: 3px; }
.simple-table .num { text-align: right; font-variant-numeric: tabular-nums; }


/* === 진입 화면 (역할 선택) ==================================================
   landing.js — 사내 테스트용으로 3역할을 한 자리에서 여는 화면.
   ========================================================================== */

.landing-page {
  background:
    radial-gradient(circle at 12% 14%, #dff36d33, transparent 26%),   /* var(--lime) 20% */
    radial-gradient(circle at 88% 84%, #17a9802e, transparent 28%),   /* var(--mint) 18% */
    var(--bg-parent);
  flex-direction: column;
  align-items: center;
  gap: 26px;
  min-height: 100vh;
  padding: 52px 24px 72px;
  display: flex;
}

.landing-page > * { width: min(1000px, 100%); }

.landing-head {
  align-items: center;
  gap: 13px;
  display: flex;
}
.landing-head b     { font-size: 19px; letter-spacing: -.05em; display: block; }
.landing-head small { color: var(--text-4); font-size: 12px; display: block; margin-top: 2px; }

.landing-env {
  color: var(--green-text);
  background: #eff7f3;   /* base .kakao-login-badge */
  border-radius: var(--r-pill);
  margin-left: auto;
  padding: 7px 12px;
  font-size: 12px;
  font-style: normal;
  font-weight: 900;
  white-space: nowrap;
}

.landing-intro h1 {
  letter-spacing: -.05em;
  margin: 0 0 12px;
  font-size: 38px;
  line-height: 1.32;
}
.landing-intro p {
  color: var(--text-1);
  max-width: 64ch;
  margin: 0;
  font-size: 15px;
  line-height: 1.85;
}

.landing-session {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  display: flex;
  box-shadow: var(--sh-card-sm);
}
.landing-session span {
  color: var(--text-1);
  flex: 1;
  min-width: 0;
  font-size: 14px;
  line-height: 1.6;
}
.landing-session .btn-secondary { padding: 10px 16px; font-size: 14px; }

.landing-grid {
  grid-template-columns: repeat(3, 1fr);
  align-items: stretch;
  gap: 16px;
  display: grid;
}

.landing-card {
  color: inherit;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  flex-direction: column;
  gap: 9px;
  padding: 26px 24px;
  text-decoration: none;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  display: flex;
  box-shadow: var(--sh-card);
}
.landing-card:hover {
  border-color: #9ecbbc;   /* base .admin-queue > button:hover */
  transform: translateY(-3px);
  box-shadow: 0 13px 30px #21433817;   /* base .product-grid article:hover */
}
.landing-card:focus-visible {
  outline: 2px solid var(--mint-dark);
  outline-offset: 3px;
}

.landing-card.is-current {
  background: #f0fbf7;   /* base .center-options button.active */
  border-color: var(--mint);
  box-shadow: inset 0 0 0 1px var(--mint), var(--sh-card);
}

.landing-icon { font-size: 34px; line-height: 1.1; }
.landing-card b { font-size: 19px; letter-spacing: -.03em; }
.landing-card p {
  color: var(--text-1);
  flex: 1;
  margin: 0;
  font-size: 14px;
  line-height: 1.65;
}

.landing-account {
  color: var(--text-5);
  background: var(--bg-subtle);
  border-radius: var(--r-chip);
  padding: 9px 11px;
  font-size: 12px;
  line-height: 1.55;
  word-break: keep-all;
}

.landing-go {
  color: var(--green-text);
  margin-top: 2px;
  font-size: 13px;
  font-style: normal;
  font-weight: 900;
}

.landing-foot p {
  color: var(--text-5);
  text-align: center;
  margin: 0;
  font-size: 12px;
  line-height: 1.7;
}


/* === 반응형 ================================================================ */

@media (max-width: 900px) {
  .landing-grid { grid-template-columns: 1fr 1fr; }
  .landing-intro h1 { font-size: 32px; }
  .form-row { grid-template-columns: 1fr; gap: 0; }
}

@media (max-width: 720px) {
  .state-block { min-height: 150px; padding: 32px 18px; }
  .state-block p { font-size: 14px; }

  /* 인라인 에러는 위 두 줄을 그대로 받으면 안 된다 — 큰 상태 블록을 좁은 화면에 맞게
     "줄이는" 규칙이지만, 인라인은 이미 작아서 저 값이 오히려 상자를 다시 부풀린다.
     그래서 두 클래스 선택자로 특이성을 올려 다시 덮는다(기본 정의 쪽 주석 참고).
     app.css 는 900/720 에서 끊고 base.css 는 900/600 에서 끊으므로 실제 구간은 넷이다 —
     이 블록 하나가 ≤720 과 ≤600 을 함께 덮는다(≤600 은 base 가 body 글자만 15px 로 줄인다). */
  .state-block.state-inline { margin: 7px 0; padding: 8px 10px; }
  .state-block.state-inline p { font-size: 13px; }

  .rule-note { margin: 15px 0; padding: 14px 16px; }
  .rule-note ul { font-size: 13px; }

  .form-label { font-size: 13px; }
  input.form-input,
  select.form-select,
  textarea.form-textarea { padding: 12px 13px; font-size: 15px; }

  label.choice-item,
  .choice-item { padding: 13px 14px; }
  .choice-badge { margin-left: auto; font-size: 11px; }
  .choice-group.choice-inline { grid-template-columns: 1fr; }

  /* 모달은 전면 시트로 (base 가 .wizard 를 다루는 방식과 동일) */
  .modal-box,
  .modal-wide {
    border-radius: 0;
    width: 100%;
    height: 100%;
    max-height: 100vh;
  }
  .modal-head    { padding: 20px 60px 16px 20px; }
  .modal-head b  { font-size: 18px; }
  .modal-body    { padding: 20px; }
  .modal-actions { padding: 14px 20px; }
  .modal-actions .btn-primary,
  .modal-actions .btn-secondary,
  .modal-actions .btn-danger { flex: 1; }

  .toast-host { width: calc(100% - 24px); bottom: 16px; }
  .toast-host > .toast { padding: 13px 16px; font-size: 14px; }

  .landing-page { gap: 22px; padding: 34px 18px 54px; }
  .landing-intro h1 { font-size: 27px; }
  .landing-intro p  { font-size: 14px; line-height: 1.75; }
  .landing-grid  { grid-template-columns: 1fr; gap: 12px; }
  .landing-card  { padding: 20px; }
  .landing-icon  { font-size: 29px; }
  .landing-session { flex-direction: column; align-items: stretch; }
  .landing-session .btn-secondary { width: 100%; }

  .simple-table { font-size: 13px; }
  .simple-table th { padding: 10px; }
  .simple-table td { padding: 12px 10px; }
}


/* === 모션 최소화 선호 ====================================================== */

@media (prefers-reduced-motion: reduce) {
  .state-spinner { animation-duration: 2.4s; }
  .toast-host > .toast { animation: none; }
  .landing-card { transition: none; }
  .landing-card:hover { transform: none; }
}

/* ⚠️ 모달을 전면 시트로 전환할 때 base `.modal-backdrop { padding: 24px }` 가 남으면
      가장자리에 여백이 생긴다 — 위 720px 블록과 짝을 맞춰 여기서 0으로 만든다. */
@media (max-width: 720px) {
  .modal-backdrop { padding: 0; }
}


/* === 신청 위저드 보완 (parent/apply.js · apply-done.js) =====================
   base.css 가 위저드 뼈대(.wizard/.steps/.form-body/.dish-options/.summary/.success)를
   이미 갖고 있어서, 여기서는 그 위에 얹히는 네 조각만 정의한다.
   ========================================================================== */

/* '다음'이 막힌 이유 — 버튼 왼쪽 끝에 붙여 두어 사장님이 왜 못 넘어가는지 바로 알게 한다.
   .wizard-actions 가 flex-end 라 margin-right:auto 로 왼쪽에 밀어 놓는다. */
.wizard-hint {
  color: var(--tone-warn-fg);
  align-self: center;
  margin-right: auto;
  font-size: 12px;
  font-weight: 700;
}

/* ⚠️ base `.search-box input { padding-left: 40px }` 이 app.css 의 `input.form-input` 에
      같은 특이성으로 밀려(뒤에 오는 시트가 이김) 돋보기 아이콘과 글자가 겹쳤다.
      core 의 input() 을 검색창에 쓰는 자리(신청 위저드 기관검색·기관 원아명부 검색)를
      요소 한정 선택자로 되돌려 놓는다. */
.search-box input.form-input {
  padding-left: 40px;
}

/* 검색 전·결과 없음 안내 문장 */
.search-guide {
  color: var(--text-5);
  margin: 10px 0;
  font-size: 12px;
  line-height: 1.6;
}

/* 약관 5종 세로 스택 — 항목 자체 모양은 base .check-line 이 담당한다 */
.terms-list {
  gap: 8px;
  margin: 14px 0 4px;
  display: grid;
}
.terms-list .check-line       { justify-content: space-between; gap: 10px; }
.terms-list .check-line span  { flex: 1; }

/* 약관 본문 모달 — 길어서 스크롤 영역을 따로 준다 */
.terms-body {
  max-height: 46vh;
  overflow: auto;
  font-size: 13px;
  line-height: 1.8;
}
.terms-body p { margin: 0 0 10px; }

/* 위저드 안의 텍스트형 보조 버튼 (기관 직접입력 전환·약관 본문 보기) */
.wizard-link {
  padding: 6px 10px;
  font-size: 12px;
}

/* === 어드민 좌하단 dock 위치 보정 =========================================
   base.css 는 dock 2개를 `left: 24px` / `left: 220px` 에 가로로 놓는다. 그런데 어드민
   사이드바가 258px 폭으로 화면 왼쪽을 차지하고 있어서 데스크톱에서 두 가지가 깨졌다.
     ① 자동화 dock(24~181px)이 사이드바 하단 '검토용 바로가기' 링크를 덮어
        "기관 로그인·운영"과 "관리자 권한 · 전체 데이터"가 읽히지 않았다.
     ② 로드맵 dock(220~377px)이 사이드바 경계선(258px)에 걸쳐 어두운 면과 밝은 면에
        절반씩 얹혔다.
   사이드바가 보이는 폭(901px~)에서만 두 dock 을 본문 열 안쪽으로 옮기고 세로로 쌓는다.
   가로 배치는 버튼 글자 길이에 따라 간격이 틀어지지만, 세로 쌓기는 폭과 무관하다.
   600px 이하에서 base 가 이미 같은 방식(세로 쌓기)을 쓰고 있어 형태도 일관된다.
   ========================================================================== */

@media (min-width: 901px) {
  .automation-dock { left: 282px; bottom: 24px; }   /* 사이드바 258px + 여백 24px */
  .rollout-dock    { left: 282px; bottom: 88px; }   /* 자동화 dock(52px) 위로 12px 띄움 */
}

/* 완료 화면의 규칙안내·상태 블록을 담는 자리.
   base `.success > div:not(.success-mark)` 가 직계 div 를 2열 정의목록으로 잡기 때문에
   규칙안내는 .success 밖 형제로 두고, 좌우 여백만 여기서 맞춘다. */
.apply-done-note {
  padding: 0 60px 6px;
}

@media (max-width: 720px) {
  .apply-done-note { padding: 0 22px 6px; }
}

/* === 테스트 의견 버튼 ======================================================
   사내 테스트 기간에만 붙는다(main.js `mountFeedbackButton`). 화면 어디에 있든 눌러서
   그 화면에 대한 의견을 남긴다 — 받는 자리를 노션 댓글에서 앱 안으로 옮긴 이유는
   feedback.js 머리말 참조.

   z-index 175 = 모달 backdrop(180) 아래, 어드민 서랍(150)·dock(90) 위. 모달이 열리면
   그 위에 덮이고, 평소에는 어느 화면 요소보다 위에 뜬다.

   ⚠️ 옮길 수 있다(2026-08-28) — 담당자 「효성CMS·결제 대조」 화면에서 이 버튼이 「회원구분
   없는 기관 정리」 버튼을 그대로 덮는 사고가 났다(사장님 지적). 드래그·위치 저장은
   feedback.js `makeFabDraggable()` 이 맡고, 여기서는 그 결과(인라인 left/top)를 받아들일
   자리(position:fixed)만 잡는다. 기본 자리(우하단)는 지금처럼 bottom/right 로 둔다 —
   한 번도 옮기지 않았다면 지금까지와 화면이 완전히 같다. */

.feedback-fab {
  z-index: 175;
  cursor: pointer;
  color: var(--white);
  background: #1d6856;
  border: 0;
  border-radius: var(--r-md);
  align-items: center;
  gap: 8px;
  padding: 13px 18px;
  font-size: 13px;
  font-weight: 900;
  opacity: .82;
  transition: transform .12s ease, background .12s ease, opacity .18s ease,
              padding .18s ease, gap .18s ease, border-radius .18s ease;
  display: flex;
  position: fixed;
  bottom: 24px;
  right: 24px;
  box-shadow: var(--sh-dock);
}

.feedback-fab:hover  { background: #17563f; transform: translateY(-1px); opacity: 1; }
.feedback-fab:active { transform: translateY(0); }
.feedback-fab:focus-visible { opacity: 1; }

/* 드래그 중 — feedback.js 가 pointerdown 이후 임계값(6px)을 넘기면 붙인다. 잡았다는
   표시는 커서와 기존 --sh-dock 그림자만으로 준다(새 그림자를 만들지 않는다 — 무그림자
   원칙). transition 을 끄는 이유 — 매 프레임 좌표가 바뀌는데 transition 이 걸려 있으면
   버튼이 포인터를 못 따라가고 뒤에서 밀려 온다. */
.feedback-fab.is-dragging {
  cursor: grabbing;
  opacity: 1;
  transition: none;
  -webkit-user-select: none;
  user-select: none;
}

.feedback-fab-icon { font-size: 15px; line-height: 1; }

/* 마우스가 있는 화면(데스크톱)에서만 평소엔 아이콘만 남기고 반투명으로 물러난다 —
   손을 올리거나(hover) 키보드로 포커스하거나 드래그 중일 때만 제 모습으로 돌아온다.
   터치 전용 화면(폰)에는 이 규칙을 걸지 않는다 — 아래 ≤360px 규칙의 이유(CS 직원이
   연필 기호만으론 못 알아봄, 2026-08-21)가 그대로 적용되므로 터치 기기는 지금처럼
   글자를 계속 보여준다. */
@media (hover: hover) and (pointer: fine) {
  .feedback-fab {
    cursor: grab;
    opacity: .5;
    gap: 0;
    padding: 12px;
    border-radius: var(--r-round);
  }
  .feedback-fab-text {
    max-width: 0;
    margin: 0;
    overflow: hidden;
    white-space: nowrap;
    opacity: 0;
    transition: max-width .18s ease, opacity .12s ease, margin .18s ease;
  }
  .feedback-fab:hover,
  .feedback-fab:focus-visible,
  .feedback-fab.is-dragging {
    gap: 8px;
    padding: 13px 18px;
    border-radius: var(--r-md);
  }
  .feedback-fab:hover .feedback-fab-text,
  .feedback-fab:focus-visible .feedback-fab-text,
  .feedback-fab.is-dragging .feedback-fab-text {
    max-width: 120px;
    opacity: 1;
  }
}

/* 공지 작성 독과 자리가 같다(base 1569: bottom 24px / right 24px). 새로 생긴 쪽이
   비키는 게 아니라, **늘 떠 있는** 의견 버튼을 아래에 두고 **그 화면에만 있는** 작성
   독을 위로 올린다 — 이 파일 위쪽 automation-dock 보정과 같은 방식이다.
   ⚠️ 의견 버튼을 드래그해 다른 자리로 옮기면 이 보정은 더 이상 맞지 않을 수 있다 —
   다만 그때는 애초에 버튼이 이 자리를 벗어난 것이라 실질적인 겹침 문제는 없다. */
.notice-composer { bottom: 88px; }

@media (max-width: 600px) {
  /* base 가 600px 이하에서 composer 를 bottom:14px 로 다시 내린다. 그 값을 이긴다. */
  .notice-composer { bottom: 74px; }
  .feedback-fab    { padding: 11px 14px; font-size: 12px; bottom: 14px; right: 14px; }
}

/* 아이콘만 남기는 것은 **아주 좁은 화면(≤360px)에서만.** CS 직원이 처음 보는 버튼이라
   연필 기호 하나로는 무엇인지 알 수 없다 — 아이폰 표준 폭(390px)에서도 글자가 남게
   기준을 낮췄다(2026-08-21 폰 화면 확인 중 수정). */
@media (max-width: 360px) {
  .feedback-fab-text { display: none; }
  .feedback-fab      { border-radius: var(--r-round); padding: 12px; }
}

/* --- 작성 모달 --- */

.feedback-form { flex-direction: column; gap: 4px; display: flex; }

.feedback-context {
  background: var(--bg-subtle);
  border: 1px solid var(--line-inner);
  border-radius: var(--r-field);
  flex-direction: column;
  gap: 3px;
  margin-bottom: 14px;
  padding: 13px 15px;
  display: flex;
}

.feedback-context-tag   { color: var(--text-4); letter-spacing: .04em; font-size: 10px; font-weight: 900; }
.feedback-context b     { color: var(--green-text); font-size: 15px; }
.feedback-context small { color: var(--text-5); font-size: 11px; }

.feedback-kinds { flex-wrap: wrap; gap: 7px; display: flex; }

.feedback-kind {
  cursor: pointer;
  color: var(--text-1);
  background: var(--white);
  border: 1px solid var(--line-field);
  border-radius: var(--r-field);
  flex: 1 1 auto;
  min-width: 108px;
  padding: 11px 12px;
  font-size: 12px;
  transition: border-color .12s ease, background .12s ease;
}

.feedback-kind b     { font-weight: 900; }
.feedback-kind:hover { border-color: #b9d3c8; }

.feedback-kind.is-selected {
  color: var(--white);
  background: var(--green-solid);
  border-color: var(--green-solid);
}

.feedback-kind-hint { color: var(--text-5); margin-top: 7px; font-size: 11px; display: block; }

/* --- 담당자 의견함 표 --- */

/* **td 에 max-width 는 듣지 않는다** (table-layout:auto 의 규칙). 실제로 4000자짜리
   의견 하나가 이 칸을 세로로 수천 픽셀 늘려 표 전체를 못 읽게 만들었다(2026-08-21 실측).
   폭 제한은 안쪽 블록에 걸고, 길이 제한은 줄 수로 자른 뒤 「전체 보기」로 넘긴다.
   본문 상한이 4000자이므로 이 자리는 반드시 넘칠 수 있는 자리다(개발규칙 ②). */
.feedback-body-cell { min-width: 240px; }

.feedback-body-cell p {
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--ink);
  max-width: 420px;
  margin: 0;
  font-size: 12px;
  line-height: 1.7;
}

/* 여섯 줄까지만 보이고 나머지는 접는다. 접힌 건에만 「전체 보기」가 붙는다. */
.feedback-body-cell p.is-clamped {
  -webkit-line-clamp: 6;
  line-clamp: 6;
  -webkit-box-orient: vertical;
  display: -webkit-box;
  overflow: hidden;
}

.feedback-body-more {
  color: var(--green-text);
  cursor: pointer;
  background: none;
  border: 0;
  margin-top: 5px;
  padding: 0;
  font-size: 11px;
  font-weight: 900;
  text-decoration: underline;
}

.feedback-admin-note {
  color: var(--green-text);
  white-space: pre-wrap;
  border-top: 1px dashed var(--line-inner-v2);
  margin-top: 7px;
  padding-top: 6px;
  font-size: 11px;
  display: block;
}

/* 어드민 「신규 신청 다시 받기」 화면(service-switch.js)의 상태 요약 줄.
   base `.summary p { display: flex }` 에는 align-items 지정이 없어 기본값 stretch가
   적용된다 — "신규 신청" 줄만 배지(.admin-status-v2, inline-flex + 세로 패딩)가 섞여
   있어서, 순수 텍스트인 아래 두 줄과 다르게 라벨 글자와 배지 중심이 어긋나 보인다.
   `.summary`는 학부모·기관 화면 등 다른 곳에서도 널리 쓰는 공용 클래스라(13곳+),
   `.summary p` 자체를 여기서 고치면 그 화면들 정렬까지 다 바뀐다 — 그래서
   .summary p 는 그대로 두고, 이 화면에만 붙는 .service-switch-summary 로 좁혀서
   같은 값을 얹는다. */
.service-switch-summary p {
  align-items: center;
}

/* === 신규 신청 일괄 승인·반려 (admin/applications.js, 2026-08-21) ===========
   `.admin-data-table` 에 첫 열로 체크박스를 더한다. 1976행(base.css)이 **마지막** 열을
   sticky 로 고정하는 규칙이라 `td:last-child` 만 골라 걸린다 — 이 열은 첫 열이라
   그 선택자에 안 걸리고, 반대로 이 규칙이 마지막 열 sticky 를 건드리지도 않는다.
   ========================================================================== */

.admin-data-table th.col-check,
.admin-data-table td.col-check { width: 34px; padding-left: 14px; padding-right: 4px; }

.admin-data-table td.col-check input[type="checkbox"],
.admin-data-table th.col-check input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }

/* 승인·반려 둘 다 되지 않는 상태(NEEDS_REVIEW·APPROVED·REJECTED)는 선택 자체를 막는다.
   서버가 409 로도 막지만, 선택 단계에서 걸러야 담당자가 "왜 실패했지"를 안 되짚는다. */
.admin-data-table td.col-check input[type="checkbox"]:disabled { cursor: not-allowed; opacity: .35; }

/* 표 위 일괄 처리 바 — 선택 0건이어도 숨기지 않는다(버튼만 비활성). 위치가 매번 바뀌면
   찾기 어렵다는 규칙 ①의 반대 사례를 피한다. */
.admin-bulk-bar {
  background: #eefaf4;
  border: 1px solid #bfe3d3;
  border-radius: 12px;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  margin-bottom: 12px;
  padding: 11px 14px;
  display: flex;
}
.admin-bulk-bar > strong { color: #14664f; font-size: 13px; }
.admin-bulk-bar > small  { color: #4c8272; font-size: 11px; }
.admin-bulk-bar > div    { margin-left: auto; gap: 8px; display: flex; }
/* 좁은 칸에 한글 버튼 문구가 들어간다 — keep-all 이 없으면 "선택 해제" 같은 두 글자
   낱말이 "해"/"제"로 쪼개진다(규칙 ②, 390px 실측으로 걸림). 기본 정의에 두는 이유는
   반응형 구간에서만 걸고 기본을 빠뜨리는 패턴이 이 프로젝트에서 반복됐기 때문이다. */
.admin-bulk-bar > div button { word-break: keep-all; }

/* 전체 목록이 화면에 다 안 보일 때(서버 LIMIT 200) — "전체 선택"이 안 보이는 나머지를
   빠뜨린 채 넘어가는 사고를 막으려고 눈에 띄게 둔다(본문 지시 — 조용히 빠지면 안 됨). */
.admin-limit-banner {
  color: #a15a1f;
  background: #fff3e2;
  border: 1px solid #f3d6ad;
  border-radius: 10px;
  margin-bottom: 12px;
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 800;
  line-height: 1.6;
}

/* 일괄 처리 결과 모달 — 성공/실패 요약 + 실패 사유별 집계 + 건별 목록 */
.bulk-result-summary { flex-direction: column; gap: 14px; display: flex; }
.bulk-result-summary .ok   { color: #167b60; font-weight: 900; }
.bulk-result-summary .fail { color: #bd554f; font-weight: 900; }
.bulk-result-list {
  border: 1px solid var(--line-inner, #e7ece9);
  border-radius: 10px;
  max-height: 260px;
  margin: 8px 0 0;
  padding: 4px 12px;
  overflow: auto;
  list-style: none;
}
.bulk-result-list li,
.bulk-result-list > div { border-bottom: 1px solid var(--line-inner, #e7ece9); padding: 9px 0; font-size: 12px; }
.bulk-result-list li:last-child,
.bulk-result-list > div:last-child { border-bottom: 0; }
.bulk-result-list li > button { margin-left: 8px; }
.bulk-result-list input.form-input { margin-top: 5px; }

@media (max-width: 720px) {
  .admin-bulk-bar > div { margin-left: 0; width: 100%; }
  .admin-bulk-bar > div .btn-primary,
  .admin-bulk-bar > div .btn-secondary { flex: 1; }
}

/* === 취소 신청 관리 — 다중 선택·일괄 처리 (admin/cancels.js, 2026-08-21) ====
   `.admin-data-table` 첫 열에 체크박스를 추가한다. base.css 는 마지막 열에 버튼이
   있으면 그 열만 sticky 로 고정한다(`:has(tbody td:last-child button)`) — 첫 열은
   그 규칙과 안 겹치므로 건드리지 않고 그대로 가로 스크롤에 맡긴다. 두 열을 동시에
   고정하면 겹치는 자리를 새로 만들게 돼 위험이 크다(개발규칙 ②, 있는 함정 위에
   새 함정을 쌓지 않는다).
   ========================================================================== */

.admin-data-table td.select-col,
.admin-data-table th.select-col { width: 36px; padding-right: 2px; padding-left: 14px; }
.admin-data-table td.select-col input[type="checkbox"],
.admin-data-table th.select-col input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.admin-data-table td.select-col input[type="checkbox"]:disabled { cursor: not-allowed; opacity: .35; }

.bulk-bar {
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  background: var(--bg-subtle);
  border: 1px solid var(--line);
  border-radius: 12px;
  margin-bottom: 12px;
  padding: 10px 14px;
  display: flex;
}
.bulk-bar-count { color: var(--text-2); font-size: 12px; font-weight: 800; white-space: nowrap; }
.bulk-bar .btn-primary,
.bulk-bar .btn-secondary { padding: 9px 14px; font-size: 12px; }
.bulk-bar-warn  { background: #fff3e7; border-color: #efddc7; }
.bulk-bar-warn small { color: #8a5a30; font-size: 11px; line-height: 1.6; }

@media (max-width: 720px) {
  .bulk-bar { flex-direction: column; align-items: stretch; }
  .bulk-bar .btn-primary,
  .bulk-bar .btn-secondary { width: 100%; }
  .admin-data-table td.select-col,
  .admin-data-table th.select-col { padding-left: 10px; }
}

/* === 원아 명부 일괄 반 이동 (2026-08-21 사장님 요청) ========================
   "한 번에 한 명만 되는데, 이름 앞에 체크해서 한 번에 옮기고 싶다."

   이 앱에는 **행 단위 다중선택 UI 가 이 전까지 하나도 없었다**(전 화면 확인 — "전체선택"·
   "선택 N건"·고정 액션바 어디에도 없음). 그래서 여기서 처음 만든다.

   ⚠️ 기존 규칙을 고치지 않는다. `.tr` 의 5열 격자(base.css 622~624행)와 좁은 화면에서
      3·5번째 칸을 숨기는 규칙(835~839행)은 **열 번호로 세고 있다.** 열을 하나 더 만들면
      그 규칙이 통째로 어긋나므로, 체크박스는 이미 flex 인 첫 칸 안에 넣고 여기서는
      **덧붙이는 클래스만** 만든다.
   ========================================================================== */

/* 체크박스 자체는 17px 인데, 그대로 두면 폰에서 겨냥이 어렵다. 감싼 라벨에 패딩을 주고
   같은 크기의 음수 마진으로 되돌려 **눌리는 넓이만 30px 남짓으로 키운다** — 아바타와의
   간격(gap 10px)은 그대로다. */
.roster-check-hit {
  cursor: pointer;
  flex: none;
  align-items: center;
  margin: -7px 0 -7px -6px;
  padding: 7px 6px;
  display: inline-flex;
}
.roster-check-hit.is-off { cursor: not-allowed; }

.roster-check {
  accent-color: var(--mint);
  cursor: inherit;
  flex: none;
  width: 17px;
  height: 17px;
  margin: 0;
}
.roster-check:disabled { opacity: .4; cursor: not-allowed; }

/* 고른 줄은 눈으로 셀 수 있어야 한다 — 체크박스만으로는 스무 줄에서 몇 개 골랐는지 모른다. */
.tr.is-picked { background: #f1faf5; }

/* --- 일괄 액션 바 ---------------------------------------------------------
   **`.table-card` 밖에 둔다.** 그 카드는 600px 이하에서 `overflow: auto` 라 안쪽
   sticky 가 카드에 붙어 버리고, 표를 옆으로 밀면 바까지 함께 밀려 나간다.
   여기(`.content` 직속)에 두면 창 스크롤을 기준으로 붙어 늘 화면 안에 남는다.

   0명일 때는 `is-active` 가 없어 **떠 있지 않다** — 아무것도 안 고른 사람의 화면 아래를
   상시로 가리지 않는다. 대신 안내 한 줄만 남겨 이 기능이 있다는 것을 알린다. */
.roster-bulk-hint {
  color: var(--text-4);
  margin-top: 12px;
  padding: 0 4px;
  font-size: 12px;
  word-break: keep-all;      /* 좁은 폭에서 낱말 가운데가 갈라지지 않게 (규칙 ③) */
}

.roster-bulk-bar.is-active {
  z-index: 20;               /* 모달(180)·토스트(200) 아래, 상단바(10) 위 */
  margin-top: 16px;
  position: sticky;
  bottom: 16px;
}

.roster-bulk-inner {
  background: var(--white);
  border: 1px solid var(--line-card);
  border-radius: var(--r-md);
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  display: flex;
  flex-wrap: wrap;
  box-shadow: var(--sh-dock);
}
.roster-bulk-count   { color: var(--ink); font-size: 14px; font-weight: 800; }
.roster-bulk-actions { align-items: center; gap: 8px; display: flex; }
.roster-bulk-actions .btn-secondary,
.roster-bulk-actions .btn-primary { padding: 11px 16px; font-size: 13px; }

/* 모달 안 이름 묶음 — 누가 옮겨지는지 이름으로 확인하는 자리. 많으면 안에서 스크롤된다
   (모달 전체가 길어지면 아래 버튼이 화면 밖으로 나간다). */
.roster-picked-names {
  gap: 6px;
  max-height: 132px;
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  overflow-y: auto;
}
.roster-picked-names span {
  background: var(--bg-subtle);
  border: 1px solid var(--line-inner);
  border-radius: var(--r-chip);
  padding: 5px 9px;
  font-size: 12px;
  word-break: keep-all;
}

@media (max-width: 600px) {
  /* 버튼 둘을 한 줄에 나눠 갖게 한다. 좁은 폭에서 `선택한 12명 반 이동 →` 이 눌려
     글자가 세로로 쪼개지는 것을 막는다(base.css 868~874행이 반 관리에서 겪은 그 자리). */
  .roster-bulk-inner   { gap: 10px; padding: 11px 12px; }
  .roster-bulk-count   { font-size: 13px; }
  .roster-bulk-actions { flex: 1 0 100%; }
  .roster-bulk-actions .btn-secondary,
  .roster-bulk-actions .btn-primary {
    flex: 1 1 0;
    padding: 12px 8px;
    font-size: 12px;
    white-space: nowrap;
  }
}

/* === 학부모 화면의 '반려' 표시 (2026-08-21) ================================
   반려된 신청은 지금까지 학부모 화면에서 **그냥 사라졌다.** 이제 카드가 남는데,
   승인 대기 건과 색까지 같으면 한눈에 안 갈린다(배지 하나로는 부족했다).

   ⚠️ 기존 규칙을 고치지 않는다. `.application-card` · `.success-mark` · `.success aside`
      는 base.css 소유이고 다른 갈래가 함께 쓴다. 여기서는 **덧붙이는 클래스만** 만든다.
   ⚠️ `.success aside` 는 선택자가 (0,1,1) 이라 클래스 하나(0,1,0)로는 못 이긴다.
      뒤에 실린다고 이기는 것이 아니라 **specificity 가 먼저**다 — `.success aside.x` 로 쓴다.
   ========================================================================== */

/* 홈 카드 — 왼쪽에 붉은 띠를 세우고 바탕을 아주 옅게 물들인다.
   테두리 색만 바꾸면 흰 카드가 늘어선 목록에서 눈에 안 들어온다. */
.application-card-rejected {
  border-color: #f4d5d1;                 /* --tone-danger-bar 를 옅게 (오류 블록과 같은 값) */
  border-left: 4px solid var(--tone-danger-bar);
  background: #fffbfa;                   /* --tone-danger-bg 를 흰색에 섞은 아주 옅은 톤 */
}

/* 카드 안의 사유 두 줄. **한글은 keep-all 이라야 어절 단위로 넘어간다** — 안 주면
   좁은 카드에서 낱말 가운데가 갈라진다(규칙 ③이 네 번 넘게 걸린 그 자리다). */
.reject-line {
  color: var(--tone-danger-fg);
  background: var(--tone-danger-bg);
  border-radius: var(--r-chip);
  margin-top: 10px;
  padding: 9px 11px;
  font-size: 12px;
  line-height: 1.7;
  word-break: keep-all;
  display: block;
}
.reject-line b { margin-right: 6px; font-weight: 900; }

/* 완료 화면 — 접수번호로 다시 열었을 때 반려면 초록 체크가 아니라 붉은 느낌표다. */
.success-mark.success-mark-rejected {
  color: var(--tone-danger-fg);
  background: var(--tone-danger-bg);
}
.success aside.reject-aside {
  color: var(--tone-danger-fg);
  background: var(--tone-danger-bg);
  text-align: left;
  word-break: keep-all;   /* 사유는 문장이라 어절 가운데서 끊기면 특히 읽기 나쁘다 */
}


/* === 1차 범위 축소 — 추후 개발로 미룬 메뉴 (사장님 지시 2026-08-23) ==========
   무엇을 왜 잠갔는지의 정본은 js/phase1-scope.js 다. 여기서는 "연한 글씨 + 「추후」 표시"
   라는 **모양만** 맡는다. 다시 열 때 이 절은 그대로 두고 그 파일의 목록에서만 빼면 된다.

   ⚠️ base.css 사이드바 규칙과 특이성을 맞춰야 이긴다 — `.sidebar.dark > button.selected`
      가 (0,3,1) 이라 `.sidebar > button.locked` (0,2,1) 로는 못 이긴다. 잠근 화면에 주소로
      직접 들어오면 그 메뉴가 `.selected` 로도 그려지므로 실제로 부딪히는 자리다.
      같은 (0,3,1) 로 쓰고 app.css 가 뒤에 실려 이긴다(index.html 의 링크 순서).
   ========================================================================== */

/* 담당자 사이드바(어두운 바탕) — 살아 있는 메뉴 #aec2bb 보다 한 단계 낮춘 회록색 */
.admin-sidebar > button.locked,
.admin-sidebar-v2 > button.locked { color: #6f8b82; cursor: default; }

/* 기관 사이드바(어두운 바탕) — 살아 있는 메뉴 #a9beb7 보다 한 단계 낮춘 회록색 */
.sidebar.dark > button.locked { color: #6c8880; cursor: default; }

/* 아이콘까지 함께 흐려야 "지금은 못 누르는 자리"로 읽힌다 */
.admin-sidebar > button.locked > .icon,
.sidebar.dark > button.locked > .icon { opacity: .55; }

/* 폰(≤900px)에서 사이드바를 대신하는 가로 칩 내비 — 밝은 바탕이라 값이 따로다 */
.admin-mobile-nav button.locked,
.mobile-subnav button.locked {
  color: #9aaaa3;
  cursor: default;
  background: #f4f6f5;
  border-color: var(--line);
}

/* 「추후」 표시 — 검토 대기 배지가 앉던 자리를 그대로 쓰되, 숫자 배지처럼 눈에 띄지
   않게 바탕을 어두운 사이드바에 묻는다. 열지도 못하는 화면에 밝은 배지가 있으면
   담당자가 처리할 건이 있는 줄 알고 눌러 본다. */
.admin-sidebar > button > b.soon,
.admin-sidebar-v2 > button > b.soon,
.sidebar.dark > button > b.soon {
  color: #92a7a0;
  background: #2c463f;
  border-radius: 12px;
  margin-left: auto;
  padding: 2px 7px;
  font-size: 10px;
  font-weight: 800;
}
.admin-mobile-nav button > b.soon {
  color: #8b9c95;
  background: #eef1f0;
  border-radius: var(--r-field);
  margin-left: 5px;
  padding: 1px 5px;
}

/* 살아 있는 메뉴와 추후 개발 메뉴의 경계 (사장님 지시 2026-08-23 — 잠근 메뉴를 맨 아래로
   모았다). **새 모양을 만들지 않는다** — 맨 위 섹션 라벨(`.menu-label`)을 한 번 더 쓰고,
   윗선은 아래 「검토용 바로가기」·기관 정보 블록이 이미 쓰는 헤어라인과 같은 색이다.
   좌우 여백을 0 으로 되돌려 선이 그 두 블록의 윗선과 같은 폭으로 그어지게 하고,
   글자만 12px 들여 위 섹션 라벨과 세로로 맞춘다. */
.admin-sidebar > .menu-label.later,
.admin-sidebar-v2 > .menu-label.later,
.sidebar.dark > .menu-label.later {
  border-top: 1px solid var(--nav-dark-line);
  margin: 14px 0 4px;
  padding: 16px 12px 0;
}

/* 폰(≤900px)의 가로 칩 내비에는 라벨이 앉을 자리가 없다 — 세로 헤어라인 하나로만 끊는다.
   (부모가 900px 초과에서 display:none 이라 이 선도 함께 사라진다) */
.admin-mobile-nav > .later-rule,
.mobile-subnav > .later-rule {
  flex: 0 0 1px;
  align-self: stretch;
  background: var(--line);
  margin: 3px 5px;
}


/* =============================================================================
   신청 현황판 (admin/dashboard.js) — 사장님 지시 2026-10-19
   가독성 우선: 큰 숫자, 1rem 이상 글자, 흐린 회색 글씨 없음(보조 글씨도 --ink 계열).
   클래스는 전부 .board-* 로 새로 만들었다(기존 .admin-* 규칙과 섞이지 않게).
   ============================================================================= */
.board { color: var(--ink); }
.board .admin-status-v2 { font-size: 0.9rem; padding: 6px 12px; margin-right: 6px; }

.board-card {
  background: var(--white);
  border: 1px solid var(--line-card);
  border-radius: var(--r-panel);
  box-shadow: var(--sh-panel);
  padding: 22px 24px;
  margin-bottom: 18px;
}
.board-head { margin-bottom: 14px; }
.board-head h2 { font-size: 1.3rem; font-weight: 800; margin: 0 0 4px; color: var(--ink); }
.board-head p { font-size: 1rem; line-height: 1.5; margin: 0; color: #3d5750; }

.board-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.board-two > .board-card { margin-bottom: 18px; }

/* 기간별 큰 숫자 */
.board-numbers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.board-numbers > div { display: flex; flex-direction: column; gap: 2px; }
.board-label { font-size: 1.05rem; font-weight: 800; color: var(--ink); }
.board-numbers strong,
.board-stage strong { font-size: 2.6rem; line-height: 1.1; font-weight: 900; color: var(--ink); letter-spacing: -0.02em; }
.board-numbers strong em,
.board-stage strong em { font-size: 1.05rem; font-weight: 700; font-style: normal; margin-left: 4px; }
.board-numbers small { font-size: 0.9rem; color: #3d5750; }
.board-cancel .board-numbers strong { color: var(--tone-danger-fg); }

/* 처리 단계 */
.board-flow { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; }
.board-stage-wrap { flex: 1 1 200px; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.board-stage {
  flex: 1 1 200px;
  min-width: 0;
  text-align: left;
  display: flex; flex-direction: column; gap: 4px;
  padding: 16px 18px;
  border-radius: var(--r-md);
  border: 2px solid var(--line);
  background: var(--bg-subtle);
  cursor: pointer;
  font-family: inherit;
}
.board-stage-wrap > .board-stage { flex: 1 1 auto; }
.board-stage:hover { box-shadow: var(--sh-card-sm); }
.board-stage:focus-visible,
.board-sublink:focus-visible,
.board-recent > button:focus-visible { outline: 3px solid var(--mint-dark); outline-offset: 2px; }
.board-stage small { font-size: 0.95rem; line-height: 1.4; color: #3d5750; }
.board-stage.tone-wait { background: var(--tone-warn-bg); border-color: var(--tone-warn-bar); }
.board-stage.tone-warn { background: #fff0e4; border-color: #d98a52; }
.board-stage.tone-done { background: var(--tone-ok-bg); border-color: var(--tone-ok-bar); }
.board-stage.tone-off { background: var(--white); border-color: #b8c7c0; }
.board-stage.compact strong { font-size: 2rem; }
.board-arrow { align-self: center; font-style: normal; font-size: 1.6rem; font-weight: 900; color: var(--mint-dark); flex: 0 0 auto; }
.board-sublink {
  align-self: flex-start;
  background: none; border: 0; padding: 4px 2px;
  font: inherit; font-size: 1rem; font-weight: 800;
  color: var(--tone-danger-fg);
  text-decoration: underline;
  cursor: pointer;
}
.board-side { margin-top: 12px; max-width: 320px; }

/* 지점별 막대 */
.board-bars { display: flex; flex-direction: column; gap: 14px; }
.board-bars > div { display: grid; grid-template-columns: 5.5em minmax(0, 1fr) auto; align-items: center; gap: 12px; }
.board-bar-name { font-size: 1.1rem; font-weight: 800; }
.board-bars i { display: block; height: 16px; background: var(--line-inner); border-radius: 8px; overflow: hidden; }
.board-bars u { display: block; height: 100%; background: var(--mint); border-radius: 8px; text-decoration: none; }
.board-bars b { font-size: 1.2rem; font-weight: 900; white-space: nowrap; }
.board-bars b small { font-size: 0.9rem; font-weight: 600; color: #3d5750; }

/* 기관 순위 */
.board-rank { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.board-rank li { display: grid; grid-template-columns: 2.2em minmax(0, 1fr) auto; align-items: center; gap: 10px; }
.board-rank-no {
  width: 2em; height: 2em; border-radius: var(--r-round);
  background: var(--tone-ok-bg); color: var(--tone-ok-fg);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1rem; font-weight: 900;
}
.board-rank b { display: block; font-size: 1.05rem; word-break: keep-all; }
.board-rank small { display: block; font-size: 0.9rem; color: #3d5750; }
.board-rank strong { font-size: 1.15rem; font-weight: 900; white-space: nowrap; }

/* 최근 신청 */
.board-recent { display: flex; flex-direction: column; gap: 8px; }
.board-recent > button {
  display: grid; grid-template-columns: 6.5em minmax(0, 1fr) auto; align-items: center; gap: 12px;
  text-align: left; width: 100%;
  background: var(--bg-subtle); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 12px 14px; cursor: pointer; font: inherit; color: var(--ink);
}
.board-recent > button:hover { background: var(--tone-ok-bg); }
.board-recent time { font-size: 1rem; font-weight: 700; }
.board-recent-main b { display: block; font-size: 1.1rem; }
.board-recent-main small { display: block; font-size: 0.95rem; color: #3d5750; word-break: keep-all; }
.board-recent-chips { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px; }

.board-foot { font-size: 0.95rem; color: #3d5750; margin: 4px 2px 24px; }

/* 폰·좁은 화면 */
@media (max-width: 900px) {
  .board-two { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  .board-card { padding: 18px 16px; }
  .board-numbers { gap: 8px; }
  .board-numbers strong,
  .board-stage strong { font-size: 2.1rem; }
  .board-flow { flex-direction: column; }
  .board-arrow { transform: rotate(90deg); align-self: center; }
  .board-bars > div { grid-template-columns: 4.5em minmax(0, 1fr); }
  .board-bars b { grid-column: 1 / -1; }
  .board-recent > button { grid-template-columns: minmax(0, 1fr) auto; }
  .board-recent time { grid-column: 1 / -1; }
  .board-side { max-width: none; }
}
