/* ============================================================
   CB-3A — AI Ways Incheon 제품 조판
   ------------------------------------------------------------
   구성 (위에서 아래로 읽으면 됩니다):

     1. 기본 조판            모든 화면에 적용되는 바탕
     2. 좁은 화면            폰·태블릿 크기의 밝은 테마
     3. PC / 전시용 화면     실제 시연에 쓰이는 주 무대
     4. PC 세부 조건         폭이 좁거나 높이가 낮은 PC
     5. 애니메이션 정의
     6. 상태 클래스          JS가 켜고 끄는 것들

   같은 화면 조건은 반드시 한 곳에만 있습니다. 어떤 값을 고칠 때는
   해당 조건 구역만 보면 되고, 한 구역 안에서는 아래쪽이 이깁니다.
   (2026-08-13 정리: 같은 조건 블록이 6곳에 흩어져 있던 것을 합쳤습니다.
   규칙 문구와 구역 안 순서는 그대로 두었으므로 화면은 변하지 않습니다.)
   ============================================================ */


/* ---------- 0. 조건 없는 규칙 (상태 클래스 등) ---------- */
/* M2 HOME keeps the live dashboard data, but gives phones a concise D+C card view. */
.mobile-home-summary,.mobile-home-status { display:none; }
/* 스냅 이동 중에는 매 프레임 다시 계산되는 효과를 잠시 내려둔다.
   특히 상단 헤더의 backdrop blur는 화면에 고정돼 있어서, 뒤로 지나가는
   내용을 프레임마다 다시 블러 처리하느라 활강이 버벅이는 주된 원인이었다.
   멈춰 있을 때의 모습은 그대로이고, 움직이는 동안에만 적용된다. */
body.is-snapping .site-header,
body.is-snapping .scroll-finale-hint { backdrop-filter:none; }
/* 활강에 관여하는 두 씬만 레이어로 올려, 어둡게 깔린 그라디언트 면을
   다시 그리는 대신 옮기기만 하도록 한다. */
.scene.is-gliding { will-change:opacity, filter; }
/* 성취기준 문구에서 가운뎃점(·)으로 이어진 낱말은 한 덩어리로 읽히도록
   그 지점에서 줄이 갈라지지 않게 묶는다. */
.keep-together { white-space:nowrap; }

/* ---------- 1. 기본 조판 — 모든 화면 공통 (cb3a 레이어) ---------- */
@layer cb3a {
:root { --scene-content-max: 112rem; --scene-inline-space: clamp(1rem,3.4vw,4rem); --scene-block-space: clamp(4.5rem,8vw,9rem); --header-size:4.875rem; --header-inline-space:clamp(1.375rem,4vw,4.5rem); --nav-link-pad:.75rem; --scene-gap:clamp(1rem,1.65vw,1.5rem); --panel-pad:clamp(1.035rem,calc(1.5571vw - .366rem),2.125rem); --copy-max:49.375rem; }
  /* D's .scene never caps total width (confirmed live: max-width:100%,
     width:100%) - it controls margins purely through side padding, which
     scales as clamp(48px,5.4vw,138px) and locks to a flat 180px at
     2560px+. No --scene-content-max cap belongs at desktop widths. */
  /* 포탈처럼 진하고 채도 높은 배경으로 - 파랑/민트 두 개뿐이던 글로우에
     브랜드 세 번째 색(보라)을 더하고 다들 더 진하게, 베이스도 완전 검정에
     가깝던 걸 살짝 띄워서 유리 패널이 채도를 끌어올릴 색 자체를 늘린다. */
  body { color:#f6fbff; line-height:normal; background:radial-gradient(circle at 72% 16%,rgb(86 141 255 / 38%),transparent 42vw),radial-gradient(circle at 14% 78%,rgb(80 255 207 / 28%),transparent 38vw),radial-gradient(circle at 50% 96%,rgb(173 140 255 / 22%),transparent 40vw),linear-gradient(135deg,#050a1c 0%,#0c1b38 44%,#070f24 100%); }
  body::before { background:none; }
  .site-header { min-block-size:var(--header-size); padding:.875rem var(--header-inline-space); gap:.7rem 1.15rem; background:rgb(2 6 17 / 88%); border-block-end-color:rgb(122 213 237 / 16%); backdrop-filter:blur(18px) saturate(160%); }
  .brand { gap:.65rem; font-size:1.08rem; white-space:nowrap; }.brand-mark { inline-size:2.45rem; block-size:2.45rem; padding:.25rem; border:1px solid rgb(96 239 224 / 56%); background:linear-gradient(145deg,#65e9de,#24759d); box-shadow:0 0 24px rgb(75 222 208 / 17%); }
  .main-nav { flex:1 1 auto; justify-content:flex-end; gap:.1rem; overflow:visible; }.main-nav a { min-block-size:2.375rem; display:grid; place-items:center; padding-inline:var(--nav-link-pad); color:#b9cddb; font-size:.92rem; font-weight:760; }.edu2g-beta-trigger { flex:0 0 auto; min-block-size:2.375rem; }
  .responsive-nav-trigger { display:none; min-block-size:2.65rem; padding-inline:.82rem; color:#dffaff; background:transparent; border-color:rgb(111 212 232 / 34%); }
  main { gap:0; padding-block-end:0; }.scene { inline-size:100%; padding-inline:var(--scene-inline-space); min-block-size:100svh; padding-block:var(--scene-block-space); scroll-margin-block-start:calc(var(--header-size) + 1rem); box-sizing:border-box; }.scene-copy,.project-intro,.hero-copy { gap:0; }.scene-copy h2,.project-statement h2 { max-inline-size:16ch; font-size:clamp(2.35rem,4.4vw,7.15rem); font-weight:950; line-height:.96; letter-spacing:normal; }.eyebrow { display:inline-flex; align-items:center; gap:.65rem; color:#65ead6; font-size:.77rem; font-weight:850; letter-spacing:.11em; }.eyebrow::before { content:""; inline-size:2.15rem; block-size:1px; background:currentColor; }
  .panel { border-radius:1.375rem; border-color:rgb(131 224 255 / 18%); background:linear-gradient(145deg,rgb(13 31 61 / 86%),rgb(5 13 29 / 66%)); box-shadow:0 18px 48px rgb(0 7 20 / 20%); }.dashboard-grid>.panel:hover { border-color:rgb(99 232 215 / 56%); box-shadow:0 20px 48px rgb(0 7 20 / 29%); }

  /* The first scene ends at the viewport: a readable hero and a true 2 x 2 dashboard. */
  /* Every .scene (including dashboard) must share the same min-block-size
     formula - an earlier, shorter dashboard-only height caused the scroll-
     snap points to drift out of alignment with every section after it. */
  .dashboard-scene { padding-block:clamp(1.1rem,2.4vh,1.7rem); align-items:center; }.dashboard-scene .hero-copy { max-inline-size:var(--copy-max); align-content:center; padding-block-start:0; }.dashboard-scene h1 { max-inline-size:none; font-size:clamp(3.9rem,6.1vw,8.25rem); line-height:.96; letter-spacing:normal; font-weight:950; }.hero-copy h1 span { display:block; white-space:nowrap; }.hero-description { max-inline-size:37rem; color:rgb(223 236 250 / 86%); font-size:clamp(.93rem,1.12vw,1.28rem); line-height:clamp(1.638rem,calc(.814rem + .9154vw),2.278rem); font-weight:760; margin-block-start:3rem; }.hero-actions { margin-block-start:2.125rem; gap:.875rem; }.hero-actions>* { flex:0 0 auto; padding-inline:1.5rem; }.dashboard-grid { gap:clamp(.9rem,calc(1.2143vw - .193rem),1.75rem); padding:clamp(.875rem,calc(1.202vw - .423rem),1.5rem); border-radius:clamp(1.875rem,calc(1.442vw + .318rem),2.625rem); min-inline-size:0; }.dashboard-grid>.panel { min-inline-size:0; min-block-size:0; padding:var(--panel-pad); gap:.65rem; overflow:visible; }.panel-head { gap:1.125rem; align-items:flex-start; margin-block-end:.125rem; padding-inline-end:7rem; }.panel-head h2 { font-size:clamp(1.02rem,1.35vw,1.32rem); font-weight:950; letter-spacing:normal; }.panel-head p { font-size:.663rem; letter-spacing:normal; }.panel-head>span,.select-line>span { display:none; }.select-line { display:block; flex:0 0 auto; }.select-line select { min-block-size:2.2rem; padding:.35rem .5rem; font-size:.78rem; font-weight:850; }.school-panel .panel-head,.class-panel .panel-head { display:grid; grid-template-columns:minmax(0,1fr) auto; }.school-panel .select-line,.class-panel .select-line { grid-column:2; grid-row:1; }
  .kpi-row,.class-kpis,.landfill-metrics { gap:.45rem; }.kpi-row>div,.class-kpis>div,.landfill-metrics>div { min-block-size:0; padding:.58rem .62rem; border-radius:.875rem; background:rgb(2 10 26 / 58%); }.kpi-row strong,.class-kpis strong,.landfill-metrics strong { line-height:1.05; }.kpi-row span,.class-kpis span,.landfill-metrics span { font-size:.78rem; line-height:1.3; word-break:keep-all; overflow-wrap:break-word; }.school-visuals { gap:.7rem; }.bar-list,.confusion { gap:.42rem; }.bar-list>div { grid-template-columns:5.75rem minmax(0,1fr) 2.625rem; gap:.35rem; font-size:.9rem; font-weight:760; color:#9eb3c7; }.confusion>div { grid-template-columns:5.875rem minmax(0,1fr) 1.875rem; gap:.35rem; font-size:.9rem; font-weight:760; color:#9eb3c7; }
  .landfill-metrics { gap:.5rem; }
  .bar-list { gap:.625rem; }
  .confusion { gap:.4375rem; margin-block-start:.875rem; }
  /* Both real .donut instances (.school-panel .donut, .landfill-kpi-ring) are
     fully self-contained below - the base .donut/.donut-pair/.donut span/
     .donut small rules that used to live here are dead weight now that
     nothing unscoped uses them, so they were removed instead of left as
     confusing shadowed duplicates. */
  .landfill-metrics { grid-template-columns:repeat(4,minmax(0,1fr)); }.chart-wrap { grid-template-columns:minmax(0,1fr) minmax(6rem,.56fr); grid-template-areas:"title title" "chart status" "progress status"; gap:.5rem 1.125rem; margin-block-start:.75rem; }.chart-title-row { grid-area:title; min-block-size:1.35rem; }.chart-title { font-size:clamp(1rem,calc(.743rem + .2857vw),1.2rem); color:rgb(240 252 255 / 92%); }.landfill-time-now { padding-inline:.8125rem; font-size:clamp(.76rem,calc(.631rem + .1429vw),.86rem); line-height:1.08; font-weight:820; color:rgb(244 254 255 / 94%); }.combo-chart { grid-area:chart; max-block-size:7.1rem; min-inline-size:0; }.progress-stack { grid-area:progress; gap:.5rem; }.progress-stack label { grid-template-columns:minmax(0,1fr) 1.6rem 4.2rem; gap:.35rem; padding-inline:.625rem; font-size:.9rem; font-weight:760; color:#9eb3c7; }.class-panel .confusion h3 { font-size:.94rem; }.select-line { font-size:clamp(.82rem,calc(.552rem + .1923vw),.92rem); font-weight:850; color:rgb(158 179 199); }.donut-pair { gap:.625rem; }.rank-note { padding:.875rem 1.5rem; font-size:.61rem; text-align:center; border:1px solid rgb(94 247 205 / 24%); border-radius:var(--radius-pill); background:linear-gradient(90deg,rgb(94 247 205 / 10%),rgb(100 169 255 / 8%),rgb(173 140 255 / 8%)); }
  .confusion { background:rgb(2 10 26 / 50%); border-radius:1rem; padding:.75rem .5rem; }
  time.landfill-time-now { display:inline-block; padding-block:.375rem; border:1px solid rgb(94 247 205 / 18%); border-radius:999px; background:rgb(4 16 34 / 42%); box-shadow:inset 0 1px rgb(255 255 255 / 4.3%),0 0 .75rem rgb(94 247 205 / 5.5%); }
  a.source-link { padding-inline:.75rem; border:2px solid rgb(94 247 205 / 42%); border-radius:999px; background:rgb(255 255 255 / 5.5%); color:rgb(238 253 255 / 86%); font-size:.82rem; font-weight:900; }
.landfill-actions { gap:.5rem; }
a.source-link:hover { border-color:rgb(94 247 205 / 48%); background:rgb(94 247 205 / 12%); color:#fff; box-shadow:0 0 18px rgb(94 247 205 / 12%); }
.data-refresh-btn:hover { border-color:rgb(94 247 205 / 52%); box-shadow:0 0 20px rgb(94 247 205 / 20%); transform:rotate(24deg); }
  .project-origin { color:rgb(231 244 255 / 86%); font-size:clamp(1.3rem,calc(.593rem + .7857vw),1.85rem); letter-spacing:normal; font-weight:860; }
  .project-subtitle,.curriculum-description,.hah-description,.gallery-description,.flow-scene .scene-copy>p:not(.eyebrow) { color:#9eb3c7; font-weight:760; }.upload-panel { align-content:space-between; }.upload-panel>p { color:#b6cedc; font-size:.78rem; }.upload-actions.upload-card-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:1rem; }.upload-actions button { min-block-size:6.6rem; padding:.7rem; border-color:rgb(95 230 214 / 37%); background:linear-gradient(145deg,rgb(17 84 96 / 62%),rgb(9 43 69 / 90%)); }.upload-panel small { font-size:.55rem; line-height:1.35; }
  .school-panel { grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(5.3rem,.75fr); grid-template-rows:auto minmax(0,1fr); grid-template-areas:"head head select" "kpi visual visual"; }.school-panel .panel-head { display:block; grid-area:head; }.school-panel .select-line { grid-area:select; align-self:start; }.school-panel .kpi-row { grid-area:kpi; grid-template-columns:1fr; align-content:start; }.school-panel .school-visuals { grid-area:visual; min-inline-size:0; }.school-panel .kpi-row>div { padding:.45rem .5rem; }.class-panel .class-kpis>div { padding:.48rem .42rem; }.class-panel .confusion>div { grid-template-columns:5.875rem minmax(0,1fr) 1.875rem; font-size:.94rem; }

  .project-scene { grid-template-columns:minmax(17rem,.82fr) minmax(0,1.18fr); align-items:center; }.project-statement { border-inline-start:.28rem solid #67ead9; border-radius:0 var(--radius-large) var(--radius-large) 0; background:linear-gradient(90deg,rgb(62 217 199 / 12%),transparent); }.project-cards article { }.project-cards article:nth-child(2) { }.curriculum-scene { grid-template-columns:minmax(15rem,.58fr) minmax(0,1.42fr); align-items:center; }.subject-grid { }.subject-card { }.standards-panel { padding:clamp(1.25rem,2.5vw,2.3rem); border-inline-start:3px solid #58d7c3; }.standards-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .hah-scene { text-align:center; }.hah-scene .scene-copy { justify-items:center; max-inline-size:none; margin-inline:auto; }.hah-scene h2.hah-title { max-inline-size:none; inline-size:min(111.25rem,100%); margin-inline:0; line-height:1.14; font-size:clamp(4.95rem,5.48vw,7.45rem); word-break:keep-all; overflow-wrap:normal; }.hah-title span { display:block; white-space:nowrap; }.hah-grid .panel {padding:clamp(1.3rem,2.7vw,2.15rem); text-align:left; }.hah-grid .panel:nth-child(2) {border-color:rgb(102 234 214 / 68%);}.hah-grid .panel>span { display:inline-grid; place-items:center; inline-size:2.3rem; block-size:2.3rem; border-radius:50%; color:#061522; background:#6beedb; font-weight:900; }.callout { max-inline-size:66rem; margin-inline:auto; padding:1.1rem 1.45rem; border:1px solid rgb(102 234 214 / 36%);color:#c6dae5; }
  .flow-scene { grid-template-columns:minmax(15rem,.5fr) minmax(0,1.5fr); align-items:start; }.flow-grid { }.flow-card {border-color:rgb(131 224 255 / 18%); }.flow-card:nth-child(3n + 2) { }.flow-card.ai-badge { border-color:rgb(100 234 255 / 30%); }.flow-card.h2-badge { border-color:rgb(173 140 255 / 34%); }.flow-card em { font-style:normal; }.flow-card h3 { min-block-size:0; font-size:clamp(1.08rem,1.05vw,1.38rem); line-height:1.24; }.flow-card p { font-size:clamp(.82rem,.76vw,.98rem); line-height:1.54; }
  .flow-scene .scene-copy h2,.gallery-scene .scene-copy h2,.sorting-scene .scene-copy h2,.resources-scene .scene-copy h2 { font-size:clamp(4.4rem,5.4vw,7.15rem); }
  .gallery-scene { grid-template-columns:minmax(13rem,.42fr) minmax(0,1.58fr); align-items:start; }.gallery-stage { grid-template-columns:minmax(0,.9fr) minmax(20rem,1.1fr); }.gallery-grid { grid-template-columns:repeat(2,minmax(0,1fr)); grid-auto-rows:1fr; }.gallery-grid article {padding:1rem; }.gallery-grid article:nth-child(3n) { }.gallery-detail {padding:clamp(1rem,2vw,1.6rem);}
  .resources-scene { }.resources-scene .scene-copy {max-inline-size:47rem;}.resource-grid {text-align:left; }.resource-grid a { }.resource-grid a:nth-child(5n + 2) { }
  .sorting-scene { grid-template-columns:minmax(16rem,.56fr) minmax(0,1.44fr); align-items:start; }.sorting-features { grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem; }.sorting-features article { min-block-size:6.7rem; padding:.85rem; border:1px solid rgb(107 211 230 / 19%); border-radius:.8rem; background:rgb(8 38 61 / 66%); }.sorting-features h3 { color:#def8ff; font-size:.88rem; }.sorting-features p { color:#a8bfcd; font-size:.75rem; line-height:1.45; }.sorting-app { gap:1rem; padding:clamp(1rem,2vw,1.65rem); border-color:rgb(102 234 214 / 31%);}.app-tabs { grid-template-columns:repeat(4,minmax(0,1fr)); gap:.4rem; padding:.35rem;}.app-tabs button,.sorting-mode-switch button { min-block-size:2.85rem; }.upload-actions.sorting-upload { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }.upload-actions.sorting-upload button { min-block-size:10rem; padding:1.2rem; border-color:rgb(98 231 214 / 43%); background:linear-gradient(145deg,rgb(14 78 92 / 68%),rgb(9 39 64 / 96%)); }.sorting-upload-icon { inline-size:2.5rem; block-size:2.5rem; color:#62ecd8; }.quick-result { min-block-size:10.5rem; border-color:rgb(97 213 234 / 29%);}.quick-action-row button,.judgement-check { min-block-size:3rem; }.hold-board-shell { grid-template-columns:minmax(0,1fr) minmax(13rem,.76fr); }

  /* Distinct scene grammar: trail, map, human loop, route, media stage, editorial index. */
  .project-scene { position:relative; }.project-cards { position:relative; grid-template-columns:1fr; padding-inline-start:clamp(2.2rem,6vw,6rem); }.project-cards::before { content:""; position:absolute; inset:1rem auto 1rem 1rem; inline-size:1px; background:linear-gradient(#72ecd7,transparent); }.project-cards article { position:relative; min-block-size:auto; padding:1.15rem 0 1.15rem 1.4rem; border:0; border-radius:0; background:transparent; box-shadow:none; }.project-cards article::before { content:""; position:absolute; inset:1.45rem auto auto -1.34rem; inline-size:.72rem; block-size:.72rem; border-radius:50%; background:#76ecd8; box-shadow:0 0 0 .35rem rgb(118 236 216 / 12%); }.project-cards article:nth-child(2) { background:transparent; border-block:1px solid rgb(118 236 216 / 16%); }
  .curriculum-layout { position:relative; grid-template-columns:minmax(0,1fr); gap:1.2rem; }.subject-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:0; border-block:1px solid rgb(105 174 255 / 32%); }.subject-card { min-block-size:0; padding:1.05rem; border:0; border-radius:0; border-inline-end:1px solid rgb(105 174 255 / 25%); background:transparent; box-shadow:none; }.subject-card:last-child { border-inline-end:0; }.standards-panel { border:1px solid rgb(105 174 255 / 30%); border-radius:1.4rem 0 1.4rem 0; background:rgb(9 34 59 / 64%); box-shadow:none; }.standards-grid article { background:rgb(79 147 255 / 8%); }
  .hah-grid { grid-template-columns:1fr minmax(14rem,.8fr) 1fr; align-items:center; gap:0; }.hah-grid .panel { min-block-size:0; border:0; border-radius:0; background:transparent; box-shadow:none; }.hah-grid .panel:nth-child(1) { border-inline-start:3px solid #63ddcf; }.hah-grid .panel:nth-child(2) { transform:none; border:1px solid rgb(237 184 96 / 46%); border-radius:50% 50% 42% 42%; background:rgb(237 184 96 / 8%); box-shadow:none; text-align:center; }.hah-grid .panel:nth-child(3) { border-inline-start:3px solid #efbf6b; background:linear-gradient(90deg,rgb(239 191 107 / 13%),transparent); }.callout { border-radius:0; border-inline:0; border-block:1px solid rgb(239 191 107 / 35%); background:transparent; }
  .flow-grid { position:relative; grid-template-columns:repeat(4,minmax(0,1fr)); gap:0; border-block-start:1px solid rgb(76 207 224 / 40%); }.flow-card { min-block-size:16rem; padding:1rem; border:0; border-radius:0; border-inline-end:1px solid rgb(76 207 224 / 21%); background:transparent; box-shadow:none; }.flow-card:nth-child(3n + 2) { background:rgb(71 202 218 / 7%); }.flow-card:nth-child(4n) { margin-block-start:2.4rem; }.flow-card:last-child { border-inline-end:0; }
  .gallery-stage { gap:0; background:linear-gradient(135deg,#071725,#050b12); }.gallery-grid { gap:1px; background:rgb(126 166 184 / 26%); }.gallery-grid article { min-block-size:7.5rem; border:0; border-radius:0; background:#0b202e; box-shadow:none; }.gallery-grid article:nth-child(3n) { background:#10313e; }.gallery-detail { min-block-size:34rem; border:0; border-radius:0; background:radial-gradient(circle at 50% 35%,rgb(81 137 160 / 15%),transparent 42%),#070f18; }
  .resources-scene { text-align:left; }.resources-scene .scene-copy { justify-items:start; margin-inline:0; }.resource-grid { grid-template-columns:minmax(16rem,1.15fr) repeat(3,minmax(0,1fr)); gap:0; border-block:1px solid rgb(138 171 195 / 28%); }.resource-grid a { min-block-size:0; padding:1.05rem; border:0; border-radius:0; border-inline-end:1px solid rgb(138 171 195 / 22%); background:transparent; box-shadow:none; }.resource-grid a:first-child { grid-row:span 2; min-block-size:15rem; background:linear-gradient(135deg,rgb(93 147 183 / 22%),rgb(13 35 54 / 36%)); }.resource-grid a:nth-child(5n + 2) { background:transparent; }
  .sorting-layout { min-inline-size:0; }.sorting-app { border-radius:1.35rem; background:#081d2d; box-shadow:0 30px 70px rgb(0 6 17 / 38%); }.app-tabs { border-radius:.5rem; background:transparent; border-block-end:1px solid rgb(110 219 220 / 22%); }.app-tabs button { border:0; border-radius:0; background:transparent; }.quick-result { border-radius:.8rem; background:#0c2a3c; }.judgement-checklist { border-inline:3px solid #efbf6b; background:rgb(239 191 107 / 8%); }

  @media (min-width:64rem) { .dashboard-scene { grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr); align-items:stretch; gap:clamp(2.125rem,3.4vw,5.5rem); }.dashboard-grid { align-self:start; margin-block-start:.4rem; block-size:calc(100svh - var(--header-size) - 7.4rem); min-block-size:0; gap:1.05rem; grid-template-columns:repeat(2,minmax(0,1fr)); grid-template-rows:1.05fr .95fr; align-content:stretch; }.dashboard-grid>.panel,.dashboard-grid>.school-panel,.dashboard-grid>.landfill-panel,.dashboard-grid>.class-panel,.dashboard-grid>.upload-panel { min-block-size:0; }.upload-actions button { min-block-size:5.3rem; }.combo-chart { max-block-size:6.25rem; }.school-panel,.landfill-panel,.class-panel,.upload-panel { grid-column:auto; }.kpi-row,.class-kpis { grid-template-columns:repeat(3,minmax(0,1fr)); }.school-visuals { grid-template-columns:minmax(0,1fr) auto; align-items:center; }.project-cards,.hah-grid { grid-template-columns:repeat(3,minmax(0,1fr)); } }
  /* Preserve the pre-refactor lecture-board rhythm above tablet width without
     touching the closed-beta controls or their state selectors. */
  @media (min-width:64rem) { :root { --scene-inline-space:clamp(3rem,5.4vw,8.625rem); --scene-block-space:clamp(4rem,6.5vw,11.25rem); }.scene-copy h2,.project-statement h2 { max-inline-size:14ch; }.panel,.sorting-app { box-shadow:0 20px 52px rgb(0 7 20 / 26%); }.project-scene,.curriculum-scene,.hah-scene,.flow-scene,.gallery-scene,.sorting-scene,.resources-scene { border-block-start:1px solid rgb(111 212 232 / 9%); }.gallery-stage,.resource-grid,.flow-grid { box-shadow:0 18px 42px rgb(0 7 20 / 18%); }.main-nav a { position:relative; }.main-nav a.is-active::after,.main-nav a[aria-current]::after { content:""; position:absolute; inset-block-end:-.5rem; inset-inline:1rem; block-size:2px; background:linear-gradient(90deg,rgb(0 0 0 / 0%),#64eaff,#5ef7cd,rgb(0 0 0 / 0%)); } }
  /* D uses a 3rd, narrower clamp tier for the hero title between 1181-1600px
     (a typical 13-15" laptop width) - the wide-desktop clamp above only
     matches D at >=1601px. Read directly from D's source, not guessed. */
  @media (min-width:1181px) and (max-width:1600px) { .dashboard-scene h1 { font-size:clamp(3.7rem,5.6vw,6.6rem); }.dashboard-scene { grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr); gap:clamp(1.375rem,2.6vw,2.75rem); } }
  @media (min-width:64rem) and (max-width:73.74rem) { .dashboard-grid { block-size:calc(100svh - var(--header-size)*2 - 2.2rem); } }
  /* --scene-content-max and the h2 ceiling already match the base values
     above at this width, so only the genuinely ultra-wide-specific rule
     (wider dashboard columns) is kept here. */
  /* D's own 2560px-tier dashboard-scene columns/gap are shadowed by a later
     unconditional rule in D's source (confirmed live at 2560px viewport, not
     guessed) - the base 0.86fr:1.14fr ratio wins at every desktop width, so
     no dashboard-scene override belongs here. */
  @media (max-width:63.99rem) { .site-header { position:sticky; flex-wrap:nowrap; padding-block:.45rem; }.responsive-nav-trigger { display:inline-flex; align-items:center; justify-content:center; margin-inline-start:auto; }.main-nav { display:none; position:absolute; inset:calc(100% + .35rem) var(--scene-inline-space) auto; padding:.65rem; border:1px solid rgb(111 212 232 / 28%); border-radius:1rem; background:#071a2c; box-shadow:0 24px 52px rgb(0 7 20 / 44%); }.site-header.is-nav-open .main-nav { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }.main-nav a { min-block-size:2.8rem; font-size:.9rem; }.dashboard-scene,.project-scene,.curriculum-scene,.flow-scene,.gallery-scene,.sorting-scene { grid-template-columns:1fr; }.dashboard-scene { min-block-size:auto; padding-block-start:3rem; }.dashboard-scene .hero-copy { max-inline-size:50rem; }.dashboard-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }.landfill-panel { grid-column:auto; }.project-intro,.scene-copy { max-inline-size:52rem; }.flow-scene>.scene-copy, .gallery-scene>.scene-copy { position:static; }.flow-grid { grid-template-columns:repeat(4,minmax(0,1fr)); }.gallery-stage { grid-template-columns:minmax(0,1fr) minmax(19rem,1fr); }.sorting-layout { grid-template-columns:1fr; } }
  @media (max-width:47.99rem) { :root { --scene-inline-space:1rem; --scene-block-space:4rem; --header-size:4rem; }.site-header { gap:.35rem .45rem; }.brand { font-size:.94rem; }.brand-mark { inline-size:2.2rem; block-size:2.2rem; }.edu2g-beta-trigger { min-block-size:2.5rem; }.responsive-nav-trigger { min-block-size:2.5rem; padding-inline:.65rem; }.dashboard-scene { padding-block-start:2.4rem; }.dashboard-scene h1 { font-size:clamp(3.05rem,14.5vw,4.55rem); }.hero-description { font-size:.94rem; }.hero-fixed-line { display:inline; }.dashboard-grid,.project-cards,.subject-grid,.hah-grid,.flow-grid,.gallery-stage,.gallery-grid,.resource-grid { grid-template-columns:1fr; }.dashboard-grid>.panel { min-block-size:auto; }.landfill-metrics { grid-template-columns:repeat(2,minmax(0,1fr)); }.chart-wrap { grid-template-columns:minmax(0,1fr) minmax(6.3rem,.5fr); }.panel-head h2 { font-size:1.1rem; }.project-scene,.curriculum-scene,.hah-scene,.flow-scene,.gallery-scene,.sorting-scene,.resources-scene { min-block-size:auto; }.hah-grid .panel:nth-child(2) { transform:none; }.standards-grid { grid-template-columns:1fr; }.gallery-detail { min-block-size:22rem; position:static; }.sorting-features,.upload-actions.sorting-upload,.hold-board-shell { grid-template-columns:1fr; }.app-tabs { grid-template-columns:repeat(2,minmax(0,1fr)); }.sorting-mode-switch { grid-template-columns:1fr; }.upload-actions.sorting-upload button { min-block-size:8.7rem; }.resource-grid a:first-child { min-block-size:0; }.flow-card:nth-child(4n) { margin-block-start:0; } }
  @media (max-width:22.5rem) { .brand>span:last-child { font-size:.82rem; }.main-nav a { padding-inline:.52rem; font-size:.76rem; }.dashboard-scene h1 { font-size:3rem; }.chart-wrap { grid-template-columns:1fr; grid-template-areas:"title" "chart" "status" "progress"; } }
}

/* ---------- 2. 아주 좁은 화면 (~767px) ---------- */
@media (max-width:47.99rem) {
body { color:#202124; background:#f8fafd; }
  body::before { background:linear-gradient(180deg,#fff,transparent 18rem); }
  .site-header { background:#fff; border-block-end-color:#e8eaed; }
  .brand,.main-nav a { color:#174ea6; }
  .dashboard-scene,.sorting-scene { background:transparent; }
  .dashboard-grid>.panel,.sorting-app,.quick-result,.sorting-features article { color:#202124; background:#fff; border-color:#e8eaed; box-shadow:0 2px 8px rgb(60 64 67 / 14%); }
  .hero-description,.upload-panel>p,.sorting-features p { color:#5f6368; }
  .app-tabs { background:#f8fafd; border-block-end-color:#e8eaed; }
  .app-tabs button { color:#5f6368; }
  .app-tabs button.is-active { color:#1a73e8; background:#e8f0fe; box-shadow:inset 0 -2px #1a73e8; }
  .upload-actions.sorting-upload button { color:#174ea6; border-color:#d2e3fc; background:#f8fafd; }
  .mobile-quick-nav { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); order:2; gap:.35rem; inline-size:100%; padding:.35rem 0; }.mobile-quick-nav a { display:grid; place-items:center; min-block-size:2.75rem; padding:.35rem; border:1px solid #d2e3fc; border-radius:.7rem; color:#174ea6; background:#f8fafd; font-size:.72rem; font-weight:800; text-align:center; }.mobile-quick-nav a:focus-visible { outline:3px solid #f9ab00; outline-offset:2px; }
  .snap-root { display:flex; flex-direction:column; }.sorting-scene { order:-1; min-block-size:calc(100svh - var(--header-size)); padding-block:1.15rem 2rem; }.sorting-scene .scene-copy { gap:.55rem; }.sorting-scene .scene-copy h2 { max-inline-size:none; font-size:clamp(2rem,10vw,2.75rem); }.sorting-scene .scene-copy>p:not(.eyebrow) { display:none; }.sorting-features { display:none; }.sorting-app { gap:.8rem; padding:1rem; }.upload-actions.sorting-upload { grid-template-columns:repeat(2,minmax(0,1fr)); }.upload-actions.sorting-upload button { min-block-size:8.25rem; }.dashboard-scene { padding-block-start:2rem; }.dashboard-grid { gap:.75rem; }.dashboard-grid>.panel { padding:1rem; }.school-panel,.class-panel,.landfill-panel { scroll-margin-block-start:calc(var(--header-size) + 1rem); }.flow-grid { gap:.7rem; }.flow-card { min-block-size:auto; border:1px solid #e8eaed; border-radius:.85rem; background:#fff; }.gallery-stage { gap:.75rem; background:transparent; }.gallery-grid { gap:.75rem; background:transparent; }.gallery-grid article,.gallery-detail { border:1px solid #e8eaed; border-radius:.85rem; background:#fff; }.resource-grid { gap:.75rem; border:0; }.resource-grid a,.resource-grid a:first-child { min-block-size:0; border:1px solid #e8eaed; border-radius:.85rem; background:#fff; box-shadow:0 2px 8px rgb(60 64 67 / 10%); }
}

/* ---------- 3. 좁은 화면 (~1023px) — 밝은 테마 ---------- */
@media (max-width:63.99rem) {
:root { --mobile-shell-nav-size:4.75rem; }
  body { padding-block-end:calc(var(--mobile-shell-nav-size) + env(safe-area-inset-bottom)); }
  .site-header { position:sticky; inset-block-start:0; z-index:30; flex-wrap:wrap; align-items:center; padding-block-start:calc(.45rem + env(safe-area-inset-top)); background:#fff; border-block-end:1px solid #e8eaed; box-shadow:0 2px 10px rgb(60 64 67 / 10%); }
  .brand { color:#174ea6; }
  .responsive-nav-trigger { min-block-size:2.75rem; color:#174ea6; border-color:#d2e3fc; background:#f8fafd; }
  .main-nav { background:#fff; border-color:#d2e3fc; }
  .main-nav a { color:#174ea6; }
  .main-nav a.is-active { color:#174ea6; background:#e8f0fe; }
  .mobile-shell-bar { display:flex; order:2; align-items:center; justify-content:space-between; gap:.75rem; inline-size:100%; padding:.3rem 0 .05rem; }
  .mobile-shell-context { display:grid; gap:.08rem; min-inline-size:0; color:#5f6368; font-size:.72rem; line-height:1.2; }
  .mobile-shell-context strong { overflow:hidden; color:#202124; font-size:.92rem; text-overflow:ellipsis; white-space:nowrap; }
  .mobile-shell-primary { display:inline-grid; flex:0 0 auto; min-block-size:2.75rem; place-items:center; padding-inline:1rem; border-radius:.85rem; color:#fff; background:#1a73e8; box-shadow:0 3px 8px rgb(26 115 232 / 24%); font-size:.9rem; font-weight:850; text-decoration:none; }
  .mobile-shell-primary:focus-visible,.responsive-nav-trigger:focus-visible { outline:3px solid #f9ab00; outline-offset:2px; }
  .mobile-shell-state { order:3; inline-size:100%; margin:0; padding:.55rem .7rem; border-radius:.65rem; color:#8a3b00; background:#fef7e0; font-size:.8rem; }
  .mobile-shell-state[data-state="offline"] { color:#a50e0e; background:#fce8e6; }
  .mobile-quick-nav { position:fixed; z-index:35; inset:auto 0 0; display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:.35rem; padding:.5rem max(1rem,env(safe-area-inset-left)) calc(.5rem + env(safe-area-inset-bottom)) max(1rem,env(safe-area-inset-right)); border-block-start:1px solid #e8eaed; background:rgb(255 255 255 / 96%); box-shadow:0 -3px 12px rgb(60 64 67 / 10%); backdrop-filter:blur(12px); }
  .mobile-quick-nav a { display:grid; min-block-size:2.75rem; place-items:center; padding:.3rem; border:1px solid #d2e3fc; border-radius:.7rem; color:#174ea6; background:#f8fafd; font-size:.72rem; font-weight:800; line-height:1.15; text-align:center; text-decoration:none; }
  .mobile-quick-nav a[aria-current] { color:#0b57d0; border-color:#a8c7fa; background:#e8f0fe; }
  .mobile-quick-nav a:focus-visible { outline:3px solid #f9ab00; outline-offset:2px; }
  .scene { scroll-margin-block-start:calc(var(--header-size) + 4.8rem); }
.sorting-scene { order:initial; }
  .mobile-home-summary { display:block; margin:0; color:#5f6368; font-size:.92rem; line-height:1.55; }
  .dashboard-scene { display:block; padding-block:1.35rem 2.25rem; }
  .dashboard-scene .hero-copy { gap:.75rem; padding:0; }
  .dashboard-scene .eyebrow { color:#1a73e8; font-size:.68rem; }
  .dashboard-scene h1 { max-inline-size:10ch; color:#202124; font-size:clamp(2.15rem,10.5vw,3.2rem); line-height:1.04; letter-spacing:-.055em; }
  .dashboard-scene .hero-description,.dashboard-scene .hero-actions { display:none; }
  .mobile-home-status { display:block; margin:1rem 0 0; padding:.68rem .78rem; border:1px solid #d2e3fc; border-radius:.75rem; color:#174ea6; background:#f8fafd; font-size:.82rem; line-height:1.45; }
  .mobile-home-status[hidden] { display:none; }
  .mobile-home-status[data-state="offline"],.mobile-home-status[data-state="error"] { color:#a50e0e; border-color:#f6aea9; background:#fce8e6; }
  .dashboard-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); grid-auto-rows:1fr; gap:.75rem; margin-block-start:1.3rem; }
  .dashboard-grid>.panel { display:flex; min-block-size:0; padding:.9rem; gap:.65rem; align-items:flex-start; border-color:#e8eaed; background:#fff; box-shadow:0 2px 8px rgb(60 64 67 / 14%); }
  .dashboard-grid>.panel .panel-head { display:block; }
  .dashboard-grid>.panel .panel-head p,.dashboard-grid>.panel .select-line,.school-panel .school-visuals,.landfill-panel .chart-wrap,.class-panel .confusion,.class-panel .rank-note,.upload-panel .upload-actions,.upload-panel small { display:none; }
  .dashboard-grid>.panel .panel-head h2 { color:#202124; font-size:.88rem; line-height:1.32; }
  .dashboard-grid>.panel .kpi-row,.dashboard-grid>.panel .class-kpis,.dashboard-grid>.panel .landfill-metrics { display:block; margin:0; }
  .dashboard-grid>.panel .kpi-row>div:not(:first-child),.dashboard-grid>.panel .class-kpis>div:not(:first-child),.dashboard-grid>.panel .landfill-metrics>div:not(:first-child) { display:none; }
  .dashboard-grid>.panel .kpi-row>div,.dashboard-grid>.panel .class-kpis>div,.dashboard-grid>.panel .landfill-metrics>div { min-block-size:0; padding:0; background:transparent; }
  .school-panel .kpi-row strong { color:#0b57d0; font-size:clamp(2rem,10vw,2.8rem); line-height:1; }
  .dashboard-grid>.panel .kpi-row span,.dashboard-grid>.panel .class-kpis span,.dashboard-grid>.panel .landfill-metrics span { display:block; margin-block-start:.32rem; color:#5f6368; font-size:.72rem; line-height:1.35; }
  .landfill-panel .landfill-actions { display:none; }
  .landfill-panel .landfill-metrics strong,.class-panel .class-kpis strong { color:#202124; font-size:clamp(1.45rem,7vw,2rem); line-height:1.08; }
  .upload-panel>p { margin:0; color:rgb(223 236 250 / 78%); font-size:1rem; line-height:1.45; }
  .upload-panel::after { content:"3초 판단"; inline-size:100%; margin-block-start:auto; color:#0b57d0; font-size:1rem; font-weight:850; white-space:nowrap; }
  .mobile-quick-nav a::before { display:block; margin-block-end:.14rem; font-size:.9rem; line-height:1; }
  .mobile-quick-nav a[href="#dashboard"]::before { content:"⌂"; }
  .mobile-quick-nav a[href="#ranking"]::before { content:"★"; }
  .mobile-quick-nav a[href="#landfill"]::before { content:"◒"; }
.scroll-finale-hint, .scroll-cue { display:none; }
  /* Below 64rem the portal wrappers dissolve so the QR flows exactly where it
     always sat in the mobile panel; the PC-only teaser copy disappears. */
  .experience-portal, .portal-frame { display:contents; }
  .portal-powered, .portal-teaser { display:none; }
.hah-title { inline-size:100%; font-size:clamp(1.9rem,6.4vw,3.2rem); }
  .hah-title span { white-space:normal; }
}

/* ---------- 4. PC / 전시용 대형 화면 (1024px~) — 이 앱의 주 무대 ---------- */
@media (min-width:64rem) {
/* The last real nav *link*'s text should line up with the right edge of the
     scene content below it, so the header stops short of the scene inset by
     exactly the link's own inline padding. (The settings gear that visually
     sits further right is nudged out with transform, not real layout space,
     so it doesn't count as "the last item" for this alignment.) Verified at
     both widths: 180-20=160 at 2560px, 77.76-12=65.6 at 1440px. */
  .site-header { box-shadow:none; padding-inline-end:calc(var(--scene-inline-space) - var(--nav-link-pad)); }
  .main-nav a { border-radius:.7rem; }
  /* Lights flip in .17s - fast enough to read as a switch ("탁"), not a fade.
     app.js keeps the destination dark for the whole ride and calls the light
     the instant the snap lands. */
  .scene { position:relative; opacity:.56; filter:saturate(.68) brightness(.66); transition:opacity .17s cubic-bezier(.2,0,.25,1),filter .17s cubic-bezier(.2,0,.25,1),transform .3s cubic-bezier(.18,.92,.24,1); }.scene.is-active { opacity:1; filter:saturate(1.08) brightness(1.08); }
  .scene::after { content:""; position:absolute; inset:auto 0 0; block-size:1px; background:linear-gradient(90deg,transparent,rgb(111 212 232 / 18%),transparent); pointer-events:none; }

  /* No CSS scroll-snap here on purpose. D used it, but D's app.js did not
     hijack the wheel; this build's snapByWheel calls preventDefault and drives
     the scroll itself, so native snapping never gets to participate on a wheel
     gesture - it only wakes up to re-snap *after* each frame our animation
     writes, dragging against the curve. The snap feel comes from the JS curve
     in app.js (snapScrollTo) instead. */

  /* PC drops the 3-second-judgement app: the nav goes gallery -> resources. The
     markup stays put because frontendDomContract.test.js requires #sorting and
     its child ids, and the mobile layout below 64rem still renders the scene.
     app.js filters display:none scenes out of the scroll sequence to match. */
  .sorting-scene { display:none; }
  .main-nav a[href="#sorting"] { display:none; }

  /* Experience portal (upload panel, PC only): the panel's mobile capture UI
     gives way to a centered "Powered by Google Gemini" over one big dashed
     frame. The frame teases that finishing the deck and returning opens a new
     experience; the rewind (body.is-rewound) swaps the teaser for the hand-off
     QR and the dashed border "locks in" solid mint - the portal opening. */
  .upload-panel > p, .upload-panel .upload-highlights, .upload-panel .upload-card-actions, .upload-panel .gemini-credit { display:none; }
  .experience-portal { display:grid; grid-template-rows:auto 1fr; gap:.4rem; block-size:100%; min-block-size:0; }
  .portal-powered { justify-self:center; display:inline-flex; align-items:center; gap:.5rem; font-size:clamp(.98rem,.8vw,1.22rem); font-weight:850; letter-spacing:.02em; background:linear-gradient(90deg,#64eaff,#5ef7cd); -webkit-background-clip:text; background-clip:text; color:transparent; }
  .portal-gemini-star { inline-size:1.25em; block-size:1.25em; flex:0 0 auto; filter:drop-shadow(0 0 6px rgb(66 133 244 / 35%)); }
  .upload-panel .experience-portal { margin-block-end:.55rem; }
  .upload-panel > small { display:block; margin-block-start:.3rem; line-height:1.5; font-size:clamp(.85rem,.72vw,1.05rem); color:#bcd3e0; }
  .portal-frame { display:grid; place-items:center; min-block-size:7.2rem; align-content:center; row-gap:.3rem; padding:.7rem 1.1rem 1.15rem; border:2px dashed rgb(131 224 255 / 36%); border-radius:1.15rem; background:rgb(100 234 255 / 4%); transition:border-color .45s ease, box-shadow .45s ease, background .45s ease; animation:finale-hint-float 3.4s ease-in-out infinite; }
  .portal-teaser { margin:0; text-align:center; color:#f6fbff; font-size:clamp(.98rem,.85vw,1.28rem); font-weight:850; line-height:1.55; letter-spacing:-.01em; }
  .portal-teaser strong { color:#5ef7cd; }
  .portal-arrow { inline-size:1.7rem; block-size:1.55rem; margin-block-start:.05rem; background:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 44'><defs><linearGradient id='p' x1='0' x2='0' y1='0' y2='1'><stop offset='0' stop-color='%2364eaff'/><stop offset='1' stop-color='%235ef7cd'/></linearGradient></defs><path d='M7 5l17 13L41 5' fill='none' stroke='url(%23p)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round' opacity='.38'/><path d='M7 21l17 13L41 21' fill='none' stroke='url(%23p)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat; filter:drop-shadow(0 0 9px rgb(94 247 205 / 5)); animation:finale-arrow-bob 1.7s cubic-bezier(.45,0,.25,1) infinite; }
  .dashboard-scene .qr-invite { display:none; }
  body.is-rewound .portal-teaser, body.is-rewound .portal-arrow { display:none; }
  body.is-rewound .portal-frame { border-style:solid; border-color:rgb(94 247 205 / 55%); background:rgb(94 247 205 / 6%); box-shadow:0 0 34px rgb(94 247 205 / 16%), inset 0 0 24px rgb(94 247 205 / 7%); }
  /* Rewound state: the checklist gives way entirely - the panel opens with
     "Powered by Google Gemini" and spends the freed room on a booth-scannable
     QR, drawn with margin to spare instead of clipping the frame. */
  body.is-rewound .upload-panel .upload-highlights { display:none; }
  /* With the checklist gone the panel has spare height: pull Powered-by up,
     grow the frame vertically so the QR sits inside with clear margins. */
  body.is-rewound .portal-powered { margin-block-start:-.5rem; }
  body.is-rewound .portal-frame { padding:1.1rem 1.4rem; min-block-size:clamp(7rem,21svh,14.5rem); place-content:center; justify-items:start; row-gap:0; }
  body.is-rewound .upload-panel > small { margin-block-start:.6rem; }
  body.is-rewound .dashboard-scene .qr-invite { display:flex; align-items:center; gap:1.1rem; margin:-.28rem 0 0; padding:0; border:0; background:transparent; box-shadow:none; animation:qr-invite-reveal .62s cubic-bezier(.2,.7,.2,1) both; }
  body.is-rewound .dashboard-scene .qr-invite img { inline-size:clamp(9rem,10.2vw,12.5rem); block-size:auto; border-radius:.9rem; }
  body.is-rewound .dashboard-scene .qr-invite p { color:#dff2fb; font-size:clamp(.95rem,.8vw,1.18rem); font-weight:750; line-height:1.7; word-break:keep-all; }
  body.is-rewound .dashboard-scene .qr-invite p strong { color:#5ef7cd; font-weight:850; }

  /* ==== D revival: scene rhythm (every scene except the dashboard) ====
     All numbers below are two-point measurements of the live D reference
     (tag pc-visual-master-d) at 1440px and 2560px, refit as linear vw
     formulas - both viewports verify to <0.5px. D's language: content
     vertically centered in the frame, a long lead-in above (146->180px),
     a short exit below, per-scene side insets, and wider copy-to-content
     gaps than the rebuild kept. The dashboard is deliberately excluded:
     its right column is viewport-height-locked, so centering does nothing
     and its own layout is still under discussion. */
  .project-scene, .curriculum-scene, .hah-scene, .resources-scene {
    align-items:center;
    padding-block-start:calc(6.39rem + 3.04vw);   /* D 146 -> 180 */
  }
  /* flow-scene/gallery-scene are excluded above on purpose: their card grid
     has 2 rows, so align-items:center pushed the title/description down to
     match the grid's taller vertical center instead of lining up with its
     actual top edge. align-items:start (already set per-scene below) fixes
     that as long as this shared padding still applies to keep the vertical
     rhythm consistent with the other scenes. */
  /* align-items:start만 걸면 두 칸의 "위쪽"은 맞지만, 행 자체가 씬 맨 위에
     쩍 달라붙어 아래쪽에 큰 빈 공간이 남는다(방금 실측/캡처로 확인됨).
     align-content:center로 행 전체(칸 중 더 큰 카드그리드 기준 높이)를
     씬 안에서 다시 세로 중앙으로 되돌리고, align-items:start는 그대로 둬서
     두 칸의 위쪽끼리는 계속 맞춘다. */
  .flow-scene, .gallery-scene { align-content:center; padding-block-start:0; }
  /* 위/아래 여백을 사용자가 실측 지정한 값(차시흐름 350/327, 갤러리 390/399)에
     맞춘다 - align-content:center에서 padding-block-start를 P만큼 늘리면
     위쪽 여백이 정확히 P/2만큼 늘어나는 성질을 이용(전체 위+아래 합은 그대로
     유지되므로 아래쪽도 같이 맞아떨어짐). */
  .flow-scene { padding-block-start:130px; }
  .gallery-scene { padding-block-start:98px; }
  /* .gallery-stage was computing align-self:center despite no rule setting it
     (verified live via full stylesheet scan) - forcing it here so the card
     grid's top lines up with the copy block's top like every other scene. */
  .gallery-stage, .flow-grid { align-self:start; }
  .project-scene, .curriculum-scene { padding-block-end:3.625rem; }          /* D 58 flat */
  .hah-scene, .flow-scene, .gallery-scene { padding-block-end:calc(.96rem + 2.05vw); } /* D 45 -> 68 */
  .project-scene { padding-inline:calc(2.05rem + 4.73vw); }                  /* D 101 -> 154 */
  .curriculum-scene { padding-inline:calc(.96rem + 6.43vw); }                /* D 108 -> 180 */
  /* D ran the gallery full-bleed, but the card grid's rounded corners then sit
     clipped against the viewport edge (user: cards overflow). Keep the left
     bleed for the copy, inset the right so the grid breathes. */
  .gallery-scene { padding-inline:0 var(--scene-inline-space); }
  .resources-scene { padding-block-start:calc(3.23rem + 5.71vw); padding-block-end:3.625rem; padding-inline:calc(2.38rem + 4.38vw); } /* D 134->198 / 58 / 101->150 */
  /* Per-scene copy-to-content gaps live with each scene's own layout rules
     further down (search "D gap"); only the shared rhythm lives here. */

  /* Ranking modal: its styling died in the Stage 7.5-0 CSS purge while the
     app.js that builds it survived, so it rendered as bare block elements.
     Recostumed to the deck's glass language. */
  .ranking-modal { position:fixed; inset:0; z-index:80; display:grid; place-items:center; padding:2rem; background:rgb(2 8 18 / 64%); backdrop-filter:blur(7px); opacity:0; pointer-events:none; transition:opacity .18s ease; }
  .ranking-modal.is-open { opacity:1; pointer-events:auto; }
  .ranking-modal[hidden] { display:none; }
  .ranking-dialog { position:relative; inline-size:min(46rem,92vw); max-block-size:min(80vh,46rem); overflow:auto; overscroll-behavior:contain; display:grid; gap:.9rem; padding:1.7rem 1.9rem; border:1px solid rgb(131 224 255 / 26%); border-radius:1.4rem; background:linear-gradient(160deg,#0e2a45,#081326); box-shadow:0 40px 90px rgb(0 6 16 / 60%), inset 0 1px rgb(255 255 255 / 7%); }
  .ranking-close { position:absolute; inset-block-start:1rem; inset-inline-end:1rem; inline-size:2.2rem; block-size:2.2rem; min-block-size:0; display:grid; place-items:center; padding:0; border:1px solid rgb(131 224 255 / 25%); border-radius:999px; background:rgb(255 255 255 / 6%); color:#dff2fb; font-size:1.05rem; }
  .ranking-dialog h2 { font-size:1.45rem; }
  .ranking-tabs { display:flex; gap:.4rem; flex-wrap:wrap; }
  .ranking-tab { min-block-size:2.1rem; padding:.3rem .9rem; border:1px solid rgb(131 224 255 / 22%); border-radius:999px; background:transparent; color:#9eb3c7; font-size:.85rem; font-weight:800; }
  .ranking-tab.is-active { color:#06111c; background:linear-gradient(135deg,#5ef7cd,#64eaff); border-color:transparent; }
  .ranking-podium { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:.7rem; }
  .podium-card { display:grid; justify-items:center; gap:.15rem; padding:.85rem .5rem; border:1px solid rgb(131 224 255 / 20%); border-radius:1rem; background:rgb(255 255 255 / 4%); }
  .podium-card b { font-size:1.6rem; }
  .podium-card em { color:#5ef7cd; font-style:normal; font-weight:900; }
  .podium-card.is-current { border-color:rgb(94 247 205 / 55%); box-shadow:0 0 18px rgb(94 247 205 / 14%); }
  .ranking-race-head { color:#9eb3c7; font-size:.8rem; font-weight:850; }
  .ranking-race-list { display:grid; gap:.55rem; }
  .race-row { display:grid; grid-template-columns:1.6rem 1.8rem minmax(0,1fr); align-items:center; gap:.55rem; padding:.55rem .7rem; border:1px solid rgb(131 224 255 / 14%); border-radius:.9rem; background:rgb(255 255 255 / 3%); }
  .race-row.is-current { border-color:rgb(94 247 205 / 50%); background:rgb(94 247 205 / 7%); }
  .race-rank { color:#9eb3c7; font-weight:900; text-align:center; }
  .race-mascot { font-size:1.2rem; text-align:center; }
  .race-body { display:grid; gap:.3rem; min-inline-size:0; }
  .race-title { display:flex; align-items:baseline; gap:.6rem; min-inline-size:0; }
  .race-title strong { font-size:.95rem; }
  .race-title small { flex:1; min-inline-size:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#9eb3c7; font-size:.72rem; }
  .race-title b { color:#5ef7cd; font-size:.9rem; }
  .race-body i { display:block; block-size:.5rem; border-radius:999px; background:rgb(255 255 255 / 8%); }
  .race-body i em { display:block; block-size:100%; max-inline-size:100%; border-radius:999px; background:linear-gradient(90deg,#5ef7cd,#64eaff); }

  /* Cue that another scene sits below: a double chevron drawn as an inline SVG
     (gradient stroke, ghosted leading stroke) with a mint glow, bobbing on the
     active scene. Suppressed on the last scene - its finale hint takes over. */
  .scene::before { content:""; position:absolute; inset-block-end:1.1rem; inset-inline-start:50%; translate:-50% 0; inline-size:3.2rem; block-size:2.9rem; background:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 44'><defs><linearGradient id='g' x1='0' x2='0' y1='0' y2='1'><stop offset='0' stop-color='%2364eaff'/><stop offset='1' stop-color='%235ef7cd'/></linearGradient></defs><path d='M7 5l17 13L41 5' fill='none' stroke='url(%23g)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round' opacity='.38'/><path d='M7 21l17 13L41 21' fill='none' stroke='url(%23g)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat; filter:drop-shadow(0 0 14px rgb(94 247 205 / 52%)) drop-shadow(0 4px 12px rgb(0 6 16 / 55%)); opacity:0; pointer-events:none; transition:opacity .3s ease; }
  .scene.is-active::before { opacity:1; animation:scene-scroll-cue 1.7s cubic-bezier(.45,0,.25,1) infinite; }
  .resources-scene::before { content:none; }
  /* The dashboard's ::before is taken by its grid overlay, so the first scene
     carries the same chevron as a real element instead. */
  .dashboard-scene::before, .dashboard-scene.is-active::before { background:linear-gradient(90deg,rgb(100 234 255 / 5%) 1px,transparent 1px),linear-gradient(rgb(100 234 255 / 4.3%) 1px,transparent 1px); background-size:2.6rem 2.6rem; opacity:1; filter:none; translate:none; inline-size:auto; block-size:auto; animation:none; transition:none; }
  /* The cue lives OUTSIDE the scenes (a direct child of body): at scroll 0 the
     sticky header pushes the dashboard 89px down so a scene-anchored cue sits
     below the fold, and position:fixed can't save it from inside - the scene's
     filter makes the scene itself the containing block for fixed descendants.
     From the body it is genuinely viewport-fixed, shown only while the
     dashboard (the one scene whose ::before is taken by its grid overlay) is
     the active scene. */
  .scroll-cue { position:fixed; z-index:5; inset-block-end:1.1rem; inset-inline-start:50%; translate:-50% 0; inline-size:3.2rem; block-size:2.9rem; background:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 44'><defs><linearGradient id='g' x1='0' x2='0' y1='0' y2='1'><stop offset='0' stop-color='%2364eaff'/><stop offset='1' stop-color='%235ef7cd'/></linearGradient></defs><path d='M7 5l17 13L41 5' fill='none' stroke='url(%23g)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round' opacity='.38'/><path d='M7 21l17 13L41 21' fill='none' stroke='url(%23g)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat; filter:drop-shadow(0 0 14px rgb(94 247 205 / 52%)) drop-shadow(0 4px 12px rgb(0 6 16 / 55%)); opacity:0; pointer-events:none; transition:opacity .3s ease; }
  body:has(.dashboard-scene.is-active) .scroll-cue { opacity:1; animation:scene-scroll-cue 1.7s cubic-bezier(.45,0,.25,1) infinite; }

  /* Finale hint: floats in the resources scene's empty top-right, telling the
     visitor one more scroll rewinds the deck and reveals the hand-off QR. */
  /* Anchored to resource card 03's column: scene inline pad + two columns and
     two gaps of the 5-col grid below. */
  .scroll-finale-hint { position:absolute; z-index:2; inset-block-start:calc(8.2rem + 7.6vw);display:grid; justify-items:center; gap:.7rem; max-inline-size:31rem; padding:2rem 2.6rem 1.7rem; text-align:center; border-radius:1.6rem; border:1px solid rgb(94 247 205 / 30%); background:linear-gradient(155deg,rgb(13 48 66 / 74%),rgb(7 22 42 / 84%)); box-shadow:0 24px 60px rgb(0 6 16 / 45%), inset 0 1px rgb(255 255 255 / 8%), 0 0 44px rgb(94 247 205 / 10%); backdrop-filter:blur(10px); animation:finale-hint-float 3.4s ease-in-out infinite; }
  .scroll-finale-hint .hint-kicker { display:inline-flex; padding:.34rem .85rem; border-radius:999px; border:1px solid rgb(94 247 205 / 32%); background:rgb(94 247 205 / 10%); color:#5ef7cd; font-size:.74rem; font-weight:850; letter-spacing:.11em; }
  .scroll-finale-hint .hint-title { margin:0; color:#f6fbff; font-size:clamp(1.45rem,1.22vw,1.85rem); font-weight:900; letter-spacing:-.02em; }
  .scroll-finale-hint .hint-sub { margin:0; color:#9eb3c7; font-size:clamp(1rem,.84vw,1.22rem); font-weight:700; line-height:1.6; }
  .scroll-finale-hint .hint-sub strong { color:#64eaff; font-weight:850; }
  .scroll-finale-hint .hint-arrow { inline-size:2.8rem; block-size:2.55rem; margin-block-start:.15rem; background:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 44'><defs><linearGradient id='h' x1='0' x2='0' y1='0' y2='1'><stop offset='0' stop-color='%2364eaff'/><stop offset='1' stop-color='%235ef7cd'/></linearGradient></defs><path d='M7 5l17 13L41 5' fill='none' stroke='url(%23h)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round' opacity='.38'/><path d='M7 21l17 13L41 21' fill='none' stroke='url(%23h)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat; filter:drop-shadow(0 0 12px rgb(94 247 205 / 55%)); animation:finale-arrow-bob 1.7s cubic-bezier(.45,0,.25,1) infinite; }
  /* The base layer's h1,h2,h3,h4{letter-spacing:-0.035em} (style.css) applies
     site-wide and compresses every card/section heading - D never uses
     negative tracking on any heading level. Cancel it once, everywhere,
     instead of overriding each h3 instance separately. */
  h1, h2, h3, h4 { letter-spacing:normal; }
  .chart-title { font-weight:780; }
  /* D revival Step 1: restore D's decorative scene grid + depth behind the dashboard (PC only). */
  .dashboard-scene { position:relative; isolation:isolate; }
  .dashboard-scene::before { content:""; position:absolute; inset:0; z-index:-1; pointer-events:none; background:linear-gradient(90deg,rgb(100 234 255 / 5%) 1px,transparent 1px),linear-gradient(180deg,rgb(100 234 255 / 4.5%) 1px,transparent 1px); background-size:88px 88px; mask-image:radial-gradient(circle at 50% 48%,#000,transparent 72%); }
  .dashboard-grid { padding:clamp(.875rem,calc(1.202vw - .423rem),1.5rem); border-style:none; border-radius:clamp(1.875rem,calc(1.442vw + .318rem),2.625rem); background:rgb(8 18 36 / 24%); backdrop-filter:blur(24px) saturate(180%); -webkit-backdrop-filter:blur(24px) saturate(180%); box-shadow:0 26px 90px rgb(0 0 0 / 34%), inset 0 1px 0 rgb(255 255 255 / 14%); }
  .dashboard-grid>.panel { border-color:rgb(131 224 255 / 18%); box-shadow:0 20px 70px rgb(14 28 54 / 34%),inset 0 1px rgb(255 255 255 / 6%); }
  .dashboard-grid>.panel:hover { box-shadow:0 28px 80px rgb(6 18 36 / 48%),0 0 34px rgb(94 247 205 / 16%),inset 0 1px rgb(255 255 255 / 8%); background:linear-gradient(145deg,rgb(255 255 255 / 10.6%),rgb(94 247 205 / 6.7%),rgb(100 169 255 / 4.3%)); }
  .dashboard-grid>.panel .panel-head h2 { font-size:clamp(1.05rem,1.25vw,1.9rem); line-height:1.22; word-break:keep-all; }
  .dashboard-grid>.panel .panel-head p { font-size:clamp(.72rem,.62vw,.95rem); }
  .project-scene,.curriculum-scene,.hah-scene,.flow-scene,.gallery-scene,.sorting-scene,.resources-scene { isolation:isolate; }
  .project-statement,.standards-panel,.sorting-app { box-shadow:0 24px 56px rgb(0 7 20 / 25%); }
  .flow-card,.resource-grid a { transition:transform .18s ease,border-color .18s ease,background .18s ease; }
  .flow-card:hover,.resource-grid a:hover { transform:translateY(-.18rem); border-color:rgb(101 234 214 / 52%); }
  .gallery-stage { }
  .sorting-app { border:1px solid rgb(102 234 214 / 34%); }
  .sorting-csv-export { display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-block-start:1rem; padding-block-start:1rem; border-block-start:1px solid rgb(111 212 232 / 18%); }
  .sorting-csv-export button { min-block-size:2.85rem; padding-inline:1rem; }
  .sorting-csv-export p { margin:0; color:#a8bfcd; font-size:.78rem; }
.mobile-shell-bar,.mobile-shell-state,.mobile-quick-nav { display:none; }
.project-scene { grid-template-columns:1fr; gap:calc(.8rem + 1.61vw); } /* D gap 36->54; align comes from the D-revival rhythm block */
  .project-intro { max-inline-size:none; }
  .project-statement { padding:0; border:0; border-radius:0; background:none; box-shadow:none; }
  .project-statement h2 { max-inline-size:none; font-size:clamp(3rem,4.85vw,7.1rem); line-height:.96; font-weight:950; letter-spacing:normal; }
  .project-scene .eyebrow { inline-size:fit-content; gap:0; border:1px solid rgb(94 247 205 / 25%); border-radius:999px; background:rgb(94 247 205 / 9%); box-shadow:inset 0 1px rgb(255 255 255 / 6%); }
  .project-scene .eyebrow::before { display:none; }
  .project-cards { grid-template-columns:repeat(3,minmax(0,1fr));padding:0; }
  .project-cards::before { display:none; }
  .project-cards article { display:grid;border:1px solid rgb(131 224 255 / 18%); border-radius:1.5rem; background:linear-gradient(150deg,rgb(20 52 84 / 34%),rgb(9 27 48 / 30%)); box-shadow:none; }
  .project-cards article::before { display:none; }

  /* Global: D pill eyebrow everywhere (replaces the cb3a dash label). */
  .eyebrow { display:inline-flex; align-items:center; inline-size:fit-content; gap:0; padding:0 1.125rem; margin-block-end:1.25rem; min-block-size:2.125rem; border:1px solid rgb(94 247 205 / 25%); border-radius:999px; background:rgb(94 247 205 / 9%); box-shadow:inset 0 1px rgb(255 255 255 / 6%); color:#5ef7cd; font-size:.88rem; letter-spacing:normal; }
  .eyebrow::before { display:none; }

  /* CURRICULUM → D: copy left, subject cards + standards panel right. */
  .curriculum-scene { grid-template-columns:minmax(0,.71fr) minmax(0,1fr); gap:calc(.84rem + 3.07vw); background:none; } /* D gap 57.6->92 */
  .curriculum-layout { grid-template-columns:1fr; gap:1.125rem; }
  .curriculum-title { display:grid; gap:.1em; font-size:clamp(4.4rem,5.4vw,7.15rem); line-height:1.11; }
  .curriculum-title span { display:block; }
  .subject-grid { grid-template-columns:repeat(4,minmax(0,1fr)); gap:clamp(1.035rem,calc(.598rem + .4857vw),1.375rem); border:0; }
  .subject-card { position:relative; display:flex; flex-direction:column; gap:.6rem; min-block-size:0; padding:clamp(1.9rem,calc(.5rem + 1.6vw),3.1rem) clamp(1rem,1vw,1.5rem) 4.6rem; border:1px solid rgb(131 224 255 / 18%); border-radius:clamp(1.5rem,calc(.857rem + .7143vw),2rem); background:linear-gradient(145deg,rgb(13 31 61 / 86%),rgb(5 13 29 / 66%)); box-shadow:0 24px 64px rgb(0 0 0 / 28%),inset 0 1px rgb(255 255 255 / 6%); }
  .subject-card em { position:absolute; inset-inline-end:1.125rem; inset-block-end:1.125rem; margin-block-start:0; opacity:0; transform:translateY(.5rem); transition:opacity .18s,transform .18s; }
  .subject-card:hover em, .subject-card:focus-within em { opacity:1; transform:none; }
  /* Right-column readability pass (user feedback): the cards ran short and the
     copy small next to the enlarged left column. */
  .subject-card h3 { font-size:clamp(1.22rem,1.02vw,1.55rem); word-break:keep-all; }
  .subject-card p { font-size:clamp(.97rem,.83vw,1.2rem); line-height:1.66; }
  .standards-panel { grid-template-columns:1fr; gap:1.5rem; padding:clamp(1.485rem,calc(.662rem + .9143vw),2.125rem); margin-block-start:1.25rem; border:1px solid rgb(131 224 255 / 18%); border-radius:clamp(1.5rem,calc(.857rem + .7143vw),2rem); background:linear-gradient(145deg,rgb(9 22 44 / 78%),rgb(5 13 29 / 62%)); box-shadow:none; }
  .standards-panel>div:first-child { display:flex; justify-content:space-between; align-items:baseline; gap:1rem; }
  .standards-panel>div:first-child p { color:#9eb3c7; font-size:1rem; }
  .standards-grid { grid-template-columns:repeat(3,minmax(0,1fr)); gap:1.05rem; margin-block-start:0; }
  .standards-grid article { display:grid;border:1px solid rgb(105 174 255 / 30%); border-radius:1rem; background:rgb(79 147 255 / 8%); }
  /* 카드의 직계 span(성취기준 코드)만 배지로 만든다. 본문 문단 안의 span까지
     잡으면 문장 일부가 배지처럼 보인다. */
  .standards-grid article > span { display:inline-flex; inline-size:fit-content; padding:.3rem .7rem; border-radius:999px; background:linear-gradient(135deg,#5ef7cd,#64eaff); color:#06111c; font-size:.85rem; font-weight:950; }
  .standards-grid article p { font-size:clamp(.92rem,.76vw,1.08rem); font-weight:760; color:#9eb3c7; line-height:1.55; }

  /* H-A-H → D: centered copy, three bordered glass cards, pill callout. */
  .hah-scene { grid-template-columns:1fr; gap:calc(.24rem + .98vw); text-align:start; background:none; } /* D gap 18->29 */
  .hah-scene .scene-copy { justify-items:start; margin-inline:0; max-inline-size:none; }
  .hah-grid .panel ul { padding-inline-start:2.5rem; list-style:disc; }
  .hah-grid .panel li { color:#9eb3c7; }
  .hah-grid { grid-template-columns:repeat(3,minmax(0,1fr)); gap:clamp(1.35rem,calc(.996rem + .3929vw),1.625rem); align-items:stretch; }
  .hah-grid .panel { display:block;padding:clamp(1.5rem,calc(.857rem + .7143vw),2rem); border:1px solid rgb(131 224 255 / 18%); border-radius:clamp(1.5rem,calc(.857rem + .7143vw),2rem); background:linear-gradient(145deg,rgb(13 31 61 / 86%),rgb(5 13 29 / 66%)); box-shadow:0 24px 64px rgb(0 0 0 / 28%),inset 0 1px rgb(255 255 255 / 6%); transform:none; text-align:left;}
  .hah-grid .panel.accent { border-color:rgb(173 140 255 / 34%); box-shadow:0 24px 74px rgb(112 84 255 / 20%); }
  .flow-card:hover { box-shadow:0 0 24px rgb(94 247 205 / 12%); }
  .hah-grid .panel:hover { box-shadow:0 30px 90px rgb(2 10 25 / 46%),0 0 34px rgb(94 247 205 / 15%); }
  .hah-grid .panel::before { display:none; }
  /* D's number badge is a 50px rounded square (18px radius), not a circle. */
  .hah-grid .panel>span { inline-size:3.125rem; block-size:3.125rem; margin-block-end:.875rem; border-radius:1.125rem; background:linear-gradient(135deg,#5ef7cd,#5a9dff); color:#07111b; }
  .callout { max-inline-size:66rem; margin-inline:auto; margin-block-start:-2.5rem; padding:.75rem 1.5rem; border:1px solid rgb(94 247 205 / 24%); border-radius:1.25rem; background:rgb(94 247 205 / 8%); color:rgb(237 255 250 / 92%); font-weight:850; font-size:clamp(1.08rem,calc(.771rem + .3429vw),1.32rem); line-height:1.4; }

  /* FLOW → D: copy left, four bordered cards with gaps (no strip / stagger). */
  .flow-scene { grid-template-columns:minmax(0,.52fr) minmax(0,1fr); gap:calc(.51rem + .93vw); background:none; } /* D gap 21.6->32 */
  .flow-grid { grid-template-columns:repeat(4,minmax(0,1fr)); gap:clamp(.84375rem,calc(.482rem + .4018vw),1.125rem) 1rem; border:0; box-shadow:none; }
  .flow-card { display:flex; flex-direction:column; gap:.625rem; min-block-size:0; margin:0; padding:clamp(1.08rem,calc(.701rem + .4214vw),1.375rem); border:1px solid rgb(131 224 255 / 18%); border-radius:1.375rem; background:rgb(255 255 255 / 6%); box-shadow:none; }
  .flow-card.ai-badge { border-color:rgb(100 234 255 / 30%); }
  .flow-card.h2-badge { border-color:rgb(173 140 255 / 34%); }
  .flow-card:nth-child(4n) { margin-block-start:0; }
  /* D renders the step label and "수업자료" tag as pill chips, not plain text. */
  .flow-card span { display:inline-flex; inline-size:auto; justify-self:start; align-self:flex-start; padding:.4375rem .8125rem; border:1px solid rgb(94 247 205 / 32%); border-radius:999px; color:#5ef7cd; background:rgb(94 247 205 / 8%); font-size:clamp(.72rem,.66vw,.88rem); line-height:1; }
  .flow-card em { align-self:flex-end; margin-block-start:auto; display:inline-flex; inline-size:max-content; min-block-size:2.125rem; align-items:center; justify-content:center; padding:0 .875rem; border-radius:999px; color:#07111c; background:linear-gradient(135deg,#5ef7cd,#64eaff); font-weight:870; font-size:.82rem; opacity:0; transform:translateY(.5rem); transition:opacity .18s,transform .18s; }
  .flow-card:hover em, .flow-card:focus-within em { opacity:1; transform:none; }

  /* RESOURCES → D: full-width, even six-up bordered tiles. */
  .resources-scene { grid-template-columns:1fr; gap:calc(1.8rem + 2vw); text-align:left; background:none; } /* D gap 57.6->80 */
  /* 5x2 (11/12 removed from the markup), taller cards, bigger type. */
  .resource-grid { grid-template-columns:repeat(5,minmax(0,1fr));border:0; }
  .resource-grid a { display:grid; gap:1rem; align-content:center; min-block-size:clamp(6.5rem,7vw,9rem); padding:1.4rem 1.5rem; border:1px solid rgb(138 171 195 / 28%); border-radius:1.5rem; background:rgb(11 30 51 / 55%); box-shadow:0 24px 64px rgb(0 0 0 / 28%),inset 0 1px rgb(255 255 255 / 6%); font-size:clamp(1.1rem,.95vw,1.4rem); font-weight:880; }
  .resource-grid a span { color:#5ef7cd; font-weight:900; font-size:clamp(1.05rem,.9vw,1.35rem); }
  .footer-credit { display:grid; grid-template-columns:1fr auto; justify-content:space-between; gap:1.5rem; border-block-start:1px solid rgb(132 220 255 / 20%); padding-block-start:1.5rem;color:#9eb3c7; font-weight:760; }.footer-credit p:last-child { justify-self:end; text-align:end; }
  .resource-pending { cursor:default; color:rgb(180 197 210 / 70%); background:rgb(255 255 255 / 3.5%); }
  .project-subtitle,.curriculum-description,.hah-description,.gallery-description,.resources-description,.flow-scene .scene-copy>p:not(.eyebrow) { color:#9eb3c7; font-weight:760; margin-block:1em; }
  .flow-scene .scene-copy>p:not(.eyebrow) { font-size:clamp(1rem,.9vw,1.3rem); }
  .project-subtitle { font-weight:400; font-size:clamp(1rem,calc(.666rem + .3714vw),1.26rem); margin-block:.625rem 0; }
  .curriculum-description { font-size:clamp(.92rem,calc(.277rem + .7143vw),1.42rem); line-height:1.65; }
  .hah-description { font-size:clamp(.94rem,calc(.67rem + .3vw),1.15rem); line-height:1.5; }
  .gallery-description { font-size:clamp(.9rem,calc(.55rem + .4vw),.98rem); line-height:1.7; }
  .project-statement { margin-block-start:clamp(2.16rem,calc(1.401rem + .8429vw),2.75rem); }
  .project-cards { gap:clamp(1.89rem,calc(.945rem + 1.05vw),2.625rem); }
  .resource-grid { box-shadow:none; gap:clamp(.75rem,calc(.268rem + .5357vw),1.125rem); }
  .resources-scene .scene-copy { gap:1rem; }
  .resources-description { font-weight:400; font-size:clamp(1rem,calc(.64rem + .4vw),1.28rem); line-height:1.72; color:rgba(218 239 247 / 76%); }
  .resource-grid a.resource-pending { background-color:rgb(255 255 255 / 3.5%); background-image:linear-gradient(145deg,rgb(15 36 64 / 52%),rgb(6 15 31 / 48%)); border-color:rgb(148 198 220 / 18%); gap:.5rem; line-height:1.28; font-size:clamp(.95rem,calc(.731rem + .2429vw),1.12rem); padding:clamp(.9rem,calc(.45rem + .5vw),1.25rem); border-radius:clamp(1.5rem,calc(.857rem + .7143vw),2rem); }
  .footer-credit { margin-block-start:clamp(.9rem,calc(.129rem + .8571vw),1.5rem); }
  .resource-pending em { color:rgb(170 188 201 / 62%); font-style:normal; }
  .resource-grid a:first-child { grid-row:auto; min-block-size:0; background:rgb(11 30 51 / 55%); }

  /* GALLERY → D: copy left, 4-up card grid; detail opens on click (is-detail). */
  .gallery-scene { grid-template-columns:minmax(0,.68fr) minmax(0,1fr); gap:calc(.39rem + 2.57vw) calc(2.41rem + .53vw); background:none; } /* D row 43.2->72, col 46.08->52; copy column widened so the description's first line fits unbroken */
  .gallery-scene .scene-copy { padding-inline-start:clamp(4.5rem,calc(2.25rem + 2.5vw),6.25rem); }
  .gallery-stage { display:block; border:0; background:none; box-shadow:none; }
  .gallery-grid { grid-template-columns:repeat(4,minmax(0,1fr)); gap:clamp(.84375rem,calc(.482rem + .4018vw),1.125rem) 1rem; background:transparent; }
  .gallery-detail-head { gap:.375rem clamp(1.35rem,calc(.675rem + .75vw),1.875rem); }
  .gallery-detail-grid { gap:clamp(1.98rem,calc(1.311rem + .7429vw),2.5rem); }
  .gallery-grid article { position:relative; overflow:hidden; display:grid; align-content:center; justify-items:center; gap:.75rem; min-block-size:clamp(11rem,11vw,14.5rem); padding:1.25rem; border:1px solid rgb(126 166 184 / 26%); border-radius:1.5rem; background:linear-gradient(145deg,rgb(16 62 84 / 55%),rgb(8 27 48 / 52%)); box-shadow:0 24px 64px rgb(0 0 0 / 28%),inset 0 1px rgb(255 255 255 / 6%); text-align:center; }
  /* D pins the "바로가기" button to the card's bottom-right corner as a pill. */
  .gallery-grid article button { position:absolute; inset-inline-end:1.125rem; inset-block-end:1.125rem; min-block-size:2.375rem; padding:0 1rem; border-radius:999px; border:1px solid rgb(94 247 205 / 28%); color:#06111c; background:linear-gradient(135deg,#5ef7cd,#64eaff); opacity:0; transform:translateY(.5rem); transition:opacity .18s,transform .18s; }
  .gallery-grid article:hover button, .gallery-grid article:focus-within button { opacity:1; transform:none; }
  .gallery-grid article>span { color:rgb(164 191 216 / 78%); font-weight:800; font-size:clamp(1.05rem,.9vw,1.3rem); }
  /* One notch off the titles buys ~10px of side air per card without touching
     the card boxes themselves. */
  .gallery-grid article h3 { font-size:clamp(1.28rem,1.15vw,1.42rem); word-break:keep-all; }
  .gallery-detail { display:none; min-block-size:0; padding:1.25rem; margin-block-start:1.125rem; border:1px solid rgb(94 247 205 / 22%); border-radius:1.375rem; background:rgb(94 247 205 / 7%); }
  .gallery-stage.is-detail .gallery-grid { display:none; }
  .gallery-stage.is-detail .gallery-detail { display:grid; }

  /* SORTING → D shell: copy+feature cards left, app card right. Functional DOM
     (mode switch, class profile, classroom skill, CSV) is preserved as-is. */
  .sorting-scene { grid-template-columns:minmax(0,.7fr) minmax(0,1fr); gap:2.7rem; background:none; }
  .sorting-features { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.9rem; }
  .sorting-features article { min-block-size:0; padding:1.375rem; border:1px solid rgb(107 211 230 / 22%); border-radius:1.125rem; background:linear-gradient(150deg,rgb(16 58 90 / 55%),rgb(8 27 48 / 52%)); }
  .sorting-features h3 { font-size:1rem; }
  .sorting-features p { font-size:.82rem; }
  .app-tabs { gap:.625rem; padding:.375rem; border-radius:1.125rem; background:rgb(4 21 37 / 62%); border:1px solid rgb(94 247 205 / 16%); }

  /* D-style hover lift across every restored PC card. */
  .project-cards article, .subject-card, .hah-grid .panel, .flow-card, .gallery-grid article, .resource-grid a, .sorting-features article { transition:transform .18s ease, border-color .18s ease, box-shadow .18s ease; }
  .project-cards article:hover, .subject-card:hover, .hah-grid .panel:hover, .flow-card:hover, .gallery-grid article:hover, .resource-grid a:hover, .sorting-features article:hover { transform:translateY(-.2rem); border-color:rgb(101 234 214 / 48%); box-shadow:0 18px 40px rgb(0 7 20 / 24%); }

  /* ============================================================
     DASHBOARD fine-tune — round 1 (user pixel review, 2026-08-09).
     ============================================================ */

  /* 1. Logo mark: the brand icon was designed on white; the teal
     gradient behind it was swallowing the artwork. */
  /* D itself only shows the full logo at 52px (illegible at that size,
     verified live) - enlarging beyond D here is a deliberate legibility
     improvement, not a mismatch. Full original logo file, not a crop. */
  /* Fill the white tile: the logo was 48px inside a 60px tile because of a
     .32rem inset. At 1440px the tile already uses 60 of the header's 61px of
     free height, so the only room left is that inset; at 2560px there are 84px
     of free height, so the tile itself can grow too. The clamp keeps 1440px
     exactly where it was and only spends the headroom wider screens have. */
  /* Tile trimmed ~14% per feedback; the logo inside stays maximized via the
     hairline inset, and the flat white gets a soft top-lit wash. */
  .brand-mark { inline-size:clamp(3.2rem,2.66vw,4.3rem); block-size:clamp(3.2rem,2.66vw,4.3rem); overflow:hidden; background:linear-gradient(155deg,#ffffff 12%,#eef8ff 56%,#d9edf7 100%); border-color:rgb(0 0 0 / 10%); box-shadow:0 2px 10px rgb(0 0 0 / 22%), inset 0 1px rgb(255 255 255 / 90%); padding:.06rem; }
  .brand { font-size:clamp(1.18rem,.88vw,1.4rem); }
  /* The PNG carries ~33% transparent margin around its ink (measured via
     canvas alpha bbox: 969x686 ink in a 1440x960 file). Scaling the img 1.38x
     brings the ink to ~93% of the tile; the tile's overflow:hidden guarantees
     nothing escapes. */
  .brand-mark img { scale:1.22; }
  .hero-actions .primary-btn, .hero-actions .secondary-btn { min-block-size:clamp(3.15rem,2.5vw,3.9rem); padding-inline:clamp(1.7rem,1.5vw,2.4rem); font-size:clamp(1.02rem,.82vw,1.3rem); transition:transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease, background 180ms ease; }
  .hero-actions .primary-btn:hover { transform:translateY(-3px); box-shadow:0 1.5rem 3.2rem rgb(94 247 205 / 32%); }
  .hero-actions .secondary-btn:hover { transform:translateY(-3px); border-color:rgb(94 247 205 / 55%); background:rgb(255 255 255 / 11%); }
  /* The markup already carries the intended line breaks as spans - let them break. */
  .project-statement h2 span { display:block; }
  .curriculum-description span { display:block; }
  .hero-fixed-line { display:block; }
  /* Whole-word wrapping across the deck: no more mid-word breaks. */
  .scene h1, .scene h2, .scene h3, .scene p, .scene li, .panel-head h2 { word-break:keep-all; overflow-wrap:anywhere; }.brand-mark img { inline-size:100%; block-size:100%;}
  .ai-modal { inline-size:min(65rem,calc(100vw - 2.25rem)); border-radius:1.75rem; background:#071326; border-color:rgb(100 234 255 / 25%); }

  /* 2 & 7. Grade/class selects: the base rule `.select-line { grid-column:2;
     grid-row:1 }` was written for a select nested inside panel-head's own
     2-col grid, but on school/class-panel the select-line is a DIRECT grid
     child instead — so that rule was forcing school-panel AND class-panel
     into an accidental 2-column layout (that's also the real cause of the
     invisible grade bars in #3 and the clipped "헷갈린 물건" text in #8).
     Taking the select out of grid flow entirely fixes both the overlap and
     the phantom column at once. */
  .school-panel, .class-panel { position:relative; }
  /* The select drops to the title's line and its right edge matches the grade
     numbers' column (both sit at the panel's content inset). */
  .school-panel .select-line, .class-panel .select-line { position:absolute; inset-block-start:2.2rem; inset-inline-end:var(--panel-pad); grid-column:auto; grid-row:auto; z-index:2; }
  .school-panel .panel-head, .class-panel .panel-head { padding-inline-end:5.4rem; }
  .landfill-panel .panel-head, .upload-panel .panel-head { padding-inline-end:0; margin-block-end:.875rem; }
  .school-panel .select-line select, .class-panel .select-line select { min-block-size:2.15rem; padding:.32rem .65rem; font-size:clamp(.82rem,.7vw,1.05rem); font-weight:850; border-radius:.8rem; }

  /* D's real fixed kpi-column width (read from source, not guessed):
     126px base / 104px at 1181-1600px / 190px at 2560px+. These breakpoint
     overrides must stay AFTER this base rule in source order - an earlier
     placement gets silently shadowed since specificity is identical. */
  .school-panel { grid-template-columns:7.5rem minmax(0,1fr); column-gap:1rem; row-gap:.625rem; grid-template-rows:auto minmax(0,1fr); grid-template-areas:"head head" "kpi visual"; }
  /* Grade bars stretched, the selected grade lit (app.js stamps .is-selected),
     donut captions given air. */
  .school-panel .bar-list { row-gap:clamp(1rem,1.05vw,1.45rem); align-content:center; }
  /* Bars over donuts, stacked: side by side the two enlarged rings squeezed
     the bar list to nothing and spilled out of the panel's right edge. */
  .school-panel .school-visuals { min-inline-size:0;justify-items:stretch; row-gap:.15rem; }
  /* Three KPI tiles at one uniform size with comfortable padding - fit-content
     made each box hug its own number and cut the dark plate too tight. */
  .school-panel .kpi-row>div { inline-size:auto; min-inline-size:0; padding:.7rem .95rem; }
  /* Donut captions move fully below the ring - inside it they overlapped the
     arc and clipped ("분리 성공률"). The pair gets bottom room to hold them. */
  .school-panel .donut { overflow:visible; position:relative; }
  .school-panel .donut-pair { }
  .school-panel .bar-list { min-inline-size:0; }
  .school-panel .bar-list b { min-inline-size:0; text-align:right; }
  /* 라벨 칸을 넓혀 막대와 사이 여백을 준 뒤, 글자가 패널 왼쪽 끝에 너무 붙어
     보였다 — 칸 너비는 그대로 두고 글자만 10px 오른쪽으로 민다. */
  .school-panel .bar-list > div span { padding-inline-start:23px; }
  .school-panel .bar-list i { block-size:.68rem; }
  .school-panel .bar-list>div.is-selected span, .school-panel .bar-list>div.is-selected b { color:#5ef7cd; }
  .school-panel .bar-list>div.is-selected i { box-shadow:0 0 12px rgb(94 247 205 / 45%); }
  .school-panel .donut-pair { align-items:center; justify-content:center;inline-size:100%; padding-block-end:0; align-self:center; }
  .class-panel { column-gap:1rem; row-gap:.625rem; }
  .school-panel .kpi-row { grid-template-columns:1fr; gap:clamp(1rem,1.1vw,1.5rem); }
  .school-panel .kpi-row > div { padding:.5rem .55rem; }
  /* D's real formula (read directly from its source, not guessed from one
     viewport): confirmed across 1440/1728/1920/2560px. */
  .kpi-row strong, .class-kpis strong, .landfill-metrics strong { font-size:clamp(1.45rem,2.05vw,3.15rem); }
  /* Left-panel readability pass: stat labels, grade bars, donut captions, TOP5
     rows and the ranking pill all ran a size class too small next to the
     enlarged panel titles, and rows sat misaligned. */
  .kpi-row span, .class-kpis span, .landfill-metrics span { font-size:clamp(.85rem,.72vw,1.1rem); }
  .school-panel .bar-list span, .school-panel .bar-list b { font-size:clamp(1rem,.85vw,1.3rem); }
  .school-panel .bar-list>div { align-items:center; column-gap:.7rem; }
  .school-panel .donut span { }
  .class-panel .confusion h3, .class-panel .confusion span, .class-panel .confusion li { font-size:clamp(.92rem,.78vw,1.18rem); }
  .class-panel .rank-note { font-size:clamp(.8rem,.68vw,1rem); }
  .school-panel .school-visuals { grid-template-columns:1fr; gap:.75rem; overflow:visible; align-self:stretch; }
  .school-panel .bar-list > div { grid-template-columns:5.75rem minmax(2.4rem,1fr) 2.625rem; font-size:.9rem; font-weight:760; color:#9eb3c7; }
  .school-panel .donut-pair { flex-wrap:nowrap; gap:clamp(1.6rem,1.8vw,2.6rem); min-inline-size:0; }
  /* D's donut is a RING with a hollow center (radial-gradient masks out the
     middle so the conic-gradient only shows as an outer band), not a solid
     pie slice. The old base .donut relies on a small per-child dark circle
     instead, which leaves almost no room for the label -> this D-revival
     override replaces the fill technique entirely for this context. */
  .school-panel .donut { inline-size:clamp(5.6rem,6.6vw,7rem); block-size:clamp(5.6rem,6.6vw,7rem); grid-template-rows:1fr; align-content:stretch; background:radial-gradient(circle at center,#081326 46%,transparent 47%),conic-gradient(#5ef7cd calc(var(--pct) * 1%),rgb(255 255 255 / 10%) 0); box-shadow:inset 0 0 1.5rem rgb(255 255 255 / 6%),0 0 1.5rem rgb(94 247 205 / 12%); transform:translateY(-15px); }
  .school-panel .donut.violet { background:radial-gradient(circle at center,#081326 46%,transparent 47%),conic-gradient(#ad8cff calc(var(--pct) * 1%),rgb(255 255 255 / 10%) 0); box-shadow:inset 0 0 .875rem rgb(255 255 255 / 6%),0 0 .875rem rgb(173 140 255 / 12%); }
  .school-panel .donut>* { background:none; }
  .school-panel .donut span { align-self:center; padding:0; margin-block-end:-.55rem; font-size:1rem; font-weight:950; }
  /* One rule only (a duplicate above used to win the cascade and drag the
     caption back onto the ring): the label floats fully below the donut. */
  /* .donut 자체를 5px 위로 올렸으므로(요청: 링+퍼센테이지만 이동, 캡션은 제자리),
     absolute 캡션은 부모를 따라 같이 5px 올라간다 — 그만큼 아래로 되돌린다. */
  .school-panel .donut small { position:absolute; inset-block-start:100%; inset-inline-start:50%; translate:-50% 15px; align-self:auto; padding:0; margin:0; margin-block-start:.5rem; max-inline-size:none; white-space:nowrap; font-size:clamp(.76rem,.64vw,.95rem); font-weight:850; line-height:1.15; }

  /* 4. Landfill: the four metric boxes were letting "16,400t" run into the
     next box ("-9.4%"); the title/time row needed a hard nowrap so it can
     never wrap into a second line and crowd itself. */
  .landfill-metrics > div { padding:.5rem .45rem .5rem .55rem; }
  .landfill-metrics strong { font-size:clamp(.88rem,1.12vw,1.12rem); white-space:nowrap; }
  .chart-title-row { flex-wrap:nowrap; justify-content:space-between; gap:.875rem; padding-inline-end:clamp(.72rem,calc(.36rem + .4vw),1rem); }
  .chart-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.08; }
  .landfill-time-now { flex:0 0 auto; margin-inline-start:0; white-space:nowrap; }
  time.landfill-time-now { padding-block:.375rem; padding-inline:.8125rem; }

  /* 5. Taller chart (bars stretch closer to donut proportions) with the
     axis numbers pulled toward the panel's true left edge — app.js moved
     chartLeft/axis font-size/label-x to match; overflow:visible lets the
     bigger axis numbers spill slightly into the panel's own padding
     without ever crossing the card's outer edge. */
  /* The panel is height-locked, so the chart must absorb the shortfall: the
     svg lives in a minmax(0,1fr) row and scales down instead of spilling out
     of the panel bottom (it was 68px over at 1440x900). */
  .landfill-panel { display:flex; flex-direction:column; min-block-size:0; }
  .landfill-panel .chart-wrap { flex:1 1 auto; min-block-size:0; display:grid; gap:.5rem .2rem; grid-template-columns:minmax(0,1fr) auto; grid-template-rows:auto minmax(0,1fr); grid-template-areas:"title title" "chart rings"; align-items:center; }
  /* Metric tiles: small caption, big percentage - but capped where four tiles
     share the row, or the numerals collide across tile borders. */
  /* Column ratios negotiated so "총량 대비 반입량" keeps its last character and
     "오늘 반입 총량" stops hoarding the spare width. */
  .landfill-panel .landfill-metrics { gap:.55rem; grid-template-columns:minmax(0,1.1fr) minmax(0,.82fr) minmax(0,1.18fr) minmax(0,.95fr); }
  .landfill-panel .landfill-metrics > div { min-inline-size:0; padding-inline:.55rem; }
  .landfill-panel .landfill-metrics strong { font-size:clamp(1.02rem,1.14vw,1.7rem); font-variant-numeric:tabular-nums; white-space:nowrap; }
  .landfill-panel .landfill-metrics span { font-size:clamp(.72rem,.6vw,.9rem); }
  .landfill-panel .chart-title-row { grid-area:title; }
  .landfill-panel .combo-chart { grid-area:chart; }
  .combo-chart .weekday-label { font-size:17.5px; font-weight:850; }
  .combo-chart .date-label { font-size:15.5px; }
  /* y축 맨 아래에 그리드 눈금과 별개로 찍히던 "0" 라벨 제거 (요청) */
  .combo-chart .y-zero { display:none; }
  /* aspect-ratio breaks the width/height circularity, so the svg's box hugs
     the drawing exactly - no dead background trailing to the panel's right.
     The freed width goes to the two rings. max-block-size is bumped from the
     original 10.4rem so the enlarged axis/label fonts above have room. */
  .combo-chart { grid-area:chart; inline-size:auto; block-size:100%; aspect-ratio:456/252; min-block-size:0; max-block-size:11.6rem; max-inline-size:100%; justify-self:start; align-self:center; overflow:visible; padding-inline:1.1125rem .45rem; padding-block:.6625rem .75rem; box-sizing:content-box; }
  .upload-panel { display:flex; flex-direction:column; min-block-size:0; }
  .upload-panel .experience-portal { flex:1 1 auto; block-size:auto; min-block-size:0; }
  .chart-axis text { font-variant-numeric:tabular-nums; }

  /* 6. Split the single incoming/remaining gauge into two distinct reads:
     the big number already IS the "총량 대비 반입량" bar's story, and the
     progress-stack rows just below already chart both metrics separately —
     so the redundant two-tone bar inside the status card is dropped in
     favor of a clean second stat line. */
  /* Landfill status → D's dual-ring donut (was a status-card + two gauge bars,
     which is why app.js's already-built updateLandfillDonuts()/.landfill-kpi-ring
     donut code was silently dead — the HTML no longer had a matching element). */
  /* Two separate rings side by side (user call: the combined ring with the
     violet inner band read as two charts stacked on each other). The old
     .landfill-kpi-legend row is gone from the markup; progress-stack's two bar
     rows are dropped on PC - the rings carry both metrics alone. */
  .landfill-panel .progress-stack { display:none; }
  .landfill-kpi-module { grid-area:rings; min-inline-size:0; min-block-size:0; display:flex; flex-direction:column; justify-content:center; align-items:flex-end; gap:.7rem; padding:0; }
  /* inline-size/block-size, the span margin-block-start and the small margin-block-end
     below are tuned together for THIS 5.5rem ring. Resizing the ring without
     re-checking those two margins (via real getBoundingClientRect measurement,
     not eyeballing) will silently reintroduce text overlap. */
  .landfill-kpi-ring { position:relative; isolation:isolate; inline-size:clamp(5.1rem,5.7vw,6.2rem); block-size:clamp(5.1rem,5.7vw,6.2rem); flex:0 0 auto; aspect-ratio:1;background:conic-gradient(rgb(94 247 205 / 78%) calc(var(--pct) * 1%), rgb(87 117 154 / 22%) 0); box-shadow:0 0 14px rgb(94 247 205 / 8%), inset 0 0 18px rgb(255 255 255 / 3.5%); }
  .landfill-kpi-ring::before, .landfill-kpi-ring::after { content:""; position:absolute; border-radius:inherit; pointer-events:none; }
  .landfill-kpi-ring::before { content:none; }
  .landfill-kpi-ring::after { z-index:-1; inset:15%; background:radial-gradient(circle at 36% 28%, rgb(33 64 102 / 98%), #0a1830 72%); box-shadow:inset 0 0 0 1px rgb(173 214 255 / 8%); }
  .landfill-kpi-ring--secondary { background:conic-gradient(#ad8cff calc(var(--pct) * 1%), rgb(126 116 177 / 20%) 0); box-shadow:0 0 14px rgb(173 140 255 / 14%); }
  .landfill-kpi-ring--secondary > span { text-shadow:0 0 12px rgb(173 140 255 / 30%); }
  .landfill-kpi-ring > span, .landfill-kpi-ring > small { position:relative; z-index:1; grid-area:1 / 1; text-align:center; }
  .landfill-kpi-ring > span { align-self:center; padding:0; margin-block-start:0; color:#f3fffe; font-size:clamp(.72rem,.6vw,.92rem); font-weight:950; line-height:1; text-shadow:0 0 12px rgb(94 247 205 / 22%);}
  /* Simple one-word name (반입량/잔여량) directly under the percentage, both
     stacked inside the ring - the side plates are gone (user call). */
  .landfill-kpi-ring { grid-template-rows:auto auto; align-content:center; justify-items:center; row-gap:.14rem; }
  .landfill-kpi-ring > span, .landfill-kpi-ring > small { position:static; grid-area:auto; translate:none; align-self:auto; margin:0; padding:0; border:0; background:transparent; }
  .landfill-kpi-ring > small { max-inline-size:none; inline-size:auto; color:rgb(219 241 249 / 80%); font-size:clamp(.64rem,.54vw,.82rem); font-weight:850; line-height:1.1; text-align:center; }

  /* 8. Class panel: same accidental-column bug as #3 — rebalance so the
     kpi numbers get a narrow column and 헷갈린 물건 TOP 5 gets the wide one. */
  /* KPI column narrowed so 헷갈린 물건 grows leftward - labels stop wrapping,
     which is what was eating the vertical room. */
  .class-panel { grid-template-columns:4.9rem minmax(0,1fr); grid-template-rows:auto minmax(0,1fr) auto; grid-template-areas:"head head" "kpi visual" "note note"; }
  /* D's real fixed kpi-column width per breakpoint (confirmed live, not
     guessed): 126px base / 104px at 1181-1600px / 190px at 2560px+. Must
     stay after both the school-panel and class-panel base rules above -
     same specificity means source order decides, and an earlier placement
     gets silently shadowed. */
  @media (min-width:1181px) and (max-width:1600px) { .school-panel,.class-panel { grid-template-columns:6.5rem minmax(0,1fr); column-gap:.75rem; }.school-panel .kpi-row,.class-panel .class-kpis,.landfill-metrics { grid-template-columns:repeat(2,minmax(0,1fr)); } }
  @media (min-width:160rem) { :root { --scene-inline-space:11.25rem; --header-inline-space:8rem; --header-size:7rem; --nav-link-pad:1.25rem; }.main-nav a { min-block-size:3.25rem; padding-inline:var(--nav-link-pad); font-size:1.18rem; }.school-panel { grid-template-columns:8.75rem minmax(0,1fr); column-gap:1.5rem; }.class-panel { grid-template-columns:7rem minmax(0,1fr); column-gap:1.5rem; } }
  .class-panel .panel-head { grid-area:head; }
  /* Both columns stretch the shared row and distribute, so the TOP5 box's
     bottom lands exactly on the 전환 사례 plate's bottom. */
  .class-panel .class-kpis { grid-area:kpi; grid-template-columns:1fr; gap:.55rem; align-self:stretch; align-content:space-between; min-block-size:0; }
  .class-panel .class-kpis > div { padding:.55rem .55rem; }
  .class-panel .class-kpis span { margin-block-start:.05rem; }
  .class-panel .class-kpis strong { font-size:clamp(1.25rem,1.5vw,2.2rem); }
  .class-panel .confusion { grid-area:visual; }
  /* Fixed label column on purpose: each row is its own grid, so fit-content
     would size per-row and scatter the bar start points. */
  .class-panel .confusion > div { grid-template-columns:8.9rem minmax(0,1fr) 1.6rem; align-items:center; column-gap:.3rem; }
  .class-panel .confusion > div b { text-align:right; min-inline-size:0; }
  /* The TOP5 box runs the full height of the KPI column (down to 전환 사례's
     plate) and spreads its rows into the room; every bar starts at one x
     because the label column is fixed, not content-sized. */
  .class-panel .confusion { row-gap:.9rem; min-block-size:0; align-content:space-between; align-self:stretch; margin-block-start:0; padding:1.05rem .9rem 1.15rem 1.05rem; }
  .class-panel .confusion > div { min-block-size:0; }
  .class-panel .rank-note { margin-block-start:1.6rem; padding-block:.45rem; }
  .class-panel .confusion span { word-break:keep-all; }
  /* The renderer wraps each emoji in <em>, whose browser default is italic -
     that's why the emoji leaned. inline-block also makes the gap immune to
     inline whitespace collapsing. */
  .class-panel .confusion span em { display:inline-block; margin-inline-end:.6rem; font-style:normal; }
  /* Native dropdown popups are white; the inherited pale text was invisible
     on them ("안 보이는 것"). */
  .select-line select option { color:#10283b; background:#ffffff; }
  .class-panel .rank-note { grid-area:note; }

  /* 9. Ranking pill: a touch smaller so it never crowds its own edge. */
  .rank-note { font-size:clamp(.66rem,calc(.12rem + .6vw),1.08rem); font-weight:900; letter-spacing:normal; line-height:1.72; color:rgb(158 179 199); }
  .primary-btn:hover, .quick-action-row button:first-child:hover, #confirmDecision:hover { background:linear-gradient(135deg,#5ef7cd,#64eaff); transform:none; }

  /* 10. Upload panel: fill the dead space between the description and the
     buttons with the product's own locked copy (AI suggests, student
     decides) instead of empty air, and credit Gemini with a text-only
     badge — no Google brand asset is bundled here without your say-so. */
  .upload-panel { align-content:start; gap:.85rem; }
  .upload-panel .upload-highlights { display:grid; gap:.28rem; margin:0; padding:.55rem .8rem; border:1px solid rgb(100 220 235 / 18%); border-radius:1rem; background:rgb(4 24 42 / 55%); }
  .upload-panel .upload-highlights li { display:flex; gap:.5rem; align-items:flex-start; color:#c9dbe6; font-size:.76rem; line-height:1.35; }
  .upload-panel .upload-highlights li::before { content:"✓"; flex:0 0 auto; color:#65ead6; font-weight:900; }
  .upload-actions.upload-card-actions { margin-block-start:0; }
  .gemini-credit { display:inline-flex; align-items:center; gap:.4rem; margin-block-start:.1rem; padding:.32rem .75rem; border:1px solid rgb(138 180 255 / 24%); border-radius:999px; background:rgb(66 133 244 / 8%); color:#a9c2ff; font-size:.68rem; font-weight:700; }
  .upload-icon,.sorting-upload-icon { inline-size:2.625rem; block-size:2.625rem; }.upload-icon svg,.sorting-upload-icon svg { stroke-width:2.2; }
  .upload-icon { font-size:clamp(1.08rem,calc(.643rem + .4857vw),1.42rem); line-height:1.3; font-weight:900; margin-block-end:.5rem; color:rgb(248 253 255); }
  .gemini-credit svg { inline-size:.8rem; block-size:.8rem; }
  .upload-panel small { font-size:clamp(.6rem,.52vw,.78rem); line-height:1.5; font-weight:800; letter-spacing:-.02em; color:#bcd3e0; white-space:nowrap; }

  /* QR invite: dashboard upload panel + sorting page, same asset/behavior. */
  .qr-invite { display:flex; align-items:center; gap:.7rem; padding:.55rem .75rem; border:1px solid rgb(100 220 235 / 20%); border-radius:1rem; background:rgb(4 24 42 / 45%); }
  .qr-invite img { flex:0 0 auto; inline-size:4.6rem; block-size:4.6rem; border-radius:.6rem; }
  .qr-invite p { margin:0; color:#a9c2d6; font-size:.72rem; font-weight:700; line-height:1.4; }
  .sorting-features + .qr-invite { margin-block-start:.9rem; }

  /* Global button texture — D: pill-shaped controls with a lifted glow
     shadow, not the flat/square buttons the closed-beta shell fell back to. */
  .primary-btn, .quick-action-row button:first-child, #confirmDecision { border:0; border-radius:999px; box-shadow:0 1.125rem 2.875rem rgb(94 247 205 / 18%); background:linear-gradient(135deg,#5ef7cd,#64eaff); color:#06111c; font-weight:850; padding-block:0; }
  .secondary-btn, .quick-action-row button, .decision-grid button, .tm-model-line button, .hold-input-line button { border-radius:999px; border:1px solid rgb(132 220 255 / 26%); background:rgb(255 255 255 / 6%); color:#f6fbff; font-weight:850; }
  .secondary-btn { padding-block:0; }
  .upload-actions button { border-radius:1.25rem; border:1px dashed rgb(94 247 205 / 36%); background:linear-gradient(145deg,rgb(94 247 205 / 22%),rgb(67 137 255 / 12%)); box-shadow:0 1.125rem 2.875rem rgb(94 247 205 / 18%); color:#f8fdff; font-weight:900; }
  .app-tabs button { border-radius:.875rem; border:1px solid transparent; color:rgb(225 244 250 / 80%); }
  .app-tabs button.is-active { border-radius:.875rem; background:linear-gradient(135deg,rgb(126 255 226 / 96%),rgb(86 218 255 / 92%)); box-shadow:0 .625rem 1.5rem rgb(94 247 205 / 15%); color:#06111c; font-weight:850; }

  /* Global form controls — D's glass inputs; #searchButton/.data-refresh-btn
     had no background rule anywhere and were falling back to the browser's
     native grey button chrome. */
  .select-line select, .search-line input, .tm-model-line input, .hold-input-line input { border-radius:.75rem; border-color:rgb(100 234 255 / 22%); background:rgb(255 255 255 / 7%); }
  .select-line select:focus-visible, .search-line input:focus-visible, .tm-model-line input:focus-visible, .hold-input-line input:focus-visible { outline:none; border-color:rgb(94 247 205 / 70%); background:rgb(94 247 205 / 10%); }
  #searchButton { border-radius:1rem; border:0; background:rgb(94 247 205 / 9%); color:#f2fdff; }
  /* Exhibition desktop is kiosk-only (3-second-sort demo); the cross-app
     closed-beta login is a mobile-only concern now, auto-opened instead
     of needing this button (see edu2gBetaUi.js autoOpenOnMobileIfUnregistered). */
  .edu2g-beta-trigger { display:none; }
  /* D's nav "effect" is text color contrast only, no filled pill background. */
  .main-nav a { font-weight:760; }
  .main-nav a.is-active, .main-nav a:hover { background:none; box-shadow:none; color:#f6fdff; }
  .main-nav a:not(.is-active):not(:hover) { color:rgb(225 242 255 / 68%); }
  /* aiways-mark.png is a cropped, upscaled bridge icon (trimmed from the
     full wordmark logo, which has no standalone square icon on its own) so
     it reads clearly at this small header size. */
  .brand-mark img { object-fit:contain; }
  .data-refresh-btn { border-radius:50%; border:1px solid rgb(100 234 255 / 28%);color:rgb(100 234 255); padding:.0625rem .375rem; }
  .close-btn { border-radius:999px; border:1px solid rgb(255 255 255 / 16%); background:rgb(255 255 255 / 8%); color:#f2fdff; }
/* 대시보드: 학교 패널 링그래프를 살짝 위로 */
  .school-panel .donut-pair { translate:0 -.6rem; }
  /* 대시보드: 매립지 반입량/잔여량 링을 살짝 왼쪽으로 */
  .landfill-kpi-module { translate:-.5625rem 0; }
  /* 잔여량 링만 살짝 아래로 */
  .landfill-kpi-ring--secondary { translate:0 .45rem; }
  /* 대시보드: '버려지는 순간' 점선 박스 — 조금 내려앉은 위치에서 위아래로 살아 움직이게 */
  .upload-panel .portal-frame { margin-block-start:1.2rem; animation:portal-live-float 3.2s ease-in-out infinite; }
  /* 대시보드: '기록' 박스 확대 */
  a.source-link { display:inline-grid; place-items:center; min-block-size:2.35rem; padding-inline:1.05rem; font-size:.95rem; }
  /* 대시보드: 새로고침 버튼 리디자인 (유리질감 + 글로우) */
  /* 버튼 자체 배경의 방사형 글로우가 중심(50%/50%)이 아니라 좌상단(32%/26%)에
     치우쳐 있었다 - 그 자체로 비대칭적인 밝은 원형 얼룩을 만들어서, 안의
     링/화살표와 별개로 "또 하나의 원"처럼 보였을 가능성이 있다. 중심으로
     맞춘다. */
  .data-refresh-btn { inline-size:2.95rem; block-size:2.95rem; border-color:rgb(100 234 255 / 45%); background:radial-gradient(circle at 50% 50%, rgb(100 234 255 / 16%), transparent 60%), linear-gradient(145deg, rgb(100 234 255 / 16%), rgb(6 20 38 / 55%)); box-shadow:inset 0 1px 0 rgb(255 255 255 / 16%), 0 6px 16px rgb(0 8 18 / 28%), 0 0 16px rgb(100 234 255 / 15%); }
  /* 평소엔 화살표(글리프, 손으로 그린 경로가 전혀 아니라서 이상하게
     그려질 여지가 없음), 로딩 중에만 링 스피너로 바꾼다 — 하나의 도형이
     "평소엔 화살표처럼, 도는 동안엔 원처럼" 두 가지로 다 읽혀야 하는
     무리한 요구를 아예 없앤다. */
  .data-refresh-arrow { display:grid; place-items:center; inline-size:1.6rem; block-size:1.6rem; font-size:1.3rem; line-height:1; }
  /* border 방식(한쪽 면만 밝게)은 모서리에서 두 색이 뚝 끊기며 섞여
     20px 크기에서 "밝은 조각 + 흐린 원"처럼 두 개로 갈라져 보였다.
     conic-gradient는 경계 없이 부드럽게 이어지는 하나의 원이라 이 문제가
     구조적으로 생길 수 없다. mask로 안쪽을 뚫어 링 모양만 남긴다. */
  .data-refresh-ring { display:none; inline-size:1.6rem; block-size:1.6rem; border-radius:50%; background:conic-gradient(from 0deg, #5ef7cd 0deg, rgb(100 234 255 / 22%) 200deg, rgb(100 234 255 / 22%) 360deg); -webkit-mask-image:radial-gradient(circle, transparent 55%, #000 68%); mask-image:radial-gradient(circle, transparent 55%, #000 68%); }
  .data-refresh-btn.is-loading .data-refresh-arrow { display:none; }
  .data-refresh-btn.is-loading .data-refresh-ring { display:block; }
  .data-refresh-btn:hover { border-color:rgb(94 247 205 / 60%); box-shadow:inset 0 1px 0 rgb(255 255 255 / 20%), 0 8px 20px rgb(0 8 18 / 32%), 0 0 24px rgb(94 247 205 / 30%); }
  /* 프로젝트: 01/02/03 번호 배지 — 세로로 넉넉한 타일. 또렷한 테두리 대신
     은은한 그라디언트와 안쪽 하이라이트로 카드 표면에서 자연스럽게 솟은 느낌. */
  .project-cards article>span { display:inline-grid; place-items:center; min-block-size:3.6rem; min-inline-size:3.1rem; padding:.55rem .95rem; border:1px solid rgb(94 247 205 / 22%); border-radius:1.15rem; background:linear-gradient(160deg, rgb(94 247 205 / 17%), rgb(100 169 255 / 7%) 62%, rgb(255 255 255 / 3%)); box-shadow:inset 0 1px 0 rgb(255 255 255 / 14%), 0 10px 24px rgb(0 8 18 / 22%), 0 0 20px rgb(94 247 205 / 10%); justify-self:start; font-size:1.24rem; font-variant-numeric:tabular-nums; letter-spacing:-.01em; }
  /* 교육과정: 성취기준 박스 숨통 */
  .standards-grid article { padding:1.35rem 1.45rem; gap:.65rem; }
  /* 차시흐름 제목바: 카드 폭이 좁아 긴 제목(2·4·6차시)만 두 줄로 접히면서 8장의
     제목바 높이가 제각각이었다. 카드 폭에 맞춰 한 줄로 고정하고 폭도 통일한다. */
  .flow-card>span { justify-self:stretch; inline-size:auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-align:center; justify-content:center; padding-inline:.5rem; font-size:clamp(.6rem,.64vw,.8rem); }
  /* 자료실: ONE MORE SCROLL 힌트를 3번째 열(차시별 활동지) 중심축에 정렬 */
  .scroll-finale-hint { inset-inline-start:calc((2.38rem + 4.38vw) + 2.5 * ((100vw - 2 * (2.38rem + 4.38vw) - 4rem) / 5) + 1.5rem); translate:-50% 0; }
  /* HAH: 세 박스는 내용(제목·설명·목록 3줄)에 맞춰 세우고 아래로 한 숨만 남긴다.
     이전 값(최대 26rem)은 1080 높이에서 내용 아래로 165px가 비어 보였다. */
  .hah-grid .panel { min-block-size:clamp(15rem, 26svh, 18.5rem); }
/* 프로젝트: 세 개의 메인 박스를 세로로 키우고 행간을 풀어 여유있게 */
  .project-cards article { min-block-size:19rem; align-content:center; gap:.9rem; padding:2.1rem 1.9rem; }
  .project-cards article h3 { line-height:1.42; }
  .project-cards article p { line-height:1.82; }
  /* 대시보드: 학년/학급 선택 바가 글자에 딱 붙어 답답했다. 이 바는 패널
     오른쪽에 고정돼 있어 폭을 키우면 왼쪽으로 자란다. 폭은 유동으로 잡는다 —
     고정 7.5rem으로 키우면 1440에서 패널 제목과 겹친다. */
  .select-line select, .class-panel .select-line select { min-inline-size:clamp(6.5rem, 4.5rem + 2.5vw, 9rem); padding-inline:.95rem; }
  /* HAH: 박스 안 글자 행간을 늘려 숨통 */
  .hah-grid .panel { line-height:1.9; }
  .hah-grid .panel p { line-height:1.92; }
  .hah-grid .panel li { line-height:1.85; margin-block-end:.28rem; }
/* ---------- 키프레임 ---------- */
@keyframes refreshSpin { to { transform: rotate(360deg); } }
@keyframes scanLine { 0% { transform: translateY(-20%); } 100% { transform: translateY(520%); } }
@keyframes quickScanSweep { 0% { transform: translateX(0) skewX(-18deg); opacity: 0; } 16% { opacity: .9; } 100% { transform: translateX(390%) skewX(-18deg); opacity: 0; } }
@keyframes quickScanLine { 0%, 100% { transform: translateY(-36px); opacity: .18; } 45% { opacity: .8; } 70% { transform: translateY(34px); opacity: .35; } }
@keyframes quickScanMeter { 0% { transform: translateX(-105%); } 100% { transform: translateX(225%); } }

/* ---------- 전역 / 헤더 / 브랜드 / 네비 ---------- */
html { background: #020611; }
/* style.css의 헤더 배경(불투명 92%)과 blur(14px)만 있고 saturate가 없던 것이
   아래 body 배경의 진한 색을 다 걸러내 검은 띠처럼 끊겨 보이던 원인이었다.
   패널/오브와 같은 처방(진한 반투명 + blur+saturate)으로 맞춘다. */
/* 헤더는 씬과 달리 뒤에 걸칠 스크롤 콘텐츠가 없어서(sticky로 맨 위 고정,
   씬들과 겹치지 않고 그냥 위에 쌓임) blur만으로는 비출 색 자체가 없어 계속
   flat한 띠로 보였다. 헤더 자체에 오브와 같은 브랜드색 radial-gradient를
   옅게 깔아서 자체적으로 톤을 갖게 한다. backdrop-filter는 일부러 뺐다 -
   각 씬 맨 위에 걸린 거대 orb-xl이 헤더 바로 아래서 잘려 있어서, saturate와
   합쳐지면 오히려 진한 단색 띠처럼 보이는 역효과가 났다. */
.site-header {
  background:
    radial-gradient(circle 260px at 6% 120%, rgb(94 247 205 / 30%), transparent 100%),
    radial-gradient(circle 300px at 94% 130%, rgb(100 169 255 / 30%), transparent 100%),
    radial-gradient(circle 220px at 52% -60%, rgb(173 140 255 / 20%), transparent 100%);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-block-end-style: none;
}
/* 배경 오버레이를 거의 없앤 대신, 씬 배경이 밝아지는 구간에서도 네비 글자가
   읽히도록 안전판으로 은은한 텍스트 그림자를 건다. */
.brand, .main-nav a { text-shadow: 0 1px 6px rgb(2 6 17 / 65%); }
/* 로고 타일은 흰 바탕에 은은한 그라데이션을 유지한다. D 참조본은 어두운 유리
   타일이었지만, 이 앱의 로고는 흰 배경 위에서 읽히도록 만들어져 있어 어두운
   타일에서는 로고가 배경에 묻힌다. 위쪽 .brand-mark 규칙(흰색 그라데이션)이
   최종값이며, 여기서는 D의 발광 테두리만 아주 옅게 얹는다. */
.brand-mark { position: relative; box-shadow: 0 2px 10px rgb(0 0 0 / 22%), inset 0 1px rgb(255 255 255 / 90%), 0 0 22px rgb(94 247 205 / 18%); }
.main-nav a { background: transparent !important; border-color: transparent !important; box-shadow: none !important; transition: color 180ms ease, background 180ms ease, box-shadow 180ms ease; }
.main-nav a.is-active, .main-nav a[aria-current="page"] { color: #f6fdff; background: transparent !important; border-color: transparent !important; border-radius: 0; box-shadow: none !important; text-shadow: 0 0 14px rgb(100 234 255 / 54%); }
.main-nav a.is-active::after, .main-nav a[aria-current="page"]::after { content: ""; position: absolute; inset-inline: 1rem; inset-block-end: -.5rem; block-size: 2px; border-radius: 999px; background: linear-gradient(90deg, transparent, #64eaff, #5ef7cd, transparent); box-shadow: 0 0 16px rgb(100 234 255 / 46%); }

/* ---------- 씬 전환 (섹션 크로스페이드) ---------- */
.scene { transition: opacity 430ms cubic-bezier(.18, .92, .24, 1), filter 430ms cubic-bezier(.18, .92, .24, 1), transform 420ms cubic-bezier(.18, .92, .24, 1) !important; }
.scene.is-active { filter: saturate(1.08) brightness(1.04); }
.scene.is-active .panel, .scene.is-active .project-cards article, .scene.is-active .flow-card, .scene.is-active .gallery-grid article, .scene.is-active .resource-grid a, .scene.is-active .subject-card, .scene.is-active .standards-grid article { box-shadow: 0 20px 70px rgb(14 28 54 / 34%), inset 0 1px 0 rgb(255 255 255 / 6%); transition: box-shadow 760ms cubic-bezier(.22, 1, .36, 1) 150ms, border-color 220ms ease, background 220ms ease, transform 180ms ease; }

/* ---------- 공통 패널 / 프로젝트 씬 ---------- */
.panel { box-shadow: 0 24px 64px rgb(0 0 0 / 28%), inset 0 1px rgb(255 255 255 / 6%); }
/* 대시보드 4개 패널만: 테두리를 없애고 반투명 + 블러로 유리질감을 낸다.
   나머지 씬의 .panel(프로젝트 카드, HAH 등)은 그대로 둔다 - 이번 요청은
   대시보드 카드 4개로 범위를 한정했다. */
/* 포탈의 유리 카드는 blur만 쓴 게 아니라 saturate까지 같이 걸어서, 뒤가
   흐려져도 색이 오히려 또렷해지는 애플식 글래스모피즘이다. blur만 쓰면
   색이 평균화되어 "씻겨나간" 느낌이 난다 - saturate로 보정하고, 순백
   대신 브랜드색(민트/블루)을 옅게 섞어 이 앱 팔레트에 맞춘다. */
/* 포탈 카드(rgba(23,26,46,.42))처럼 실제로 꽤 진한 반투명 베이스 위에
   blur+saturate를 건다 - 흰색 5~12%짜리 살짝 틴트는 "유리"가 아니라
   그냥 밝은 선이었다. 이 앱의 남색 톤으로 맞춰 42% 불투명도를 쓴다. */
.dashboard-grid > .panel { border-style: none; background: rgb(10 22 42 / 42%); backdrop-filter: blur(24px) saturate(180%); -webkit-backdrop-filter: blur(24px) saturate(180%); box-shadow: 0 28px 72px rgb(0 0 0 / 32%), inset 0 0 0 1px rgb(255 255 255 / 14%); }
.dashboard-grid > .panel:hover { box-shadow: 0 28px 72px rgb(0 0 0 / 30%), inset 0 1px 0 rgb(255 255 255 / 18%), 0 0 28px rgb(94 247 205 / 12%); }
/* 포탈의 "오브" 기법 그대로: radial-gradient가 아니라 실제 단색 원 + filter:blur().
   훨씬 부드럽고 자연스러운 빛 번짐이 나온다(radial-gradient의 transparent
   falloff는 가장자리가 티가 남). 대부분 애니메이션 없이 고정(성능 - 이미
   패널 4개가 backdrop-filter를 쓰는 중이라 움직이는 원까지 겹치면 무겁다). */
.scene-orbs { position: absolute; inset: 0; overflow: hidden; z-index: 0; pointer-events: none; border-radius: inherit; }
.scene-orbs span { position: absolute; border-radius: 50%; }
/* 포탈(edutogether/portal)의 실제 오브 5단 티어를 그대로 이식 - 클수록 더
   흐리고 옅게, 작을수록 더 또렷하고 진하게 (blur와 opacity가 반비례). */
.scene-orbs .orb-xl { filter: blur(100px); opacity: .3; }
.scene-orbs .orb { filter: blur(44px); opacity: .32; }
.scene-orbs .orb2 { filter: blur(24px); opacity: .34; }
.scene-orbs .orb-md { filter: blur(30px); opacity: .42; }
.scene-orbs .orb-sm { filter: blur(16px); opacity: .5; }
.scene > *:not(.scene-orbs):not(.scroll-finale-hint) { position: relative; z-index: 1; }
.project-intro { background: transparent; box-shadow: none; }
.curriculum-description, .gallery-description { background: transparent !important; box-shadow: none !important; }
.project-cards article { background: linear-gradient(145deg, rgb(13 31 61 / 78%), rgb(5 13 29 / 58%)); transition: transform 190ms ease, border-color 190ms ease, background 190ms ease, box-shadow 190ms ease; }
.project-cards article:hover { transform: translateY(-6px); border-color: rgb(94 247 205 / 42%); background: rgb(255 255 255 / 8.5%); box-shadow: 0 0 36px rgb(94 247 205 / 16%), inset 0 0 24px rgb(100 234 255 / 5%); }

/* ---------- 교과(과목 카드) / 흐름(flow) ---------- */
.subject-card { transition: transform 180ms ease, border-color 180ms ease; }
.subject-card:hover { transform: translateY(-3px); border-color: rgb(94 247 205 / 34%); }
.subject-card em { display: inline-flex; inline-size: max-content; min-block-size: 34px; align-items: center; justify-content: center; padding: 0 14px; border-radius: 999px; color: #07111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); font-style: normal; font-weight: 870; font-size: .82rem; }
/* 과목명("실과" 등): 원래 굵기/색 지정이 전혀 없어 본문 글자와 다를 게 없었다 -
   브랜드 그라디언트로 색을 입혀 눈에 띄는 소제목처럼 만든다. */
.subject-card > span {
  display: inline-block;
  background: linear-gradient(135deg, #5ef7cd, #64eaff);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 900;
  font-size: 1.05rem;
  letter-spacing: .01em;
}
.subject-card:focus { outline: none; }
.subject-card:focus-visible { outline: 2px solid rgb(94 247 205 / 72%); outline-offset: 5px; }
.subject-card:hover:not(.is-hover-suppressed) em, .subject-card:focus-visible em { opacity: 1 !important; transform: translateY(0) !important; }
.subject-card.is-hover-suppressed:hover:not(:focus-visible) em, .subject-card.is-hover-suppressed:focus-within:not(:focus-visible) em { opacity: 0 !important; transform: translateY(8px) !important; }
.flow-card { opacity: 1; transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; }
.flow-card em { display: inline-flex; inline-size: max-content; min-block-size: 34px; align-items: center; justify-content: center; padding: 0 14px; border-radius: 999px; color: #07111c; font-style: normal; font-weight: 870; font-size: .82rem; }
.flow-card:hover { transform: translateY(-4px); border-color: rgb(94 247 205 / 34%); background: rgb(255 255 255 / 8%); box-shadow: 0 0 24px rgb(94 247 205 / 12%); }
.flow-card[href="./miniapp/3second.html"]:hover { border-color: rgb(94 247 205 / 38%); }
.flow-card[data-miniapp-missing="true"] { border-color: rgb(131 224 255 / 18%); background: rgb(255 255 255 / 6%); box-shadow: none; opacity: 1; }
.flow-card[data-miniapp-missing="true"]:hover { transform: translateY(-4px); border-color: rgb(94 247 205 / 34%); background: rgb(255 255 255 / 8%); box-shadow: 0 0 24px rgb(94 247 205 / 12%); }
.flow-grid article { background: linear-gradient(145deg, rgb(13 31 61 / 86%), rgb(5 13 29 / 66%)); box-shadow: 0 24px 64px rgb(0 0 0 / 28%), inset 0 1px rgb(255 255 255 / 6%); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; }
.flow-grid article:hover { transform: translateY(-4px); border-color: rgb(94 247 205 / 42%); background: linear-gradient(145deg, rgb(17 43 82 / 92%), rgb(7 19 38 / 74%)); }
/* .flow-grid span 그라디언트는 이식하지 않는다 — D 라이브에서 차시 제목바는
   .flow-card>span의 은은한 칩(민트 8%)이 최종 승자라서, 그라디언트를 넣으면
   민트 글자가 민트 배경에 묻힌다 (2026-08-12 제목바 깨짐 원인). */

/* ---------- 성취기준(standards) ---------- */
.standards article { background: linear-gradient(145deg, rgb(13 31 61 / 86%), rgb(5 13 29 / 66%)); box-shadow: 0 24px 64px rgb(0 0 0 / 28%), inset 0 1px rgb(255 255 255 / 6%); }
.standards-grid article { background: rgb(2 10 26 / 48%); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; }
.standards-grid article:hover { transform: translateY(-3px); border-color: rgb(94 247 205 / 32%); background: rgb(255 255 255 / 7.5%); }
.standards-grid article > span { background: linear-gradient(135deg, #5ef7cd, #64eaff); }

/* ---------- 갤러리 / 자료실 그리드 ---------- */
.gallery-grid article { background: linear-gradient(145deg, rgb(13 31 61 / 86%), rgb(5 13 29 / 66%)); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; }
.gallery-grid article:hover { transform: translateY(-4px); border-color: rgb(94 247 205 / 42%); background: linear-gradient(145deg, rgb(17 43 82 / 92%), rgb(7 19 38 / 74%)); }
.resource-grid a, .resource-grid a:first-child { background: linear-gradient(145deg, rgb(13 31 61 / 86%), rgb(5 13 29 / 66%)); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; }
.resource-grid a:hover, .resource-grid a:first-child:hover { transform: translateY(-4px); border-color: rgb(94 247 205 / 34%); background: rgb(255 255 255 / 8%); }
.resource-grid a.resource-pending { background: linear-gradient(145deg, rgb(15 36 64 / 52%), rgb(6 15 31 / 48%)), rgb(255 255 255 / 3.5%); }

/* ---------- 분리배출 특징 카드(sorting-features) ---------- */
.sorting-features article { position: relative; border-color: rgb(94 247 205 / 18%); background: linear-gradient(145deg, rgb(15 38 70 / 76%), rgb(5 14 30 / 58%)), rgb(255 255 255 / 4.5%); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease, box-shadow 180ms ease; }
.sorting-features article::before { content: ""; position: absolute; inset: 0 0 auto; block-size: 3px; background: linear-gradient(90deg, rgb(94 247 205 / 86%), rgb(100 234 255 / 42%), rgb(173 140 255 / 36%)); opacity: .76; }
.sorting-features article:hover { transform: translateY(-5px); border-color: rgb(94 247 205 / 42%); background: linear-gradient(145deg, rgb(22 58 96 / 86%), rgb(7 22 44 / 72%)), rgb(94 247 205 / 7%); box-shadow: 0 18px 44px rgb(0 0 0 / 26%), 0 0 24px rgb(94 247 205 / 12%); }

/* ---------- 콜아웃 / H-A-H 그리드 ---------- */
.callout { opacity: 1 !important; transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease; }
.callout:hover { transform: translateY(-2px); border-color: rgb(94 247 205 / 34%); box-shadow: 0 0 26px rgb(94 247 205 / 14%); }
.hah-scene .callout { inline-size: min(1780px, 100%); text-align: center; transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease, background 180ms ease; }
.hah-scene .callout:hover { transform: translateY(-3px); border-color: rgb(94 247 205 / 38%); background: rgb(94 247 205 / 9%); box-shadow: 0 0 30px rgb(94 247 205 / 15%); }
/* 세로 크기는 cb3a.css의 사용자 피드백 블록이 D보다 넉넉하게 잡는다 (여기서 지정하지 않음). */
.hah-grid .panel { position: relative; transition: transform 190ms ease, border-color 190ms ease, background 190ms ease, box-shadow 190ms ease; }
.hah-grid .panel::before { content: ""; position: absolute; inset: 0 0 auto; block-size: 4px; background: linear-gradient(90deg, rgb(94 247 205 / 18%), rgb(94 247 205 / 72%), rgb(100 234 255 / 18%)); opacity: .72; transition: opacity 190ms ease, filter 190ms ease; }
.hah-grid .panel:nth-child(2)::before { background: linear-gradient(90deg, rgb(100 169 255 / 18%), rgb(100 234 255 / 78%), rgb(173 140 255 / 18%)); }
.hah-grid .panel:nth-child(3)::before { background: linear-gradient(90deg, rgb(65 245 194 / 14%), rgb(46 225 184 / 72%), rgb(100 234 255 / 16%)); }
.hah-grid .panel>span { color: #c4fff0; border-color: rgb(94 247 205 / 42%); background: rgb(94 247 205 / 13%); box-shadow: 0 0 18px rgb(94 247 205 / 16%); }
.hah-grid .panel:nth-child(2)>span { border-color: rgb(100 169 255 / 46%); background: rgb(100 169 255 / 15%); box-shadow: 0 0 18px rgb(100 234 255 / 18%); }
.hah-grid .panel:nth-child(3)>span { border-color: rgb(46 225 184 / 42%); background: rgb(46 225 184 / 12%); box-shadow: 0 0 18px rgb(46 225 184 / 15%); }
.hah-grid li, .hah-grid .panel span, .hah-grid .panel h3, .hah-grid .panel p { transition: color 180ms ease, text-shadow 180ms ease, transform 180ms ease; }
.hah-grid .accent { border-color: rgb(173 140 255 / 34%); box-shadow: 0 24px 74px rgb(112 84 255 / 20%); }
.hah-grid .panel:hover { transform: translateY(-7px); border-color: rgb(94 247 205 / 42%); background: linear-gradient(145deg, rgb(255 255 255 / 9.5%), rgb(94 247 205 / 5.5%), rgb(100 234 255 / 3.5%)); box-shadow: 0 30px 90px rgb(2 10 25 / 46%), 0 0 34px rgb(94 247 205 / 15%); }
.hah-grid .panel:nth-child(2):hover { border-color: rgb(100 234 255 / 46%); box-shadow: 0 30px 90px rgb(2 10 25 / 46%), 0 0 36px rgb(100 234 255 / 18%); }
.hah-grid .panel:nth-child(3):hover { border-color: rgb(46 225 184 / 44%); box-shadow: 0 30px 90px rgb(2 10 25 / 46%), 0 0 34px rgb(46 225 184 / 16%); }
.hah-grid .panel:hover::before { opacity: 1; filter: drop-shadow(0 0 10px currentColor); }
.hah-grid .panel:hover>span { box-shadow: 0 0 24px rgb(94 247 205 / 28%); }
.hah-grid .panel:nth-child(2):hover>span { box-shadow: 0 0 26px rgb(100 234 255 / 30%); }
.hah-grid .panel:nth-child(3):hover>span { box-shadow: 0 0 24px rgb(46 225 184 / 28%); }
.hah-grid .panel:hover li { text-shadow: 0 0 16px rgb(100 234 255 / 28%); transform: translateX(4px); }
.hah-grid .panel:hover span, .hah-grid .panel:hover h3, .hah-grid .panel:hover p { text-shadow: 0 0 16px rgb(100 234 255 / 28%); }

/* ---------- 전 페이지 유리질감 확장 (대시보드 4개 패널에서 검증된 처방을
   모든 씬의 카드/호버 버튼에 동일 적용) ----------
   테두리(border)는 없애고, 대신 호버 시 각 카드가 이미 갖고 있던 색상별
   글로우(box-shadow)로 경계를 표현한다 - 포탈 카드와 같은 원리. */
.project-cards article, .flow-grid article, .standards-grid article, .standards article,
.gallery-grid article, .resource-grid a, .resource-grid a:first-child,
.sorting-features article, .hah-grid .panel, .subject-card, .flow-card, .callout {
  border-style: none;
  background: linear-gradient(160deg, rgb(28 46 74 / 56%), rgb(10 22 42 / 50%));
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  box-shadow: 0 24px 64px rgb(0 0 0 / 30%), inset 0 0 0 1px rgb(255 255 255 / 16%), inset 0 1px 0 rgb(255 255 255 / 10%);
}
/* 갤러리는 카드 8개가 2x4로 화면 대부분을 채워서, 다른 씬과 같은 파란기 도는
   유리 톤(rgb(28 46 74))이 카드 하나일 때보다 훨씬 넓게 깔려 전체 화면이
   "푸르딩딩"하게 보였다 - 갤러리 카드만 파란기를 빼고 중성적인 남색으로. */
.gallery-grid article {
  background: linear-gradient(160deg, rgb(20 24 40 / 56%), rgb(8 10 20 / 50%));
}
/* 보조(고스트) 버튼류 - 카드보다 작으니 블러를 살짝 줄인다. */
.quick-buttons button, .app-tabs button, .close-btn, .ranking-close {
  border-style: none;
  background: rgb(10 22 42 / 40%);
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 12%);
}
.app-tabs button.is-active, .quick-buttons button.is-active { backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; }
/* secondary-btn("수업 설계 보기" 등)은 primary-btn과 나란히 있어서 존재감이
   있으면 안 된다 - 배경 자체가 거의 안 보일 만큼 옅은 유리로, 테두리도 없앤다. */
.secondary-btn {
  border-style: none;
  background: rgb(10 22 42 / 14%);
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 8%);
  transition: background 180ms ease, box-shadow 180ms ease, transform 180ms ease;
}
.secondary-btn:hover {
  background: rgb(94 247 205 / 10%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 22%), 0 0 24px rgb(94 247 205 / 14%);
  transform: translateY(-3px);
}
/* 대시보드 상단의 드롭다운(학년/반 선택)과 "기록" 링크도 같은 원칙:
   테두리 없애고 옅은 유리로. */
.select-line select, a.source-link {
  border-style: none;
  background: rgb(10 22 42 / 22%);
  backdrop-filter: blur(12px) saturate(180%);
  -webkit-backdrop-filter: blur(12px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 10%);
}
a.source-link:hover {
  border-style: none;
  background: rgb(94 247 205 / 10%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 24%);
}
/* 01/02/03 번호 배지: 테두리 없애고 안쪽 하이라이트만 남긴다. */
.project-cards article > span { border-style: none; }
/* H1 / AI / H2 배지: 프로젝트 01/02/03 배지와 동일한 유리 타일 구조로 통일
   (고정 원형 + 밝은 단색 배경 -> 자동 크기 + 옅은 그라디언트 유리 타일). */
.hah-grid .panel > span {
  display: inline-grid;
  place-items: center;
  inline-size: auto;
  block-size: auto;
  min-block-size: 3.6rem;
  min-inline-size: 3.6rem;
  padding: .55rem .95rem;
  border-style: none;
  border-radius: 1.15rem;
  background: linear-gradient(160deg, rgb(94 247 205 / 17%), rgb(100 169 255 / 7%) 62%, rgb(255 255 255 / 3%));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 14%), 0 10px 24px rgb(0 8 18 / 22%), 0 0 20px rgb(94 247 205 / 10%);
  color: #f6fbff;
  font-size: .95rem;
}
.hah-grid .panel:nth-child(2) > span { background: linear-gradient(160deg, rgb(100 169 255 / 17%), rgb(94 247 205 / 7%) 62%, rgb(255 255 255 / 3%)); box-shadow: inset 0 1px 0 rgb(255 255 255 / 14%), 0 10px 24px rgb(0 8 18 / 22%), 0 0 20px rgb(100 234 255 / 12%); }
.hah-grid .panel:nth-child(3) > span { background: linear-gradient(160deg, rgb(46 225 184 / 17%), rgb(100 169 255 / 7%) 62%, rgb(255 255 255 / 3%)); box-shadow: inset 0 1px 0 rgb(255 255 255 / 14%), 0 10px 24px rgb(0 8 18 / 22%), 0 0 20px rgb(46 225 184 / 12%); }
/* eyebrow(각 씬 제목 위 "Project Frame" 같은 알약 배지): 테두리 없애고 유리 + 호버. */
.eyebrow {
  border-style: none;
  background: rgb(94 247 205 / 12%);
  backdrop-filter: blur(10px) saturate(180%);
  -webkit-backdrop-filter: blur(10px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 18%);
  transition: background 180ms ease, box-shadow 180ms ease, transform 180ms ease;
}
.eyebrow:hover {
  background: rgb(94 247 205 / 18%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 30%), 0 0 18px rgb(94 247 205 / 16%);
  transform: translateY(-1px);
}
/* "지금 분류하기" 히어로 CTA: 예전엔 진한 단색 민트-블루 그라디언트라 촌스러웠다
   - 위 eyebrow 배지와 같은 옅은 유리 톤으로 맞춘다(색만 입힌 텍스트 + 은은한 배경). */
.hero-actions .primary-btn {
  border-style: none;
  background: rgb(8 18 36 / 55%);
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 30%);
  color: #5ef7cd;
  font-weight: 850;
  text-shadow: 0 1px 6px rgb(0 0 0 / 40%);
}
.hero-actions .primary-btn:hover {
  background: rgb(94 247 205 / 18%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 32%), 0 0 22px rgb(94 247 205 / 16%);
}
/* "연계 성취기준" 안의 "6실02-03" 같은 코드 배지도 같은 진한 단색 그라디언트라
   촌스러웠다 - eyebrow와 같은 옅은 유리 톤으로 통일. */
.standards-grid article > span {
  background: rgb(94 247 205 / 12%);
  backdrop-filter: blur(8px) saturate(180%);
  -webkit-backdrop-filter: blur(8px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 22%);
  color: #5ef7cd;
}
/* 호버해야 나오는 태그/버튼("E-book", "수업자료", "바로가기")도 같은 진한
   단색 그라디언트였다 - eyebrow와 같은 옅은 유리 톤으로 통일. */
.subject-card em, .flow-card em, .gallery-grid article button {
  background: rgb(94 247 205 / 14%);
  backdrop-filter: blur(8px) saturate(180%);
  -webkit-backdrop-filter: blur(8px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgb(94 247 205 / 24%);
  color: #5ef7cd;
  border-style: none;
}
/* "연계 성취기준" 바깥 박스: 테두리 없애고 카드류와 같은 유리 처방. */
.standards-panel {
  border-style: none;
  background: linear-gradient(160deg, rgb(28 46 74 / 50%), rgb(10 22 42 / 44%));
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  box-shadow: 0 24px 64px rgb(0 0 0 / 26%), inset 0 0 0 1px rgb(255 255 255 / 14%);
}

/* ---------- 대시보드 패널 ---------- */
.dashboard-grid>.panel { transition: transform 190ms ease, border-color 190ms ease, box-shadow 190ms ease, background 190ms ease, filter 190ms ease; }
.dashboard-grid>.panel:hover { transform: translateY(-7px); filter: brightness(1.03); border-color: rgb(94 247 205 / 48%); }
.class-panel .confusion { background: rgb(2 10 26 / 50%); }
.bar-list i, .confusion i, .progress-stack i { background: rgb(255 255 255 / 8%); }
.bar-list i::after, .confusion i::after, .progress-stack em { background: linear-gradient(90deg, #5ef7cd, #6aa9ff); }
.bar-list div.is-selected { border-color: rgb(94 247 205 / 28%); background: rgb(94 247 205 / 7.5%); box-shadow: inset 3px 0 0 rgb(94 247 205 / 60%); }
/* 도넛: 민트 도넛은 cb3a의 .school-panel .donut이 이미 D식(홀 46% + 글로우)으로 구현.
   D에만 있던 보라(violet) 변형만 같은 기하로 추가한다. */
.school-panel .donut.violet { background: radial-gradient(circle at center, #081326 46%, transparent 47%), conic-gradient(#ad8cff calc(var(--pct) * 1%), rgb(255 255 255 / 10%) 0); }
.data-refresh-btn { transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease; }
.data-refresh-btn:hover { transform: rotate(24deg) scale(1.04); }
/* 무한 반복(infinite)이던 것을 유한 2바퀴로 바꿨다. JS가 1600ms 뒤
   .is-loading 클래스를 강제로 떼는데, 760ms 주기의 정수배가 아니라서
   화살표가 도는 중간 각도에서 클래스가 사라져 0도로 순간이동하듯 뚝
   끊겼다 — "이상하게 동작한다"는 게 그 스냅백이었다. 2바퀴(1520ms)면
   1600ms 시점엔 이미 애니메이션이 스스로 끝나 회전 없는 원위치에서
   멈춰 있으므로, 클래스 제거는 이미 멈춘 뒤라 아무 영향이 없다. */
.data-refresh-btn.is-loading .data-refresh-ring { animation: refreshSpin 760ms linear 2; }
/* 클릭/포커스 시 뜨는 골드색 접근성 링이 버튼 자체의 원형 테두리와 겹쳐
   "겹겹의 원"처럼 보였다. 포커스 표시는 유지하되 브랜드 민트로 맞춘다. */
/* outline-color만 바꿨을 뿐 outline 자체는 그대로 살아있었다 - 브라우저
   기본 포커스 링(클릭 시에도 버튼에는 뜬다)이 로딩 스피너 링과 겹쳐서
   "원 두 개"로 보인 진짜 원인이었다. 링 모양 자체를 없애고, 포커스는
   배경을 밝히는 방식으로 표시한다. */
.data-refresh-btn:focus-visible { outline: none; background-color: rgb(100 234 255 / 14%); }
/* .ranking-close/.close-btn도 새로고침 버튼과 같은 원형 버튼 구조라 같은
   문제가 있었다: :focus-visible 규칙이 아예 없어서 브라우저 기본 골드색
   outline이 클릭 시 그대로 뜬다(둥근 버튼 테두리와 겹쳐 이중 원처럼 보임).
   같은 패턴으로 outline 제거 + 배경 틴트로 교체. */
.ranking-close:focus-visible, .close-btn:focus-visible { outline: none; background-color: rgb(100 234 255 / 16%); }
/* 화살표 아이콘 자체는 이제 괜찮은데도 "동심원 두 개"로 계속 보인 진짜
   이유: 버튼 자체가 처음부터 원형 테두리(1px solid, border-radius:50%)를
   갖고 있었다 — 안에 어떤 아이콘을 넣어도 원형 버튼 테두리와 겹쳐 원이 두
   겹으로 보일 수밖에 없는 구조였다. 테두리를 없애고 은은한 방사형 글로우
   배경(이미 있던 radial-gradient)만으로 버튼임을 알리게 한다. */
.data-refresh-btn { border-style: none; }
.combo-chart .chart-area { opacity: .16; filter: none !important; }
.combo-chart .chart-bars rect { opacity: .98; filter: none !important; }
.combo-chart .chart-line { filter: drop-shadow(0 0 5px rgb(94 247 205 / 24%)); }
.combo-chart .chart-points circle { filter: drop-shadow(0 0 5px rgb(94 247 205 / 32%)); }
.combo-chart .chart-average path { opacity: .9; filter: drop-shadow(0 0 8px rgb(255 224 108 / 32%)); }
.landfill-kpi-ring::before { content: ""; position: absolute; inset: 18%; border-radius: inherit; background: conic-gradient(rgb(173 140 255 / 92%) 32.7%, rgb(108 94 151 / 20%) 0); box-shadow: none; pointer-events: none; z-index: -1; }
.landfill-kpi-ring>span { text-shadow: 0 0 10px rgb(94 247 205 / 14%); }

/* 데이터 새로고침 시 재페인트 상태(app.js가 is-dashboard-repaint 토글) —
   막대 채움 애니메이션만 다시 재생하고 나머지 진입 애니메이션은 억제 */
.school-panel.is-dashboard-repaint .bar-list i, .class-panel.is-dashboard-repaint .confusion i { animation: none !important; transform: none !important; }
.school-panel.is-dashboard-repaint .bar-list i::after, .class-panel.is-dashboard-repaint .confusion i::after { animation: inlineBarFill 720ms cubic-bezier(.22, 1, .36, 1) both !important; }
.landfill-panel.is-dashboard-repaint { border-color: rgb(94 247 205 / 34%); box-shadow: 0 22px 76px rgb(10 25 48 / 44%), 0 0 28px rgb(94 247 205 / 10%); }
.landfill-panel.is-dashboard-repaint .chart-bars rect, .landfill-panel.is-dashboard-repaint .chart-area, .landfill-panel.is-dashboard-repaint .chart-line { animation: none !important; }

/* ---------- 업로드 패널 ---------- */
.upload-actions button:first-child { color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); box-shadow: 0 18px 46px rgb(94 247 205 / 18%); }
.upload-actions button:last-child { background: rgb(255 255 255 / 6%); }
.upload-panel .upload-actions button { background: linear-gradient(145deg, rgb(94 247 205 / 22%), rgb(67 137 255 / 12%)); }
.upload-icon { background: rgb(2 6 17 / 18%); }
.upload-icon svg { transform: translateY(3px); }
.upload-card-actions button, .upload-card-actions button:first-child { text-shadow: 0 0 14px rgb(5 16 30 / 62%); }
.upload-card-actions button:hover { box-shadow: 0 0 28px rgb(94 247 205 / 20%), inset 0 0 22px rgb(255 255 255 / 5%); }
.upload-card-actions .upload-icon { background: transparent !important; box-shadow: none !important; }
.upload-card-actions .upload-icon svg { transform: translateY(2px); background: transparent !important; box-shadow: none !important; }

/* ---------- AI 판정 모달 / 스캔 연출 ---------- */
.ai-modal { box-shadow: 0 34px 120px rgb(0 0 0 / 60%); }
.ai-modal::backdrop { background: rgb(0 0 0 / 68%); backdrop-filter: blur(10px); }
.modal-preview { position: relative; background: linear-gradient(135deg, rgb(100 234 255 / 14%), rgb(173 140 255 / 12%)); }
.modal-preview::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(180deg, rgb(100 234 255 / 8%), transparent 34%, rgb(94 247 205 / 8%)); mix-blend-mode: screen; opacity: 0; animation: none; transition: opacity 180ms ease; }
.ai-modal.is-scanning .modal-preview::after { opacity: 1; }
.scan-overlay { background: linear-gradient(180deg, transparent, rgb(100 234 255 / 30%), rgb(94 247 205 / 62%), rgb(100 234 255 / 20%), transparent); filter: blur(1px); opacity: 0; animation: none; transition: opacity 180ms ease; }
.ai-modal.is-scanning .scan-overlay { opacity: .72; animation: scanLine 2.1s ease-in-out infinite; }
.modal-body dl div { background: rgb(255 255 255 / 6%); }
.student-question { background: rgb(255 255 255 / 6%); }
.modal-cancel-action { background: rgb(255 255 255 / 5.5%); }
.modal-cancel-action:hover { border-color: rgb(94 247 205 / 38%); color: #fff; background: rgb(94 247 205 / 11%); }
.decision-grid button:hover { color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); }
.decision-grid button:last-child { color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); }
.decision-grid button.is-selected { border-color: rgb(94 247 205 / 72%); box-shadow: 0 0 18px rgb(94 247 205 / 22%); }
.decision-confirm { background: rgb(94 247 205 / 8%); }
#cancelDecision { background: rgb(255 255 255 / 6%); }

/* ---------- 3초판단(분리배출) 검색 / 퀵버튼 / 스캔 ---------- */
.search-line button { color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); font-weight: 850; }
#searchButton { background: radial-gradient(circle at 32% 24%, rgb(94 247 205 / 22%), transparent 40%), rgb(94 247 205 / 9%) !important; }
.sorting-upload button { background: radial-gradient(circle at 30% 18%, rgb(94 247 205 / 12%), transparent 40%), linear-gradient(145deg, rgb(255 255 255 / 7.5%), rgb(6 17 35 / 62%)) !important; box-shadow: inset 0 1px 0 rgb(255 255 255 / 8%); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease, box-shadow 180ms ease; }
.sorting-upload button:hover { transform: translateY(-3px); border-color: rgb(94 247 205 / 48%) !important; background: radial-gradient(circle at 28% 18%, rgb(94 247 205 / 18%), transparent 42%), linear-gradient(145deg, rgb(94 247 205 / 10.5%), rgb(9 24 47 / 78%)) !important; box-shadow: 0 16px 36px rgb(0 0 0 / 22%), 0 0 22px rgb(94 247 205 / 10%); }
.sorting-upload button:hover .sorting-upload-icon svg { color: #fff; filter: drop-shadow(0 0 10px rgb(94 247 205 / 36%)); }
.quick-buttons button { background: rgb(255 255 255 / 4.5%); border-color: rgb(100 234 255 / 16%); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease, box-shadow 180ms ease; }
.quick-buttons button:hover { transform: translateY(-3px); border-color: rgb(94 247 205 / 48%) !important; background: radial-gradient(circle at 28% 18%, rgb(94 247 205 / 18%), transparent 42%), linear-gradient(145deg, rgb(94 247 205 / 10.5%), rgb(9 24 47 / 78%)) !important; box-shadow: 0 16px 36px rgb(0 0 0 / 22%), 0 0 22px rgb(94 247 205 / 10%); }
.quick-buttons button.is-active { color: #071521; background: linear-gradient(135deg, #5ef7cd, #64eaff) !important; border-color: transparent !important; box-shadow: 0 0 24px rgb(94 247 205 / 22%); }
.quick-result { position: relative; overflow: hidden; border-color: rgb(94 247 205 / 18%); background: radial-gradient(circle at 14% 12%, rgb(94 247 205 / 10.5%), transparent 38%), linear-gradient(145deg, rgb(255 255 255 / 5.5%), rgb(8 22 43 / 56%)) !important; }
.quick-result.is-empty { border-style: dashed; border-color: rgb(94 247 205 / 20%); background: radial-gradient(circle at 14% 12%, rgb(94 247 205 / 8.5%), transparent 38%), linear-gradient(145deg, rgb(255 255 255 / 4.5%), rgb(8 22 43 / 52%)) !important; }
.quick-result.is-scanning { border-color: rgb(94 247 205 / 34%) !important; background: radial-gradient(circle at 20% 12%, rgb(94 247 205 / 16%), transparent 42%), linear-gradient(145deg, rgb(94 247 205 / 8%), rgb(8 22 43 / 62%)) !important; box-shadow: inset 0 0 28px rgb(94 247 205 / 8%), 0 0 24px rgb(54 221 255 / 8%); }
.quick-result.is-scanning>span { color: rgb(228 255 249 / 98%) !important; }
.quick-result.is-scanning::before { content: ""; position: absolute; z-index: 0; inset: -18% auto -18% -42%; inline-size: 38%; background: linear-gradient(90deg, transparent, rgb(94 247 205 / 26%), rgb(94 201 255 / 14%), transparent); filter: blur(1px); transform: skewX(-18deg); animation: quickScanSweep 1.12s cubic-bezier(.22, 1, .36, 1) infinite; pointer-events: none; }
.quick-result.is-scanning::after { content: ""; position: absolute; z-index: 0; inset-inline: 16px; inset-block-start: 52%; block-size: 1px; background: linear-gradient(90deg, transparent, rgb(94 247 205 / 72%), transparent); box-shadow: 0 0 18px rgb(94 247 205 / 42%); animation: quickScanLine 1.12s ease-in-out infinite; pointer-events: none; }
.quick-scan-meter { background: rgb(255 255 255 / 8%); }
.quick-scan-meter i { background: linear-gradient(90deg, rgb(94 247 205 / 10%), rgb(94 247 205 / 94%), rgb(90 194 255 / 78%)); animation: quickScanMeter 1.08s cubic-bezier(.22, 1, .36, 1) infinite; }
.quick-tip { background: linear-gradient(135deg, rgb(94 247 205 / 7.5%), rgb(100 169 255 / 3.5%)), rgb(255 255 255 / 2.8%) !important; }
.quick-tip strong { background: rgb(94 247 205 / 8.5%); }
.quick-result>span em { padding: 4px 9px; border-radius: 999px; color: #081421; background: linear-gradient(135deg, #5ef7cd, #64eaff); font-size: .72rem; font-style: normal; }
.quick-result>span b { background: rgb(94 247 205 / 6.5%); }
.quick-action-row button { background: rgb(94 247 205 / 8%); }
.quick-action-row button:disabled { opacity: .42; }
.mini-stats div { background: radial-gradient(circle at 20% 0%, rgb(94 247 205 / 10%), transparent 40%), rgb(255 255 255 / 4.5%); }

/* ---------- 앱 탭(검색/사진 전환) ---------- */
.app-tabs { background: linear-gradient(135deg, rgb(255 255 255 / 4.5%), rgb(94 247 205 / 2.5%)), rgb(255 255 255 / 2.8%) !important; box-shadow: inset 0 1px 0 rgb(255 255 255 / 6%); }
.app-tabs button { border-color: transparent !important; transition: color 170ms ease, background 170ms ease, transform 170ms ease, box-shadow 170ms ease !important; }
.app-tabs button:hover { color: #f6feff !important; transform: translateY(-1px); border-color: transparent !important; outline: 0 !important; background: radial-gradient(circle at 50% 0%, rgb(94 247 205 / 16%), transparent 56%), rgb(94 247 205 / 6.5%) !important; box-shadow: 0 0 22px rgb(94 247 205 / 12%) !important; }
.app-tabs button.is-active { border-color: transparent !important; outline: 0 !important; }
.app-tabs button.is-active:hover { color: #06111c !important; border-color: transparent !important; outline: 0 !important; }

/* ---------- 퀴즈 카드 ---------- */
.tab-panel button[data-quiz-answer] { background: rgb(255 255 255 / 5.5%); }
.tab-panel button[data-quiz-answer]:hover { color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); }
.quiz-card { background: radial-gradient(circle at 16% 12%, rgb(94 247 205 / 10%), transparent 38%), radial-gradient(circle at 86% 0%, rgb(173 140 255 / 10%), transparent 34%), rgb(255 255 255 / 4.5%) !important; }
.quiz-card i { background: rgb(255 255 255 / 8%); }
.quiz-card i em { background: linear-gradient(90deg, #5ef7cd, #64eaff); transition: width 260ms ease; }
#quizEmoji { background: linear-gradient(145deg, rgb(255 255 255 / 9%), rgb(94 247 205 / 8%)); box-shadow: inset 0 1px 0 rgb(255 255 255 / 8%); }
#quizQuestion { background: rgb(4 15 32 / 42%); }
.quiz-choice-row button { position: relative; border-color: rgb(100 234 255 / 14%) !important; background: rgb(255 255 255 / 4.5%) !important; box-shadow: inset 0 1px 0 rgb(255 255 255 / 6%); transition: transform 160ms ease, border-color 160ms ease, background 160ms ease, box-shadow 160ms ease; }
.quiz-choice-row button[data-quiz-choice="true"] { border-color: rgb(94 247 205 / 22%) !important; }
.quiz-choice-row button[data-quiz-choice="false"] { border-color: rgb(255 129 164 / 24%) !important; }
.quiz-choice-row button[data-quiz-choice="true"]::before, .quiz-choice-row button[data-quiz-choice="false"]::before { content: ""; position: absolute; inset: 0; border-radius: inherit; opacity: 0; background: radial-gradient(circle at 50% 0%, rgb(255 255 255 / 16%), transparent 48%); transition: opacity 160ms ease; pointer-events: none; }
.quiz-choice-row button:hover::before { opacity: 1; }
.quiz-choice-row button[data-quiz-choice="true"]:hover { border-color: rgb(94 247 205 / 50%) !important; background: linear-gradient(145deg, rgb(94 247 205 / 15%), rgb(255 255 255 / 5.5%)) !important; box-shadow: 0 14px 34px rgb(94 247 205 / 12%); }
.quiz-choice-row button[data-quiz-choice="false"]:hover { border-color: rgb(173 140 255 / 50%) !important; background: linear-gradient(145deg, rgb(173 140 255 / 15%), rgb(255 255 255 / 5.5%)) !important; box-shadow: 0 14px 34px rgb(173 140 255 / 12%); }
.quiz-choice-row button:active { transform: translateY(1px) scale(.985); }
.quiz-choice-row button:not(:disabled):not(.is-picked):not(.is-answer):hover { transform: translateY(-2px); border-color: rgb(100 234 255 / 14%) !important; background: radial-gradient(circle at 50% 0%, rgb(100 234 255 / 20%), transparent 54%), rgb(100 234 255 / 9%) !important; box-shadow: 0 0 24px rgb(100 234 255 / 18%), inset 0 1px 0 rgb(255 255 255 / 9%) !important; }
.quiz-choice-row button.is-picked { color: #f7feff !important; background: radial-gradient(circle at 50% 0%, rgb(94 247 205 / 20%), transparent 56%), rgb(94 247 205 / 12%) !important; }
.quiz-choice-row button.is-answer { color: #f9fffe !important; border-color: rgb(126 255 226 / 30%) !important; background: radial-gradient(circle at 50% 0%, rgb(126 255 226 / 28%), transparent 56%), rgb(94 247 205 / 16%) !important; box-shadow: 0 0 24px rgb(94 247 205 / 20%), inset 0 1px 0 rgb(255 255 255 / 22%); }
.quiz-choice-row button.is-picked:not(.is-answer) { color: #ffeef3 !important; border-color: rgb(255 129 164 / 26%) !important; background: radial-gradient(circle at 50% 0%, rgb(255 129 164 / 22%), transparent 56%), rgb(255 129 164 / 10%) !important; box-shadow: 0 0 20px rgb(255 129 164 / 16%), inset 0 1px 0 rgb(255 255 255 / 12%); }
.quiz-card.is-correct { border-color: rgb(94 247 205 / 46%); box-shadow: 0 0 28px rgb(94 247 205 / 12%); }
.quiz-card.is-wrong { border-color: rgb(255 129 164 / 45%); box-shadow: 0 0 28px rgb(255 129 164 / 10%); }
.quiz-result { background: rgb(255 255 255 / 3.5%); }
.quiz-card.is-correct .quiz-result { color: rgb(225 255 248 / 94%); background: rgb(94 247 205 / 9%); }
.quiz-card.is-wrong .quiz-result { color: rgb(255 230 237 / 94%); background: rgb(255 129 164 / 9%); }
.quiz-result strong.is-good { color: #91ffe8; text-shadow: 0 0 16px rgb(94 247 205 / 25%); }
.quiz-result strong.is-bad { color: #ffadc3; text-shadow: 0 0 16px rgb(255 129 164 / 20%); }
.quiz-rank-table span { background: rgb(255 255 255 / 2.8%); }
.quiz-rank-table span.is-current { color: rgb(238 254 255 / 96%); border-color: rgb(94 247 205 / 32%); background: rgb(94 247 205 / 8%); box-shadow: 0 0 18px rgb(94 247 205 / 10%); }
.quiz-retry-btn { min-block-size: 38px; padding: 0 18px; border: 1px solid rgb(94 247 205 / 22%); border-radius: 999px; color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); font-weight: 950; cursor: pointer; }
.quiz-card.is-finished { border-color: rgb(255 219 112 / 28%); box-shadow: 0 0 34px rgb(255 219 112 / 8%), inset 0 1px 0 rgb(255 255 255 / 8%); }
.quiz-card.is-finished #quizResult { background: transparent; }
.quiz-card.is-finished .quiz-rank-table span { border-radius: 17px !important; }
.quiz-card.is-finished .quiz-retry-btn { border: 1px solid rgb(94 247 205 / 30%) !important; color: rgb(236 255 250 / 96%) !important; background: linear-gradient(135deg, rgb(94 247 205 / 16%), rgb(82 175 255 / 11%)), rgb(255 255 255 / 4%) !important; box-shadow: inset 0 1px 0 rgb(255 255 255 / 8%), 0 12px 30px rgb(94 247 205 / 8%); transition: transform 180ms ease, border-color 180ms ease, background 180ms ease, box-shadow 180ms ease; }
.quiz-card.is-finished .quiz-retry-btn:hover { transform: translateY(-1px); border-color: rgb(94 247 205 / 48%) !important; background: linear-gradient(135deg, rgb(94 247 205 / 22%), rgb(82 175 255 / 15%)), rgb(255 255 255 / 5.5%) !important; box-shadow: inset 0 1px 0 rgb(255 255 255 / 10%), 0 16px 34px rgb(94 247 205 / 12%); }
.quiz-report-panel { background: radial-gradient(circle at 16% 0%, rgb(255 219 112 / 13%), transparent 42%), radial-gradient(circle at 90% 12%, rgb(94 247 205 / 12%), transparent 34%), linear-gradient(145deg, rgb(255 255 255 / 6.5%), rgb(9 24 45 / 76%)); box-shadow: inset 0 1px 0 rgb(255 255 255 / 8%), 0 18px 44px rgb(0 0 0 / 18%); }
.quiz-report-rank>b { background: radial-gradient(circle at 32% 18%, rgb(255 255 255 / 20%), transparent 38%), rgb(255 219 112 / 8.5%); }
.quiz-report-score span { background: rgb(255 255 255 / 4.2%); }
.quiz-report-score .quiz-total-score b { text-shadow: 0 0 20px rgb(255 219 112 / 18%); }
.quiz-report-label { background: rgb(255 219 112 / 7.5%); }
.badge-progress { background: radial-gradient(circle at 8% 0%, rgb(94 247 205 / 9%), transparent 42%), rgb(255 255 255 / 3.5%); }
.badge-track article { background: rgb(255 255 255 / 2.6%); }
.badge-track article.is-earned { color: rgb(239 254 255 / 96%); border-color: rgb(94 247 205 / 25%); background: linear-gradient(145deg, rgb(94 247 205 / 10%), rgb(255 255 255 / 3.5%)); }
.impact-insights article { background: radial-gradient(circle at 18% 0%, rgb(94 247 205 / 12%), transparent 42%), rgb(255 255 255 / 3.5%); }

/* ---------- 랭킹 모달 / 포디움 / 레이스 ---------- */
.rank-note { transition: border-color 180ms ease, box-shadow 180ms ease, background 180ms ease; }
.rank-note:hover { border-color: rgb(94 247 205 / 42%); box-shadow: 0 0 24px rgb(94 247 205 / 14%); background: linear-gradient(90deg, rgb(94 247 205 / 15%), rgb(100 169 255 / 12%), rgb(173 140 255 / 10%)); }
.rank-note:focus-visible { outline: 2px solid rgb(94 247 205 / 76%); outline-offset: 4px; }
.ranking-modal { background: rgb(2 8 20 / 72%); backdrop-filter: blur(18px); transition: opacity 160ms ease; }
.ranking-dialog { background: linear-gradient(150deg, rgb(12 28 56 / 94%), rgb(3 10 24 / 92%)); box-shadow: 0 34px 100px rgb(0 0 0 / 48%), 0 0 52px rgb(94 247 205 / 12%); transform: translateY(18px) scale(.985); transition: transform 160ms ease; }
.ranking-modal.is-open .ranking-dialog { transform: translateY(0) scale(1); }
.ranking-tabs { background: rgb(255 255 255 / 3.8%); }
.ranking-tab { transition: color 160ms ease, background 160ms ease, transform 160ms ease; }
.ranking-tab:hover { color: #f6feff; background: rgb(94 247 205 / 8%); transform: translateY(-1px); }
.ranking-tab.is-active { background: linear-gradient(135deg, rgb(126 255 226 / 96%), rgb(86 218 255 / 92%)); }
.podium-card { background: radial-gradient(circle at 50% 0%, rgb(94 247 205 / 18%), transparent 48%), linear-gradient(160deg, rgb(255 255 255 / 8%), rgb(6 18 38 / 78%)); box-shadow: inset 0 1px 0 rgb(255 255 255 / 8%); }
.podium-card.is-current { border-color: rgb(94 247 205 / 42%); box-shadow: 0 0 28px rgb(94 247 205 / 13%), inset 3px 0 0 rgb(94 247 205 / 72%); }
.podium-medal { filter: drop-shadow(0 0 16px rgb(255 226 122 / 34%)); }
.race-row { background: rgb(4 15 32 / 64%); }
.race-row.is-current { border-color: rgb(94 247 205 / 42%); box-shadow: 0 0 28px rgb(94 247 205 / 13%), inset 3px 0 0 rgb(94 247 205 / 72%); }
.race-mascot { background: rgb(255 255 255 / 7%); }
.race-body i em { background: linear-gradient(90deg, #5ef7cd, #64eaff, #ad8cff); }
.ranking-race-wrap { background: repeating-linear-gradient(90deg, rgb(255 255 255 / 3.5%) 0 1px, transparent 1px 52px), rgb(255 255 255 / 3.5%); }
.ranking-race-head em { display: none !important; } /* D 최종본이 숨긴 요소 */

/* ---------- 기다림 목록(hold) / 타임라인 ---------- */
.hold-list-head { background: rgb(255 255 255 / 3.5%); }
.hold-list-head button { background: rgb(255 255 255 / 4%) !important; }
#holdList li { background: rgb(255 255 255 / 5.5%); }
.hold-card { border-color: rgb(94 247 205 / 11%) !important; background: rgb(255 255 255 / 4.5%); transition: transform 150ms ease, border-color 150ms ease, background 150ms ease; }
.hold-card:hover { transform: translateY(-1px); border-color: rgb(94 247 205 / 28%) !important; background: rgb(94 247 205 / 5.5%); }
.hold-card button { background: rgb(94 247 205 / 8%); }
.hold-card-actions button[data-delete-hold] { background: rgb(255 129 164 / 7.5%); }
.hold-body em { background: rgb(94 247 205 / 7.5%); }
.hold-emoji { background: rgb(94 247 205 / 10%); }
.hold-input-line { background: rgb(255 255 255 / 3.2%); }
.hold-input-line input { border-color: transparent; background: rgb(3 13 29 / 42%); }
.hold-input-line button { min-block-size: 48px; padding-inline: 18px; border: 1px solid rgb(94 247 205 / 28%); border-radius: 14px !important; color: #06111c; background: linear-gradient(135deg, #5ef7cd, #64eaff); font-weight: 950; }
.hold-entry-card, .hold-guide-card { background: radial-gradient(circle at 12% 0%, rgb(94 247 205 / 10%), transparent 42%), rgb(255 255 255 / 3.5%); }
.hold-entry-emoji { background: rgb(94 247 205 / 9%); transition: transform 180ms ease, box-shadow 180ms ease, background 180ms ease; }
.hold-entry-emoji.is-cycling { box-shadow: 0 0 22px rgb(94 247 205 / 10%); }
.hold-entry-emoji.is-matched { transform: scale(1.04); background: radial-gradient(circle at 36% 22%, rgb(255 255 255 / 20%), transparent 42%), rgb(94 247 205 / 14%); }
.empty-state { background: rgb(255 255 255 / 4.5%); }
.timeline-box .empty-state, #holdList .empty-state { background: radial-gradient(circle at 50% 8%, rgb(94 247 205 / 8%), transparent 42%), rgb(255 255 255 / 3%); }
.sorting-empty-state>span, .hold-empty-state>span { background: radial-gradient(circle at 32% 22%, rgb(255 255 255 / 16%), transparent 38%), rgb(94 247 205 / 8%); }
.timeline-box { background: rgb(255 255 255 / 3.5%); }
.timeline-box li { background: rgb(255 255 255 / 4.5%); }
.timeline-box>strong { display: flex; align-items: center; justify-content: space-between; color: rgb(237 252 255 / 92%); }
.timeline-box>strong::after { content: "LIVE LOG"; padding: 4px 8px; border-radius: 999px; color: rgb(165 255 233 / 82%); background: rgb(94 247 205 / 7%); font-size: .62rem; letter-spacing: .08em; }
.timeline-emoji { background: radial-gradient(circle at 35% 20%, rgb(255 255 255 / 18%), transparent 42%), rgb(94 247 205 / 8.5%); }

/* ---------- 갤러리 상세 ---------- */
.gallery-detail span { background: rgb(255 255 255 / 4.5%); }
.gallery-detail div span { background: rgb(255 255 255 / 4.5%); background-size: cover; background-position: center; }
.gallery-stage.is-detail .gallery-detail { border: 1px solid rgb(85 255 235 / 18%); border-radius: 30px; background: linear-gradient(145deg, rgb(9 24 48 / 90%), rgb(6 14 31 / 82%)), rgb(8 24 42 / 72%); box-shadow: 0 32px 110px rgb(0 0 0 / 32%), inset 0 1px 0 rgb(255 255 255 / 6%); }
.gallery-detail .gallery-detail-head #galleryDetailNumber { border-color: rgb(94 247 205 / 42%) !important; background: linear-gradient(145deg, rgb(94 247 205 / 16%), rgb(100 169 255 / 8%)), rgb(255 255 255 / 4.5%) !important; box-shadow: inset 0 1px 0 rgb(255 255 255 / 12%), 0 0 24px rgb(94 247 205 / 8%); }
.gallery-detail-photo { background: rgb(8 24 42 / 72%); box-shadow: 0 24px 80px rgb(0 0 0 / 28%); }
.gallery-detail-photo figcaption { background: rgb(2 8 18 / 58%); backdrop-filter: blur(10px); }
.gallery-detail-photo.is-missing { border-style: dashed; color: rgb(222 240 255 / 72%); }
.gallery-detail .gallery-detail-grid .gallery-detail-photo.is-missing span { border: 0; border-radius: 0; background: transparent; color: inherit; }
#galleryBack { border: 1px solid rgb(94 247 205 / 26%); border-radius: 999px; padding: 0 18px; min-block-size: 44px; background: rgb(255 255 255 / 5.5%); color: rgb(237 248 255 / 90%); font-weight: 900; }
#galleryBack:hover { border-color: rgb(94 247 205 / 50%); background: rgb(94 247 205 / 12%); }

/* ---------- 출처 링크 / TM 컨트롤 ---------- */
.source-link { background: rgb(255 255 255 / 5.5%); }
.source-link:hover { color: #fff; border-color: rgb(94 247 205 / 48%); background: rgb(94 247 205 / 12%); box-shadow: 0 0 18px rgb(94 247 205 / 12%); }
.tm-model-line { background: linear-gradient(135deg, rgb(173 140 255 / 7%), rgb(94 247 205 / 3.5%)); }
.tm-model-controls input { background: rgb(3 12 24 / 48%); }
.tm-model-controls button { min-block-size: 30px; padding-inline: 11px; border: 1px solid rgb(94 247 205 / 28%); border-radius: 10px; color: #071521; background: linear-gradient(135deg, #5ef7cd, #64eaff); font-weight: 900; }
}

/* ---------- 5. PC 중 좁은 폭 (1024~1600px) ---------- */
@media (min-width:64rem) and (max-width:100rem) {
.school-panel .donut { inline-size:5rem; block-size:5rem; }
  .school-panel .donut-pair { gap:1rem; margin-block-end:1.3rem; }
  .school-panel .donut small { margin-block-start:.28rem; }
  .landfill-kpi-ring { inline-size:4.6rem; block-size:4.6rem; }
  /* Narrow panels (~310px at 1280): a fixed 6.9rem label column left the bar
     track at literal 0px. Let labels wrap and hand the bars real width. */
  .class-panel .confusion > div { grid-template-columns:minmax(4rem,max-content) minmax(2.2rem,1fr) 2rem; }
  .class-panel .confusion span { word-break:normal; overflow-wrap:break-word; }
  /* Tracks were collapsing to content width at ~1280, zeroing the bar column;
     pin the panel templates (double-class selectors outrank the stray rule
     that shrank col2 to min-content) and every row container to full width. */
  .dashboard-grid > .school-panel { grid-template-columns:7rem minmax(0,1fr); }
  .dashboard-grid > .class-panel { grid-template-columns:3.75rem minmax(0,1fr); }
  .school-panel .school-visuals { display:grid; grid-template-columns:minmax(0,1fr); justify-items:stretch; inline-size:100%; }
  .school-panel .bar-list, .school-panel .bar-list>div, .class-panel .confusion>div { inline-size:100%; min-inline-size:0; }
  /* 학년 라벨(예: "5학년")이 칸 폭에 거의 딱 맞아서(2.9rem) 막대와 사이 간격이
     column-gap(.7rem)뿐이었다 — 선택된 학년일수록 글자가 막대에 바짝 붙어
     보였다. 라벨 칸을 넓혀 글자 뒤로 실제 여백을 준다. */
  .school-panel .bar-list>div { grid-template-columns:3.75rem minmax(0,1fr) 2.4rem; }
  .portal-frame { padding-block:.5rem .75rem; min-block-size:0; }
  .portal-teaser { line-height:1.45; }
  body.is-rewound .portal-frame { min-block-size:clamp(6rem,19svh,15.5rem); padding-block:.9rem; }
  body.is-rewound .dashboard-scene .qr-invite img { inline-size:clamp(6.8rem,8.6vw,12.5rem); }
}

/* ---------- 6. PC 중 낮은 화면 (높이 ~848px) ---------- */
@media (min-width:64rem) and (max-height:53rem) {
  /* project/curriculum/hah/flow/gallery/resources 여섯 씬이 공유하는
     padding-block-start가 뷰포트 너비에만 비례해 커지고(calc(6.39rem +
     3.04vw)) 높이는 전혀 고려하지 않아, 화면이 좁지 않아도 세로로 짧으면
     (예: 1366x768) 씬 하나가 100svh를 넘어버렸다. 씬 하나가 넘치면 그
     뒤로 이어지는 모든 스크롤 스냅 위치가 넘친 만큼씩 밀린다 — 화면
     세로가 짧을 때만 여백을 줄여 씬이 한 화면 안에 다시 들어오게 한다. */
  .project-scene, .curriculum-scene, .hah-scene, .flow-scene, .gallery-scene, .resources-scene { padding-block-start:5.25rem; padding-block-end:2.5rem; }
  .dashboard-grid { block-size:auto; min-block-size:0; grid-template-rows:auto auto; margin-block-start:.6rem; }
  .landfill-panel .chart-wrap { grid-template-rows:auto auto; }
  .combo-chart { inline-size:100%; block-size:auto; max-block-size:8.5rem; }
  .school-panel .donut { inline-size:4.6rem; block-size:4.6rem; }
  .school-panel .donut-pair { gap:.9rem; margin-block-end:1.2rem; }
  .landfill-kpi-ring { inline-size:4.2rem; block-size:4.2rem; }
  .kpi-row strong, .class-kpis strong, .landfill-metrics strong { font-size:clamp(1.2rem,1.5vw,1.9rem); }
  .dashboard-grid>.panel .panel-head h2 { font-size:clamp(1rem,1.15vw,1.5rem); }
  .scroll-cue { display:none; }
}

/* ---------- 애니메이션 정의 ---------- */
@keyframes scene-scroll-cue {
  0%, 100% { translate:-50% 0; opacity:.5; }
  50% { translate:-50% .6rem; opacity:1; }
}
@keyframes finale-arrow-bob {
  0%, 100% { translate:0 0; opacity:.55; }
  50% { translate:0 .45rem; opacity:1; }
}
@keyframes finale-hint-float {
  0%, 100% { transform:translateY(0); }
  50% { transform:translateY(.45rem); }
}
@keyframes qr-invite-reveal {
  from { opacity:0; transform:translateY(.55rem) scale(.96); }
  to { opacity:1; transform:none; }
}
@keyframes portal-live-float {
  0%, 100% { transform:translateY(-.25rem); }
  50% { transform:translateY(.45rem); }
}

/* 8개 씬 배경 정리 (요청: 갤러리의 은은한 초록빛과 차시흐름의 격자무늬를
   전 씬에 반영, 단 전부 같은 자리에 중심을 찍지 않게).
   실측해보니 이 배경 장식이 실제로 켜져 있던 건 project-scene(글로우)과
   flow-scene(격자무늬) 둘뿐이었다 — 나머지 6개 씬(대시보드/교육과정/HAH/
   갤러리/3초판단/자료실)에 있던 배경 규칙은 전부 뒤에 나오는 다른 규칙에
   덮여 죽어 있었다. 파일 맨 끝에서 8개 씬 전부에 공통 격자무늬 + 씬마다
   다른 좌표의 브랜드색 글로우 3개를 다시 지정한다 — 다른 모든 배경
   규칙보다 소스 순서상 뒤이므로 확실히 이긴다. */
@media (min-width:64rem) {
  .dashboard-scene, .project-scene, .curriculum-scene, .hah-scene, .flow-scene, .gallery-scene, .sorting-scene, .resources-scene {
    background-size: auto, auto, auto, 88px 88px, 88px 88px;
  }
  /* 대시보드와 같은 이유로 전부 없앤다 - body 배경 + 씬마다 있는 오브
     요소(아래 .scene-orbs) 2겹만 남겨서 명암 대비를 살린다. */
  .project-scene, .curriculum-scene, .hah-scene, .flow-scene, .gallery-scene, .sorting-scene, .resources-scene { background-image: none; }
  /* 대시보드만 격자가 안 보였다: body 자체가 이미 22% 밝기의 파란 글로우를
     깔고 있는데, 대시보드는 스크롤 없이 늘 그 글로우 바로 아래 있어서
     격자선(4~5%)이 상대적으로 묻혔다. 다른 씬은 스크롤로 body 글로우
     중심에서 멀어지니 상대적으로 잘 보였다. 대시보드만 격자 진하기를
     올려 같은 자리에서도 보이게 한다. */
  /* 포탈은 씬 개념이 없어서 body 배경 + 오브, 딱 2겹만 쌓는다. 여기에
     씬 전용 글로우까지 더하면(예전 이 규칙) body+오브+씬 3겹이 겹쳐 전부
     뭉개져서 밝은 부분과 어두운 여백의 대비가 안 났다. 씬 자체 글로우는
     빼고 body 배경 + .scene-orbs 두 겹으로만 간다(전 씬 공통 적용). */
  .dashboard-scene { background-image: none; }
}

/* 프로젝트: 01/02/03 배지가 카드 위쪽 여백(63px)에 비해 바로 아래 제목과의
   간격(.9rem=14px)이 너무 좁아, 배지가 콘텐츠 덩어리 맨 위에 붙은 채로
   카드 중앙 정렬 때문에 오히려 아래로 처져 보였다. 배지를 위로 당긴다. */
@media (min-width:64rem) {
  .project-cards article>span { margin-block-start:-1.6rem; margin-block-end:1.125rem; }
}

/* 갤러리 설명문("학생들이 발견한 불편함..."): 요청한 확대가 실제로는 한
   번도 반영된 적이 없었다 — 다른 규칙에 덮인 게 아니라 이 규칙 자체가
   좁은 clamp 범위(.9~.98rem)에 머물러 있었다. 다른 씬 설명문과 어울리는
   폭으로 한 단계 키운다. */
.gallery-description { font-size: clamp(1.02rem, .62rem + .45vw, 1.12rem) !important; }

/* ---------- 3단 기기 라우팅 (2026-08-21, 2026-08-27 판정 단일화) ----------
   폰 폭: index.html 자체 콘텐츠를 전부 숨기고, deviceTier.js가 src를 채운
   #phoneShellFrame(mobile/ 앱 iframe)만 풀스크린으로 보여준다.
   패드 폭: 대시보드 씬의 4개 패널만 남기고 나머지(헤더/히어로/다른 씬)는
   전부 숨긴다 - PC와 같은 다크 유리 카드를 그대로 재사용(이 규칙이 파일
   뒤쪽 unlayered라서, 앞쪽 @layer의 밝은 테마 축소판 규칙을 이긴다).
   PC 폭: 기존 그대로.

   2026-08-27: 실사용 교사 제보(학교 PC에서 모바일 화면이 풀스크린으로
   뜸) 원인 수정 - 이 블록이 예전엔 여기서도 @media (max-width: 47.99rem)
   로 독자적으로 폭을 rem 단위로 쟀는데, deviceTier.js는 px(767)로 잰다.
   브라우저 기본 글꼴크기가 16px가 아닌 환경(접근성 설정 등)에서는 두
   판정이 어긋나서, JS는 "PC"라고 판단해도 이 CSS는 이미 폰으로 판정해
   모바일 iframe을 풀스크린으로 강제 표시했다. 이제 폭 판정은
   deviceTier.js 한 곳에서만 하고, 그 결과를 <html data-tier="phone">
   으로 남긴다 - 이 블록은 더 이상 폭을 직접 재지 않고 그 속성만 그대로
   따른다. */
.phone-shell-frame { display: none; }

html[data-tier="phone"] .site-header,
html[data-tier="phone"] .mobile-quick-nav,
html[data-tier="phone"] .snap-root { display: none !important; }
/* edu2gBetaUi.js가 만드는 <dialog>는 네이티브 top-layer라 iframe의
   z-index로는 절대 못 가린다 - 폰 폭에서는 이 다이얼로그 자체를 숨긴다
   (이 별도 베타게이트 시스템 자체는 건드리지 않음, 화면에서만 뺌). */
html[data-tier="phone"] .edu2g-beta-dialog { display: none !important; }
html[data-tier="phone"] .phone-shell-frame {
  display: block;
  position: fixed;
  inset: 0;
  inline-size: 100vw;
  block-size: 100vh;
  border: 0;
  z-index: 9999;
}

/* 태블릿 대시보드 전용 뷰: 48~64rem 구간은 폭만으로(브라우저 창을 늘렸다
   줄였다 해도 즉시 전환), 64~87.4rem 구간은 "실제 터치스크린"일 때만
   적용한다 - iPad Air/Pro 가로모드(1180~1366px)는 64rem을 넘지만 여전히
   패드이므로 커버해야 하는데, 그 폭은 노트북/PC 창 폭과 겹쳐서 폭만으로는
   구분이 안 된다. (hover:none) and (pointer:coarse)로 실제 터치 기기만
   가려낸다 - 마우스로 그 폭까지 줄인 PC 창은 그대로 PC 화면을 유지한다. */
@media (min-width: 48rem) and (max-width: 63.99rem),
       (min-width: 64rem) and (max-width: 87.4rem) and (hover: none) and (pointer: coarse) {
  .site-header, .mobile-quick-nav, .phone-shell-frame { display: none !important; }
  /* 이 <dialog>는 네이티브 top-layer라 z-index로 못 가린다(폰 폭 규칙과 동일 이유). */
  .edu2g-beta-dialog { display: none !important; }
  body { background: #050a1c; color: #f6fbff; padding-block-end: 0 !important; }
  .snap-root { display: block; }
  .scene { display: none !important; }
  .dashboard-scene {
    display: block !important;
    min-block-size: 100svh;
    padding: 1rem;
    background: radial-gradient(circle at 72% 16%, rgb(86 141 255 / 38%), transparent 42vw), radial-gradient(circle at 14% 78%, rgb(80 255 207 / 28%), transparent 38vw), radial-gradient(circle at 50% 96%, rgb(173 140 255 / 22%), transparent 40vw), linear-gradient(135deg, #050a1c 0%, #0c1b38 44%, #070f24 100%);
  }
  /* 오브(.scene-orbs)는 살려둔다 - 이게 빠지니 하루종일 만든 다크 유리
     분위기가 하나도 없이 텅 빈 검은 배경 위에 흰 박스만 뜬 꼴이 됐었다. */
  .dashboard-scene > *:not(.dashboard-grid):not(.scene-orbs) { display: none !important; }
  .dashboard-grid {
    display: grid !important;
    position: relative;
    z-index: 1;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    block-size: calc(100svh - 2rem);
  }
  .dashboard-grid > .panel {
    display: grid !important;
    align-content: start;
    gap: .9rem;
    min-block-size: 0;
    padding: 1.1rem;
    overflow-y: auto;
    border-style: none !important;
    background: rgb(10 22 42 / 42%) !important;
    backdrop-filter: blur(24px) saturate(180%) !important;
    -webkit-backdrop-filter: blur(24px) saturate(180%) !important;
    box-shadow: 0 28px 72px rgb(0 0 0 / 32%), inset 0 0 0 1px rgb(255 255 255 / 14%) !important;
  }
  /* landfill-panel/upload-panel은 PC에서 grid가 아니라 flex-column이다
     (1440px 실측: getComputedStyle().display). 위의 일괄 grid 규칙 다음에
     이 예외를 둬서 두 패널만 PC와 같은 flex로 되돌린다. */
  .dashboard-grid > .landfill-panel,
  .dashboard-grid > .upload-panel {
    display: flex !important;
    flex-direction: column !important;
  }
  /* school-panel/class-panel은 PC용 "좁은 라벨열+넓은 내용열" grid-template이
     64rem 이상 블록에만 있어서, 이 폭엔 그 템플릿 없이 옛 "아주 좁은 화면"
     규칙의 grid-template-columns만 남아 패널헤드가 폭 12px짜리 첫 칸에
     끼여 글자가 세로로 한 자씩 쪼개지는 버그가 났다(실측: getBoundingClientRect
     width 12px, height 204px). 태블릿에서는 아예 한 칸짜리 세로 스택으로
     고정해 이 종류의 버그 자체를 차단한다. */
  /* 2026-08-29 대표님 지적 - "안 겹치면 끝"이 아니다. 위 2026-08-28 수정은
     겹침만 없애려고 school-panel/class-panel을 통째로 1칸짜리 세로 스택으로
     눌러놔서, PC의 "좁은 숫자열 + 넓은 시각화열"(라인 562 "kpi visual")
     밀도가 사라지고 큰 숫자만 뚝뚝 떨어져 쌓인 느낌이 났다. PC와 같은
     2열 골격을 이 태블릿 폭에 맞게 다시 만든다 - 단, PC 전용 영역 이름
     (head/select/kpi/visual, 라인 78/562/697)을 그대로 재사용하면 2026-08-28에
     겪은 이름-충돌 버그가 재발하므로, 이 블록 안에서만 쓰는 새 이름
     (t-head/t-kpi/t-visual/t-note)을 쓴다. */
  .school-panel, .class-panel { position: relative; }
  .school-panel {
    grid-template-columns: minmax(7.25rem, 8.25rem) minmax(0, 1fr) !important;
    grid-template-areas: "t-head t-head" "t-kpi t-visual" !important;
  }
  .class-panel {
    grid-template-columns: minmax(5.25rem, 6.25rem) minmax(0, 1fr) !important;
    grid-template-areas: "t-head t-head" "t-kpi t-visual" "t-note t-note" !important;
  }
  .school-panel .panel-head, .class-panel .panel-head {
    grid-area: t-head !important;
    padding-inline-end: 5.4rem !important;
  }
  .school-panel .kpi-row { grid-area: t-kpi !important; grid-template-columns: 1fr !important; align-content: start; }
  .class-panel .class-kpis { grid-area: t-kpi !important; grid-template-columns: 1fr !important; align-content: start; }
  .school-panel .school-visuals { grid-area: t-visual !important; }
  .class-panel .confusion { grid-area: t-visual !important; }
  .class-panel .rank-note { grid-area: t-note !important; }
  /* select-line을 kpi/head와 같은 t-head 칸에 그냥 그리드 배치하면 같은
     칸을 공유하는 두 형제(panel-head, select-line)가 서로 겹쳐 그려진다
     (2026-08-28에 겪은 것과 같은 종류의 겹침) - PC(라인 553)처럼 절대
     위치로 빼서 패널 우상단에 얹는다. */
  .school-panel .select-line, .class-panel .select-line {
    position: absolute !important;
    inset-block-start: .1rem;
    inset-inline-end: 0;
    grid-area: auto !important;
    z-index: 2;
  }
  /* 아래는 전부 1440px PC 화면에서 getComputedStyle로 실측한 값을 그대로
     옮긴 것이다(추측 금지 - "아주 좁은 화면" 구간용 옛 규칙이 이 태블릿
     폭에도 걸쳐 패널 안쪽 콘텐츠를 block/none으로 축소해 놓았던 걸
     되돌리는 작업). */
  .dashboard-grid > .panel .panel-head p,
  .dashboard-grid > .panel .select-line,
  .class-panel .rank-note,
  .upload-panel .portal-teaser,
  .upload-panel .portal-arrow,
  .upload-panel > small {
    display: block !important;
  }
  .school-panel .school-visuals,
  .landfill-panel .chart-wrap,
  .class-panel .confusion,
  .dashboard-grid > .panel .kpi-row,
  .dashboard-grid > .panel .class-kpis,
  .dashboard-grid > .panel .landfill-metrics,
  .dashboard-grid > .panel .kpi-row > div,
  .dashboard-grid > .panel .class-kpis > div,
  .dashboard-grid > .panel .landfill-metrics > div,
  .upload-panel .experience-portal,
  .upload-panel .portal-frame {
    display: grid !important;
    margin: revert !important;
  }
  /* 아래부터는 63.99rem 이하 규칙이 손도 안 댄 부분이 아니라, PC 전용
     장식 스타일(테두리·그라데이션·아이콘 크기)이 64rem 이상 블록에만
     있어서 이 태블릿 폭엔 아예 안 걸려 있던 것 - PC 원본 선언을 그대로
     옮긴다(라인 227-251, 740-742 실측 대조). */
  .landfill-panel .landfill-actions {
    display: flex !important;
    gap: 8px;
    align-items: center;
  }
  /* PC는 총량 대비 반입량이 도넛 두 개로 이미 표현되니 막대 두 줄짜리
     progress-stack을 중복으로 보고 뺀다(라인 671, PC 전용 블록이라 이
     폭엔 안 걸려 있었음) - 768px처럼 가장 좁은 태블릿 폭에서 매립지
     패널이 세로로 넘치는 걸 여기서 줄인다. */
  .landfill-panel .progress-stack { display: none !important; }
  .upload-panel .portal-powered {
    display: flex !important;
    justify-self: center;
    align-items: center;
    gap: .5rem;
    font-size: clamp(.98rem, .8vw, 1.22rem);
    font-weight: 850;
    letter-spacing: .02em;
    background: linear-gradient(90deg, #64eaff, #5ef7cd);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .upload-panel .portal-gemini-star {
    inline-size: 1.25em;
    block-size: 1.25em;
    flex: 0 0 auto;
    filter: drop-shadow(0 0 6px rgb(66 133 244 / 35%));
  }
  .upload-panel .portal-frame {
    place-items: center;
    min-block-size: 7.2rem;
    align-content: center;
    row-gap: .3rem;
    padding: .7rem 1.1rem 1.15rem;
    border: 2px dashed rgb(131 224 255 / 36%) !important;
    border-radius: 1.15rem;
    background: rgb(100 234 255 / 4%) !important;
    animation: finale-hint-float 3.4s ease-in-out infinite;
  }
  .upload-panel .portal-teaser {
    margin: 0;
    text-align: center;
    color: #f6fbff;
    font-size: clamp(.98rem, .85vw, 1.28rem);
    font-weight: 850;
    line-height: 1.55;
    letter-spacing: -.01em;
  }
  .upload-panel .portal-teaser strong { color: #5ef7cd; }
  .upload-panel .portal-arrow {
    inline-size: 1.7rem;
    block-size: 1.55rem;
    margin-block-start: .05rem;
    background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 44'><defs><linearGradient id='p' x1='0' x2='0' y1='0' y2='1'><stop offset='0' stop-color='%2364eaff'/><stop offset='1' stop-color='%235ef7cd'/></linearGradient></defs><path d='M7 5l17 13L41 5' fill='none' stroke='url(%23p)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round' opacity='.38'/><path d='M7 21l17 13L41 21' fill='none' stroke='url(%23p)' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat;
    filter: drop-shadow(0 0 9px rgb(94 247 205 / 5));
    animation: finale-arrow-bob 1.7s cubic-bezier(.45, 0, .25, 1) infinite;
  }
  .upload-panel .experience-portal { grid-template-rows: auto 1fr !important; gap: .4rem; margin-block-end: .55rem; }
  .upload-panel .upload-highlights {
    display: grid !important;
    gap: .28rem;
    margin: 0;
    padding: .55rem .8rem;
    border: 1px solid rgb(100 220 235 / 18%);
    border-radius: 1rem;
    background: rgb(4 24 42 / 55%);
  }
  .upload-panel .upload-highlights li { display: flex !important; gap: .5rem; align-items: flex-start; color: #c9dbe6; font-size: .76rem; line-height: 1.35; }
  .upload-panel .upload-highlights li::before { content: "✓"; flex: 0 0 auto; color: #65ead6; font-weight: 900; }
  .upload-panel > small { color: #bcd3e0; font-size: clamp(.85rem, .72vw, 1.05rem); line-height: 1.5; margin-block-start: .3rem; }
  /* PC는 upload-panel에서 카메라/QR 촬영 버튼 대신 "체험 포탈" 안내를
     보여준다(실측: .upload-card-actions/.gemini-credit/.qr-invite는 PC에서
     전부 display:none). 태블릿도 PC와 동일하게 맞춘다. */
  .upload-panel > p,
  .upload-panel .upload-card-actions,
  .upload-panel .gemini-credit,
  .upload-panel .qr-invite {
    display: none !important;
  }
  /* PC는 "화면을 끝까지 스크롤했다가 대시보드로 돌아오면" 큐알이
     열리는데(body.is-rewound, min-width:64rem 전용 블록), 이 패드
     대시보드 전용 뷰는 위에서 다른 모든 섹션(.scene)을 꺼버려서 스크롤
     할 대상 자체가 없다 - 그 제스처가 성립하지 않는데 학생더러 하라고
     할 수 없다(사용자 지적). 조건 없이 큐알을 바로 보여준다. 바로 위
     규칙(.upload-panel .qr-invite)보다 뒤에 있어야 이긴다(둘 다
     명시도가 같아서 나중에 나온 규칙이 이김). */
  .dashboard-scene .qr-invite { display: flex !important; }
  .dashboard-scene .portal-teaser, .dashboard-scene .portal-arrow { display: none !important; }
  /* 옛 밝은 테마가 박아둔 어두운 글자색(#202124, #0b57d0 등)이 다크 배경
     위에서 그대로 남아있으면 안 보이거나 붕 떠 보인다 - PC 실측 색상으로
     되돌린다(큰 숫자는 흰색, panel-head 부제만 민트 - 1440px 확인). */
  .dashboard-grid > .panel .panel-head h2 { color: #f6fbff !important; }
  .dashboard-grid > .panel .panel-head p { color: #5ef7cd !important; }
  .school-panel .kpi-row strong,
  .landfill-panel .landfill-metrics strong,
  .class-panel .class-kpis strong {
    color: #f6fbff !important;
  }
  .dashboard-grid > .panel .kpi-row span,
  .dashboard-grid > .panel .class-kpis span,
  .dashboard-grid > .panel .landfill-metrics span {
    color: rgb(158 179 199) !important;
  }
  /* 2026-08-29 - kpi-row/class-kpis가 이제 전체 폭이 아니라 t-kpi 칸(약
     7.25-8.25rem/5.25-6.25rem 너비)에만 있으므로, 옛 max-width:63.99rem
     블록의 clamp(2rem,10vw,2.8rem)(전체 폭 기준으로 잡은 값이라 이 좁은
     칸에서는 자간이 빡빡하다)를 이 칸 너비에 맞게 다시 잡는다. */
  .school-panel .kpi-row strong, .class-panel .class-kpis strong {
    font-size: 1.85rem !important;
  }
  .school-panel .kpi-row > div, .class-panel .class-kpis > div {
    padding: .55rem .6rem !important;
  }
  /* 2026-08-28 실사용 제보(대표님, 768x1024 패드 세로) - "16,400t
     -9.4% 68.3% 32.7%" 4개 큰 숫자가 서로 겹쳐 보였다. 실측: 이 폭에서
     .landfill-metrics가 4열 그리드로 남아있어 칸 하나가 66px인데,
     그 안의 숫자(<strong>)가 32px 폰트로 렌더링돼 실제로는 119px가
     필요했다(그리드 트랙 폭을 53px 초과해서 옆 칸을 덮어 겹쳐 보인
     것). 폰 폭(114행)이 이미 쓰고 있는 "2열로 줄이기" 해법을 여기도
     그대로 적용하고, 늘어난 칸 폭(약 140px)에 맞춰 폰트 크기를 다시
     잡는다. */
  .landfill-panel .landfill-metrics {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    row-gap: .5rem;
  }
  .landfill-panel .landfill-metrics strong {
    font-size: 1.35rem !important;
    white-space: nowrap;
  }
  /* 같은 실사용 제보 - 학년별 표("3학년 214" 등, .bar-list)가 도넛
     차트와 살짝 겹쳐 보였다. 실측: .bar-list>div의 라벨+숫자 칸
     템플릿(5.75rem+2.625rem=134px)이 .school-visuals가 이 폭에서
     .bar-list에 배정한 칸(120px)보다 넓어서 숫자가 옆 도넛 칸으로
     흘러나왔다. .bar-list 칸을 그 최소필요폭보다 넉넉하게 늘린다. */
  /* 2026-08-29 - school-visuals가 이제 패널 전체 폭이 아니라 t-visual 칸
     (숫자 rail 옆의 좁은 나머지 폭)에만 있어서, 막대목록+도넛을 가로로
     나누던 이전 2열 분할은 너무 좁아져 도넛이 짜부라진다. 세로로
     쌓는다(막대목록 위, 도넛 아래 가운데 정렬). */
  .school-panel .school-visuals {
    grid-template-columns: minmax(0, 1fr) !important;
    row-gap: .6rem;
  }
  .school-panel .donut-pair { justify-content: center !important; }
  .upload-panel::after { content: none !important; }
}

/* 학교 설정 모달 - 사용자가 직접 제시한 참고 이미지대로, 이 앱의 나머지
   화면과 다르게 밝은 카드 스타일로 의도적으로 만든다(전체 다크 테마의
   예외 - 사용자가 이 모달만 콕 집어 이 디자인을 요청함). 공용 다크
   토큰을 이어받지 않고 이 모달 전용 밝은 팔레트를 직접 정의한다.
   이 파일(cb3a.css)은 @layer 밖의 일반 규칙이라, style.css의
   @layer components 안에 있던 .ai-modal 배경(#071326, layer 규칙)보다
   항상 이긴다 - CSS 스펙상 레이어 밖 규칙이 레이어 안 규칙을 명시도와
   무관하게 항상 이기기 때문에, 처음엔 style.css의 @layer 안에 이
   오버라이드를 넣었다가 안 먹혀서(다크 배경 그대로 남음) 여기로
   옮겼다. 크기/닫기버튼 위치도 #aiModal과 다르게 써야 해서(가로로만
   쓸데없이 길다, X는 우상단이 보편적) .ai-modal 공통 크기를 여기서만
   좁게 덮어쓴다. 검색 결과 목록은 position:absolute로 입력창 바로
   아래 떠야 하는데, .modal-body의 overflow:auto뿐 아니라 <dialog>
   자체의 브라우저 기본 overflow:auto까지 이중으로 잘라내서 아래쪽이
   잘려 보였다 - 이 모달은 내용이 짧고 고정이라 스크롤이 필요 없으므로
   둘 다 풀어준다. */
#dashboardSchoolModal {
  --ssm-ink: #12181f; --ssm-muted: #5b6572; --ssm-line: #e7eaee; --ssm-bg: #ffffff;
  --ssm-accent: #2b6cf6; --ssm-accent-soft: #eaf1ff;
  inline-size: min(100% - 2rem, 27rem) !important; overflow: visible !important; position: relative;
  background: var(--ssm-bg) !important; color: var(--ssm-ink); border: none !important;
  border-radius: 1.4rem !important;
  box-shadow: 0 30px 70px rgb(10 20 35 / 30%), 0 2px 8px rgb(10 20 35 / 10%) !important;
}
#dashboardSchoolModal::backdrop { background: rgb(6 12 22 / 66%); }
#dashboardSchoolModal .modal-body { overflow: visible; padding: 2.3rem 2rem 2rem; min-block-size: 20rem; text-align: center; }
#dashboardSchoolModal .close-btn {
  position: absolute; inset-block-start: 1.1rem; inset-inline-end: 1.1rem; z-index: 2;
  background: none; border: none; color: var(--ssm-muted); font-size: 1.3rem; opacity: .7;
}
#dashboardSchoolModal .close-btn:hover { opacity: 1; color: var(--ssm-ink); }
/* 위 두 <p>였던 "Find Your School" 영어 배지 + 안내문 대신, 실제 그린
   아이콘 하나로 이 단계가 뭘 하는 곳인지 보여준다 - 이모지나 흔한
   "eyebrow" 라벨 대신 이 화면 전용 SVG. */
.school-setup-icon { display: grid; place-items: center; inline-size: 3.6rem; block-size: 3.6rem; margin: 0 auto .9rem; border-radius: 50%; background: var(--ssm-accent-soft); color: var(--ssm-accent); }
.school-setup-icon svg { inline-size: 1.7rem; block-size: 1.7rem; }
#dashboardSchoolModal h2 { color: var(--ssm-ink); margin-block: 10px 1.4rem; font-size: 1.5rem; line-height: 1.32; font-weight: 800; }
.school-search-row { position: relative; inline-size: 100%; max-inline-size: 18.5rem; margin: calc(1.3rem + 20px) auto 0; }
.school-search-row input {
  inline-size: 100%; padding: .75rem 1rem .75rem 2.5rem; font-size: .92rem; text-align: start;
  background: #f4f6f9; color: var(--ssm-ink); border: 1.5px solid transparent; border-radius: 999px;
  transition: border-color .15s ease, background .15s ease;
}
.school-search-row input::placeholder { color: color-mix(in srgb, var(--ssm-muted) 70%, transparent); }
.school-search-row input:focus { outline: none; background: var(--ssm-bg); border-color: var(--ssm-accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ssm-accent) 14%, transparent); }
#dashboardSchoolSearchBtn { position: absolute; inset-block-start: 50%; inset-inline-start: .9rem; translate: 0 -50%; inline-size: 1.05rem; block-size: 1.05rem; padding: 0; display: grid; place-items: center; background: none; border: none; color: var(--ssm-muted); }
#dashboardSchoolSearchBtn svg { inline-size: 100%; block-size: 100%; fill: none; stroke: currentColor; stroke-width: 2; }
#dashboardSchoolResults:not(:empty) { display: block; position: absolute; left: -1.5rem; right: -1.5rem; z-index: 20; margin-block-start: .5rem; max-block-size: 14rem; overflow-y: auto; background: var(--ssm-bg); color: var(--ssm-ink); border: 1px solid var(--ssm-line); border-radius: .8rem; box-shadow: 0 16px 40px rgb(10 20 35 / 16%); text-align: start; }
#dashboardSchoolResults:empty { display: none; }
#dashboardSchoolResults li { border-color: var(--ssm-line) !important; padding: 0 !important; }
#dashboardSchoolResults li:hover { background: var(--ssm-accent-soft); }
#dashboardSchoolResults li + li { border-block-start: 1px solid var(--ssm-line); border-radius: 0 !important; }
#dashboardSchoolStatus { margin-block-start: 1rem; color: var(--ssm-muted); font-size: .82rem; }

/* 학교 미설정 상태에서 화면 속 숫자가 진짜 데이터처럼 보이던 문제
   (사용자 지적) - 계속 떠있는 배지로 "샘플일 뿐"임을 알리고, 눌러서
   언제든 다시 학교 검색 모달을 연다. style.css의 raw-color 개수 제한
   테스트(design system 가드)를 건드리지 않도록 이 파일(레이어 밖)에 둔다. */
.sample-data-badge { position: fixed; inset-block-start: 50%; inset-inline-start: 50%; translate: -50% -50%; z-index: 50; padding: .65rem 1rem; border-radius: 999px; border: 1px solid rgb(255 200 100 / 45%); background: rgb(20 15 5 / 88%); color: #ffd27a; font-size: .8rem; font-weight: 700; box-shadow: 0 10px 30px rgb(0 0 0 / 35%); cursor: pointer; backdrop-filter: blur(6px); }
.sample-data-badge:hover { border-color: rgb(255 200 100 / 80%); }

/* 반 선택 2단계 화면 (학교 고른 뒤) - 학년은 학교별로 이미 정해져 있고
   (SCHOOL_CLASS_CONFIG), 반 번호만 숫자 버튼 그리드로 직접 고르게 한다.
   특정 선생님 반을 기본값으로 미리 선택해두지 않는다(교사 지적: 1반
   선생님도 2반 선생님도 각자 다르게 직접 설정해야 함). */
.class-picker-selects { display: flex; justify-content: center; gap: .6rem; margin-block-start: 1.3rem; }
.class-picker-selects select { min-inline-size: 7rem; padding: .65rem .9rem; border-radius: .7rem; border: 1.5px solid var(--ssm-line); background: var(--ssm-bg); color: var(--ssm-ink); font-weight: 700; font-size: .92rem; text-align: center; cursor: pointer; }
.class-picker-selects select:focus { outline: none; border-color: var(--ssm-accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ssm-accent) 14%, transparent); }
.class-picker-confirm { display: block; inline-size: 100%; max-inline-size: 18.5rem; margin: 1.1rem auto 0; padding: .8rem; border-radius: 999px; border: none; background: var(--ssm-accent); color: #fff; font-weight: 800; font-size: .95rem; cursor: pointer; }
.class-picker-confirm:hover { background: color-mix(in srgb, var(--ssm-accent) 88%, black); }
/* 2026-09-01 UX 재감사 대응 - window.prompt() 대신 쓰는 텍스트 입력(교사
   인증코드 등). .class-picker-selects select와 같은 디자인 언어를 쓰되
   input용으로 폭을 넓힌다. */
.class-picker-text-input { display: block; inline-size: 100%; max-inline-size: 18.5rem; margin: 1.3rem auto 0; padding: .65rem .9rem; border-radius: .7rem; border: 1.5px solid var(--ssm-line); background: var(--ssm-bg); color: var(--ssm-ink); font-weight: 700; font-size: .92rem; text-align: center; }
.class-picker-text-input:focus { outline: none; border-color: var(--ssm-accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ssm-accent) 14%, transparent); }
/* 반 선택 단계에서 학교 검색 단계로 돌아가는 뒤로가기 - 닫기(X)와
   같은 우상단 클러스터에 나란히 둔다(사용자 지적: 뒤로가기가 우상단에
   있어야 함). 학교 검색 단계에서는 갈 곳이 없으니 숨긴다. */
.modal-back-btn { position: absolute; inset-block-start: 1.1rem; inset-inline-start: 1.1rem; z-index: 2; display: grid; place-items: center; inline-size: 2rem; block-size: 2rem; padding: 0; border-radius: 50%; background: none; border: none; color: var(--ssm-muted); opacity: .7; cursor: pointer; }
.modal-back-btn svg { inline-size: 1.3rem; block-size: 1.3rem; }
.modal-back-btn:hover { opacity: 1; color: var(--ssm-ink); background: var(--ssm-accent-soft); }
.modal-back-btn[hidden] { display: none; }

/* 헤더 톱니바퀴 설정 메뉴 - main-nav의 형제로 다시 빼서(그 안에 있으면
   4구역 미만 폭에서 main-nav 자체가 드롭다운으로 position이 바뀌어
   버튼 위치가 같이 틀어짐), .site-header(position:sticky)를 기준으로
   position:absolute로 띄운다. 자료실은 이제 이 톱니와 무관하게 다시
   nav의 실제 마지막 항목이 되어 본문(.scene) 우측 끝과 정렬된다.
   톱니는 헤더의 우측 여백(최대 4rem=64px, --scene-inline-space의 clamp
   상한) 안에서 최대한 오른쪽 - 뷰포트 진짜 끝에 거의 붙는 자리 - 에
   놓았다. 100px만큼 더 밀면 헤더 폭(뷰포트 100%) 밖으로 나가 화면에
   안 보이게 되므로, 물리적으로 가능한 가장 오른쪽 지점으로 잡았다. */
/* position:fixed로 뷰포트 기준 배치 - .site-header 밖으로 옮긴 뒤에도
   (요소 자체는 더 이상 헤더의 자식이 아니라) 화면상 정확히 같은
   자리(헤더 높이의 절반, 사용자 지적대로 위로 2px/왼쪽으로 30px 보정)에
   고정된다. --header-size가 브레이크포인트마다 바뀌므로 그 변수를
   그대로 참조해 PC 쪽 위치가 조금도 달라지지 않게 한다. */
.dashboard-settings { position: fixed; z-index: 25; inset-block-start: calc(var(--header-size) / 2 - 2px); inset-inline-end: 30px; translate: 0 -50%; }
.dashboard-settings-toggle { display: grid; place-items: center; inline-size: 2.3rem; block-size: 2.3rem; padding: 0; border: none; background: none; color: #cfe4ea; cursor: pointer; }
.dashboard-settings-toggle:hover { color: #f2fdff; }
.dashboard-settings-toggle svg { inline-size: 1.3rem; block-size: 1.3rem; }
.dashboard-settings-menu { position: absolute; inset-block-start: calc(100% + .5rem); inset-inline-end: 0; z-index: 60; min-inline-size: 13rem; padding: .5rem; border-radius: .8rem; background: #0c1a2c; border: 1px solid rgb(100 234 255 / 22%); box-shadow: 0 20px 50px rgb(0 0 0 / 40%); display: grid; gap: .2rem; }
.dashboard-settings-menu[hidden] { display: none; }
.dashboard-settings-menu button { display: block; width: 100%; text-align: left; padding: .6rem .7rem; border-radius: .5rem; border: none; background: none; color: #eaf3f8; font-size: .85rem; cursor: pointer; }
.dashboard-settings-menu button:hover { background: rgb(100 234 255 / 12%); }

/* 서비스 준비중 등, 조용히 지나치면 안 되는 안내를 눈에 띄는 팝업
   이벤트로 보여준다(문단 텍스트로만 두지 말라는 요청). */
.dashboard-toast-host { position: fixed; inset-block-start: 50%; inset-inline-start: 50%; translate: -50% -50%; z-index: 200; display: grid; gap: .5rem; pointer-events: none; }
.dashboard-toast { pointer-events: auto; padding: .8rem 1.2rem; border-radius: .8rem; background: #1a2436; color: #f2fdff; border: 1px solid rgb(100 234 255 / 30%); box-shadow: 0 16px 40px rgb(0 0 0 / 35%); font-size: .88rem; font-weight: 700; animation: toastPop .25s ease both; }
@keyframes toastPop { from { opacity: 0; translate: 0 -.6rem; } to { opacity: 1; translate: 0 0; } }
