/* ==========================================================================
   LAPISAN TAMBAHAN
   Dimuat setelah styles.css. Berisi gaya yang ditambahkan sesudah berkas
   utama tayang: baris jejak dengan tombol kembali, dan beberapa perbaikan
   kecil. Dipisah agar berkas utama tidak perlu ikut dikirim ulang setiap
   kali ada tambahan sekecil ini.
   ========================================================================== */

/* --------------------------------------------------------------------------
   BARIS JEJAK: tombol kembali + remah jejak
   ----------------------------------------------------------------------- */
.crumb-bar {
  display: flex; align-items: center; gap: .55rem;
  padding: .85rem 1.75rem .1rem;
}

/* Jarak tepi kini dipegang .crumb-bar, jadi remah jejaknya sendiri rata. */
.crumbs { padding: 0; min-width: 0; }

/* Tombol kembali — selalu menyebut nama halaman tujuannya, sehingga
   pengguna tahu ke mana ia akan dibawa sebelum menekannya. Lebarnya tidak
   berubah saat disorot: remah jejak di sebelahnya tidak ikut bergeser. */
.back-btn {
  position: relative; overflow: hidden; flex: none;
  display: inline-flex; align-items: center; gap: .3rem;
  height: 34px; max-width: 220px; padding: 0 .72rem 0 .5rem;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  background: var(--paper); color: var(--green-800);
  font: inherit; font-size: var(--t-xs); font-weight: 600; cursor: pointer;
  box-shadow: var(--sh-1);
  transition: max-width .28s var(--ease), background .16s var(--ease),
              border-color .16s var(--ease), box-shadow .2s var(--ease),
              transform .16s var(--ease), opacity .22s var(--ease),
              padding .28s var(--ease);
}
.back-btn .arw {
  width: 20px; height: 20px; flex: none;
  display: grid; place-content: center;
  transition: transform .24s var(--ease);
}
.back-btn .arw svg { width: 18px; height: 18px; display: block; }
.back-btn .txt {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.back-btn:hover, .back-btn:focus-visible {
  background: var(--green-100); border-color: var(--green-300);
  box-shadow: var(--sh-2);
}
.back-btn:hover .arw { transform: translateX(-3px); }
.back-btn:active { transform: scale(.95); }
.back-btn:focus-visible { outline: 2px solid var(--green-300); outline-offset: 2px; }
.back-btn::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(circle at 18% 50%, rgba(255,255,255,.8), transparent 60%);
  opacity: 0;
}
.back-btn.tap::after { animation: back-riak .5s var(--ease); }
.back-btn.tap .arw { animation: back-lesat .42s var(--ease); }
.back-btn.hide {
  max-width: 0; padding: 0; margin: 0; border-width: 0;
  opacity: 0; pointer-events: none; box-shadow: none;
}
@keyframes back-riak { from { opacity: 1 } to { opacity: 0 } }
@keyframes back-lesat {
  40% { transform: translateX(-7px); opacity: .35 }
  41% { transform: translateX(7px); opacity: 0 }
  100% { transform: translateX(0); opacity: 1 }
}
@media (prefers-reduced-motion: reduce) {
  .back-btn, .back-btn .arw { transition: none; }
  .back-btn.tap .arw { animation: none; }
}

/* Pesan panjang pada kotak pemberitahuan boleh berganti baris sendiri. */
.alert-box p { white-space: pre-line; }

@media (max-width: 720px) {
  .crumb-bar { padding-left: 1rem; padding-right: 1rem; }
}

@media (max-width: 560px) {
  .crumb-bar { padding: .6rem .9rem .1rem; gap: .45rem; align-items: flex-start; }
  /* Layar sempit: jejaknya digeser ke samping, tidak dilipat ke bawah. */
  .crumbs { flex-wrap: nowrap; padding: 0 0 .5rem; }
  /* Nama tujuan dipangkas — panahnya yang menjadi penanda utama. */
  .back-btn { max-width: 132px; font-size: var(--t-2xs); height: 38px; }
}

@media (hover: none) {
  .back-btn { height: 38px; }
}

@media print {
  .crumb-bar { display: none !important; }
}

/* ==========================================================================
   RIWAYAT TAHUN AJARAN (layar siswa / orang tua)
   ========================================================================== */
.riwayat { display: flex; flex-direction: column; gap: 1.6rem; }
.riwayat-tahun { display: flex; flex-direction: column; gap: .6rem; }
.riwayat-head {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  font-size: var(--t-xs);
}
.riwayat-head .thn { font-weight: 800; color: var(--ink-900); letter-spacing: .01em; }
.riwayat-head .tanda {
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  padding: .12rem .6rem; font-size: var(--t-2xs); font-weight: 700;
  color: var(--ink-600); background: var(--paper);
}
.riwayat-head .tanda.kini {
  background: var(--green-100); border-color: var(--green-300); color: var(--green-800);
}
.riwayat .tiles { margin: 0 auto; }

/* Progres nilai lintas tahun */
.progres-tahun { max-width: 880px; margin: 2.4rem auto 0; width: 100%; }
.progres-tahun h3 { margin: 0; font-size: var(--t-lg); text-align: center; }
.progres-tahun p.small { text-align: center; margin: .3rem 0 0; }
.progres-tahun .score-bars { margin-top: 1rem; }
.progres-tahun .score-bar { grid-template-columns: 178px 1fr; }
.progres-tahun .score-bar .tag { font-size: var(--t-xs); line-height: 1.25; }
.score-bar .out .delta { margin-left: .5rem; font-weight: 800; }
.score-bar .out .delta.naik { color: var(--green-700); }
.score-bar .out .delta.turun { color: var(--danger); }
@media (max-width: 560px) {
  .progres-tahun .score-bar { grid-template-columns: 104px 1fr; }
  .progres-tahun .score-bar .tag { font-size: var(--t-2xs); }
}

/* Keterangan tuntas / belum tuntas pada tabel rekap */
.status-tag {
  display: inline-block; border-radius: var(--r-pill); padding: .12rem .62rem;
  font-size: var(--t-2xs); font-weight: 700; border: 1px solid transparent;
}
.status-tag.ok { background: var(--green-100); color: var(--green-800); border-color: var(--green-300); }
.status-tag.no { background: #fdecea; color: #c0392b; border-color: #f3c9c3; }
.status-tag.netral { background: var(--green-050); color: var(--ink-600); border-color: var(--line); }

/* ==========================================================================
   JAM SERVER (kepala halaman, di samping lonceng)
   ========================================================================== */
.jam-menu { position: relative; flex: none; }

/* Pilnya kini sebuah TOMBOL. Rupanya sengaja tidak banyak berubah — yang
   berubah adalah isinya (detik pindah ke kartu) dan bahwa ia dapat ditekan.
   Sifat tombol bawaan peramban dilepas satu per satu supaya ia tetap terbaca
   sebagai keterangan, bukan sebagai tombol tindakan yang menunggu ditekan. */
.clock {
  display: flex; align-items: center; gap: .55rem; flex: none;
  padding: .32rem .8rem .32rem .64rem;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  background: linear-gradient(180deg, #fff, var(--green-050));
  box-shadow: var(--sh-1);
  color: var(--green-800); line-height: 1.12;
  font: inherit; text-align: left; cursor: pointer;
  user-select: none;
  transition: background .16s var(--ease), border-color .16s var(--ease);
}
.clock:hover { border-color: var(--green-300); background: var(--green-050); }
.clock:focus-visible { outline: 2px solid var(--green-600); outline-offset: 2px; }
.clock[aria-expanded="true"] { border-color: var(--green-300); background: var(--green-050); }

.clock .ic { display: grid; place-content: center; flex: none; color: var(--green-600); }
.clock .ic svg { width: 17px; height: 17px; display: block; }
.clock .teks { display: flex; flex-direction: column; gap: .04rem; }
.clock .hari {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: .02em;
  color: #55645c; white-space: nowrap;
}
.clock .jam {
  font-size: var(--t-lg); font-weight: 800; letter-spacing: .01em;
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
  display: flex; align-items: baseline; line-height: 1.05;
}
/* Titiknya DIAM. Ia dulu berkedip sekali sedetik, selamanya, di sudut mata
   setiap orang yang membuka aplikasi ini. */
.clock .titik { font-style: normal; padding: 0 .05rem; color: var(--green-600); opacity: .7; }

/* Server tidak terjangkau: jamnya tetap jalan, tetapi ditandai. */
.clock.lokal { border-style: dashed; color: var(--ink-600); background: var(--paper); }
.clock.lokal .ic { color: var(--ink-400); }
.clock.lokal .titik { color: var(--ink-400); }

@media (max-width: 900px) {
  .clock .hari { display: none; }
  .clock { padding: .28rem .62rem; }
}
@media (max-width: 560px) {
  .clock .ic { display: none; }
  /* Angka ini diukur, bukan dikira-kira. Pada 360px kepala halaman TIDAK punya
     ruang sisa sama sekali: menaikkan bantalan pil jam dari .5rem ke .55rem
     melebarkannya satu piksel, dan satu piksel itu langsung diambil dari
     tombol lambang sekolah di ujung kiri — 24px menjadi 23px, tepat di bawah
     batas sasaran sentuh. Dirapatkan sedikit dari angka semula supaya ada
     ruang bernapas untuk perubahan berikutnya. */
  .clock { padding: .24rem .46rem; }
  .clock .jam { font-size: var(--t-md); }
}

/* ---- kartu keterangan waktu ----
   Isinya adalah segala yang dulu hanya hidup di dalam atribut title: tanggal
   lengkap, tanggal Hijriah, detik, dan keadaan penyelarasan. Tooltip tidak
   pernah muncul pada layar sentuh, sehingga bagi sebagian besar orang di
   sekolah ini keterangan itu selama ini tidak ada sama sekali. */
/* Digantung dari tepi KIRI pilnya, bukan tepi kanan seperti menu lonceng dan
   menu pengguna. Sebabnya letak: pil jam adalah benda PERTAMA di sisi kanan
   kepala halaman, sehingga di sebelah kanannya selalu masih ada tombol unit,
   cari, lonceng, dan nama pengguna — ruang yang cukup untuk kartunya. Digantung
   dari kanan seperti tetangganya, kartunya justru menjulur ke luar tepi kiri
   layar begitu pilnya menyempit di tablet dan ponsel. */
.jam-drop {
  position: absolute; left: 0; right: auto; top: calc(100% + .55rem); z-index: 46;
  width: min(272px, calc(100vw - 2rem));
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-3);
  padding: .85rem 1rem .9rem;
  animation: pop .16s var(--ease);
  cursor: default; user-select: text;
}
.jam-drop.hide { display: none; }
.jk-tanggal { font-size: var(--t-sm); font-weight: 800; color: var(--green-900); }
.jk-hijriah { margin-top: .12rem; font-size: var(--t-xs); color: var(--ink-600); }
.jk-hijriah.hide { display: none; }
.jk-garis { height: 1px; background: var(--line); margin: .7rem 0; }
/* "WIB" bukan bagian dari angkanya dan tidak pantas sebesar angkanya —
   ia keterangan zona, dibaca sekali lalu tidak dibaca lagi. */
.jk-jam {
  display: flex; align-items: baseline; gap: .4rem;
  font-size: var(--t-2xl); font-weight: 800; color: var(--green-800);
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
  letter-spacing: .01em; line-height: 1.1;
}
.jk-zona {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: .06em;
  color: var(--ink-400);
}
.jk-status {
  margin-top: .5rem; font-size: var(--t-2xs); font-weight: 600;
  border-radius: var(--r-sm); padding: .3rem .5rem;
}
.jk-status.ok { background: var(--green-050); color: var(--green-800); }
.jk-status.lokal { background: var(--gold-100); color: #8a6d00; }
.jk-catatan {
  margin: .55rem 0 0; font-size: var(--t-2xs); line-height: 1.45; color: var(--ink-400);
}
.jk-catatan.hide { display: none; }

/* Layar tersempit: kartunya melepaskan diri dari pilnya dan memakai lebar
   layar, sama seperti daftar lonceng. Angka 58px adalah tinggi kepala halaman
   pada lebar ini — sama dengan yang sudah dipakai .bell-drop.

   Blok ini HARUS berdiri sesudah aturan dasar .jam-drop di atas. Kekhususan
   keduanya sama, jadi yang berlaku adalah yang terbaca belakangan; ditaruh
   sebelum aturan dasarnya, ia tidak berpengaruh sama sekali. */
@media (max-width: 560px) {
  .jam-drop { position: fixed; left: .6rem; right: .6rem; width: auto; top: 58px; }
}

/* ==========================================================================
   PENCARIAN CEPAT
   ========================================================================== */
.cari-button {
  display: inline-flex; align-items: center; gap: .42rem; flex: none;
  height: 34px; padding: 0 .5rem 0 .6rem;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  background: var(--paper); color: var(--ink-600);
  font: inherit; font-size: var(--t-xs); font-weight: 600; cursor: pointer;
  box-shadow: var(--sh-1);
  transition: background .16s var(--ease), border-color .16s var(--ease),
              color .16s var(--ease), box-shadow .2s var(--ease);
}
.cari-button .ic { display: grid; place-content: center; }
.cari-button .ic svg { width: 17px; height: 17px; display: block; }
.cari-button:hover, .cari-button:focus-visible {
  background: var(--green-100); border-color: var(--green-300); color: var(--green-800);
  box-shadow: var(--sh-2);
}
/* Latar gelap + panel */
.cari-latar {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(11, 30, 20, .38); backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: max(6vh, 2rem) 1rem 2rem;
  animation: cari-masuk .16s var(--ease);
}
.cari-latar.pergi { animation: cari-keluar .13s var(--ease) forwards; }
@keyframes cari-masuk { from { opacity: 0 } }
@keyframes cari-keluar { to { opacity: 0 } }

.cari-panel {
  width: min(680px, 100%); max-height: min(72vh, 620px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--paper); border: 1px solid var(--line-2);
  border-radius: var(--r-lg); box-shadow: 0 24px 60px rgba(16, 40, 28, .28);
  animation: cari-naik .2s var(--ease) both;
}
@keyframes cari-naik { from { transform: translateY(-10px) scale(.985); opacity: .6 } }

.cari-atas {
  display: flex; align-items: center; gap: .7rem;
  padding: .85rem 1rem; border-bottom: 1px solid var(--line);
}
.cari-atas .ic { color: var(--green-700); display: grid; place-content: center; }
.cari-atas .ic svg { width: 20px; height: 20px; display: block; }
.cari-input {
  flex: 1; min-width: 0; border: 0; outline: none; background: transparent;
  font: inherit; font-size: var(--t-lg); color: var(--ink-900); padding: .2rem 0;
}
.cari-input::placeholder { color: var(--ink-400); }
.cari-input::-webkit-search-cancel-button { display: none; }

.cari-terakhir {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  padding: .6rem 1rem; border-bottom: 1px solid var(--line);
}
.cari-terakhir .lbl { font-size: var(--t-2xs); font-weight: 700; color: var(--ink-400); margin-right: .2rem; }
.cari-terakhir .chip { padding: .18rem .62rem; font-size: var(--t-2xs); min-height: 0; }

.cari-hasil { overflow-y: auto; padding: .4rem; flex: 1; }
.cari-grup {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-400); padding: .7rem .8rem .25rem;
}
.cari-item {
  width: 100%; display: flex; align-items: center; gap: .75rem; text-align: left;
  border: 0; background: transparent; font: inherit; cursor: pointer;
  padding: .55rem .8rem; border-radius: var(--r-sm); color: var(--ink-900);
}
.cari-item .ic {
  width: 30px; height: 30px; flex: none; border-radius: var(--r-sm);
  display: grid; place-content: center; font-size: var(--t-md);
  background: var(--green-050); border: 1px solid var(--line);
}
.cari-item .isi { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.cari-item .t { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cari-item .d {
  font-size: var(--t-2xs); color: var(--ink-600);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cari-item.on { background: var(--green-100); }
.cari-item.on .ic { background: #fff; border-color: var(--green-300); }

.cari-kosong { text-align: center; color: var(--ink-600); padding: 2.6rem 1rem; }
.cari-kosong .mark { font-size: 1.7rem; display: block; margin-bottom: .5rem; opacity: .7; }
.cari-kosong p { margin: 0; font-size: var(--t-sm); }


@media (max-width: 900px) {
  .cari-button .lbl { display: none; }
  .cari-button { width: 36px; justify-content: center; padding: 0; }
}
@media (max-width: 560px) {
  .cari-latar { padding: 0; align-items: stretch; }
  .cari-panel { width: 100%; max-height: 100%; border-radius: 0; border: 0; }
  .cari-hasil { padding: .2rem; }
  .cari-item { padding: .7rem .7rem; }
}

/* Kotak saring di atas tabel data */
.saring-bar {
  display: flex; align-items: center; gap: .55rem;
  margin: 0 0 .7rem; padding: .3rem .8rem .3rem .75rem;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  background: var(--paper); box-shadow: var(--sh-1);
  max-width: 460px;
}
.saring-bar .ic { font-size: var(--t-sm); opacity: .6; flex: none; }
.saring-bar input {
  flex: 1; min-width: 0; border: 0; outline: none; background: transparent;
  font: inherit; font-size: var(--t-sm); padding: .34rem 0; color: var(--ink-900);
}
.saring-bar input::-webkit-search-cancel-button { display: none; }
.saring-bar .jml {
  flex: none; font-size: var(--t-2xs); font-weight: 600; color: var(--ink-400);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.saring-bar:focus-within { border-color: var(--green-300); box-shadow: var(--sh-2); }
@media (max-width: 560px) { .saring-bar { max-width: none; } }

/* Kotak pencarian di palet tidak perlu bingkai ganda */
.cari-input:focus-visible { box-shadow: none; }

/* ==========================================================================
   PITA KAKI
   Di dalam aplikasi hanya garis warnanya. Di halaman login, alamat situs dan
   Instagram sekolah tampil sebagai tautan yang bisa dibuka.
   ========================================================================== */
.footer-strip .bar { flex: 1 1 auto; }

.footer-strip.tautan { flex-wrap: nowrap; gap: .8rem; }
.footer-strip.tautan .pranala {
  display: flex; align-items: center; gap: .5rem; flex: none; flex-wrap: wrap;
  justify-content: flex-end;
}
.footer-strip a.site {
  text-decoration: none; display: inline-flex; align-items: center;
  transition: background .16s var(--ease), transform .12s var(--ease), box-shadow .16s var(--ease);
}
.footer-strip a.site:hover, .footer-strip a.site:focus-visible {
  background: var(--green-700); box-shadow: var(--sh-2); transform: translateY(-1px);
}
.footer-strip a.site:active { transform: translateY(0); }

/* ==========================================================================
   HALAMAN LOGIN DI PONSEL
   Dirapatkan supaya seluruh kartu masuk dalam satu layar tanpa digulir —
   termasuk pada ponsel 360 x 640 yang masih banyak dipakai orang tua siswa.
   ========================================================================== */
@supports (min-height: 100dvh) {
  .login-page { min-height: 100dvh; }
}

@media (max-width: 560px) {
  .login-top { padding: .6rem 1rem 0; gap: .35rem; }
  .login-top .brand-logo { height: 36px; }
  .login-top .brand-tagline { height: 17px; }

  .login-body { padding: .6rem 1rem .8rem; gap: .35rem; }
  .wordmark { line-height: 1.02; padding-bottom: .15rem; }
  .wordmark .hpsa { font-size: 2rem; }
  .wordmark .live { font-size: var(--t-xl); }
  .login-sub { font-size: var(--t-2xs); margin: .25rem 0 .8rem; line-height: 1.4; }

  .login-card { padding: 1.25rem 1.1rem 1.1rem; border-radius: var(--r-lg); }
  .login-card .avatar-icon { width: 54px; height: 54px; margin: -2.2rem auto .55rem; }
  .login-card .avatar-icon svg { width: 29px; height: 29px; }
  .login-greet { font-size: var(--t-xs); margin-bottom: .75rem; }

  .login-field { margin-bottom: .58rem; }
  .login-field .lbl { margin-bottom: .25rem; font-size: var(--t-2xs); }
  .login-field input { padding: .55rem 0; }
  .btn-login { padding: .66rem; }
  .link-btn { margin-top: .5rem; font-size: var(--t-xs); }
  .login-hint { margin-top: .7rem; font-size: var(--t-2xs); }

  /* Pita kaki: garis di satu baris, kedua tautan di baris berikutnya,
     rata tengah — bukan terdorong ke pinggir sampai terlipat. */
  .login-page .footer-strip { padding: .5rem 1rem .7rem; }
  .footer-strip.tautan { flex-wrap: wrap; gap: .5rem; }
  .footer-strip.tautan .bar { flex: 1 1 100%; }
  .footer-strip.tautan .pranala { flex: 1 1 100%; justify-content: center; }
  .footer-strip a.site { font-size: var(--t-2xs); padding: .3rem .8rem; }
}

/* Layar pendek (ponsel lama, atau jendela peramban yang dikecilkan):
   semuanya tetap tampil, hanya dirapatkan lagi. */
@media (max-height: 640px) and (max-width: 900px) {
  .wordmark .hpsa { font-size: 1.75rem; }
  .wordmark .live { font-size: var(--t-md); }
  .login-sub { font-size: var(--t-2xs); margin: .2rem 0 .55rem; }
  .login-card { padding: 1.1rem 1rem 1rem; }
  .login-card .avatar-icon { width: 46px; height: 46px; margin: -1.8rem auto .4rem; }
  .login-greet { font-size: var(--t-2xs); margin-bottom: .55rem; }
  .login-hint { margin-top: .5rem; }
}

/* ==========================================================================
   PILIHAN SEMESTER DI PONSEL
   Dua kartu semester lebih enak berdampingan daripada menjadi dua baris
   panjang; ukurannya tetap besar dan mudah ditekan jempol.
   ========================================================================== */
@media (max-width: 560px) {
  /* Di ponsel, deretan tile memang dimaksudkan selebar layar. Tanpa baris ini
     wadahnya menyusut mengikuti isi (akibat margin auto di dalam kolom flex),
     sehingga tombolnya jadi sempit di tengah. */
  .tiles { width: 100%; }

  /* Kartu kotak (Semester, Level) tetap dua kolom di ponsel — ia pendek dan
     berpasangan, jadi menumpuknya ke bawah cuma memanjangkan halaman. */
  .tiles:has(.tile.square) {
    grid-template-columns: 1fr 1fr; justify-content: center; gap: .8rem;
    width: 100%;
  }
  .tiles:has(.tile.square) .tile.square {
    width: 100%; max-width: 180px; min-height: 116px; justify-self: center;
    flex-direction: column; justify-content: center; gap: .1rem;
    padding: 1rem .6rem; text-align: center;
  }
  .tiles:has(.tile.square) .tile.square .top { font-size: var(--t-xs); }
  .tiles:has(.tile.square) .tile.square .big { font-size: 2.1rem; line-height: 1.1; }

  /* Kartu menu berikon juga DUA KOLOM di ponsel — "kenapa gak ke samping
     aja?" tanya sekolah, dan memang: enam kartu yang ditumpuk penuh ke bawah
     memakan tiga layar untuk mengatakan enam kata. Bentuknya kembali tegak
     (ikon di atas, nama di bawah) supaya dua kartu sebaris tetap lapang.
     Kartu blok dan pil TETAP satu kolom penuh: isinya kalimat, bukan nama. */
  .tiles:has(.tile.icon) { grid-template-columns: 1fr 1fr; gap: .8rem; }
  .tiles:has(.tile.icon) .tile.icon {
    width: 100%; min-height: 118px;
    flex-direction: column; justify-content: center; gap: .3rem;
    padding: .9rem .55rem; text-align: center;
  }
  .tiles:has(.tile.icon) .tile.icon .glyph { margin: 0 0 .15rem; }
  .tiles:has(.tile.icon) .tile.icon .label { font-size: var(--t-sm); }
  /* Aturan ≤720 sempat membariskan keterangan ke KIRI — pas ketika kartunya
     baris mendatar penuh, salah sekarang setelah kartunya tegak lagi:
     "3 guru" menempel di tepi kiri sementara namanya di tengah. */
  .tiles:has(.tile.icon) .tile.icon.berisi .isi {
    flex-direction: column; align-items: center; justify-content: center;
    gap: .25rem; margin-top: .15rem;
  }
}

/* ==========================================================================
   TAHUN AJARAN & KENAIKAN KELAS
   ========================================================================== */
.kenaikan h3 { margin: 0 0 .3rem; font-size: var(--t-lg); }
.kenaikan-pilih {
  display: flex; flex-wrap: wrap; gap: .9rem; margin: 1.1rem 0 1.2rem;
}
.kenaikan-pilih .field { margin: 0; min-width: 190px; flex: 1 1 190px; max-width: 280px; }
.kenaikan table.list td input[type="text"] {
  width: 100%; max-width: 190px; padding: .35rem .6rem;
  border: 1px solid var(--line-2); border-radius: var(--r-sm); font: inherit; font-size: var(--t-sm);
}
.kenaikan table.list td input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--green-700); }
.kenaikan table.list tbody tr:has(input[type="checkbox"]:checked) { background: var(--green-050); }
@media (max-width: 560px) {
  .kenaikan-pilih .field { max-width: none; flex: 1 1 100%; }
}

/* ==========================================================================
   TABEL NILAI: KOLOM NAMA MENEMPEL, SOROTAN BARIS & KOLOM
   Guru mengisi puluhan sel berderet; tanpa kolom nama yang menempel, satu
   gulir ke samping membuat ia lupa sedang mengisi nilai siapa.
   ========================================================================== */
/* Dulu potongan ini dibatasi layar >=721px, jadi justru di ponsel — satu-satunya
   tempat tabel PASTI harus digulir — nama siswanya ikut hanyut. Sekarang
   berlaku di semua ukuran; lebar kolomnya saja yang dikecilkan di ponsel
   (lihat blok max-width: 720px di bawah). */
/* awal-kolom-menempel */
/* Sudut membulat tabel dipindah ke wadahnya. Selama tabelnya sendiri
   memakai overflow:hidden, sel yang menempel (sticky) tidak pernah bekerja
   — potongan itulah yang memutus hubungannya dengan wadah yang digulir. */
table.grid { overflow: visible; border-radius: 0; box-shadow: none; }
.table-wrap:has(table.grid) {
  border-radius: var(--r); box-shadow: var(--sh-2); background: var(--paper);
  padding: 0 0 8px;
}

/* :not(.name) penting. Di baris kepala ketiga, sel pertama JUSTRU th.name
   (sel "No" memakai rowSpan dari baris di atasnya). Tanpa penyaring ini
   aturan :first-child yang lebih khusus menang, kepala "Nama Siswa" menempel
   di 0 sementara badannya menempel di 46 — kolomnya jadi meleset. */
table.grid thead th:first-child:not(.name),
table.grid tbody td:first-child { position: sticky; left: 0; z-index: 3; }
table.grid th.name,
table.grid td.name { position: sticky; left: 46px; z-index: 3; }

table.grid thead th:first-child:not(.name),
table.grid tbody td:first-child { min-width: 46px; width: 46px; }
table.grid th.name, table.grid td.name { min-width: 178px; }

/* Sel "Nilai KKM" berada di kolom yang sama dengan nama siswa, jadi ia ikut
   menempel — kalau tidak, kepala tabel akan berlubang saat digulir. */
table.grid thead tr:nth-child(2) th:nth-child(2) {
  position: sticky; left: 46px; z-index: 4; background: var(--green-050);
  box-shadow: 1px 0 0 var(--line);
}

/* Sel yang menempel butuh latar sendiri, kalau tidak isi tabel akan
   terlihat menembus di belakangnya saat digulir. */
table.grid tbody td:first-child,
table.grid tbody td.name { background: var(--paper); }
table.grid tbody tr:nth-child(even) td:first-child,
table.grid tbody tr:nth-child(even) td.name { background: #fcfdfc; }
table.grid tbody tr:hover td:first-child,
table.grid tbody tr:hover td.name { background: var(--green-050); }
table.grid thead th:first-child:not(.name) { background: var(--green-050); z-index: 4; }
/* Sel sudut kiri-atas sengaja dibuat transparan agar sudut tabel terlihat
   membulat. Begitu ia menempel, transparan berarti isi tabel yang digulir
   terlihat menembusnya — jadi di sini ia dibuat buram lagi. !important
   diperlukan karena aturan .blank di styles.css juga memakainya. */
table.grid thead tr:first-child th:first-child,
table.grid thead tr:first-child th.blank { background: var(--paper) !important; }
table.grid th.name { background: var(--green-050); z-index: 4; }

/* garis pemisah halus di tepi kanan kolom yang menempel */
table.grid tbody td.name, table.grid th.name { box-shadow: 1px 0 0 var(--line); }
/* akhir-kolom-menempel */

/* Di ponsel kolom yang menempel dikuruskan. 46 + 178 piksel hanya menyisakan
   166 piksel untuk kolom nilai di layar 390 piksel — tidak cukup untuk dua sel
   penuh. 30 + 108 menyisakan sekitar 250 piksel, cukup untuk tiga. Nama yang
   kepanjangan dipotong elipsis; lengkapnya tetap ada di atribut title.
   Blok ini harus berada SESUDAH blok di atas: media query tidak menambah
   kekhususan, jadi yang menang adalah yang belakangan. */
@media (max-width: 720px) {
  table.grid thead th:first-child:not(.name),
  table.grid tbody td:first-child {
    min-width: 30px; width: 30px; padding-left: .3rem; padding-right: .3rem;
  }
  table.grid th.name,
  table.grid td.name,
  table.grid thead tr:nth-child(2) th:nth-child(2) { left: 30px; }
  table.grid th.name,
  table.grid td.name,
  table.grid thead tr:nth-child(2) th:nth-child(2) {
    min-width: 0; width: 108px; max-width: 108px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* legger memaksa nama tidak dipotong — di ponsel justru itu yang merusak */
  table.grid.legger td.name { white-space: nowrap; }
}

/* Baris dan kolom yang sedang diisi */
table.grid tbody tr.fokus td { background: var(--green-050); }
table.grid tbody tr.fokus td:first-child,
table.grid tbody tr.fokus td.name { background: var(--green-100); font-weight: 700; }
table.grid td.fokus-kolom { background: #f2f9f3; }
table.grid tbody tr.fokus td.fokus-kolom { background: var(--green-100); }
table.grid input[type=number]:focus { box-shadow: var(--ring), 0 0 0 1px var(--green-500); }

/* ==========================================================================
   STATISTIK & GRAFIK
   Angka besar dulu, baru grafik, baru tabel — supaya pimpinan cukup melirik
   layar tiga detik untuk tahu keadaan hari itu.
   ========================================================================== */
.stat-tabs { display: flex; gap: .5rem; flex-wrap: wrap; margin: 0 0 1.4rem; }
.stat-tab {
  border: 1px solid var(--line-2); background: var(--paper); color: var(--ink-600);
  border-radius: var(--r-pill); padding: .5rem 1.1rem; font: inherit;
  font-size: var(--t-md); font-weight: 600; cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.stat-tab:hover { border-color: var(--green-300); color: var(--green-800); }
.stat-tab.aktif {
  background: var(--green-700); border-color: var(--green-700); color: #fff;
  box-shadow: var(--sh-1);
}

.stat-cards {
  display: grid; gap: 1rem; margin: 0 0 1.6rem;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.stat-card {
  display: flex; gap: .85rem; align-items: flex-start;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-1); padding: 1rem 1.1rem;
  transition: box-shadow .2s var(--ease), transform .2s var(--ease);
}
.stat-card:hover { box-shadow: var(--sh-2); transform: translateY(-2px); }
.stat-card .stat-glyph { font-size: var(--t-2xl); line-height: 1.2; }
.stat-card .stat-nilai {
  font-size: 1.7rem; font-weight: 800; color: var(--green-800); line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.stat-card .stat-label { font-size: var(--t-sm); font-weight: 700; color: var(--ink-800); margin-top: .15rem; }
.stat-card .stat-sub { font-size: var(--t-2xs); color: var(--ink-400); margin-top: .2rem; }
.stat-card .progress { margin-top: .5rem; }
.stat-card.awas { border-color: #f3c9c3; background: #fffaf9; }
.stat-card.awas .stat-nilai { color: var(--danger); }
.stat-card.aman .stat-nilai { color: var(--green-700); }

.stat-grid { display: grid; gap: 1.2rem; grid-template-columns: 1fr 1fr; margin: 0 0 1.4rem; }
@media (max-width: 900px) { .stat-grid { grid-template-columns: 1fr; } }

.panel {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-1); padding: 1.1rem 1.2rem 1.3rem;
  margin: 0 0 1.4rem;
}
.panel .panel-judul {
  /* Judul panel adalah h3 dan harus seukuran h3 mana pun (17px). Pada 15px ia
     sama besar dengan badan halaman, jadi tidak terbaca sebagai judul. */
  margin: 0; font-size: var(--t-lg); font-weight: 800; color: var(--green-900);
}
.panel .panel-lead { margin: .25rem 0 1rem; font-size: var(--t-xs); color: var(--ink-400); }
.panel .table-wrap { box-shadow: none; }

.chart { width: 100%; color: var(--ink-400); }
.chart svg { width: 100%; height: auto; display: block; overflow: visible; }
.chart text { font-family: var(--sans); fill: var(--ink-600); }
.chart text.sumbu { font-size: 11px; fill: var(--ink-400); }
.chart text.tipis { font-size: 10px; fill: var(--ink-200); }
.chart text.nilai-batang { font-size: 11px; font-weight: 800; fill: var(--ink-800); }
.chart rect { transition: opacity .18s var(--ease); }
.chart:hover rect { opacity: .82; }
.chart rect:hover { opacity: 1; }

.bar-list { display: flex; flex-direction: column; gap: .85rem; }
.bar-item { display: flex; flex-direction: column; gap: .28rem; }
.bar-head { display: flex; justify-content: space-between; align-items: baseline; gap: .6rem; }
.bar-nama {
  font-size: var(--t-sm); font-weight: 700; color: var(--ink-800);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bar-angka { font-size: var(--t-md); font-weight: 800; color: var(--green-800); font-variant-numeric: tabular-nums; }
.bar-track { height: 10px; background: var(--green-050); border-radius: var(--r-pill); overflow: hidden; }
.bar-isi { display: block; height: 100%; border-radius: var(--r-pill); transition: width .5s var(--ease); }
.bar-kaki { font-size: var(--t-2xs); color: var(--ink-400); }

.isi-sel { display: flex; align-items: center; gap: .55rem; min-width: 130px; }
.isi-sel .progress { flex: 1; min-width: 70px; }
.isi-sel .pct { font-size: var(--t-2xs); color: var(--ink-600); font-variant-numeric: tabular-nums; }
table.list td.tengah, table.list th.tengah { text-align: center; }
.status-tag.awas { background: #fdecea; color: var(--danger); border-color: #f3c9c3; }

@media (max-width: 560px) {
  .stat-card .stat-nilai { font-size: var(--t-2xl); }
  .isi-sel { min-width: 0; justify-content: flex-start; }
}

/* ==========================================================================
   RAPOR SIAP CETAK
   Halaman biasa yang dirapikan CSS cetak — bukan PDF rakitan pustaka.
   Tombol "Cetak / Simpan PDF" memanggil dialog cetak peramban, yang di semua
   peramban dan ponsel modern sudah punya pilihan "Simpan sebagai PDF".
   ========================================================================== */
.rapor-alat {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: center;
  justify-content: space-between; margin: 0 0 1.2rem;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-1); padding: .85rem 1.1rem;
}
.rapor-alat-kanan { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.rapor-semester { display: flex; gap: .4rem; background: none; border: 0; padding: 0; }
.rapor-semester .stat-tab { padding: .4rem .85rem; font-size: var(--t-xs); }

.rapor-sheet {
  background: #fff; color: #111; margin: 0 auto; max-width: 820px;
  border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--sh-2); padding: 2.2rem 2.4rem 2.6rem;
  font-size: var(--t-sm); line-height: 1.5;
}

.rapor-kop { display: flex; align-items: center; gap: 1.1rem; }
.rapor-logo { width: 62px; height: auto; flex: none; }
.rapor-kop-teks { text-align: center; flex: 1; }
.rapor-kop-teks .baris1 { font-size: var(--t-xl); font-weight: 800; letter-spacing: .02em; }
.rapor-kop-teks .baris2 { font-size: var(--t-md); font-weight: 700; margin-top: .1rem; }
.rapor-kop-teks .baris3 { font-size: var(--t-2xs); color: #555; margin-top: .25rem; }
.rapor-garis {
  border-top: 3px solid #111; border-bottom: 1px solid #111;
  height: 4px; margin: .7rem 0 1.4rem;
}

.rapor-judul {
  text-align: center; font-size: var(--t-md); font-weight: 800;
  letter-spacing: .04em; text-decoration: underline; margin: 0 0 1.4rem;
}

/* Kertas A4 dikurangi margin @page 14mm kiri-kanan menyisakan 182mm — sekitar
   688 piksel CSS, bukan 794. Pada lebar itu kedua tabel yang dibagi rata
   memberi kolom nilai hanya ~206px, dan nama siswa terlipat dua baris di
   rapor yang dibawa pulang orang tua.

   Isinya memang tidak seimbang: kolom kiri memuat nama lengkap dan dua nomor
   panjang, kolom kanan cuma "SD Bukit" dan "2026/2027". Jadi pembagiannya
   ikut dimiringkan, bukan dibiarkan rata. */
.rapor-identitas { display: flex; gap: 1.4rem; flex-wrap: wrap; margin: 0 0 1.2rem; }
.rapor-identitas table { border-collapse: collapse; }
.rapor-identitas table:first-child { flex: 1.18 1 320px; }
.rapor-identitas table:last-child  { flex: .82 1 240px; }
/* Lebar label dulu dipatok 44%. Pada kolom selebar ~340px itu menyisakan
   sekitar 168px untuk NILAINYA — dan "Andi Pratama Wijaya" tidak muat, jadi
   nama siswa terlipat dua baris di rapor yang dibawa pulang, padahal di
   sebelah kanannya masih ada ruang kosong. Nomor NIS/NISN mengalami hal yang
   sama.

   width:1% + nowrap adalah cara meminta tabel memberi kolom ini SELEBAR
   isinya saja, tidak lebih; sisanya jatuh ke kolom nilai. Label terpanjang
   ("Nama Peserta Didik") sekitar 130px, jadi nilainya mendapat ~195px.

   vertical-align:top supaya titik dua sejajar dengan BARIS PERTAMA nilainya
   bila suatu saat memang ada nama yang terpaksa dua baris — bukan melayang
   di tengah-tengah. */
.rapor-identitas th {
  text-align: left; font-weight: 500; color: #333; padding: .16rem 0;
  width: 1%; white-space: nowrap; vertical-align: top;
}
.rapor-identitas td { padding: .16rem 0; font-weight: 700; vertical-align: top; }
.rapor-identitas td.titik { width: 14px; font-weight: 500; padding-right: .35rem; }

table.rapor-nilai { width: 100%; border-collapse: collapse; margin: 0 0 1.4rem; }
table.rapor-nilai th, table.rapor-nilai td {
  border: 1px solid #9aa5a0; padding: .38rem .55rem; vertical-align: top;
}
table.rapor-nilai thead th {
  background: #eef4ef; font-weight: 700; font-size: var(--t-xs); text-align: left;
  letter-spacing: .02em;
}
table.rapor-nilai .no { width: 34px; text-align: center; }
table.rapor-nilai .mid { text-align: center; }
table.rapor-nilai .tebal { font-weight: 800; }
table.rapor-nilai tfoot td { background: #f6faf7; font-weight: 700; }

.rapor-catatan { margin: 0 0 1.8rem; }
.rapor-catatan .label { font-weight: 700; margin-bottom: .5rem; }
/* Garis titik-titik untuk tulisan tangan.
   HANYA pada cetakan. Di layar sudah ada kotak yang jelas dapat diketik, dan
   memajang keduanya sekaligus membuat satu bagian tampak seperti dua: kotak
   isian yang di bawahnya masih ada tiga garis kosong entah untuk apa.
   Di atas kertas kebalikannya yang benar — tidak ada yang bisa diketik, jadi
   wali kelas yang catatannya kosong tetap punya tempat menulis tangan.
   JS-nya membuang garis ini begitu catatannya terisi, jadi rapor yang sudah
   ada catatannya tidak tercetak dengan tiga garis menggantung di bawahnya. */
.rapor-catatan .garis { display: none; }
@media print {
  .rapor-catatan .garis {
    display: block; border-bottom: 1px dotted #7d8a84; height: 1.5rem;
  }
}

/* Kotak catatan wali kelas yang TERSIMPAN (ronde 88).
   Di layar ia terlihat sebagai kotak yang jelas dapat diketik; pada CETAKAN
   kotaknya hilang dan yang tersisa hanya tulisannya — rapor resmi tidak
   memajang kotak isian. min-height menjaga tingginya tetap seperti tiga garis
   lama, jadi tata letak halaman cetak tidak melompat saat catatannya kosong. */
.rapor-catatan-isi {
  min-height: 4.5rem;
  padding: .55rem .7rem;
  border: 1px dashed var(--line-2);
  border-radius: var(--r-sm);
  background: var(--paper);
  white-space: pre-wrap;
  line-height: 1.6;
}
.rapor-catatan-isi:empty { border-style: dashed; }
.rapor-catatan-isi:focus-visible {
  outline: none; border-color: var(--green-600); box-shadow: var(--ring);
}
@media print {
  .rapor-catatan-isi {
    border: 0; padding: 0; background: none; min-height: 0;
  }
  /* Kotak yang benar-benar kosong tidak menyisakan ruang hampa di atas garis
     tulisan tangan. */
  .rapor-catatan-isi:empty { display: none; }
}

.rapor-ttd { display: flex; gap: 1.4rem; text-align: center; }
.ttd-kolom { flex: 1; }
.ttd-tanggal { font-size: var(--t-xs); min-height: 1.2rem; margin-bottom: .3rem; }
.ttd-jabatan { font-size: var(--t-xs); }
.ttd-ruang { height: 62px; }
.ttd-nama { font-weight: 700; border-top: 1px solid #111; padding-top: .3rem; font-size: var(--t-xs); }

@media (max-width: 720px) {
  .rapor-sheet { padding: 1.2rem 1rem 1.6rem; font-size: var(--t-xs); }
  .rapor-kop-teks .baris1 { font-size: var(--t-md); }
  .rapor-ttd { flex-direction: column; gap: 1.6rem; }
  .rapor-alat { flex-direction: column; align-items: stretch; }

  /* Tabel enam kolom tidak muat di layar ponsel. Ia digulir sendiri di dalam
     lembarnya — halaman tetap tidak bergeser ke samping, dan saat dicetak
     tabelnya kembali utuh (aturan ini tidak berlaku pada media cetak). */
  .rapor-nilai-gulir { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 0 1.4rem; }
  .rapor-nilai-gulir table.rapor-nilai { min-width: 460px; margin: 0; font-size: var(--t-xs); }
  .rapor-nilai-gulir::after {
    content: 'geser ke samping →'; display: block; text-align: right;
    font-size: var(--t-2xs); color: var(--ink-400); padding: .25rem .1rem 0;
  }
  table.rapor-nilai th, table.rapor-nilai td { padding: .3rem .4rem; }
}

/* --- yang benar-benar keluar di kertas ------------------------------------ */
@media print {
  /* Hanya lembar rapornya yang dicetak: kepala aplikasi, remah jejak, pita
     kaki, tombol, dan pemberitahuan semuanya disembunyikan. */
  .app-header, .crumb-bar, .footer-strip, .toast-host, #modal-host,
  .layar-saja, .to-top { display: none !important; }

  html, body { background: #fff !important; }
  .app, .page { display: block !important; padding: 0 !important; margin: 0 !important;
    max-width: none !important; background: #fff !important; }
  .page { animation: none !important; }

  .rapor-sheet {
    border: 0; box-shadow: none; border-radius: 0;
    max-width: none; width: 100%; padding: 0; font-size: 11pt;
  }
  .rapor-nilai-gulir { overflow: visible !important; }
  .rapor-nilai-gulir::after { content: none !important; }
  .rapor-nilai-gulir table.rapor-nilai { min-width: 0 !important; }

  /* Baris tabel jangan terpotong di tengah saat berganti halaman. */
  table.rapor-nilai tr { break-inside: avoid; page-break-inside: avoid; }
  .rapor-ttd { break-inside: avoid; page-break-inside: avoid; }
  a[href]::after { content: none !important; }
}

@page { size: A4; margin: 14mm 14mm 16mm; }

/* ==========================================================================
   PEMILIH UNIT
   Muncul hanya bila akun ini menjangkau lebih dari satu unit.
   ========================================================================== */
.unit-menu { position: relative; }
.unit-button {
  display: flex; align-items: center; gap: .4rem;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: .34rem .7rem;
  font: inherit; font-size: var(--t-sm); font-weight: 700; color: var(--green-800);
  cursor: pointer; box-shadow: var(--sh-1);
  transition: background .16s var(--ease), border-color .16s var(--ease), transform .16s var(--ease);
}
.unit-button:hover { background: var(--green-050); border-color: var(--green-300); }
.unit-button:active { transform: scale(.97); }
.unit-button .ic { font-size: var(--t-md); line-height: 1; }
.unit-button .caret { font-size: var(--t-2xs); color: var(--ink-400); }

.unit-drop {
  position: absolute; right: 0; top: calc(100% + .55rem); z-index: 46;
  width: min(280px, calc(100vw - 2rem));
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-3); overflow: hidden;
  animation: pop .16s var(--ease);
}
.unit-drop.hide { display: none; }
.unit-drop .unit-judul {
  padding: .7rem 1rem; font-size: var(--t-2xs); font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-400);
  border-bottom: 1px solid var(--line); background: var(--green-050);
}
.unit-drop button {
  display: flex; align-items: center; gap: .6rem; width: 100%;
  border: 0; background: none; font: inherit; text-align: left;
  padding: .6rem 1rem; cursor: pointer; color: var(--ink-800);
}
.unit-drop button:hover { background: var(--green-050); }
.unit-drop button .kode {
  flex: none; min-width: 46px; font-size: var(--t-2xs); font-weight: 800;
  color: var(--green-800); background: var(--green-100);
  border-radius: var(--r-sm); padding: .16rem .4rem; text-align: center;
}
.unit-drop button .nama { flex: 1; font-size: var(--t-sm); }
.unit-drop button .tanda { color: var(--green-700); font-weight: 800; opacity: 0; }
.unit-drop button.on { background: var(--green-050); font-weight: 700; }
.unit-drop button.on .tanda { opacity: 1; }

@media (max-width: 720px) {
  .unit-button { padding: .3rem .5rem; }
  .unit-button .caret { display: none; }
  .unit-drop { position: fixed; left: .6rem; right: .6rem; width: auto; top: 58px; }
}

/* ==========================================================================
   PITA RINGKASAN DASHBOARD
   Empat angka yang paling sering ditanyakan, tepat di atas kartu pilihan.
   ========================================================================== */
.ringkas { width: 100%; max-width: 940px; margin: 0 auto 1.8rem; }
.ringkas-grid {
  display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.ringkas-item {
  display: flex; gap: .7rem; align-items: center;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-1); padding: .8rem .95rem;
  min-height: 74px;
  transition: box-shadow .2s var(--ease), transform .2s var(--ease);
}
.ringkas-item:hover { box-shadow: var(--sh-2); transform: translateY(-2px); }
.ringkas-item .rg-ic { font-size: var(--t-2xl); line-height: 1; flex: none; }
.ringkas-item .rg-nilai {
  font-size: var(--t-2xl); font-weight: 800; color: var(--green-800);
  line-height: 1.1; font-variant-numeric: tabular-nums;
}
.ringkas-item .rg-label { font-size: var(--t-2xs); font-weight: 700; color: var(--ink-800); }
.ringkas-item .rg-sub { font-size: var(--t-2xs); color: var(--ink-400); margin-top: .1rem; }
.ringkas-item.awas { border-color: #f3c9c3; background: #fffaf9; }
.ringkas-item.awas .rg-nilai { color: var(--danger); }

/* Kotak angka yang dapat ditekan. Sifat tombol bawaan peramban dilepas —
   ia harus tetap terbaca sebagai kotak angka, bukan berubah menjadi tombol
   tindakan; yang ditambahkan hanya tanda bahwa ia bisa ditekan. */
button.ringkas-item {
  font: inherit; text-align: left; width: 100%; color: inherit;
}
.ringkas-item.bisa { cursor: pointer; }
.ringkas-item.bisa:hover { border-color: var(--green-300); }
.ringkas-item.bisa:focus-visible { outline: 2px solid var(--green-600); outline-offset: 2px; }
.ringkas-item.bisa::after {
  content: '→'; margin-left: auto; align-self: flex-start;
  color: var(--green-700); font-weight: 800; font-size: var(--t-sm);
  opacity: 0; transition: opacity .16s var(--ease), transform .16s var(--ease);
}
.ringkas-item.bisa:hover::after,
.ringkas-item.bisa:focus-visible::after { opacity: 1; transform: translateX(2px); }
.ringkas-item.bisa.awas:hover { border-color: var(--danger); }

/* Kerangka sementara angkanya belum tiba. */
.ringkas-grid.memuat .ringkas-item { box-shadow: none; }
.ringkas-grid.memuat .ringkas-item:hover { transform: none; box-shadow: none; }
.rg-rangka {
  display: block; width: 100%; height: 34px; border-radius: var(--r-sm);
  background: var(--green-050);
  background-image: linear-gradient(100deg, transparent 20%, rgba(47,139,82,.09) 40%, transparent 60%);
  background-size: 250% 100%;
  animation: shimmer 1.3s linear infinite;
}

.ringkas-tautan {
  display: block; margin: .7rem auto 0; border: 0; background: none;
  font: inherit; font-size: var(--t-xs); font-weight: 700; color: var(--green-700);
  cursor: pointer; padding: .35rem .7rem; border-radius: var(--r-pill);
}
.ringkas-tautan:hover { background: var(--green-050); }

/* ==========================================================================
   POLES ANTARMUKA MENYELURUH
   ========================================================================== */

/* 1. Cincin fokus yang sama di seluruh aplikasi — penting bagi pengguna papan
      ketik, dan membuat tampilan terasa disengaja, bukan bawaan peramban. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--green-600);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.tile:focus-visible { outline-offset: 3px; border-radius: var(--r); }

/* 2. Kolom login sudah punya cincinnya sendiri di sekeliling kotak. Cincin
      bawaan di atas dimatikan di sana supaya tidak tampak dua kotak bertumpuk. */
.login-field .box input:focus-visible,
.login-field .box button:focus-visible,
.saring-bar input:focus-visible { outline: none; }
.saring-bar:focus-within {
  outline: 2px solid var(--green-600); outline-offset: 2px;
}

/* 3. Tindakan merusak diberi warna bahaya, bukan hijau seperti tautan biasa. */
/* ---------- kolom Aksi merapat ke tepi kanan ----------
   Kolom terakhir dulu selebar bagiannya sendiri dan berhenti di tengah jalan,
   meninggalkan lajur kosong selebar dua ratus piksel di sisi kanan tabel —
   tabel yang terbaca "ada kolom yang belum dimuat". Sekarang ia sesempit
   isinya dan menempel di tepi, sehingga kolom nama yang menyerap sisa
   lebarnya. */
@media (min-width: 861px) {
  table.list th.act, table.list td.act { width: 1%; white-space: nowrap; text-align: right; }
  table.list td.act .act-links { justify-content: flex-end; }
  table.list td.act .aksi-lain { margin-left: auto; }
}

/* ---------- lebar kolom Data Guru ----------
   Tanpa aturan ini tiap kolom menawar sendiri, dan yang kalah selalu yang
   sama: nama guru terpotong tiga baris ("Fathiyyah / Yasmin, / M.Pd.")
   sementara NIP — deret angka yang panjangnya tetap — kelebihan ruang. */
@media (min-width: 861px) {
  table.list th.nama, table.list td.nama { min-width: 216px; }
  table.list th.uname, table.list td.uname { min-width: 150px; }
  table.list th.nip, table.list td.nip { width: 1%; white-space: nowrap; }
  table.list th.unit, table.list td.unit { width: 1%; white-space: nowrap; }
  table.list th.ampu, table.list td.ampu { min-width: 200px; }
}
/* Lencana "belum punya akun" adalah satu penanda, bukan kalimat: memenggalnya
   menjadi dua baris membuat barisnya ikut meninggi tanpa alasan. */
table.list td.uname .badge { white-space: nowrap; }

/* Angka pencarian: bentuk panjang di layar lebar, bentuk pendek di ponsel. */
.saring-bar .jml .pendek { display: none; }
@media (max-width: 560px) {
  .saring-bar .jml .panjang { display: none; }
  .saring-bar .jml .pendek { display: inline; }
}

/* ==========================================================================
   DAFTAR PILIH SISWA (layar Periksa Nilai)
   ==========================================================================
   Menggantikan setumpuk kartu lonjong satu lajur di tengah layar. Tiga puluh
   nama panjang ke bawah, dua pertiga lebar halaman kosong di kiri-kanannya,
   dan tidak satu pun keterangan selain namanya — tidak ada nomor absen, tidak
   ada NIS.
   ========================================================================== */
/* Selebar kolom halaman, sama seperti tabel di layar lain — kalau dibatasi
   940px ia menjorok ke dalam sementara bilah tombol di atasnya tetap menempel
   di tepi kiri, dan keduanya terbaca tidak sejajar. */
.saring-siswa { width: 100%; }
.saring-siswa .saring-bar { max-width: none; margin-bottom: 1rem; }

/* Dua lajur begitu layarnya cukup lebar. Satu rombel berisi tiga puluhan
   siswa; satu lajur berarti halaman setinggi 2.400 piksel sementara separuh
   lebar layar dibiarkan kosong. Di bawah 900px ia kembali satu lajur — nama
   dan NIS tidak boleh berdesakan demi menghemat guliran. */
.pilih-daftar {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr));
  gap: .5rem; align-content: start;
}

.pilih-baris {
  display: grid; grid-template-columns: 3.2rem 1fr auto 1.2rem;
  align-items: center; gap: .9rem; width: 100%;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); padding: .8rem 1rem;
  box-shadow: var(--sh-1); cursor: pointer; font: inherit;
  color: var(--ink-900); text-align: left; min-height: 52px;
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), border-color .16s;
}
.pilih-baris:hover {
  transform: translateY(-1px); box-shadow: var(--sh-2); border-color: var(--green-300);
}
.pilih-baris:active { transform: none; }
.pilih-baris:focus-visible { outline: 2px solid var(--green-600); outline-offset: 2px; }

.pilih-baris .no {
  color: var(--ink-400); font-variant-numeric: tabular-nums;
  font-size: var(--t-sm); text-align: right;
}
.pilih-baris .nama { font-weight: 600; font-size: var(--t-md); }
.pilih-baris .nis {
  color: var(--ink-400); font-size: var(--t-xs);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.pilih-baris .tanda {
  color: var(--ink-200); font-size: var(--t-lg); line-height: 1;
  transition: color .16s, transform .16s var(--ease);
}
.pilih-baris:hover .tanda { color: var(--green-600); transform: translateX(2px); }

/* Di ponsel NIS pindah ke bawah namanya: memaksanya tetap sebaris membuat
   nama panjang terpenggal di tengah kata. */
@media (max-width: 560px) {
  .pilih-baris {
    grid-template-columns: 2.4rem 1fr 1rem;
    grid-template-areas: 'no nama tanda' '. nis tanda';
    row-gap: .1rem; padding: .7rem .8rem;
  }
  .pilih-baris .no { grid-area: no; }
  .pilih-baris .nama { grid-area: nama; }
  .pilih-baris .nis { grid-area: nis; }
  .pilih-baris .tanda { grid-area: tanda; align-self: center; }
}

/* ---------- menu tindakan (⋯) ---------- */
.aksi-lain {
  background: none; border: 1px solid transparent; border-radius: var(--r-pill);
  cursor: pointer; font: inherit; color: var(--ink-600);
  font-size: var(--t-lg); line-height: 1;
  min-width: 34px; min-height: 34px; padding: 0 .35rem;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .14s var(--ease), color .14s var(--ease), border-color .14s;
}
.aksi-lain:hover { background: var(--green-100); color: var(--green-800); }
.aksi-lain[aria-expanded="true"] {
  background: var(--green-100); color: var(--green-800); border-color: var(--green-300);
}
.aksi-lain:focus-visible { outline: 2px solid var(--green-600); outline-offset: 2px; }

.aksi-menu {
  position: fixed; z-index: 90; min-width: 190px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-3);
  padding: .35rem; display: flex; flex-direction: column;
  animation: rise .16s var(--ease) both;
  /* Lima tindakan pada sasaran sentuh 44px setinggi 240 piksel — persis
     setinggi layar HP yang dimiringkan, setelah dipotong bilah peramban.
     Panelnya melayang (position: fixed), jadi ia tidak ikut tergulir bersama
     halaman: tanpa batas ini, tindakan terakhir berada di bawah lipatan dan
     tidak dapat dijangkau sama sekali. */
  max-height: calc(100vh - 16px); overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.aksi-menu button {
  background: none; border: 0; cursor: pointer; font: inherit;
  color: var(--ink-800); text-align: left;
  padding: .6rem .75rem; border-radius: var(--r-sm);
  min-height: 40px; font-size: var(--t-md);
}
.aksi-menu button:hover { background: var(--green-050); color: var(--green-900); }
.aksi-menu button:focus-visible { outline: 2px solid var(--green-600); outline-offset: -2px; }
.aksi-menu button.danger { color: var(--danger); }
.aksi-menu button.danger:hover { background: #fdecea; color: var(--danger); }

/* Sasaran sentuhnya mengikuti batas yang sama dengan tombol lain di aplikasi
   ini: (max-width: 1024px), (pointer: coarse). Semula ia hanya (hover: none),
   dan tablet yang melaporkan dirinya punya penunjuk halus — iPad pada mode
   lanskap — karena itu tetap mendapat tombol 34px. Penyapuan layar HP/tablet
   yang menangkapnya. */
@media (max-width: 1024px), (pointer: coarse) {
  .aksi-lain { min-width: 44px; min-height: 44px; }
  .aksi-menu button { min-height: 44px; }
}

table.list .act-links button:last-child:hover { color: var(--danger); }
table.list .act-links button { transition: color .14s var(--ease); }

/* 4. Kepala tabel ikut menempel pada daftar panjang, jadi nama kolom tetap
      terbaca sampai baris terakhir. */
@media (min-width: 721px) {
  .table-wrap table.list thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--green-050);
    box-shadow: inset 0 -1px 0 var(--line);
  }
}

/* 5. Pesan sekilas (toast) dapat ditutup dengan sekali klik. */
.toast { cursor: pointer; }
.toast:hover { filter: brightness(1.08); }

/* 6. Menghormati setelan "kurangi gerak" pada sistem pengguna.
      Beberapa orang pusing oleh animasi; aplikasi tetap berfungsi penuh. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .ringkas-item:hover, .stat-card:hover { transform: none; }
}

/* 7. Tombol yang sedang bekerja: berputar kecil, tidak bisa ditekan dua kali. */
.btn.busy, .btn-login.busy { position: relative; color: transparent !important; }
.btn.busy::after, .btn-login.busy::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  color: var(--green-700);
  animation: putar .7s linear infinite;
}
.btn.primary.busy::after, .btn-login.busy::after { color: #fff; }
@keyframes putar { to { transform: rotate(360deg); } }

/* 8. Lima kartu atau lebih dirapatkan sedikit supaya muat satu baris penuh —
      satu kartu yatim di baris kedua membuat dashboard tampak belum jadi. */
@media (min-width: 1024px) {
  .tiles:has(> .tile.icon:nth-child(5)) { max-width: 1010px; }
  .tiles:has(> .tile.icon:nth-child(5)) .tile.icon { width: 178px; height: 178px; }
  .tiles:has(> .tile.icon:nth-child(5)) .tile.icon .glyph {
    width: 68px; height: 68px; font-size: 2.1rem; border-radius: var(--r-lg);
  }
}

/* 8b. Enam kartu atau lebih diberi ruang selebar barisan angka di atasnya,
       sehingga keduanya berdiri di lebar yang sama alih-alih berupa pulau
       sempit di bawah baris yang lebar. Kalau enamnya tetap tidak muat —
       jendela yang lebih sempit — ui.js sendiri yang membaginya 3 + 3. */
@media (min-width: 1200px) {
  .tiles:has(> .tile.icon:nth-child(6)) { max-width: 1100px; }
  .tiles:has(> .tile.icon:nth-child(6)) .tile.icon { width: 168px; height: 168px; }
  .tiles:has(> .tile.icon:nth-child(6)) .tile.icon .glyph {
    width: 64px; height: 64px; font-size: 2rem;
  }
}

/* Di ponsel empat kartu ringkas ditata dua kolom — kalau bertumpuk ke bawah,
   kartu pilihan di bawahnya terdorong jauh dari layar pertama. */
@media (max-width: 560px) {
  .ringkas-grid { grid-template-columns: 1fr 1fr; gap: .6rem; }
  .ringkas-item { padding: .65rem .7rem; gap: .5rem; min-height: 66px; }
  .ringkas-item .rg-ic { font-size: var(--t-lg); }
  .ringkas-item .rg-nilai { font-size: var(--t-xl); }
  .ringkas-item .rg-label { font-size: var(--t-2xs); }
  .ringkas-item .rg-sub { display: none; }
  .ringkas { margin-bottom: 1.2rem; }
}

/* Kalimat pengantar tepat di bawah judul mengikuti judulnya: keduanya rata
   tengah. (Dulu ada ragam judul rata kiri; ragam itu sudah dihapus supaya
   tidak ada dua perataan berbeda dalam satu layar.) */
.page-title + .page-lead,
.page-title + p.legger-judul { text-align: center; }

/* Dialog bertumpuk: yang terbaru selalu di atas, dan lapisan gelapnya tidak
   menggandakan kegelapan sehingga layar tidak berubah hitam pekat. */
#modal-host .backdrop + .backdrop { z-index: 91; background: rgba(11,40,25,.18); }
#modal-host .backdrop + .backdrop + .backdrop { z-index: 92; }

/* ==========================================================================
   LAYAR WAJIB GANTI PASSWORD
   Menggantikan seluruh isi halaman: tidak ada jalan memutar.
   ========================================================================== */
.ganti-wajib { display: grid; place-items: center; min-height: 60vh; padding: 1rem; }
.gw-kartu {
  width: min(430px, 100%); background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); padding: 2rem 1.9rem 1.7rem; text-align: center;
}
.gw-lambang {
  width: 62px; height: 62px; margin: 0 auto 1rem; border-radius: 50%;
  display: grid; place-content: center; font-size: 1.7rem;
  background: linear-gradient(150deg, var(--green-100), var(--green-200));
  box-shadow: var(--line-in);
}
.gw-kartu h1 { margin: 0 0 .5rem; font-size: var(--t-xl); color: var(--green-900); }
.gw-lead { margin: 0 0 1.5rem; font-size: var(--t-sm); color: var(--ink-600); line-height: 1.55; }
.gw-kolom { display: block; text-align: left; margin-bottom: .8rem; }
.gw-kolom span {
  display: block; margin-bottom: .3rem; font-size: var(--t-2xs); font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase; color: var(--ink-600);
}
.gw-kolom input {
  width: 100%; border: 1.5px solid var(--line-2); border-radius: var(--r-pill);
  background: #fbfdfb; padding: .62rem .95rem; font: inherit; color: var(--ink-900);
}
.gw-kolom input:focus {
  outline: none; border-color: var(--green-500); background: #fff; box-shadow: var(--ring);
}
.gw-tip { margin: 0 0 1rem; font-size: var(--t-2xs); color: var(--ink-400); text-align: left; }
.gw-kartu .notice { margin: 0 0 1rem; max-width: none; text-align: left; }
.gw-kartu .notice.sembunyi { display: none; }
.btn.lebar { width: 100%; justify-content: center; }
.tautan-keluar {
  display: block; margin: 1rem auto 0; border: 0; background: none; font: inherit;
  font-size: var(--t-xs); color: var(--ink-400); cursor: pointer; padding: .35rem .6rem;
  border-radius: var(--r-pill);
}
.tautan-keluar:hover { color: var(--danger); background: var(--danger-bg); }

/* Selama hadangan ganti password, tombol yang tidak berfungsi disembunyikan. */
.app.mode-hadang .crumb-bar,
.app.mode-hadang .cari-button,
.app.mode-hadang .bell,
.app.mode-hadang .unit-menu { display: none; }

/* Panel saklar keamanan password pada layar Kelola Akun. */
.panel.keamanan { max-width: 780px; margin: 0 auto 1.6rem; }
.keamanan-baris {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}
.keamanan-baris .status-tag { margin-right: .5rem; }
@media (max-width: 560px) {
  .keamanan-baris { flex-direction: column; align-items: stretch; }
  .keamanan-baris .btn { width: 100%; justify-content: center; }
}

/* ---- kepala kolom R yang belum ada: tombol tambah ---- */
table.grid th.tambah-r { padding: 2px 4px; }
table.grid th.tambah-r .mini-tambah {
  border: 1px dashed var(--green-600); background: transparent;
  color: var(--green-800); border-radius: var(--r-xs); cursor: pointer;
  font: inherit; font-size: var(--t-2xs); font-weight: 700; line-height: 1;
  padding: .3rem .42rem; width: 100%; transition: background .15s, color .15s;
}
table.grid th.tambah-r .mini-tambah:hover,
table.grid th.tambah-r .mini-tambah:focus-visible {
  background: var(--green-600); color: #fff; border-style: solid;
}

/* ---- kolom Keterangan: predikat di atas, ketuntasan di bawah ----
   Keduanya dulu berdiri berdampingan dalam satu baris — "Tuntas  C" — dan
   dibaca sebagai satu keterangan, padahal keduanya menjawab pertanyaan yang
   berbeda: huruf menjawab "setinggi apa", lencana menjawab "sudah cukup atau
   belum". Ditumpuk, keduanya terbaca sebagai dua hal, dan kolomnya menyempit
   pula karena tidak perlu memuat keduanya berjajar. */
table.grid th.ket-kepala { min-width: 108px; }
table.grid td.ket { white-space: nowrap; text-align: center; line-height: 1.25; }
table.grid td.ket > * { display: block; margin-left: auto; margin-right: auto; }

/* Huruf sebagai keping bulat, bukan kotak kecil berbingkai abu-abu. Warnanya
   mengikuti maknanya sehingga satu kolom penuh dapat dipindai tanpa membaca:
   hijau pekat = A, hijau muda = B, emas = C, merah = D. Warna TIDAK berdiri
   sendiri — hurufnya tetap tertulis di dalamnya, jadi tetap terbaca oleh yang
   tidak membedakan warna dan tetap benar saat dicetak hitam-putih. */
.predikat {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.65rem; height: 1.65rem; border-radius: 50%;
  border: 1px solid transparent;
  font-size: var(--t-xs); font-weight: 800; letter-spacing: 0;
  background: #fff; color: #4a5a51;
}
.predikat.p-a { background: var(--green-600); border-color: var(--green-700); color: #fff; }
.predikat.p-b { background: var(--green-100); border-color: var(--green-300); color: var(--green-800); }
.predikat.p-c { background: var(--gold-100); border-color: #f0e0ae; color: #8a6d00; }
.predikat.p-d { background: var(--danger-bg); border-color: #f0cfcb; color: var(--danger); }

.lencana-tuntas {
  margin-top: .34rem;
  border-radius: var(--r-pill); padding: .14rem .5rem;
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: .01em;
  width: max-content;
}
.lencana-tuntas.ok { background: var(--green-100); color: var(--green-800); }
.lencana-tuntas.kurang { background: var(--danger-bg); color: var(--danger); }
/* KKM kolomnya belum diisi: bukan lulus, bukan gagal, belum dapat dihitung. */
.lencana-tuntas.belum { background: #eef1ef; color: var(--ink-600); }

@media (max-width: 640px) {
  table.grid th.ket-kepala { min-width: 92px; }
  .predikat { width: 1.45rem; height: 1.45rem; font-size: var(--t-2xs); }
  .lencana-tuntas { margin-top: .26rem; padding: .12rem .38rem; }
}
table.grid th.sub-kosong { padding: 0; }

/* ---- chip "Atur ulang": disediakan, tetapi tidak mengundang ---- */
.chips .chip.ulang {
  border-color: #e3c9c5; color: var(--danger); background: #fff;
}
.chips .chip.ulang:hover,
.chips .chip.ulang:focus-visible {
  background: var(--danger-bg); border-color: var(--danger);
}

/* ---- panel bobot Nilai Rapor ---- */
.panel.bobot { max-width: 780px; margin: 0 auto 1.6rem; }
.bobot-kolom { display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0 .6rem; }
.bobot-kolom label { flex: 1 1 140px; display: flex; flex-direction: column; gap: .3rem; }
.bobot-kolom label span { font-size: var(--t-2xs); font-weight: 600; color: var(--ink-600); }
.bobot-isian { width: 100%; }
.bobot-contoh {
  margin: 1rem 0 0; padding: .8rem 1rem; border-radius: var(--r-sm);
  background: var(--green-050); border: 1px solid var(--green-200);
}
.bobot-baris {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; padding: .22rem 0;
}
.bobot-baris strong { color: var(--green-800); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .bobot-kolom { flex-direction: column; }
  .bobot-kolom label { flex-basis: auto; }
  .bobot-baris { flex-direction: column; gap: .1rem; }
}

/* ---- pita status pengesahan lembar ---- */
.pita-sah {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  margin: 0 0 1.1rem; padding: .8rem 1rem;
  border: 1px solid var(--line); border-left-width: 4px; border-radius: var(--r-sm);
  background: #fff;
}
.pita-sah.s-draft     { border-left-color: #c7cfca; }
.pita-sah.s-submitted { border-left-color: var(--warn);  background: #fffdf6; }
.pita-sah.s-approved  { border-left-color: var(--green-600); background: var(--green-050); }
.pita-sah.s-rejected  { border-left-color: var(--danger); background: #fffaf9; }
.pita-kiri {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .3rem; min-width: 0; flex: 1 1 320px;
}
.pita-catatan { font-size: var(--t-xs); color: var(--ink-900); }
.pita-tombol { display: flex; gap: .55rem; flex-wrap: wrap; }
@media (max-width: 560px) {
  /* flex-basis diukur pada sumbu utama. Begitu wadahnya berganti menjadi
     kolom, angka 320px/140px/18rem itu berhenti berarti "lebar minimal" dan
     berubah menjadi TINGGI — anak yang isinya dua baris teks pun ditarik
     setinggi itu, dan di ponsel panelnya menganga kosong ratusan piksel.
     Dikembalikan ke auto begitu arahnya berubah. */
  .pita-sah { flex-direction: column; }
  .pita-kiri { flex-basis: auto; }
  .pita-tombol { width: 100%; }
  .pita-tombol .btn { flex: 1 1 auto; justify-content: center; }
}

/* ---- legger: satu rombel, seluruh mata pelajaran ---- */
.legger-judul { margin: -.4rem 0 1rem; color: var(--ink-600); font-size: var(--t-md); }
table.grid.legger th, table.grid.legger td { padding: .42rem .5rem; font-size: var(--t-xs); }
table.grid.legger td.mid, table.grid.legger th.miring { text-align: center; }
table.grid.legger th.miring { white-space: nowrap; font-size: var(--t-2xs); letter-spacing: .02em; }
table.grid.legger td.name { white-space: nowrap; }
table.grid.legger td.below { color: var(--danger); font-weight: 700; }
table.grid.legger td.tebal { font-weight: 700; }
@media print {
  .legger-alat { display: none !important; }
  table.grid.legger { font-size: .72rem; }
  .table-wrap { overflow: visible !important; }
}

/* ---- sel capaian kompetensi pada rapor ---- */
table.rapor-nilai td.capaian { min-width: 240px; line-height: 1.45; }
table.rapor-nilai td.capaian:focus {
  outline: 2px solid var(--green-500); outline-offset: -2px; background: #fff;
}
@media print { table.rapor-nilai td.capaian:focus { outline: none; } }

/* ---- sanggahan nilai ---- */
.panel.sanggah { max-width: 900px; margin: 0 auto 1.4rem; }
.sanggah-kartu {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: .7rem .2rem; border-top: 1px solid var(--line);
}
.sanggah-isi { flex: 1 1 320px; min-width: 0; }
.sanggah-alasan { margin-top: .25rem; font-size: var(--t-sm); color: var(--ink-900); }
.sanggah-tombol { display: flex; gap: .5rem; flex-wrap: wrap; }
.tautan-sanggah {
  border: 0; background: none; padding: .2rem 0; margin-top: .25rem;
  color: var(--green-700); font: inherit; font-size: var(--t-2xs);
  font-weight: 600; text-decoration: underline; cursor: pointer;
}
.tautan-sanggah:hover { color: var(--green-900); }
.sanggah-tanda { margin-top: .3rem; }
@media (max-width: 560px) {
  .sanggah-kartu { flex-direction: column; }
  .sanggah-isi { flex-basis: auto; }
  .sanggah-tombol { width: 100%; }
  .sanggah-tombol .btn { flex: 1 1 auto; justify-content: center; }
}

/* ==========================================================================
   Halaman login — tiga perbaikan
   ========================================================================== */

/* 1. "Kotak di dalam kotak" pada kolom yang sedang diisi.
      Aturan umum `:focus-visible` memberi SETIAP elemen yang mendapat fokus
      papan ketik sebuah cincin box-shadow beserta border-radius 10px. Kotak
      isian login tidak menetapkan radiusnya sendiri, jadi ia menerima radius
      10px itu — lalu cincinnya digambar di DALAM pembungkus pil yang sudah
      punya cincin sendiri (.box:focus-within). Hasilnya dua cincin bersarang.
      Pembungkusnya sudah cukup menandakan fokus, jadi cincin bagian dalam
      dimatikan. */
.login-field input:focus-visible,
.saring-bar input:focus-visible { box-shadow: none; }

/* 2. Bilah gulir dan ruang kosong di bawah pita kaki.
 *
 *    Dua lingkaran hiasan di latar halaman login diletakkan MELUAR dari
 *    kotaknya — yang bawah dipasang `bottom: -160px`. Halaman hanya memberi
 *    `overflow-x: hidden`, yang memotong sumbu mendatar saja. Sumbu tegaknya
 *    tetap terbuka, sehingga lingkaran itu menambah 160px pada tinggi
 *    dokumen: muncul bilah gulir, dan di bawah pita kaki tersisa ruang kosong
 *    setinggi lingkaran yang tidak terlihat itu.
 *
 *    Sekaligus ada jebakan kedua: menurut spesifikasi CSS, `overflow-y`
 *    yang `visible` berubah menjadi `auto` begitu sumbu lain diberi `hidden`.
 *    Jadi .login-page diam-diam menjadi wadah gulirnya sendiri — bilah gulir
 *    di dalam bilah gulir jendela.
 *
 *    `overflow: clip` memotong KEDUA sumbu tanpa membuat wadah gulir.
 *    `hidden` ditulis lebih dulu sebagai cadangan peramban lama. */
.login-page { overflow: hidden; overflow: clip; }

/* 3. 100vh pada peramban tertentu lebih tinggi daripada bagian yang benar-
      benar terlihat, sehingga halaman yang sebenarnya pas justru menyisakan
      bilah gulir. 100dvh mengikuti tinggi yang benar-benar tersedia. */
@supports (height: 100dvh) {
  .login-page { min-height: 100dvh; }
}

/* Tombol tindakan tambahan pada kaki dialog (mis. "Hapus kolom") sengaja
   didorong ke sisi kiri, sejauh mungkin dari tombol Simpan. */
.dialog-foot .btn.kiri { margin-right: auto; }

/* --------------------------------------------------------------------------
   Matriks penugasan mengajar (mata pelajaran × rombel)
   --------------------------------------------------------------------------
   Kolomnya bisa belasan, jadi tabelnya bergulir sendiri di dalam .table-wrap
   sementara kolom nama mata pelajaran tetap menempel di kiri — tanpa itu,
   menggulir ke kanan membuat pengguna kehilangan jejak barisnya.
   ----------------------------------------------------------------------- */
table.grid.matriks { border-collapse: separate; border-spacing: 0; font-size: var(--t-xs); }
table.grid.matriks th.mapel {
  position: sticky; left: 0; z-index: 2;
  background: var(--paper); text-align: left; white-space: nowrap;
  padding: .35rem .6rem; cursor: pointer; font-weight: 600;
  box-shadow: 1px 0 0 var(--line);
}
table.grid.matriks thead th.mapel { z-index: 3; }
table.grid.matriks th.rombel {
  white-space: nowrap; padding: .35rem .5rem; cursor: pointer; font-weight: 600;
}
table.grid.matriks th.mapel:hover,
table.grid.matriks th.rombel:hover { color: var(--green-700); text-decoration: underline; }
table.grid.matriks td.kotak { text-align: center; padding: .25rem .4rem; }
table.grid.matriks td.kotak input { width: 17px; height: 17px; cursor: pointer; }
table.grid.matriks td.terkunci { background: var(--canvas); }
table.grid.matriks td.terkunci input { cursor: not-allowed; }
table.grid.matriks tbody tr:nth-child(even) th.mapel { background: var(--paper); }

/* --------------------------------------------------------------------------
   Lambang sekolah sebagai cap air pada halaman masuk
   --------------------------------------------------------------------------
   Yang dipakai hanya BINTANG DAN BUKUNYA — tanpa tulisan melengkung
   "SEKOLAH ISLAM TERPADU", tanpa "BINA ILMI", dan tanpa lockup
   "ISLAMIC BILINGUAL SCHOOL" di sebelahnya.

   Alasannya bukan selera. Percobaan pertama memakai lambang lengkap beserta
   tulisannya: hasilnya tulisan itu terbaca setengah dan terpotong di tepi
   layar ("SEKOLAH ISLAM TERPAD…"), sehingga terlihat seperti gambar yang
   salah taruh, bukan cap air. Lambang yang terpotong justru melemahkan
   merek. Bentuk buku dan bintangnya sendiri sudah cukup dikenali, dan ia
   tetap utuh pada ukuran layar mana pun.

   Opasitasnya dibakar ke dalam berkas PNG-nya (alpha tertinggi 20 dari 255,
   sekitar 8%), jadi tidak perlu elemen tambahan apa pun di atas halaman —
   cukup satu latar belakang. Berkasnya kecil (340 px, 3,5 KB) lalu dibesarkan
   oleh CSS: pada opasitas serendah ini mata tidak dapat melihat bedanya,
   sedangkan yang diunduh sekolah jauh lebih ringan.

   Letaknya di tengah, tepat di belakang kartu masuk, sehingga yang terlihat
   hanya tepi-tepinya yang mengelilingi kartu secara seimbang — bukan
   menumpuk di satu sudut bersama tautan kaki halaman.

   Sengaja HANYA di halaman masuk. Halaman lain berisi tabel angka yang rapat;
   nilai rapor tidak boleh berebut perhatian dengan hiasan.
   ----------------------------------------------------------------------- */
/* Sejak ronde 88 latar halaman masuk adalah FOTO GEDUNG SEKOLAH, bukan lagi
   cap air lambang. Dua lapis, dari yang paling depan:
     1. selubung — gradasi kanvas yang meredam foto di seluruh tinggi halaman;
     2. gedung.webp — foto gedung sekolah;
   ditambah warna kanvas sebagai alas bila fotonya gagal dimuat.

   Cap air lambangnya DIBUANG, bukan tertimpa. Alasannya: lambang sekolah sudah
   berdiri di kiri atas halaman sebagai lockup header. Menaruhnya sekali lagi
   sebagai cap air DI ATAS foto gedung berarti dua penanda identitas berebut
   satu layar, dan yang kalah justru kartu masuknya.

   Selubungnya bukan hiasan dan tidak boleh ditipiskan "biar fotonya kelihatan".
   Foto ini terang, berwarna kuning-hijau pekat, dan penuh garis diagonal.
   Tanpa selubung: baris "Penilaian waktu nyata — Sekolah Islam Terpadu Bina
   Ilmi Palembang" jatuh ke 1,9 : 1 dan "Kesulitan masuk?" ke 2,1 : 1 — dua-
   duanya jauh di bawah AA, dan keduanya adalah kalimat yang dibaca guru yang
   baru pertama kali membuka aplikasi ini. Selubung tidak pernah turun di bawah
   .72 supaya seluruh tulisan halaman ini tetap berdiri di atas kanvas terang,
   persis seperti waktu dirancang. Yang tersisa dari fotonya adalah bentuk dan
   warna gedungnya — cukup untuk dikenali, tidak cukup untuk mengganggu. */
.login-page {
  background-image:
    linear-gradient(90deg, rgba(243,251,244,0) 0%,
                    rgba(243,251,244,.86) 20%,
                    rgba(243,251,244,.94) 34%,
                    rgba(243,251,244,.94) 66%,
                    rgba(243,251,244,.86) 80%,
                    rgba(243,251,244,0) 100%),
    linear-gradient(180deg, rgba(243,251,244,.55) 0%,
                    rgba(243,251,244,.48) 30%,
                    rgba(243,251,244,.52) 62%,
                    rgba(232,244,235,.62) 100%),
    url('./gedung.webp');
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-attachment: scroll, scroll, fixed;
  background-color: var(--canvas);
  /* background-position dan background-size TIDAK ditulis di sini. Keduanya
     diatur satu kali di blok "SUSUNAN HALAMAN MASUK" jauh di bawah berkas ini,
     supaya tidak ada dua tempat yang saling menimpa. Ronde 88 sempat kejeblos
     justru di sini: nilai tiga lapis ditulis di blok ini, lalu blok bawah yang
     masih bersisa dari era satu-lapis menimpanya dengan satu nilai tunggal —
     foto gedungnya menyusut jadi kotak 760 piksel di tengah layar. */
}
/* Peramban seluler kerap mengabaikan background-attachment: fixed dan
   menyisakan foto yang meregang aneh saat halaman digulir. */
@media (max-width: 860px), (max-height: 640px) {
  .login-page { background-attachment: scroll, scroll, scroll; }
}

@media print { .login-page { background-image: none; background-color: #fff; } }

/* Panel peringatan "akun siswa belum tertaut". Sengaja BUKAN .panel.bobot —
   panel itu punya lebar dan jarak sendiri, dan menumpanginya membuat dua hal
   yang berbeda berbagi satu nama. */
.panel.gantung {
  max-width: 780px; margin: 0 auto 1.6rem;
  border-color: var(--warn); background: var(--warn-bg);
}
.panel.gantung .panel-lead { color: var(--ink-600); }

/* ==========================================================================
   SASARAN SENTUH
   Diukur di layar 360 piksel: lonceng 36×36, tombol Cari 36×34, tombol unit
   75×33, tautan tindakan pada baris tabel 143×23. Semuanya di bawah 44 piksel
   yang menjadi anjuran umum untuk jempol — dan tombol-tombol itu justru yang
   paling sering ditekan.

   Sengaja tidak berlaku di layar besar bermouse: di sana penunjuknya presisi,
   dan membesarkan seluruh kepala halaman hanya membuatnya terasa kasar.

   Kotak nilai di dalam tabel sengaja DIBIARKAN 74×30. Menaikkannya ke 44
   menambah sekitar 40% tinggi tabel — untuk rombel 30 siswa itu berarti satu
   layar penuh gulir tambahan setiap kali. Ukurannya masih di atas batas
   minimum 24×24, dan baris serta kolom yang sedang diisi ikut disorot,
   sehingga salah tekan tetap terlihat.
   ========================================================================== */
@media (max-width: 1024px), (pointer: coarse) {
  .bell-button,
  .cari-button { min-width: 44px; min-height: 44px; }
  .user-button,
  .unit-button,
  /* Pil jam menjadi tombol pada ronde ini, jadi ia ikut terhitung sebagai
     sasaran sentuh dan harus setinggi yang lain. */
  .clock,
  .back-btn,
  .ringkas-tautan,
  .saring-bar input,
  .chips .chip,
  .toolbar .chip,
  .field select,
  .crumbs a { min-height: 44px; }

  .to-top { width: 44px; height: 44px; }

  /* Tautan tindakan di baris tabel ("Ubah", "Hapus", "Tautkan") adalah yang
     paling sempit sekaligus paling berisiko salah tekan — keduanya berdempetan
     dan salah satunya menghapus data. */
  .act-links button,
  .act-links a { min-height: 44px; padding-top: .5rem; padding-bottom: .5rem; }

  /* Semua tombol utama ikut disamakan; beberapa berhenti di 43 piksel.
     .btn.sm dulu berhenti di 40 dengan alasan "ia memang tombol kecil" —
     alasan yang tidak berlaku bagi jari. Ia dipakai untuk "Pilih foto",
     "Hapus foto", dan tombol kunci semester pada tabel Tahun Ajaran: tiga
     tombol yang salah tekannya berakibat nyata. Yang membesar hanya ruang
     sentuhnya; hurufnya tetap kecil, jadi ia tetap terbaca sebagai tombol
     sekunder. */
  .btn { min-height: 44px; }
  .btn.sm { min-height: 44px; }

  /* Tombol "+R" hidup di dalam kepala tabel. Membesarkannya ke 44 ikut
     menaikkan seluruh baris kepala, jadi ia berhenti di 32 — cukup naik dari
     23, tanpa mendorong tabelnya. */
  table.grid th.tambah-r .mini-tambah { min-height: 32px; }

  /* Kotak centang bawaan peramban hanya 13×13; 24 adalah batas minimum yang
     lazim dipakai. Aturan .kenaikan ikut disebut
     karena ia jauh lebih khusus dan akan menang tanpa itu; flex: none supaya
     ia tidak menciut lagi ketika barisnya berubah menjadi kartu di ponsel. */
  input[type=checkbox],
  input[type=radio],
  .kenaikan table.list td input[type="checkbox"] {
    width: 24px; height: 24px; flex: none;
  }

  /* Kotak centang yang berdiri sendiri di dalam sel tabel tidak punya label
     yang ikut menerima sentuhan, jadi 24 piksel itulah seluruh sasarannya.
     Pembungkusnya yang dibesarkan ke 44, bukan kotaknya — kotak centang
     44×44 terlihat seperti kesalahan. */
  .sentuh-kotak { min-width: 44px; min-height: 44px; }

  /* Breadcrumb tetap satu baris; yang bertambah hanya ruang sentuhnya. */
  .crumbs a { display: inline-flex; align-items: center; }
}

.sentuh-kotak {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

/* Peringatan "peringkat masih sementara" di bawah legger. Ia tetap kecil dan
   kalem — ini keterangan, bukan galat — tetapi diberi garis tepi supaya tidak
   larut menjadi baris ketiga dari catatan kaki di atasnya. */
.legger-awas {
  max-width: 46rem;
  margin-top: .35rem;
  padding: .55rem .85rem;
  border-left: 3px solid var(--warn);
  background: var(--warn-bg);
  border-radius: var(--r-sm);
}

@media print { .legger-awas { background: none; } }

/* ==========================================================================
   PANEL PASSWORD BAWAAN

   Lencana "masih 123456" tetap ada, tetapi kini ia TOMBOL: menekannya membuka
   kartu akun orang itu. Ia harus tetap terlihat seperti lencana — kecil dan
   kalem — sambil cukup jelas bahwa ia bisa ditekan.
   ========================================================================== */
.badge-tombol {
  border: 1px solid transparent;
  font: inherit;
  font-size: var(--t-2xs);
  font-weight: 700;
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.badge-tombol:hover,
.badge-tombol:focus-visible {
  border-color: var(--green-500);
  color: var(--green-900);
}

/* Sasaran sentuh: di layar sentuh lencananya diberi ruang tekan yang layak
   tanpa ikut menggemukkan barisnya, karena ia berdampingan dengan teks
   "Aktif" di dalam satu sel. */
@media (max-width: 1024px), (pointer: coarse) {
  .badge-tombol { min-height: 44px; padding-left: .7rem; padding-right: .7rem; }
}

/* Panel ringkasan password bawaan dihapus atas permintaan sekolah — lencana
   pada tiap baris tabel sudah mengatakan hal yang sama. Yang tersisa dari
   bagian itu hanyalah lencananya, dan lencana itu kini sebuah tombol. */

/* ==========================================================================
   LONCENG — kabar yang boleh dibuang

   Barisnya kini <div> berisi dua tombol: badan yang membuka halamannya, dan ✕
   yang membuang kabarnya. Aturan .bell-item lama di styles.css masih memberi
   kerangka warna dan garisnya; yang ditambahkan di sini hanya pembagian ruang
   antara keduanya.
   ========================================================================== */
.bell-item {
  padding: 0;                 /* padding pindah ke tombol badannya */
  align-items: stretch;
  gap: 0;
}

.bell-utama {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  gap: .75rem;
  align-items: flex-start;
  text-align: left;
  padding: .8rem .4rem .8rem 1rem;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

/* Titik kecil penanda belum dibaca. Sebelumnya satu-satunya penandanya adalah
   latar yang nyaris putih (#f7fcf8) — perbedaan yang tidak terlihat di layar
   HP di bawah cahaya matahari. */
.bell-item.unread .t::after {
  content: '';
  display: inline-block;
  width: 7px; height: 7px;
  margin-left: .4rem;
  vertical-align: middle;
  border-radius: 50%;
  background: var(--green-600);
}

.bell-buang {
  flex: none;
  align-self: center;
  width: 32px; height: 32px;
  margin-right: .5rem;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--ink-400);
  font-size: var(--t-sm);
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-content: center;
  /* Tidak disembunyikan sampai kursor lewat: di layar sentuh tidak ada kursor
     yang bisa lewat, dan tombol yang tak terlihat sama dengan tombol yang
     tidak ada. Yang berubah saat disentuh cuma warnanya. */
  transition: background .15s, color .15s;
}
.bell-buang:hover,
.bell-buang:focus-visible { background: var(--danger-bg); color: var(--danger); }

/* Dua tombol berdampingan di kepala lonceng yang lebarnya cuma 370px. Tanpa
   nowrap keduanya patah menjadi dua baris ("Tandai / dibaca"), dan kepalanya
   menjadi setinggi dua baris untuk dua kata. */
.bell-aksi { display: flex; gap: .15rem; align-items: center; flex: none; }
.bell-head { gap: .4rem; }
.bell-head strong { white-space: nowrap; }
.bell-head .link { white-space: nowrap; }

/* Di bawah 400px, judul + dua tombol tidak muat dalam satu baris selebar
   341px. Yang turun adalah TOMBOLNYA ke baris kedua — bukan judulnya yang
   dipotong, dan bukan tulisan tombolnya yang dipatah menjadi dua baris. */
@media (max-width: 400px) {
  .bell-head { flex-wrap: wrap; }
  .bell-aksi { width: 100%; justify-content: flex-end; }
}
.bell-head .link.buang { color: var(--ink-600); }
.bell-head .link.buang:hover { background: var(--danger-bg); color: var(--danger); }

.bell-empty .link {
  margin-top: .7rem;
  border: 0; background: none; cursor: pointer; font: inherit;
  font-size: var(--t-2xs); font-weight: 600; color: var(--green-700);
  padding: .35rem .6rem; border-radius: var(--r-sm);
}
.bell-empty .link:hover { background: var(--green-100); }

@media (max-width: 1024px), (pointer: coarse) {
  .bell-buang { width: 44px; height: 44px; margin-right: .25rem; }
  .bell-utama { padding-left: .85rem; }
}

/* ==========================================================================
   KOTAK KETIK-CARI

   Pengganti <select> untuk daftar panjang. <select> bawaan peramban membuka
   daftarnya sebagai lembaran yang menutupi hampir seluruh layar — di HP ia
   menutupi SELURUHNYA, sehingga admin kehilangan formulir yang sedang diisi.
   Di sini daftarnya menggantung di bawah isian, tingginya dipatok, dan
   menyusut sendiri begitu diketik.
   ========================================================================== */
.cari-pilih { position: relative; }

.cp-kotak {
  display: none;
  position: absolute;
  z-index: 40;
  left: 0; right: 0; top: calc(100% + .3rem);
  max-height: 17rem;
  overflow-y: auto;
  overscroll-behavior: contain;      /* guliran tidak merembet ke halaman */
  background: var(--paper);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  box-shadow: var(--sh-3);
  padding: .3rem;
}
.cp-kotak.buka { display: block; }

.cp-baris {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  padding: .55rem .7rem;
  min-height: 40px;
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: var(--t-sm);
}
.cp-baris:hover,
.cp-baris.sorot { background: var(--green-100); }

/* Nama boleh menyusut dan berujung elipsis; NIS di sebelahnya TIDAK, karena
   NIS-lah satu-satunya pembeda dua siswa yang bernama sama. */
.cp-nama {
  font-weight: 600;
  color: var(--ink-900);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Keterangan pembeda — NIS. Dua siswa bernama sama hanya dapat dibedakan di
   sini, jadi ia tidak boleh ikut menyusut sampai tidak terbaca. */
.cp-sub {
  margin-left: auto;
  flex: none;
  font-size: var(--t-2xs);
  color: var(--ink-600);
  font-variant-numeric: tabular-nums;
}

.cp-kosong {
  padding: .9rem .7rem;
  font-size: var(--t-sm);
  color: var(--ink-400);
  text-align: center;
}

@media (max-width: 1024px), (pointer: coarse) {
  /* Sasaran sentuh, dan daftar yang tidak menghabiskan layar HP. */
  .cp-baris { min-height: 44px; }
  .cp-kotak { max-height: 15rem; }
  /* Tetap satu baris per siswa, juga di HP. Menumpuk NIS di bawah namanya
     membuat tiap baris 66 piksel, sehingga hanya tiga setengah nama muat di
     layar — daftar yang harus digulir lagi, yang justru sedang diperbaiki. */
}

/* ==========================================================================
   SATU KOLOM ISI — penyeragaman
   Pasangan dari blok "SATU KOLOM ISI" di styles.css. Ditaruh di berkas ini,
   dan di paling bawah, karena beberapa blok memasang lebarnya sendiri di sini
   (.panel.gantung 780, .panel.sanggah 900, .tiles 1010). Kekhususannya sama
   persis, jadi yang menang adalah yang belakangan — dan yang harus menang
   adalah penyeragaman ini.

   Yang diseragamkan hanya ANAK LANGSUNG halaman. Blok yang sama ketika berada
   di dalam dialog atau di dalam blok lain tetap memakai lebarnya sendiri,
   karena di sana lebar itu memang beralasan.
   ========================================================================== */
.page > .notice,
.page > .panel,
.page > .ringkas,
.page > .progres-tahun,
.page > .profile-grid,
.page > .kartu-akun,
.page > .dropzone { max-width: 100%; margin-left: 0; margin-right: 0; }

/* Kekecualian: petak kartu tetap dipusatkan di dalam kolom. Isinya memang
   sudah rata tengah (justify-content: center) dan judul halaman pun rata
   tengah, jadi memakukannya ke tepi kiri justru membuat kartunya miring
   sendiri terhadap judulnya. Lebar khusus lima kartu (1010px) tetap berlaku. */
.page > .tiles,
.page > .tile-rows { max-width: 100%; margin-inline: auto; }
@media (min-width: 1024px) {
  .page > .tiles:has(> .tile.icon:nth-child(5)) { max-width: 1010px; }
}
/* Enam kartu diberi ruang selebar barisan angka di atasnya, sehingga keduanya
   berdiri di lebar yang sama alih-alih berupa pulau sempit di bawah baris yang
   lebar. Kalau enamnya tetap tidak muat — jendela yang lebih sempit — ui.js
   sendiri yang membaginya 3 + 3. */
@media (min-width: 1200px) {
  .page > .tiles:has(> .tile.icon:nth-child(6)) { max-width: 1100px; }
}

/* Tabel pun mengikuti kolom. table.list dibatasi 1080px dan dipusatkan, jadi
   di kolom 1124px ia menjorok 22 piksel di kedua sisi — cukup untuk terlihat
   tidak sejajar dengan kotak cari tepat di atasnya. table.grid menyusut
   selebar isinya lalu dipusatkan, yang membuatnya bergeser sendiri setiap
   kali jumlah kolomnya berubah. Keduanya kini bertumpu pada tepi yang sama
   dengan blok lain. Di dalam dialog tidak ada .page, jadi aturan ini tidak
   ikut campur di sana. */
.page table.list { max-width: 100%; }
.page .table-wrap > table.grid { margin-inline: 0; }

/* Wadah tabel menyusut selebar tabelnya. Wadah inilah yang membawa latar
   putih, sudut membulat, dan bayangannya; kalau ia tetap selebar kolom
   sementara tabelnya hanya 743 piksel (legger satu rombel kecil), yang
   terlihat adalah kertas putih kosong memanjang di sebelah kanan tabel.
   max-width menahannya di dalam kolom, dan overflow-x yang sudah ada
   mengambil alih begitu tabelnya lebih lebar. */
.page > .table-wrap:has(> table.grid) { width: fit-content; max-width: 100%; }

/* Halaman bertabel lebar memakai kolom yang lebih lebar.
   Lembar nilai dengan dua PH + remedial + ASAS + ASAT + rapor + keterangan
   sudah butuh 1237 piksel, dan legger lebih lagi. Memaksanya ke kolom 1180
   berarti guru harus menggeser tabel di layar laptop yang sebenarnya masih
   lapang. Yang dijaga adalah kerapian DI DALAM satu halaman: di sini judul,
   kartu keterangan, deretan kolom, dan tabelnya tetap bertumpu pada satu tepi
   yang sama — hanya tepinya yang lebih lebar daripada halaman lain.
   Dipilih lewat :has() supaya tidak ada halaman yang perlu menandai dirinya
   sendiri; yang punya .table-wrap sebagai anak langsung memang hanya lembar
   nilai dan legger. */
.page:has(> .table-wrap) { max-width: min(1500px, 100%); }

/* Baris jejak ikut kolom yang sama dengan isi halaman. Sebelum ini remah
   jejak berhenti di 28 piksel sementara judul di bawahnya mulai di 158 —
   dua tepi kiri berbeda yang bersebelahan langsung. Halaman bertabel lebar
   memakai kolom lebar, jadi baris jejaknya ikut melebar; :has() dibaca dari
   .app karena keduanya bersaudara, bukan beranak. */
.crumb-bar { width: 100%; max-width: var(--kolom); margin-inline: auto; }
.app:has(.page > .table-wrap) .crumb-bar { max-width: min(1500px, 100%); }

/* Halaman rapor: alat di atas mengikuti lebar kertasnya. Kertas rapor sengaja
   dibatasi 820 piksel supaya menyerupai A4; kalau baris alatnya tetap selebar
   kolom, keduanya berdiri di dua lebar berbeda tepat bersebelahan. */
.page > .rapor-alat { max-width: 820px; margin-inline: auto; }

/* Legger: alatnya rata KIRI, bukan rata kanan.
   Tabel legger menyusut selebar isinya dan bertumpu pada tepi kiri kolom
   (lihat .page > .table-wrap di atas). Baris alatnya rata kanan selebar
   kolom penuh, sehingga tombol "Unduh CSV" dan "Cetak" berdiri 650 piksel di
   sebelah kanan tepi tabel yang mereka layani — dua benda yang jelas satu
   urusan, diletakkan di dua ujung layar yang berbeda. */
.page > .legger-alat { justify-content: flex-start; }

/* Keterangan panjang di bawah tabel: satu baris selebar 1384 piksel memaksa
   mata melompat jauh mencari awal baris berikutnya. Batasnya diberikan dalam
   satuan karakter, bukan piksel, supaya ikut menyesuaikan bila pembacanya
   memperbesar hurufnya. */
.page > p.small.muted { max-width: 82ch; }

/* Dua kotak bobot berisi angka satu digit; diregangkan selebar setengah
   panel, keduanya terbaca sebagai kolom teks panjang yang menunggu diisi
   kalimat. */
.bobot-isian { max-width: 8rem; }

/* ==========================================================================
   KEPALA HALAMAN YANG TIDAK IKUT MELAR
   Nama guru di Indonesia kerap panjang bergelar ("Fathiyyah Yasmin, M.Pd.").
   Tanpa penahan, nama itu melipat menjadi dua atau tiga baris dan kepala
   halaman tumbuh dari 73 menjadi 103 piksel — persis di lebar 760–1100 yang
   dipakai tablet dan laptop kecil. Karena kepala halaman menempel (sticky),
   tingginya yang berubah-ubah menggeser seluruh isi di bawahnya.
   ========================================================================== */
.user-button .name {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 20ch;
}

/* Di bawah 1180 piksel semboyan di kiri dilepas lebih dulu — ia hiasan,
   sedangkan jam, unit, lonceng, dan nama adalah alat. */
@media (max-width: 1180px) {
  .app-header .brand-tagline { display: none; }
  .user-button .name { max-width: 16ch; }
}
@media (max-width: 900px) {
  .user-button .name { max-width: 10ch; }
}

/* ==========================================================================
   TABEL BERKOLOM BANYAK DI LAYAR SEDANG
   Daftar pengajuan lupa password punya tujuh kolom. Di tablet 820 piksel
   ketujuhnya diperas ke dalam 780 piksel, dan karena table.list memakai
   overflow:hidden untuk sudut membulatnya, kolom terakhir — yang berisi
   tombol "Beri password" dan "Tolak" — terpotong dan tidak dapat ditekan
   sama sekali. Bukan sekadar tidak rapi: satu tindakan hilang.

   Perubahan menjadi kartu bertumpuk sudah ada, tetapi baru mulai di 560
   piksel. Di sini ambangnya dinaikkan menjadi 900 piksel KHUSUS tabel yang
   kolomnya enam atau lebih; daftar siswa yang cuma empat kolom tetap
   berbentuk tabel karena di 820 piksel ia masih lapang.
   ========================================================================== */
@media (max-width: 900px) {
  table.list:has(> thead > tr > th:nth-child(6)),
  table.list:has(> thead > tr > th:nth-child(6)) tbody,
  table.list:has(> thead > tr > th:nth-child(6)) tr,
  table.list:has(> thead > tr > th:nth-child(6)) td { display: block; width: 100%; }
  table.list:has(> thead > tr > th:nth-child(6)) thead { display: none; }
  table.list:has(> thead > tr > th:nth-child(6)) tr {
    border: 1px solid var(--line); border-radius: var(--r);
    margin-bottom: .7rem; padding: .6rem .8rem; background: var(--paper);
    box-shadow: var(--sh-1);
  }
  table.list:has(> thead > tr > th:nth-child(6)) td {
    border: 0; padding: .28rem 0; display: flex; gap: .6rem;
  }
  /* Sel yang memang kosong tidak perlu barisnya sendiri. "Mengajar" bagi guru
     yang belum diberi mapel dulu tetap memakan satu baris berisi label dan
     ruang kosong di sebelahnya — lima baris seperti itu dalam satu kartu. */
  table.list:has(> thead > tr > th:nth-child(6)) td:empty { display: none; }
  table.list:has(> thead > tr > th:nth-child(6)) td::before {
    content: attr(data-label); flex: none; min-width: 92px;
    color: var(--ink-400); font-size: var(--t-2xs); font-weight: 600;
  }
  table.list:has(> thead > tr > th:nth-child(6)) td.act { padding-top: .5rem; }
  table.list:has(> thead > tr > th:nth-child(6)) .act-links { gap: 1rem; }
}

/* Dalam bentuk kartu, nomor urut kehilangan gunanya: kartunya sendiri sudah
   menjadi barisnya. Yang disembunyikan hanya sel bernama "No" — kolom .no
   juga dipakai untuk kotak centang kenaikan kelas, dan itu harus tetap ada. */
@media (max-width: 900px) {
  table.list td.no[data-label="No"] { display: none; }
}

/* Remah jejak punya bantalan .5rem di kiri supaya sorotannya berbentuk pil.
   Ketika tidak ada tombol kembali (halaman paling atas), bantalan itu membuat
   tulisan "Dashboard" bergeser 8 piksel ke kanan dari tepi isi di bawahnya.
   Ditarik kembali persis sebesar bantalannya, jadi sorotannya tetap utuh. */
.crumb-bar > .crumbs:first-child { margin-left: -.5rem; }

/* ==========================================================================
   LANGKAH AWAL — satu langkah menonjol, sisanya dilipat

   Bentuk lamanya menampilkan keenam langkah sekaligus. Pada unit yang hampir
   selesai itu berarti lima baris centang setinggi hampir 500 piksel yang tidak
   menyuruh apa-apa, lalu satu baris di paling bawah yang sebenarnya perlu
   dikerjakan — yang paling penting mendapat tempat paling tidak menonjol.
   ========================================================================== */
.langkah-awal {
  width: 100%; max-width: 100%;
  border: 1px solid var(--green-200, #cfe6d6);
  background: linear-gradient(180deg, #f7fcf8, #f2f9f4);
  border-radius: var(--r-lg); padding: 1.1rem 1.2rem 1rem;
  margin: 0 0 1.8rem; box-shadow: var(--sh-1);
  text-align: left;
}

/* --- kepala: judul + kemajuan --------------------------------------------- */
.la-kepala { display: flex; align-items: center; gap: .85rem; margin-bottom: .9rem; }
.la-ikon {
  flex: none; width: 40px; height: 40px; border-radius: 12px;
  background: var(--green-100); border: 1px solid var(--green-200, #cfe6d6);
  display: grid; place-content: center; font-size: var(--t-lg);
}
.la-kepala-teks { flex: 1 1 auto; min-width: 0; }
.la-kepala strong { display: block; color: var(--ink-900); }

.la-kemajuan { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: .3rem; }
.la-angka { font-size: var(--t-sm); font-weight: 800; color: var(--green-800); }
.la-bilah {
  display: block; width: 92px; height: 6px; border-radius: var(--r-pill);
  background: var(--green-100); overflow: hidden;
}
.la-bilah > span {
  display: block; height: 100%; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--green-500), var(--green-700));
  transition: width .5s var(--ease);
}

/* --- langkah yang sedang giliran ------------------------------------------ */
.la-kini {
  display: flex; align-items: center; gap: .85rem;
  padding: .9rem 1rem; border-radius: var(--r);
  background: var(--paper); border: 1px solid var(--green-200, #cfe6d6);
  box-shadow: var(--sh-1);
}
.la-tanda {
  flex: none; width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-content: center;
  font-size: var(--t-xs); font-weight: 700;
  background: #eef1ef; color: var(--ink-600); border: 1px solid var(--line-2);
}
.la-tanda.kini { background: var(--green-700); color: #fff; border-color: var(--green-700); }
.la-tanda.kecil { width: 20px; height: 20px; font-size: var(--t-2xs);
  background: var(--green-100); color: var(--green-700); border-color: var(--green-200, #cfe6d6); }

.la-teks { flex: 1 1 auto; min-width: 0; }
.la-judul { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; }
.la-judul > span:first-child { font-weight: 700; color: var(--ink-900); }
.la-isi { font-size: var(--t-xs); color: var(--ink-400); }
.la-catatan { font-size: var(--t-xs); color: var(--ink-600); margin-top: .2rem; }
.la-tombol { flex: none; }

/* --- lipatan langkah yang sudah selesai ----------------------------------- */
.la-lipat {
  display: flex; align-items: center; justify-content: center; gap: .45rem;
  width: 100%; margin: .7rem 0 0; padding: .5rem;
  background: none; border: 0; cursor: pointer; font: inherit;
  font-size: var(--t-xs); font-weight: 600; color: var(--green-700);
  border-radius: var(--r-sm); transition: background .14s, color .14s;
}
.la-lipat:hover { background: var(--green-100); color: var(--green-900); }
.la-lipat-tanda { font-size: var(--t-2xs); }

.la-selesai { list-style: none; margin: .25rem 0 0; padding: 0; display: grid; gap: .1rem; }
.la-selesai.hide { display: none; }
.la-mini {
  display: flex; align-items: center; gap: .6rem;
  padding: .35rem .5rem; border-radius: var(--r-sm);
}
.la-mini:nth-child(odd) { background: rgba(255,255,255,.6); }
.la-mini-judul { color: var(--ink-600); font-size: var(--t-xs); font-weight: 600; }
.la-mini-isi { color: var(--ink-400); font-size: var(--t-xs); margin-left: auto; }

@media (max-width: 640px) {
  /* Kemajuan pindah ke bawah judul: berdampingan, ia memeras judul unit
     menjadi dua-tiga baris pada layar sempit. */
  .la-kepala { flex-wrap: wrap; }
  /* flex-basis auto membuat blok judul selebar isinya, sehingga ia terdorong
     ke baris kedua dan lambang roket tertinggal sendirian di baris pertama.
     Basis 0 menyuruhnya menyusut mengikuti ruang yang tersisa. */
  .la-kepala-teks { flex: 1 1 0; }
  /* Tiga baris untuk menjelaskan urutan yang sudah dipaksakan oleh panelnya
     sendiri — di HP ruang itu lebih baik dipakai langkahnya. */
  .la-urut { display: none; }
  .la-kemajuan {
    flex-direction: row; align-items: center; gap: .5rem;
    width: 100%; order: 3; margin-top: .1rem;
  }
  .la-bilah { flex: 1 1 auto; width: auto; }
  .la-kini { flex-wrap: wrap; }
  .la-tombol { width: 100%; margin-top: .5rem; }
  .la-mini-isi { margin-left: 0; }
  .la-mini { flex-wrap: wrap; gap: .25rem .6rem; }
}

/* ==========================================================================
   PANDUAN
   Bagian milik peran pembaca terbuka; sisanya terlipat supaya halamannya
   tidak menjadi dinding teks yang tidak dibaca siapa pun.
   ========================================================================== */
.panduan { display: grid; gap: .7rem; text-align: left; }
.pd-bagian {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper); box-shadow: var(--sh-1); overflow: hidden;
}
.pd-kepala {
  width: 100%; display: flex; align-items: center; gap: .7rem;
  padding: .9rem 1rem; background: none; border: 0; cursor: pointer;
  font: inherit; text-align: left; color: var(--ink-900);
  transition: background .14s;
}
.pd-kepala:hover { background: var(--green-050); }
.pd-ikon { flex: none; font-size: var(--t-lg); }
.pd-judul { flex: 1 1 auto; font-weight: 700; }
.pd-tanda { flex: none; color: var(--ink-400); font-size: var(--t-xs); }
.pd-isi { padding: 0 1rem 1rem; display: grid; gap: .9rem; }
.pd-isi.hide { display: none; }
.pd-tanya { border-left: 3px solid var(--green-200); padding-left: .85rem; }
.pd-t { font-weight: 600; color: var(--ink-900); margin-bottom: .15rem; }
.pd-d { color: var(--ink-600); font-size: var(--t-md); line-height: 1.55; }

@media (max-width: 560px) {
  .pd-kepala { padding: .8rem .8rem; }
  .pd-isi { padding: 0 .8rem .9rem; }
}

/* ==========================================================================
   STATUS AKUN — "Aktif" berdampingan dengan lencana keadaan password
   Kolom ini sekarang memuat dua keterangan. Di layar sempit tabelnya sudah
   menjadi kartu bertumpuk, dan sel .status-akun ikut dibaca sebagai satu
   baris keterangan — jadi keduanya dibiarkan mendatar dan boleh melipat,
   bukan ditumpuk paksa.
   ========================================================================== */
.status-akun {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
}
.status-akun .badge { white-space: nowrap; }

/* ==========================================================================
   KERAPATAN TABEL — padat di laptop, lega di layar sentuh

   Dua pemakaian yang berbeda kebutuhannya, dan sebelumnya diberi bantalan
   yang sama:

   - Di LAPTOP, guru mengisi nilai satu rombel berisi 30 siswa. Yang ia mau
     adalah melihat sebanyak mungkin nama sekaligus; setiap gulir adalah satu
     kali kehilangan tempat. Bantalan .62rem × 30 baris memaksa menggulir dua
     kali untuk satu rombel.
   - Di HP dan TAB, yang menekan adalah jari, bukan penunjuk. Baris yang
     rapat berarti salah tekan, dan di layar nilai salah tekan berarti angka
     masuk ke baris siswa yang keliru.

   Jadi ambangnya dibalik dari kebiasaan: dirapatkan justru di layar besar.
   Batas 44 piksel di layar sentuh yang dipasang di bagian 54 tidak disentuh.
   ========================================================================== */
@media (min-width: 1025px) and (pointer: fine) {
  table.grid th,
  table.grid td { padding: .42rem .8rem; }
  table.grid input[type=number] { padding: .2rem; }

  table.list th { padding: .6rem 1rem; }
  table.list td { padding: .55rem 1rem; }

  /* Kepala tabel nilai bertingkat dua; merapatkan barisnya tanpa merapatkan
     kepalanya membuat kepala itu tampak melayang. */
  table.grid thead th { padding-top: .5rem; padding-bottom: .5rem; }
}

/* Di layar sentuh, baris dilonggarkan — bukan dibiarkan seperti semula. */
@media (max-width: 1024px), (pointer: coarse) {
  table.list td { padding: .85rem 1rem; }
  table.grid th,
  table.grid td { padding: .7rem .9rem; }
}

/* ==========================================================================
   ANGKA NILAI DI LAYAR SENTUH

   Tabel nilai di bawah 560 piksel sengaja mengecilkan hurufnya menjadi 12px
   supaya kolomnya muat. Itu benar untuk kepala tabel dan nama — tetapi ikut
   mengecilkan KOTAK ISIAN ANGKANYA, dan di situ ada dua masalah:

   1. Safari di iPhone MEMPERBESAR PAKSA seluruh halaman begitu kursor masuk
      ke isian yang hurufnya di bawah 16px. Guru yang mengisi nilai lewat HP
      mengalami halamannya melompat pada setiap sel — dan sesudah melompat,
      kolom berikutnya tidak lagi di tempat yang ia lihat sedetik lalu.
   2. Angka adalah satu-satunya isi yang benar-benar dibaca ulang untuk
      diperiksa. Ia justru yang paling tidak boleh kecil.

   Jadi hurufnya dikecilkan, isiannya tidak.
   ========================================================================== */
@media (max-width: 560px) {
  table.grid input[type=number] {
    font-size: 1rem;          /* 16px — batas Safari iOS, jangan diturunkan */
    font-weight: 600;
  }
  /* Nama siswa adalah kolom yang dipakai memastikan "angka ini milik siapa".
     Ia dinaikkan satu langkah dari sisa tabel. */
  table.grid td.name { font-size: var(--t-xs); }
}

/* ==========================================================================
   PANEL LANGKAH AWAL DI LAYAR SEMPIT

   Di HP, panel ini setinggi 567 piksel dan mendorong pintu menu pertama ke
   1040 piksel — admin harus menggulir satu layar penuh sebelum melihat satu
   pun menu. Panel penuntun yang menutupi yang dituntunnya bekerja melawan
   dirinya sendiri.

   Yang dirapatkan cuma jaraknya. Tidak ada langkah yang disembunyikan, dan
   catatan cara mengerjakannya tetap ada pada langkah yang sedang giliran —
   itu justru isinya.
   ========================================================================== */
/* Sesudah panel ini ditulis ulang menjadi "satu langkah + lipatan", .la-item
   dan .la-daftar tidak ada lagi; aturan untuk keduanya dibuang di sini alih-alih
   dibiarkan menggantung. Yang tersisa memang masih berlaku. */
@media (max-width: 560px) {
  .langkah-awal { padding: .85rem .9rem .8rem; margin-bottom: 1.2rem; }
  .la-kepala { gap: .65rem; margin-bottom: .55rem; }
  .la-ikon { width: 34px; height: 34px; font-size: var(--t-md); }
  .la-kini { padding: .75rem .8rem; gap: .65rem; }
  .la-tanda { width: 24px; height: 24px; font-size: var(--t-2xs); }
  .la-catatan { margin-top: .15rem; }
}

/* Tombol lipat terukur 36px — di bawah batas 44 yang dipakai seluruh
   aplikasi. Ia baris tunggal berlatar polos, jadi tidak ada tanda apa pun
   bahwa sasarannya kurang lebar; hanya jarinya yang tahu. */
@media (max-width: 1024px), (pointer: coarse) {
  .la-lipat { min-height: 44px; }
}

/* Aturan kerapatan di atas berlaku untuk tabel yang MASIH berbentuk tabel.
   Di bawah 900 piksel, tabel berkolom enam atau lebih sudah berubah menjadi
   kartu bertumpuk (lihat bagian 55) — di sana tiap sel menjadi satu baris
   keterangan, jadi bantalan 0,85rem dikalikan jumlah kolom. Pada halaman
   Tahun Ajaran itu menambah 529 piksel: satu setengah layar HP tanpa satu pun
   keterangan tambahan. Bantalan kartunya dikembalikan di sini, sesudah aturan
   kerapatan, supaya urutan berkasnya yang menentukan — bukan kebetulan. */
@media (max-width: 900px) {
  table.list:has(> thead > tr > th:nth-child(6)) td { padding: .28rem 0; }
  table.list:has(> thead > tr > th:nth-child(6)) td.act { padding-top: .5rem; }
}
/* Di bawah 560 piksel SELURUH table.list menjadi kartu, berapa pun kolomnya
   (styles.css baris 1208). Halaman Tahun Ajaran memakai tabel LIMA kolom, jadi
   ia tidak tertangkap penjaga :has(th:nth-child(6)) di atas — dan itulah yang
   membengkak 529 piksel. */
@media (max-width: 560px) {
  table.list td { padding: .28rem 0; }
  table.list td.act { padding-top: .5rem; }
}

/* ==========================================================================
   CATATAN: nilai cadangan warna sengaja TIDAK dipakai di berkas ini.

   Sebelumnya ada 17 bentuk seperti var(--warn, #b7791f) — token beserta warna
   cadangan bila tokennya tidak ada. Terdengar aman, tetapi semuanya sudah
   melenceng dari nilai tokennya yang sebenarnya:

       var(--warn, #b7791f)      token sesungguhnya #96600b
       var(--ok, #2f8b52)        token sesungguhnya #1e7040
       var(--ink-900, #1d2b23)   token sesungguhnya #16221b
       var(--line, #e2e8e4)      token sesungguhnya #e2ebe5

   Selama tokennya ada, cadangan itu tidak pernah terpakai — jadi selisihnya
   tidak pernah terlihat, dan tidak pernah ikut diperbaiki. Justru itu
   bahayanya: pada hari sebuah token diganti nama, halaman tidak akan rusak
   dengan jelas, melainkan diam-diam memakai warna lama yang salah. Dua di
   antaranya (#2f8b52 dan #b7791f) bahkan tidak lolos WCAG AA sebagai warna
   teks — perbaikan kontras di bagian ini akan batal tanpa satu pun tanda.

   Lebih baik pecah dengan nyaring daripada salah dengan diam.
   ========================================================================== */

/* ==========================================================================
   KARTU MATA PELAJARAN YANG BERISI KEADAAN

   Tile .icon biasa memusatkan ikon dan nama secara mendatar. Varian .berisi
   menambah satu blok keterangan di bawahnya, jadi tinggi dan perataannya
   berubah — tetapi lebar dan jaraknya tetap sama supaya berdampingan rapi
   dengan tile polos di halaman lain.
   ========================================================================== */
.tile.icon.berisi {
  height: auto; min-height: 176px;
  justify-content: flex-start; padding-top: 1.1rem; padding-bottom: .95rem;
}
.tile.icon.berisi .label { margin-bottom: .5rem; }
.tile.icon.berisi .isi {
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
  width: 100%; margin-top: auto;
}
.tile-rombel { font-size: var(--t-2xs); color: var(--ink-600); font-weight: 600; }
.tile-kosong {
  font-size: var(--t-2xs); color: var(--ink-400); font-style: italic;
  text-align: center; line-height: 1.3;
}
.tile-persen {
  display: flex; flex-direction: column; align-items: center; gap: .25rem;
  width: 100%; font-size: var(--t-2xs); color: var(--ink-600); font-weight: 600;
}

/* Bilah tipis di dalam tile digantikan cincin (kartu.js) pada ronde ini:
   pada lebar 64–76 piksel, 70% dan 80% terlihat sama panjang, sedangkan
   sudut lingkaran dapat dibedakan mata dengan mudah. */
.tp-kata { font-size: var(--t-2xs); color: var(--ink-600); }

/* Di layar sempit tile menjadi baris mendatar lebar penuh (bagian 54), jadi
   keterangannya ikut berbaris di kanan, bukan bertumpuk di bawah. */
@media (max-width: 720px) {
  .tile.icon.berisi { min-height: 0; padding-top: .7rem; padding-bottom: .7rem; }
  .tile.icon.berisi .label { margin-bottom: .15rem; }
  .tile.icon.berisi .isi {
    flex-direction: row; flex-wrap: wrap; align-items: center;
    justify-content: flex-start; gap: .45rem .7rem; margin-top: .1rem;
  }
  .tile-persen { flex-direction: row; width: auto; gap: .4rem; }
}

/* ==========================================================================
   BARIS TANGGAL DI BAWAH JUDUL BERANDA
   Masehi dulu, hijriah menyusul dengan pemisah titik. Hijriah dibuat lebih
   kecil dan lebih redup dengan sengaja: ia keterangan pendamping, bukan
   tanggal yang berlaku (lihat catatan di clock.js).
   ========================================================================== */
.baris-tanggal {
  display: flex; align-items: baseline; justify-content: center;
  flex-wrap: wrap; gap: .1rem .6rem;
  margin: -.7rem 0 1.5rem; text-align: center;
}
.bt-masehi { font-size: var(--t-sm); color: var(--ink-600); font-weight: 600; }
.bt-hijriah {
  font-size: var(--t-xs); color: var(--ink-400); font-weight: 500;
  cursor: help;
}
.bt-hijriah::before { content: '·'; margin-right: .55rem; color: var(--ink-200); }
/* Dugaan yang keliru dan sudah diperbaiki: saya kira di bawah 560px keduanya
   selalu bertumpuk, jadi titik pemisahnya dihapus. Ternyata pada 360–430px
   keduanya masih muat satu baris — dan tanpa titik maupun jarak, hasilnya
   terbaca "…25 Agu 202612 Rabiulawal 1448 H". Angka tahun menempel angka
   tanggal, dan mata membacanya sebagai satu bilangan.

   Titiknya dikembalikan. Bila memang terpaksa melipat, titik itu jatuh di awal
   baris kedua — sedikit janggal, tetapi jauh lebih baik daripada dua tanggal
   yang menyatu. */
@media (max-width: 560px) {
  .baris-tanggal { gap: .1rem .45rem; }
  .bt-hijriah::before { margin-right: .4rem; }
}

/* ==========================================================================
   SOROTAN DI BERANDA SISWA
   Tiga kartu kecil yang menjawab "bagaimana keadaan saya sekarang?".
   Nada "perhatian" sengaja memakai kuning, bukan merah: yang membaca layar ini
   anak sepuluh tahun bersama orang tuanya, dan angka yang menghakimi tidak
   membuat siapa pun belajar lebih giat.
   ========================================================================== */
.sorotan {
  display: grid; gap: .8rem; margin: 0 0 1.8rem;
  grid-template-columns: repeat(auto-fit, minmax(216px, 1fr));
  text-align: left;
}
.sorot {
  display: flex; align-items: flex-start; gap: .8rem;
  padding: .9rem 1rem; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--paper);
  box-shadow: var(--sh-1); min-width: 0;
}
.sorot.baik { border-color: var(--green-200); background: linear-gradient(180deg,#f8fdf9,#f3fbf5); }
.sorot.perhatian { border-color: #f2e0b8; background: linear-gradient(180deg,#fffdf6,#fdf8ec); }
.sorot-ikon {
  flex: none; width: 38px; height: 38px; border-radius: var(--r-sm);
  display: grid; place-content: center; font-size: var(--t-lg);
  background: var(--paper); border: 1px solid var(--line);
}
.sorot-teks { min-width: 0; flex: 1 1 auto; }
.sorot-judul {
  font-size: var(--t-2xs); color: var(--ink-600); font-weight: 700;
  letter-spacing: .02em; text-transform: uppercase;
}
.sorot-angka {
  font-size: var(--t-2xl); font-weight: 800; color: var(--green-800);
  line-height: 1.15; margin: .05rem 0 .1rem;
}
.sorot.perhatian .sorot-angka { color: #8a5f13; }
/* Nama mapel bisa panjang dan bisa banyak; dipotong satu baris dengan
   keterangan lengkapnya tersedia saat ditunjuk. */
.sorot-ket {
  font-size: var(--t-xs); color: var(--ink-600);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 560px) {
  .sorotan { grid-template-columns: 1fr; gap: .55rem; }
  .sorot { padding: .7rem .85rem; align-items: center; }
  .sorot-angka { font-size: var(--t-xl); }
}

/* ==========================================================================
   KARTU TERLIPAT (Kelola Akun -> Tambah akun baru)
   Bentuknya sengaja disamakan dengan .pd-bagian di halaman Panduan: satu
   aplikasi tidak boleh punya dua gaya lipatan yang berbeda.
   ========================================================================== */
.lipat {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper); box-shadow: var(--sh-1);
  margin: 0 0 1.8rem; overflow: hidden; text-align: left;
}
.lipat-kepala {
  width: 100%; display: flex; align-items: center; gap: .7rem;
  padding: .95rem 1.1rem; background: none; border: 0; cursor: pointer;
  font: inherit; text-align: left; color: var(--ink-900);
  transition: background .14s;
}
.lipat-kepala:hover { background: var(--green-050); }
.lipat-teks { flex: 1 1 auto; min-width: 0; }
.lipat-tanda { flex: none; color: var(--ink-400); font-size: var(--t-2xs); }
.lipat-isi { padding: .2rem 1.1rem 1.2rem; }
.lipat-isi.hide { display: none; }
@media (max-width: 560px) {
  .lipat-kepala { padding: .85rem .85rem; }
  .lipat-isi { padding: .2rem .85rem 1rem; }
}

/* ==========================================================================
   HALAMAN MASUK — satu lajur di atas lambang sekolah

   Susunan dua panel (identitas sekolah sebagai kartu hijau di kiri, kartu
   masuk di kanan) sempat dipasang lalu DIBATALKAN. Sebabnya bukan selera:
   panel hijau itu menempati tempat lambang sekolah, dan lambang itulah cap air
   yang membuat halaman ini terasa milik sekolahnya. Menukar lambang dengan
   kotak hijau berarti menukar identitas dengan hiasan.

   Yang tersisa: wordmark di atas kartu masuk, keduanya di tengah, dan
   lambangnya diperbesar supaya benar-benar terlihat — bukan sekadar ada.
   ========================================================================== */
.login-page {
  /* WAJIB dua nilai — selubung, lalu foto gedung. Menulis satu nilai saja
     berarti nilai itu diberlakukan ke KEDUA lapis, dan foto gedungnya menyusut
     jadi kotak di tengah layar dengan pita putih di kiri-kanannya. Itu persis
     kesalahan yang terjadi di ronde 88, dan sisa aturan satu-lapis di blok ini
     yang menyebabkannya.
     Fotonya digeser ke 30% supaya yang berdiri di belakang kartu masuk adalah
     badan gedungnya, bukan langit kosong di atasnya. */
  background-size: cover, cover, cover;
  background-position: center, center, center 30%;
}

/* Wordmark dan baris identitas sekolah dirapatkan ke kartu masuk supaya
   ketiganya terbaca sebagai satu kesatuan, bukan tiga benda yang kebetulan
   bertumpuk. */
.login-body .wordmark { margin-bottom: .1rem; }
.login-sub { margin-bottom: 1.35rem; }

@media (max-width: 640px) {
  /* Di HP fotonya dipotong jauh lebih sempit — pada 390 piksel yang tersisa
     hanya sepertiga tengah gambar. Digeser ke 38% supaya potongan itu jatuh
     pada badan gedung dan papan namanya, bukan pada langit kosong.

     Selubung LORONG diganti selubung RATA. Di laptop tulisan berdiri di
     sepertiga tengah layar, jadi meredam lorong itu saja sudah cukup dan
     fotonya tetap terlihat jelas di kiri-kanannya. Di HP tulisannya selebar
     layar — tidak ada "kiri-kanan" yang tersisa untuk dibiarkan terbuka, dan
     tepi lorong yang melembut justru jatuh tepat di bawah huruf-hurufnya:
     "Penilaian waktu nyata" terukur 2,4 : 1 di sana, jauh di bawah AA. */
  .login-page {
    background-image:
      linear-gradient(180deg, rgba(243,251,244,.90) 0%,
                      rgba(243,251,244,.90) 12%,
                      rgba(243,251,244,.95) 30%,
                      rgba(243,251,244,.95) 50%,
                      rgba(243,251,244,.95) 70%,
                      rgba(243,251,244,.96) 86%,
                      rgba(232,244,235,.93) 100%),
      url('./gedung.webp');
    background-repeat: no-repeat, no-repeat;
    background-size: cover, cover;
    background-position: center, center 38%;
    background-attachment: scroll, scroll;
  }
}

/* ==========================================================================
   ISIAN TEKS DI LAYAR SENTUH TIDAK BOLEH DI BAWAH 16 PIKSEL

   Safari di iPhone dan iPad MEMPERBESAR PAKSA seluruh halaman begitu kursor
   masuk ke isian yang hurufnya di bawah 16px. Ini bukan pengaturan yang bisa
   dimatikan aplikasi — satu-satunya cara menghindarinya adalah tidak
   menurunkan hurufnya.

   Akibatnya paling parah justru di halaman MASUK: guru menyentuh Username,
   halaman melompat membesar, dan kolom Password beserta tombol Login terdorong
   keluar layar. Orang yang baru pertama kali memakai aplikasi ini menyimpulkan
   aplikasinya rusak — pada layar pertama.

   Perbaikan yang sama sudah dipasang untuk kotak angka di tabel nilai; di sini
   ia diberlakukan untuk SELURUH isian teks, karena masalahnya sama di mana pun.
   Yang tidak disentuh: kotak centang dan tombol radio (tidak ada teksnya), dan
   isian di dalam @media print.
   ========================================================================== */
@media (max-width: 1024px), (pointer: coarse) {
  input[type="text"], input[type="password"], input[type="email"],
  input[type="search"], input[type="tel"], input[type="url"],
  input[type="number"], input[type="date"], input[type="time"],
  select, textarea {
    font-size: max(1rem, var(--t-md));   /* 16px, tidak pernah kurang */
  }

  /* Tingginya pun. Isian yang berdiri sendiri di dalam baris tabel — "Nama
     rombel tujuan" pada Kenaikan Kelas — berhenti di 35 piksel karena ia
     hanya mewarisi padding tabelnya, dan tidak ada satu pun kelas yang
     menyentuhnya.

     table.grid DIKECUALIKAN dengan sengaja. Itu tabel isi nilai: satu layar
     memuat puluhan kotak angka bersebelahan, dan menaikkan tiap kotak ke 44
     piksel menambah ratusan piksel tinggi pada lembar yang memang dirancang
     padat. Di sana yang berlaku bukan 44 piksel per kotak melainkan huruf
     16 piksel di atas — penjaga yang benar untuk masalah yang berbeda. */
  .page input[type="text"], .page input[type="password"], .page input[type="email"],
  .page input[type="search"], .page input[type="tel"], .page input[type="url"],
  .page input[type="date"], .page input[type="time"],
  .dialog input[type="text"], .dialog input[type="email"], .dialog input[type="tel"],
  .page select, .dialog select {
    min-height: 44px;
  }
  table.grid input, table.grid select { min-height: 0; }
}

/* ==========================================================================
   SASARAN SENTUH DI HALAMAN MASUK DAN PITA KAKI

   Batas 44 piksel yang dipasang di bagian 54 menyasar kelas-kelas umum —
   .btn, .chip, .act-links button, .crumbs a. Halaman masuk memakai kelasnya
   SENDIRI (.btn-login, .link-btn, .peek) dan karena itu terlewat seluruhnya.

   Akibatnya halaman pertama yang disentuh setiap orang justru punya sasaran
   terkecil di seluruh aplikasi:

       tombol intip password   32px
       "Lupa password?"        30px
       tautan pita kaki        28px
       tombol Login            43px   (kurang satu piksel)

   Dua yang teratas dipakai persis oleh orang yang sedang kesulitan masuk —
   saat itulah salah tekan paling menjengkelkan.
   ========================================================================== */
@media (max-width: 1024px), (pointer: coarse) {
  .btn-login { min-height: 44px; }

  /* "Lupa password?" berdiri sendiri di bawah tombol, jadi menaikkannya tidak
     mendorong apa pun. */
  .link-btn { min-height: 44px; padding-left: 1rem; padding-right: 1rem; }

  /* Tombol intip berada DI DALAM kotak isian setinggi 44px, jadi ia boleh
     mengisi kotak itu sepenuhnya tanpa membuat kotaknya bertambah tinggi. */
  .login-field .peek { min-width: 44px; min-height: 44px; padding: .5rem; }

  /* Pita kaki muncul di setiap halaman, jadi ini bukan perbaikan halaman
     masuk saja. */
  .footer-strip a { min-height: 44px; display: inline-flex; align-items: center; }
}



/* ==========================================================================
   PANDUAN — sesudah disaring per peran

   Halaman ini dulu memuat kelima bagian, jadi bentuknya dirancang untuk
   menahan dinding teks: semuanya terlipat rapat, judulnya kecil, tidak ada
   yang menonjol. Sesudah disaring, yang tersisa paling banyak DUA bagian —
   jadi ia boleh tampil lebih lapang dan lebih terbaca.
   ========================================================================== */
.pd-bagian.buka { border-color: var(--green-200, #cfe6d6); box-shadow: var(--sh-2); }
.pd-bagian.buka .pd-kepala { background: linear-gradient(180deg, #f7fcf8, #f1f9f3); }

.pd-kepala { padding: 1rem 1.1rem; }
.pd-ikon {
  width: 34px; height: 34px; border-radius: var(--r-sm);
  background: var(--green-100); border: 1px solid var(--green-200, #cfe6d6);
  display: grid; place-content: center; font-size: var(--t-md);
}
.pd-judul { font-size: var(--t-lg); }

/* Jumlah pertanyaan per bagian: memberi tahu sebelum dibuka seberapa panjang
   isinya, sehingga membuka lipatan bukan langkah dalam gelap. */
.pd-jml {
  flex: none; font-size: var(--t-2xs); font-weight: 700;
  color: var(--ink-600); background: #eef1ef;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  padding: .1rem .55rem;
}

.pd-isi { padding: .2rem 1.1rem 1.2rem; gap: 1.05rem; }
.pd-tanya { border-left: 3px solid var(--green-200, #cfe6d6); padding-left: 1rem; }
.pd-t { font-size: var(--t-md); }
.pd-d { font-size: var(--t-sm); }

.pd-kaki {
  text-align: center; margin: 1.6rem auto 0; max-width: 34rem;
  font-size: var(--t-xs); color: var(--ink-600);
}
.pd-kosong {
  text-align: center; margin: 2rem auto; max-width: 32rem;
  font-size: var(--t-md); color: var(--ink-600);
}

@media (max-width: 560px) {
  .pd-kepala { padding: .85rem .8rem; gap: .55rem; }
  .pd-ikon { width: 30px; height: 30px; }
  .pd-judul { font-size: var(--t-md); }
  /* Di layar sempit, judul bagian sudah memenuhi barisnya; lencana jumlah
     pindah ke bawahnya alih-alih memeras judulnya. */
  .pd-kepala { flex-wrap: wrap; }
  .pd-jml { order: 3; margin-left: calc(30px + .55rem); }
  .pd-tanda { margin-left: auto; }
  .pd-isi { padding: .2rem .8rem 1rem; }
}

/* ==========================================================================
   BATANG NILAI YANG DAPAT DITEKAN — topik & tujuan pembelajaran
   ==========================================================================
   Angka 80 tidak memberi tahu orang tua apa pun; "80 pada Bilangan Cacah
   sampai 10.000" memberi tahu banyak. Batangnya karena itu menjadi kendali,
   bukan sekadar gambar.

   Yang dijaga: bentuk batangnya TIDAK berubah sedikit pun saat menjadi tombol.
   .sb-kepala mewarisi grid .score-bar apa adanya, sehingga baris "Nilai Akhir"
   yang tetap berupa div sejajar sempurna dengan baris PH yang kini tombol —
   pergeseran satu piksel di antara keduanya akan langsung terlihat. */
.score-bar.bisa-buka { display: block; }

.sb-kepala {
  display: grid;
  grid-template-columns: 116px 1fr;
  gap: 1.1rem;
  align-items: center;
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.sb-kepala:hover .tag { background: var(--green-100); border-color: var(--green-300); }
.sb-kepala:hover .progress > span { filter: saturate(1.15); }
.sb-kepala:focus-visible { outline: 2px solid var(--green-600); outline-offset: 3px; }

/* Tanda ▸ / ▾ berdiri di dalam pil label, bukan di sebelahnya: label yang
   melebar akan mendorong seluruh batang dan merusak kesejajaran kolomnya. */
.score-bar.bisa-buka .tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  width: 100%;
}
.sb-tanda { font-size: var(--t-2xs); color: var(--green-700); line-height: 1; }

.sb-sanggah { margin: .45rem 0 0 calc(116px + 1.1rem); }

.sb-detail { margin: .7rem 0 0 calc(116px + 1.1rem); }
.sb-detail.hide { display: none; }

.pn-kartu {
  background: var(--green-050);
  border: 1px solid var(--green-200);
  border-left: 3px solid var(--green-600);
  border-radius: var(--r-sm);
  padding: .9rem 1.05rem;
}
.pn-judul {
  font-weight: 700;
  color: var(--green-900);
  margin-bottom: .55rem;
}
.pn-kosong {
  margin: 0 0 .55rem;
  font-size: var(--t-sm);
  color: var(--ink-600);
}
/* Titik dua sejajar seperti pada blangko sekolah — itu bentuk yang sudah
   dikenal pembacanya, dan sengaja ditiru dari gambar yang dikirim sekolah. */
.pn-baris {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .3rem .5rem;
  margin: 0;
  font-size: var(--t-sm);
}
.pn-baris dt { color: var(--ink-600); }
.pn-baris dt::after { content: ' :'; }
.pn-baris dd { margin: 0; color: var(--ink-900); font-weight: 600; }
.pn-jenis {
  margin-top: .55rem;
  font-size: var(--t-xs);
  color: var(--ink-600);
}

@media (max-width: 560px) {
  .sb-kepala { grid-template-columns: 88px 1fr; gap: .8rem; }
  .sb-sanggah,
  .sb-detail { margin-left: 0; }
  .pn-baris { grid-template-columns: 1fr; gap: .1rem .5rem; }
  .pn-baris dt { margin-top: .35rem; }
  .pn-baris dt::after { content: ''; }
}

/* ==========================================================================
   TABEL NILAI BACA-SAJA — sel kosong dan nilai di bawah KKM
   ==========================================================================
   Dua hal yang dicari pengawas di layar ini: mana yang belum diinput, dan
   siapa yang belum tuntas. Keduanya sebelumnya harus dibaca satu per satu.

   Arsirannya sengaja abu-abu netral, bukan merah: sel kosong bukan kesalahan,
   ia pekerjaan yang belum sampai. Yang merah hanya angka yang memang sudah ada
   dan memang di bawah KKM. */
/* Sebelumnya sel kosong diberi arsiran diagonal abu-abu — persis seperti
   td.disabled, yang artinya justru KEBALIKANNYA: "kolom ini memang tidak ada
   untuk baris ini". Dua arti berlawanan dengan satu rupa yang sama adalah
   kesalahan yang lebih buruk daripada tidak menandai apa-apa. Arsiran karena
   itu dikembalikan sepenuhnya kepada td.disabled, dan sel yang BELUM diinput
   memakai warna emas yang sama dengan spanduk "N% data belum diinput" di
   atasnya — satu warna, satu arti, di seluruh layar. */
/* !important karena baris genap tabel diwarnai oleh
   `table.grid tbody tr:nth-child(even) td`, yang lebih kuat daripada satu
   kelas — tanpa ini sel kosong di baris genap tetap putih dan penandanya
   berselang-seling, yang jauh lebih membingungkan daripada tanpa penanda.
   Ini pola yang sudah dipakai td.calc dan td.disabled di berkas induk.

   Warnanya sengaja setipis ini: pada satu lembar yang baru terisi seperempat,
   sebagian besar sel memang kosong, dan warna yang lebih pekat akan mengubah
   seluruh tabel menjadi bidang emas yang justru tidak menunjuk apa pun. */
table.grid td.sel-kosong {
  background: #fdf1c9 !important;
  box-shadow: inset 0 0 0 1px #ecd489;
}
/* Tanda – di tengah sel. :empty menahannya pada tabel BACA-SAJA saja; sel
   tabel guru berisi <input>, sehingga ::after di sana akan mendorong isiannya
   — di sana tandanya dipasang sebagai placeholder oleh scoretable.js. */
table.grid td.sel-kosong:empty::after {
  content: '–'; color: #a8801a; font-weight: 800;
}
table.grid td.sel-kosong input { background: transparent; }
table.grid td.sel-kosong input::placeholder { color: #a8801a; opacity: 1; font-weight: 800; }

/* ---- berapa yang kosong: di kepala kolom, dan di ujung tiap baris ---- */
.kol-kosong {
  display: block; margin-top: .15rem;
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0;
  color: #8a6d00; text-transform: none;
}
.kol-kosong:empty { display: none; }
table.grid th.sub-kosong { padding: .3rem .4rem; }
/* Keping "N kosong" di dalam sel Keterangan — lihat alasannya di scoretable.js. */
.ket-kosong {
  margin-top: .3rem; border-radius: var(--r-pill); padding: .12rem .5rem;
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .01em;
  background: #fdf1c9; color: #8a6d00; width: max-content;
}

/* ---- alat "N sel kosong · Sorot yang kosong" ---- */
.alat-kosong {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin: 1.2rem 0 .7rem; padding: .55rem .8rem;
  background: var(--gold-100); border: 1px solid #f0e0ae;
  border-left: 3px solid var(--gold-500); border-radius: var(--r-sm);
  width: fit-content; max-width: 100%;
}
.alat-kosong.hide { display: none; }
.alat-kosong .ak-tanda {
  display: inline-grid; place-content: center; width: 1.4rem; height: 1.4rem;
  border-radius: var(--r-xs); background: #fdf1c9; box-shadow: inset 0 0 0 1px #ecd489;
  color: #a8801a; font-weight: 800; line-height: 1;
}
.alat-kosong .ak-teks { font-size: var(--t-sm); font-weight: 700; color: var(--ink-900); }
.alat-kosong .ak-tombol.on { background: var(--green-700); color: #fff; border-color: var(--green-700); }

/* ---- sorot: yang sudah terisi dipudarkan, yang kosong dibiarkan terang ----
   Kolom No dan Nama sengaja tidak ikut pudar: tanpa keduanya, sel kosong yang
   tersorot kehilangan satu-satunya keterangan tentang MILIK SIAPA ia. */
table.grid.sorot-kosong tbody td:not(.sel-kosong):not(.name):not(:first-child) {
  opacity: .22; transition: opacity .2s var(--ease);
}
table.grid.sorot-kosong tbody td.ket { opacity: 1; }
table.grid.sorot-kosong tbody td.ket .predikat,
table.grid.sorot-kosong tbody td.ket .lencana-tuntas { opacity: .3; }
table.grid.sorot-kosong thead th { opacity: .55; }
table.grid.sorot-kosong thead th.blank { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  table.grid.sorot-kosong tbody td { transition: none; }
}
/* ---- tampilan ringkas: hanya Nilai Akhir ----
   Kolomnya disembunyikan, bukan dibongkar: sel yang sudah terdaftar pada
   penghitung sel kosong tetap ada di DOM, jadi cacah "N kosong" tidak berubah
   sedikit pun ketika rinciannya dibuka atau ditutup. */
table.grid.ringkas .rinci { display: none; }
table.grid:not(.ringkas) .hanya-ringkas { display: none; }

.alat-baris {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin: 1.2rem 0 .7rem;
}
.alat-baris .alat-kosong { margin: 0; }
.rinci-tombol.on { background: var(--green-700); color: #fff; border-color: var(--green-700); }

td.sel-kurang { color: var(--danger); font-weight: 700; }

/* ==========================================================================
   PANEL KELENGKAPAN — "N% data belum diinput" + Ingatkan guru
   ========================================================================== */
.kelengkapan {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .9rem 1.4rem;
  margin: 1.4rem 0 0;
  padding: 1rem 1.15rem;
  background: var(--gold-100);
  border: 1px solid #f0e0ae;
  border-left: 3px solid var(--gold-500);
  border-radius: var(--r-sm);
}
.kelengkapan .kl-kiri { min-width: 0; flex: 1 1 18rem; }
.kelengkapan .kl-teks { min-width: 0; }
.kelengkapan .kl-teks b { display: block; color: var(--ink-900); }
/* Rincian selnya turun ke baris kedua. Digantung di ujung kalimat dengan
   tanda pisah, ia terbaca sebagai anak kalimat dan matanya terus berjalan;
   sebagai baris sendiri ia terbaca sebagai angka, yang memang itu isinya. */
.kelengkapan .kl-teks .small {
  display: block; margin-top: .12rem; color: var(--ink-600);
}
.kl-bar {
  display: block; margin-top: .55rem; height: 6px; max-width: 22rem;
  background: #f2e6b8; border-radius: var(--r-pill); overflow: hidden;
}
.kl-bar > span {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--gold-500), var(--green-600));
  border-radius: var(--r-pill);
}
.kelengkapan .btn { flex: 0 0 auto; }

@media (max-width: 560px) {
  .kelengkapan { flex-direction: column; align-items: stretch; }
  .kelengkapan .kl-kiri { flex-basis: auto; }
  .kelengkapan .btn { width: 100%; }
  .kl-bar { max-width: none; }
}

/* Yang dicetak adalah dokumen, bukan layar kerja: arsiran sel kosong menjadi
   kotak abu-abu yang boros tinta dan membingungkan pembaca rapor, dan panel
   "ingatkan guru" sama sekali bukan urusan kertas. */
@media print {
  /* Kolom "Kosong" dan alat penyorot adalah alat kerja layar, bukan isi
     dokumen. Di atas kertas keduanya hanya menambah lebar. */
  .alat-kosong, .kol-kosong, .ket-kosong,
  .alat-baris, .rinci-tombol { display: none !important; }
  table.grid td.sel-kosong { background: none !important; box-shadow: none !important; }
  table.grid td.sel-kosong:empty::after { content: ''; }
  .kelengkapan { display: none; }
  /* Keping berwarna menjadi kotak tinta pekat di atas kertas; di sana
     hurufnya saja sudah cukup, dan hurufnya memang tidak pernah hilang. */
  .predikat {
    background: none !important; color: #000 !important;
    border-color: #999 !important; border-radius: var(--r-xs);
    width: auto; min-width: 1.3rem; height: auto; padding: .06rem .24rem;
  }
}

/* ==========================================================================
   TABEL NILAI — lebih lapang, garisnya lebih tenang
   ==========================================================================
   Tabel ini adalah layar yang paling lama dipandangi guru dan pengawas, dan
   bentuk lamanya adalah kisi penuh: setiap sel dikurung empat garis dengan
   ketebalan yang sama. Kisi seperti itu benar, tetapi setiap garis menuntut
   perhatian yang sama besar dengan angkanya.

   Yang diubah hanya dua hal, dan keduanya menambah keterbacaan tanpa
   memindahkan satu kolom pun:

   1. Ruang. Tinggi baris bertambah, sehingga jari di ponsel punya sasaran
      yang lebih jujur dan mata punya tempat beristirahat antarbaris.
   2. Bobot garis. Garis DALAM tubuh tabel dimudakan; yang tetap tegas hanya
      garis yang benar-benar memisahkan sesuatu — batas kepala tabel dengan
      isinya, dan tepi kolom hitungan (Nilai Akhir, Nilai Rapor, Keterangan).

   Kolom, lebar, dan urutan tidak disentuh sama sekali: unggah CSV, cetak, dan
   seluruh uji tata letak bergantung padanya. */
/* Legger dikecualikan di mana pun: ia memuat SELURUH mata pelajaran satu
   rombel dalam satu layar, dan padatnya memang tujuannya. */
/* Bantalan mendatar 1rem, bukan 1,05rem. Selisihnya 1,6 piksel per kolom dan
   tidak terlihat mata — tetapi lembar nilai dengan dua PH + remedial + ASAS +
   ASAT berdiri PERSIS selebar kolom halaman, sehingga tambahan sekecil apa pun
   (di sini: angka "N kosong" di kepala kolom) menyalakan guliran mendatar.
   Sebelas kolom × 3,2 piksel mengembalikan ruang itu dengan bersih. */
table.grid:not(.legger) th,
table.grid:not(.legger) td { padding: .78rem 1rem; }
table.grid:not(.legger) tbody td { height: 46px; }

table.grid tbody td { border-right-color: #eef3ef; border-bottom-color: #ebf1ed; }
table.grid thead th { border-right-color: #dfeae2; }

/* Batas kepala tabel: satu-satunya garis yang sengaja dipertegas. Digambar
   sebagai tepi ATAS baris pertama, bukan tepi bawah kepala tabel — sel "No",
   "Nilai Rapor", dan "Keterangan" memakai rowspan, jadi tepi bawah mereka
   bukan tepi bawah baris terakhir kepala tabel, dan garisnya akan patah tepat
   di tiga kolom itu. */
table.grid thead th { border-bottom-color: #dfeae2; }
table.grid tbody tr:first-child td { border-top: 2px solid var(--green-200); }

/* Nama siswa sedikit lebih lega dari tepi kiri. Kolom hitungan tidak diberi
   garis tambahan: latar hijau mudanya sudah memisahkan, dan garis di atas
   warna hanya menumpuk dua penanda untuk satu maksud. */
table.grid:not(.legger) td.name { padding-left: 1.15rem; }

@media (max-width: 640px) {
  table.grid:not(.legger) th,
  table.grid:not(.legger) td { padding: .6rem .7rem; }
  table.grid:not(.legger) tbody td { height: 42px; }
}

/* ==========================================================================
   KARTU TOPIK & TUJUAN — dirapikan
   ==========================================================================
   Kartunya dulu berlatar hijau penuh; di layar siswa yang sudah hijau dari
   kop sampai kaki, satu blok hijau lagi membuat isinya justru tenggelam.
   Sekarang kartunya putih dengan rel hijau di tepi kiri: yang menonjol adalah
   tulisannya, bukan kotaknya. */
.pn-kartu {
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 3px solid var(--green-600);
  border-radius: var(--r-sm);
  padding: 1.05rem 1.2rem;
  box-shadow: var(--sh-1);
}
.pn-judul {
  margin-bottom: .7rem;
  padding-bottom: .55rem;
  border-bottom: 1px solid var(--line);
  font-size: var(--t-md);
}
.pn-baris { gap: .5rem .8rem; }
.pn-baris dd { line-height: 1.45; }
.pn-jenis {
  margin-top: .8rem;
  padding-top: .55rem;
  border-top: 1px dashed var(--line);
}

/* ==========================================================================
   PERKAKAS BERANDA — cincin, tren, batang, panel tugas
   ==========================================================================
   Pasangan gaya untuk app/kartu.js. Alasan tiap keputusannya ada di sana;
   di sini hanya bentuknya. */

/* ---- cincin persentase ---- */
.cincin { display: block; flex: none; overflow: visible; }
.cincin .cn-alas { fill: none; stroke: var(--green-100); }
.cincin .cn-isi {
  fill: none; stroke: var(--green-600);
  /* Nol derajat SVG ada di arah jam tiga; diputar supaya mulai dari atas,
     seperti jam dinding — arah yang sudah dikenal semua orang. */
  transform: rotate(-90deg); transform-origin: 50% 50%;
  transition: stroke-dashoffset .9s var(--ease);
}
.cincin .cn-teks {
  font-size: .72rem; font-weight: 800; fill: var(--green-800);
  font-variant-numeric: tabular-nums;
}
.cincin.rendah .cn-isi { stroke: var(--danger); }
.cincin.rendah .cn-teks { fill: var(--danger); }
.cincin.sedang .cn-isi { stroke: var(--gold-600); }
.cincin.sedang .cn-teks { fill: #8a6d00; }
.cincin.penuh .cn-isi { stroke: var(--green-700); }
.cincin.kosong .cn-alas { stroke: var(--line); }
.cincin.kosong .cn-teks { fill: var(--ink-400); }
@media (prefers-reduced-motion: reduce) { .cincin .cn-isi { transition: none; } }

/* ---- garis tren ---- */
/* Lebarnya dibatasi. Diregangkan selebar 1280 piksel, tiga semester yang
   berselisih enam angka menjadi garis datar yang tidak mengatakan apa pun —
   grafik butuh tinggi relatif terhadap lebarnya untuk dapat dibaca. */
.tren-wadah { width: 100%; max-width: 480px; margin: 0 auto; }
.tren { display: block; width: 100%; height: auto; overflow: visible; }
.tr-bidang { fill: var(--green-100); opacity: .55; }
.tr-garis { stroke: var(--green-600); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round;
  transition: stroke-dashoffset 1s var(--ease); }
.tr-titik { fill: #fff; stroke: var(--green-700); stroke-width: 2.5; }
.tr-rusuk { stroke: var(--line); stroke-width: 1; }
.tr-sumbu { font-size: 9px; fill: var(--ink-400); font-weight: 700; }
/* Garis acuan (mis. rata-rata kelas) sengaja putus-putus dan pucat: ia
   pembanding, bukan capaian yang sedang diceritakan. */
.tr-acuan { stroke: var(--ink-400); stroke-width: 1.5; stroke-dasharray: 5 5; opacity: .7; }
.tren-label {
  display: flex; justify-content: space-between; margin-top: -.2rem;
  padding-left: 10%; padding-right: 4%;
  font-size: var(--t-2xs); color: var(--ink-600);
}
.tren-label span { flex: 1 1 0; text-align: center; }
.tren-label span:first-child { text-align: left; }
.tren-label span:last-child { text-align: right; }
@media (prefers-reduced-motion: reduce) { .tr-garis { transition: none; } }

/* Batang perbandingan sempat dibuat di sini, lalu dibuang: halaman Statistik
   sudah punya grafikMapel() yang lebih kaya — berwarna menurut nilainya dan
   berketerangan "N nilai di bawah KKM" di tiap baris. Perkakas umum yang lebih
   miskin daripada yang sudah ada bukan tambahan, melainkan pilihan kedua yang
   suatu saat dipakai orang tanpa tahu ada yang lebih baik. */

/* ---- muncul berurutan ---- */
.muncul {
  animation: naik-masuk .38s var(--ease) both;
  animation-delay: calc(var(--urut, 0) * 40ms);
}
@keyframes naik-masuk { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .muncul { animation: none; } }

/* ---- panel "Yang perlu dikerjakan" ---- */
.tugas {
  max-width: 760px; margin: 1.2rem auto 1.4rem; text-align: left;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-1); overflow: hidden;
}
.tg-kepala {
  display: flex; align-items: center; gap: .6rem;
  padding: .7rem 1rem; border-bottom: 1px solid var(--line); background: var(--green-050);
}
.tg-kepala h2 {
  margin: 0; flex: 1; font-size: var(--t-2xs); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--green-800);
}
.tg-jumlah {
  flex: none; min-width: 1.5rem; text-align: center;
  background: var(--green-700); color: #fff; border-radius: var(--r-pill);
  font-size: var(--t-2xs); font-weight: 800; padding: .1rem .45rem;
}
.tg-daftar { display: grid; }
.tg-item {
  display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: center; gap: .2rem .7rem;
  padding: .7rem 1rem; border-top: 1px solid var(--line);
  border-left: 3px solid transparent;
}
.tg-item:first-child { border-top: 0; }
.tg-item.danger { border-left-color: var(--danger); }
.tg-item.warn { border-left-color: var(--gold-500); }
.tg-item.info { border-left-color: var(--green-300); }
.tg-item.bisa { cursor: pointer; transition: background .16s var(--ease); }
.tg-item.bisa:hover { background: var(--green-050); }
.tg-item.bisa:focus-visible { outline: 2px solid var(--green-600); outline-offset: -2px; }
.tg-ikon { font-size: var(--t-lg); line-height: 1; }
.tg-teks { min-width: 0; }
.tg-item .tg-judul {
  font-size: var(--t-sm); font-weight: 700; color: var(--ink-900); line-height: 1.35;
}
.tg-item .tg-sub { font-size: var(--t-2xs); color: var(--ink-600); line-height: 1.4; }
.tg-kapan { font-size: var(--t-2xs); color: var(--ink-400); white-space: nowrap; }
.tg-panah { color: var(--green-700); font-weight: 800; }
.tg-item.bisa:hover .tg-panah { transform: translateX(2px); }
.tg-panah { transition: transform .16s var(--ease); }
.tg-sisa {
  padding: .55rem 1rem; border-top: 1px solid var(--line);
  font-size: var(--t-2xs); color: var(--ink-600); background: #fbfdfb;
}

/* Tidak ada yang tertunda: ini keadaan yang PANTAS DIRAYAKAN, bukan panel
   kosong. Sekolah yang seluruh lembarnya sudah beres harus melihatnya. */
.tugas.beres {
  display: flex; align-items: center; gap: .8rem;
  padding: .9rem 1.1rem; background: var(--green-050); border-color: var(--green-200);
}
.tugas.beres .tg-ceria { font-size: 1.6rem; line-height: 1; }
.tugas.beres .tg-judul { font-size: var(--t-sm); font-weight: 800; color: var(--green-800); }
.tugas.beres .tg-sub { font-size: var(--t-2xs); color: var(--ink-600); }

@media (max-width: 560px) {
  .tugas { margin: 1rem 0 1.2rem; }
  .tg-item { grid-template-columns: auto 1fr auto; padding: .65rem .8rem; }
  .tg-kapan { display: none; }
}

/* ---- rangka sementara ---- */
.rangka-baris {
  height: 74px; border-radius: var(--r-sm); background: var(--green-050);
  background-image: linear-gradient(100deg, transparent 20%, rgba(47,139,82,.09) 40%, transparent 60%);
  background-size: 250% 100%; animation: shimmer 1.3s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .rangka-baris { animation: none; } }

@media print {
  .tugas, .rangka-baris { display: none !important; }
  .muncul { animation: none !important; }
}

/* ---- kartu unit pada beranda Pengawas ----
   Kode unit tidak lagi menjadi lambang raksasa di tengah kotak; tempat itu
   sekarang milik cincin kelengkapan, karena itulah yang dicari matanya. Kode
   turun menjadi keping kecil di bawahnya — tetap ada, tidak lagi menuntut. */
/* Memakai perlakuan .berisi (tinggi mengikuti isi, isi menempel ke bawah)
   supaya kartu yang penuh keterangan tidak melimpah keluar kotaknya —
   .tile.icon polos bertinggi mati 194px, dan cincinnya menjulur ke atas. */
.tile.icon.unit-kartu { gap: .3rem; min-height: 194px; }
.unit-kartu .cincin { margin-bottom: .1rem; }
/* Di layar sempit kartu berubah menjadi baris mendatar (bagian 54). Tinggi
   mati 194px di situ hanya menyisakan udara: enam unit menjadi enam kotak
   setinggi layar penuh yang isinya satu baris tulisan. */
@media (max-width: 720px) {
  .tile.icon.unit-kartu { min-height: 0; }
  .unit-kartu .cincin { margin-bottom: 0; }
}
.unit-kartu .uk-kode {
  font-size: var(--t-2xs); font-weight: 800; letter-spacing: .06em;
  color: var(--green-800); background: var(--green-100);
  border-radius: var(--r-sm); padding: .1rem .45rem;
}
.unit-kartu .uk-belum { font-size: var(--t-2xs); color: var(--danger); font-weight: 600; }
.unit-kartu .uk-sepi { font-size: var(--t-2xs); color: var(--ink-400); }
.unit-kartu .uk-aman { font-size: var(--t-2xs); color: var(--green-700); }
.nanti-tugas, .nanti-unit { width: 100%; }
.nanti-unit .tiles .rangka-baris { height: 148px; }
.tren-ket { margin: .35rem 0 1rem; font-size: var(--t-2xs); color: var(--ink-600); text-align: center; }

/* Penanda kecil di dalam tile balok, mis. "sedang berjalan" pada pemilih
   semester. Kotaknya bertinggi mati 168px, jadi penandanya ditaruh di bawah
   angkanya tanpa mengubah ukuran kotak. */
.tile.square.bertanda { justify-content: center; gap: .15rem; }
.tile-tanda {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: .01em;
  color: var(--green-800); background: var(--green-100);
  border-radius: var(--r-pill); padding: .12rem .55rem;
}

/* ==========================================================================
   RONDE 85 — WAJAH 2026
   Sidebar samping, bento grid, tabel pintar dengan laci, dan denyut hidup.
   Semuanya di atas kerangka lama: kepala halaman tidak berpindah sejengkal.
   ========================================================================== */

/* ---- kerangka: sidebar + kolom isi ---- */
.app-body { display: flex; flex: 1; min-height: 0; align-items: stretch; }
.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* ==========================================================================
   RONDE 88 — FOTO GEDUNG SEKOLAH DI LATAR HALAMAN DALAM

   Foto yang sama dengan halaman masuk, tetapi jauh, JAUH lebih samar: di sini
   selubungnya tidak pernah turun di bawah .94, jadi yang tersisa dari fotonya
   hanya bayang bentuk gedung di sela-sela kartu. Itu disengaja dan tidak boleh
   dinaikkan. Halaman dalam berisi tabel nilai yang rapat — angka rapor tidak
   boleh berebut perhatian dengan hiasan, dan seorang wali kelas yang sedang
   mengecek satu angka di antara dua ratus angka lain tidak butuh gedung.

   Dipasang pada .app dan bukan pada body: body sudah punya tiga gradasi radial
   sendiri, dan halaman masuk memakai body yang sama. Menaruhnya di body berarti
   foto ini menumpuk di atas foto halaman masuk.

   background-attachment: fixed — supaya latar diam saat tabel panjang digulir.
   Kalau ikut bergulir, garis-garis diagonal gedungnya bergerak di belakang
   deretan angka dan itu membuat pusing pada layar kecil.
   ========================================================================== */
.app {
  background-image:
    linear-gradient(180deg, rgba(243,251,244,.972) 0%,
                    rgba(243,251,244,.968) 30%,
                    rgba(243,251,244,.972) 62%,
                    rgba(240,249,242,.978) 100%),
    url('./gedung.webp');
  background-repeat: no-repeat, no-repeat;
  background-position: center, center 26%;
  background-size: cover, cover;
  background-attachment: scroll, fixed;
}
/* Peramban seluler kerap mengabaikan background-attachment: fixed. Di layar
   sekecil itu fotonya juga tinggal secuil, jadi lebih baik dimatikan sekalian
   daripada meregang aneh: satu berkas 25 KB tetap terunduh untuk halaman masuk,
   tidak ada yang dihemat dengan menahannya, tetapi ada yang dirusak dengan
   menampilkannya salah. */
@media (max-width: 860px) {
  .app { background-attachment: scroll, scroll; }
}
@media print {
  .app { background-image: none; }
}
@media (prefers-reduced-motion: reduce) {
  .app { background-attachment: scroll, scroll; }
}

/* Rel kiri. Lengket di bawah kepala halaman; tingginya mengikuti layar,
   bukan mengikuti isi, supaya tombol lipat selalu terlihat. */
.side {
  position: sticky; top: 64px; z-index: 20; flex: none;
  width: 218px; height: calc(100vh - 64px);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 1rem .7rem .9rem;
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  border-right: 1px solid rgba(226,235,229,.7);
  transition: width .22s var(--ease);
}
.sn-isi { display: flex; flex-direction: column; gap: .3rem; overflow-y: auto; }

.sn-item {
  display: flex; align-items: center; gap: .7rem;
  width: 100%; min-height: 44px; padding: .55rem .75rem;
  border: 0; border-radius: var(--r-sm); background: none;
  font: inherit; font-size: var(--t-sm); font-weight: 600; color: var(--ink-600);
  cursor: pointer; text-align: left; position: relative;
  transition: background .16s var(--ease), color .16s var(--ease),
    transform .16s var(--ease);
}
.sn-ikon { width: 21px; height: 21px; flex: none; display: grid; place-content: center; }
.sn-ikon svg { width: 21px; height: 21px; display: block; transition: transform .18s var(--ease); }
.sn-label { white-space: nowrap; overflow: hidden; }
/* Label pendek hanya hidup di bilah bawah ponsel. */
.sn-label-pendek { display: none; }

.sn-item:hover { background: rgba(230,245,232,.75); color: var(--green-800); transform: translateX(2px); }
.sn-item:hover .sn-ikon svg { transform: scale(1.12); }

/* Item aktif: pil hijau lembut + jarum penanda di tepi kiri. */
.sn-item.aktif {
  background: linear-gradient(120deg, var(--green-100), rgba(230,245,232,.5));
  color: var(--green-900);
}
.sn-item.aktif::before {
  content: ''; position: absolute; left: -.7rem; top: 22%; bottom: 22%;
  width: 3.5px; border-radius: var(--r-pill);
  background: var(--green-600); box-shadow: var(--sorot-ok);
}

.sn-lipat {
  align-self: flex-end; margin-top: .6rem;
  /* 44px, bukan 38: sasaran sentuh minimum yang dijaga audit-layar. */
  width: 44px; height: 44px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--paper);
  color: var(--ink-600); cursor: pointer; display: grid; place-content: center;
  transition: background .16s, transform .22s var(--ease);
}
.sn-lipat:hover { background: var(--green-050); }
.sn-lipat .sn-ikon, .sn-lipat svg { width: 18px; height: 18px; }

/* Terlipat: rel ikon. Label menghilang, ikon tetap 21px, judulnya pindah ke
   tooltip (title sudah terpasang sejak awal, bukan hanya saat terlipat). */
.side.mini { width: 66px; padding-inline: .55rem; }
.side.mini .sn-label { display: none; }
.side.mini .sn-item { justify-content: center; padding-inline: 0; }
.side.mini .sn-item.aktif::before { left: -.55rem; }
.side.mini .sn-lipat { align-self: center; }
.side.mini .sn-lipat svg { transform: rotate(180deg); }

/* Laptop kecil (861–1279px): rel otomatis menjadi rel ikon. Lebar 218px di
   layar 1024 memakan seperlima layar; menyusut ke 66px memberi bento ruang
   untuk tetap menyamping. Tombol lipat disembunyikan — lebar layarnya yang
   sudah memutuskan. */
@media (min-width: 861px) and (max-width: 1279px) {
  .side, .side.mini { width: 66px; padding-inline: .55rem; }
  .side .sn-label { display: none; }
  .side .sn-item { justify-content: center; padding-inline: 0; }
  .side .sn-item.aktif::before { left: -.55rem; }
  .side .sn-lipat { display: none; }
}

/* Halaman bertabel nilai: lebarnya diukur tanpa sisa (tabel guru 1372px di
   kolom 1384). Rel samping selebar apa pun mencurinya, jadi di halaman itu
   rel MENEPI di bawah 1600px — halaman tabel berperilaku persis seperti
   sebelum rel ada — dan menjadi rel ikon di layar yang sungguh lapang.
   Bilah bawah ponsel tidak ikut aturan ini: ia tidak memakan lebar. */
@media (min-width: 861px) and (max-width: 1599px) {
  .app:has(.page > .table-wrap) .side { display: none; }
}
@media (min-width: 1600px) {
  .app:has(.page > .table-wrap) .side { width: 66px; padding-inline: .55rem; }
  .app:has(.page > .table-wrap) .side .sn-label { display: none; }
  .app:has(.page > .table-wrap) .side .sn-item { justify-content: center; padding-inline: 0; }
  .app:has(.page > .table-wrap) .side .sn-lipat { display: none; }
}

/* Ponsel & tablet tegak: simpul yang sama berpindah ke dasar layar menjadi
   bilah ikon. Tidak ada tombol lipat di sana — lebar layarnya yang melipat. */
@media (max-width: 860px) {
  .app-body { display: block; }
  .side, .side.mini {
    position: fixed; top: auto; bottom: 0; left: 0; right: 0;
    width: 100%; height: auto; z-index: 28;
    flex-direction: row; justify-content: center;
    padding: .3rem .5rem calc(.3rem + env(safe-area-inset-bottom, 0px));
    border-right: 0; border-top: 1px solid rgba(226,235,229,.85);
    box-shadow: 0 -6px 18px rgba(16,40,28,.06);
  }
  .sn-isi { flex-direction: row; gap: .15rem; justify-content: space-around; width: 100%; overflow: visible; }
  .side .sn-item, .side.mini .sn-item {
    flex-direction: column; gap: .15rem; justify-content: center;
    flex: 1 1 0; min-width: 0; max-width: 96px; min-height: 48px;
    padding: .3rem .2rem; font-size: var(--t-2xs); font-weight: 600;
  }
  .side .sn-item:hover { transform: none; }
  .side .sn-label, .side.mini .sn-label { display: none; }
  .side .sn-label-pendek {
    display: block; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
    white-space: nowrap;
  }
  /* Bilah bawah hanya muat lima: pekerjaan meja menepi di layar genggam. */
  .side .sn-item.sn-p2 { display: none; }
  .sn-item.aktif::before, .side.mini .sn-item.aktif::before {
    left: 18%; right: 18%; top: auto; bottom: -.3rem; width: auto; height: 3px;
  }
  .sn-lipat { display: none; }
  /* isi halaman diberi ruang napas di bawah supaya tidak tertutup bilah */
  .app-main { padding-bottom: 76px; }
}

/* ---- denyut hidup pada jam ---- */
/* Titik kecil di pojok pil jam: aplikasi ini "live" — jamnya jam server.
   Diletakkan MELAYANG di pojok, bukan di dalam barisan, karena kepala halaman
   di 360px pernah kalah selebar SATU piksel; benda melayang tidak menambah
   lebar apa pun. */
.clock { position: relative; }
.clock::after {
  content: ''; position: absolute; top: 5px; right: 7px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--live);
  box-shadow: 0 0 0 0 rgba(25,180,90,.45);
  animation: denyut-live 2.4s var(--ease) infinite;
}
@keyframes denyut-live {
  0%   { box-shadow: 0 0 0 0 rgba(25,180,90,.45); }
  60%  { box-shadow: 0 0 0 7px rgba(25,180,90,0); }
  100% { box-shadow: 0 0 0 0 rgba(25,180,90,0); }
}
@media (prefers-reduced-motion: reduce) {
  .clock::after { animation: none; box-shadow: 0 0 6px rgba(25,180,90,.5); }
}

/* ---- bento grid beranda ----
   Satu kolom di layar sempit; di layar lebar menjadi kotak-kotak asimetris.
   Panel di dalamnya adalah simpul lama yang sama — bento hanya wadah, jadi
   setiap uji yang menghitung .tugas, .unit-kartu, dan .ringkas-item tetap
   menemukan barang yang sama. */
.bento { display: grid; gap: 1.15rem; align-items: stretch; margin: 0 0 1.4rem; }
.bento .tugas, .bento .ringkas { max-width: none; margin: 0; }
.bento .nanti-tugas, .bento .nanti-unit { min-width: 0; }
/* Hanya beranda admin yang meregang penuh: pita 2×2-nya memang dirancang
   mengisi tinggi panel tugas. Di beranda lain kartu memakai TINGGI ALAMINYA
   dan rata atas — kartu yang dipaksa setinggi tetangganya menggendong perut
   kosong: kartu mapel guru pernah melar 900 piksel demi dua baris isi. */
.b-admin .nanti-tugas .tugas,
/* Beranda kepala sekolah memakai susunan yang sama dengan beranda admin:
   panel tugas di kiri, pita angka 2×2 di kanan. Bentuknya memang sama karena
   pertanyaannya sama — "apa yang menunggu saya" lalu "bagaimana keadaan unit
   saya" — dan dua layar yang menjawab pertanyaan yang sama sebaiknya tidak
   berbeda hanya karena ditulis pada ronde yang berbeda. */
.b-kepsek .nanti-tugas .tugas { height: 100%; }
.b-spv, .b-guru, .b-siswa { align-items: start; }
.b-spv .tiles, .b-guru .tiles { align-content: start; }

/* Menyamping mulai 960px. Ambang lama 1100 terasa aman di mesin uji, tetapi
   laptop 1366px yang memakai skala tampilan 125% (bawaan Windows) hanya punya
   viewport ±1093px — SELURUH beranda jatuh menjadi satu kolom memanjang,
   persis yang dilaporkan sekolah. */
@media (min-width: 960px) {
  .b-admin, .b-kepsek { grid-template-columns: 7fr 5fr; }
  /* Pita menjadi 2×2 di sisi kanan. height:100% pada grid-nya sempat
     mendorong tautan "Lihat statistik lengkap" keluar sel — grid yang
     dipaksa setinggi sel tidak menyisakan ruang untuk adik di bawahnya.
     Flex kolomlah yang benar: grid melar, tautan tetap kebagian tempat. */
  .b-admin .pita-ringkas, .b-kepsek .pita-ringkas { height: 100%; }
  .b-admin .ringkas, .b-kepsek .ringkas { height: 100%; display: flex; flex-direction: column; }
  .b-admin .ringkas-grid, .b-kepsek .ringkas-grid {
    flex: 1; grid-template-columns: 1fr 1fr; align-content: stretch;
  }

  .b-spv { grid-template-columns: 5fr 7fr; }
  .b-spv .tiles { --tile-kolom: 2; max-width: none; justify-content: stretch; }
  .b-spv .tiles > * { width: auto; }

  .b-guru { grid-template-columns: 5fr 7fr; }
  .b-guru .tiles { max-width: none; }

  .b-siswa { grid-template-columns: 6fr 6fr; }
  .b-siswa .sorot-siswa { margin: 0; max-width: none; }
}
/* Bento berisi SATU anak (mis. siswa tanpa sorotan): kolom tunggal saja. */
.bento:not(:has(> :nth-child(2))) { grid-template-columns: 1fr !important; }

/* ---- tabel pintar: garis kaku pergi, zebra lembut datang ----
   Garis tegak antar sel dihapus di TUBUH tabel; pemisah baris sekarang
   selang-seling latar. Garis KEPALA dipertahankan tipis — kepala tiga
   tingkat dengan colspan kehilangan bentuknya tanpa garis. */
table.grid tbody td { border-right: 0; border-bottom: 0; }
table.grid tbody tr:nth-child(even) td { background: #f6faf7; }
table.grid tbody tr:hover td { background: #eef7f0; }
table.grid thead th { border-right-color: rgba(226,235,229,.6); }
table.grid tbody tr td:first-child { border-radius: var(--r-xs) 0 0 var(--r-xs); }
table.grid tbody tr td:last-child { border-radius: 0 var(--r-xs) var(--r-xs) 0; }

/* Lencana bercahaya redup — hijau menyala untuk Tuntas, merah untuk Belum.
   Cahayanya cukup untuk terbaca "hidup", tidak cukup untuk mengganggu. */
.lencana-tuntas.ok { box-shadow: var(--sorot-ok); }
.lencana-tuntas.kurang { box-shadow: var(--sorot-bahaya); }
.badge.warn { box-shadow: var(--sorot-warn); }

/* Nama siswa yang dapat ditekan (tabel baca-saja) */
.nama-buka {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer; text-align: left;
  border-radius: var(--r-xs);
  text-decoration: underline transparent;
  text-underline-offset: 3px;
  transition: color .14s, text-decoration-color .14s;
}
.nama-buka:hover { color: var(--green-700); text-decoration-color: var(--green-300); }
.nama-buka:focus-visible { outline: none; box-shadow: var(--ring); }

/* ---- laci rincian (Quick View) ---- */
.laci-wadah { position: fixed; inset: 0; z-index: 70; }
.laci-tirai {
  position: absolute; inset: 0; background: rgba(11,40,25,.28);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  animation: tirai-masuk .2s var(--ease) both;
}
@keyframes tirai-masuk { from { opacity: 0 } }
.laci {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(420px, 94vw);
  background: var(--paper); box-shadow: var(--sh-3);
  border-left: 1px solid var(--line);
  display: flex; flex-direction: column;
  animation: laci-masuk .26s var(--ease) both;
}
@keyframes laci-masuk { from { transform: translateX(40px); opacity: 0 } }
.laci-wadah.pergi .laci { transition: transform .2s var(--ease), opacity .2s; transform: translateX(30px); opacity: 0; }
.laci-wadah.pergi .laci-tirai { transition: opacity .2s; opacity: 0; }

.lc-atas {
  display: flex; align-items: flex-start; justify-content: space-between; gap: .8rem;
  padding: 1.05rem 1.2rem .9rem; border-bottom: 1px solid var(--line);
}
.lc-nama { font-weight: 800; font-size: var(--t-lg); line-height: 1.25; }
.lc-sub { color: var(--ink-400); font-size: var(--t-xs); margin-top: .15rem; }
.lc-tutup { flex: none; width: 36px; height: 36px; padding: 0; display: grid; place-content: center; }

.lc-rapor {
  margin: 1rem 1.2rem .2rem; padding: .95rem 1.1rem;
  border-radius: var(--r); text-align: center;
  background: linear-gradient(135deg, var(--green-050), var(--green-100));
  border: 1px solid var(--green-200);
}
.lc-rapor.kurang { background: linear-gradient(135deg, #fffafa, var(--danger-bg)); border-color: #f3c9c3; }
.lc-rapor .lc-angka { font-size: 2.1rem; font-weight: 800; letter-spacing: -.02em; color: var(--green-800); line-height: 1.1; }
.lc-rapor.kurang .lc-angka { color: var(--danger); }
.lc-rapor .lc-ket { margin-top: .35rem; display: flex; gap: .45rem; align-items: center; justify-content: center; color: var(--ink-600); font-size: var(--t-xs); }

.lc-isi {
  flex: 1; overflow-y: auto; padding: .8rem 1.2rem 1.2rem;
  display: grid; gap: .7rem;
  /* start, bukan stretch: tanpa ini empat kartu membagi rata seluruh tinggi
     laci dan masing-masing menggendong ruang kosong di perutnya. */
  align-content: start;
}
.lc-kelompok {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: .6rem .8rem .5rem; background: #fdfefd;
}
.lc-kepala {
  display: flex; justify-content: space-between; align-items: baseline; gap: .6rem;
  margin-bottom: .35rem; font-size: var(--t-sm);
}
.lc-kkm { color: var(--ink-400); font-size: var(--t-2xs); font-weight: 600; }
.lc-baris {
  display: flex; justify-content: space-between; gap: .6rem;
  font-size: var(--t-sm); padding: .18rem 0;
}
.lc-baris .lc-label { color: var(--ink-600); }
.lc-baris .lc-nilai { font-weight: 700; }
.lc-baris.kosong .lc-nilai { color: #a8801a; }
.lc-baris.bawah .lc-nilai { color: var(--danger); }

@media (prefers-reduced-motion: reduce) {
  .laci, .laci-tirai { animation: none; }
}
@media print { .laci-wadah { display: none !important; } }


/* ---- grafik bercahaya ----
   Garis tren dan cincin persen diberi sinar hijau lembut — drop-shadow pada
   SVG-nya, tanpa menyentuh datanya. Di kertas, sinar dimatikan. */
.tr-garis { filter: drop-shadow(0 0 5px rgba(25,180,90,.35)); }
.cn-isi { filter: drop-shadow(0 0 4px rgba(25,180,90,.4)); }
@media print { .tr-garis, .cn-isi { filter: none; } }


/* ---- ronde 86: menu profil ----
   Panduan pindah sepenuhnya ke sidebar; menu ini tinggal tiga hal tentang
   DIRI pembacanya: siapa dia, profilnya, dan pintu keluar. Wajahnya
   disamakan dengan bahasa 2026 ronde 85: kaca, sudut besar, gerak halus. */
.user-drop {
  background: rgba(255,255,255,.88);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  border: 1px solid var(--glass-brd);
  border-radius: var(--r-lg);
  min-width: 268px;
  box-shadow: var(--sh-3), inset 0 1px 0 rgba(255,255,255,.85);
  overflow: hidden;
}
.user-drop .who {
  background: linear-gradient(135deg, var(--green-050), var(--green-100));
  border-bottom: 1px solid rgba(211,224,216,.7);
  padding: 1rem 1.05rem;
}
.user-drop .who span { color: var(--green-800); font-weight: 600; }
.user-drop button, .user-drop a {
  padding: .72rem 1.05rem;
  font-weight: 600; color: var(--ink-800);
  transition: background .14s var(--ease), transform .14s var(--ease), color .14s;
}
.user-drop button:hover, .user-drop a:hover {
  background: var(--green-050); transform: translateX(2px); color: var(--green-900);
}
.user-drop .danger:hover { color: var(--danger); background: #fffafa; }
.ud-ikon { width: 20px; height: 20px; flex: none; display: grid; place-content: center; color: var(--green-700); }
.ud-ikon svg { width: 20px; height: 20px; display: block; }
.user-drop .danger .ud-ikon { color: var(--danger); }
.user-drop .sep { margin: .15rem .8rem; background: rgba(211,224,216,.6); }

/* ==========================================================================
   PANEL ATURAN RAPOR (ronde 88)

   Sepuluh isian dalam satu panel. Disusun sebagai BARIS PER HURUF — huruf,
   nilai minimalnya, lalu katanya — bukan sebagai formulir dua kolom biasa,
   karena yang dibaca orang di sini adalah hubungan antara ketiganya:
   "A mulai 80, tertulis Sangat Baik". Formulir dua kolom memisahkan angka dari
   katanya dan memaksa mata melompat bolak-balik untuk memeriksa satu aturan.
   ========================================================================== */
.rapor-atur-kisi { display: flex; flex-direction: column; gap: .55rem; margin: .2rem 0 1rem; }
.rapor-atur-baris {
  display: flex; align-items: end; gap: .8rem; flex-wrap: wrap;
  padding: .55rem .7rem; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--paper);
}
.rapor-atur-huruf {
  font-weight: 800; font-size: var(--t-lg); color: var(--green-800);
  min-width: 1.6rem; text-align: center; line-height: 2.2;
}
.rapor-atur-huruf.kata { font-size: var(--t-sm); min-width: 6rem; text-align: left; }
.rapor-atur-medan { display: flex; flex-direction: column; gap: .2rem; }
.rapor-atur-medan.lebar { flex: 1; min-width: 11rem; }
.rapor-atur-medan > span { font-size: var(--t-2xs); font-weight: 700; color: var(--ink-600); }

/* Di ponsel huruf dan isian tidak muat sebaris. Hurufnya naik menjadi judul
   barisnya sendiri, dan kedua isian berdiri penuh di bawahnya — lebih tinggi,
   tetapi tidak ada satu pun isian yang menyempit sampai tidak terbaca. */
@media (max-width: 560px) {
  .rapor-atur-baris { flex-direction: column; align-items: stretch; gap: .5rem; }
  .rapor-atur-huruf { text-align: left; line-height: 1.3; }
  .rapor-atur-medan, .rapor-atur-medan.lebar { min-width: 0; width: 100%; }
  .rapor-atur-medan input { width: 100% !important; }
}

/* ==========================================================================
   KAKI HALAMAN TANPA PITA GARIS  (permintaan sekolah, ronde ini)

   Pita kuning-hijau di kaki tiap halaman dibuang dari layar aplikasi. Ia tidak
   mengatakan apa pun — bukan tautan, bukan keterangan — dan pada halaman yang
   isinya pendek ia berdiri sebagai garis penutup yang tampak seperti batas
   yang salah tempat.

   Yang menggantikannya bukan ruang kosong yang sama besarnya, melainkan NAPAS
   di bawah isi halaman: cukup supaya baris terakhir tidak menempel ke tepi
   layar, tidak lebih. Aturan ini ditulis di sini, bukan dengan menyunting
   styles.css, supaya berkas salinan dari aplikasi lama tetap dapat
   dibandingkan baris per baris dengan aslinya.
   ========================================================================== */
.app-main .footer-strip { display: none; }

.app-main .page { padding-bottom: clamp(2.2rem, 5vh, 3.6rem); }

/* Halaman masuk MEMPERTAHANKAN kakinya: di sana isinya dua tautan sungguhan —
   situs sekolah dan Instagramnya. Yang dibuang hanya pita garisnya, sehingga
   tautannya berdiri sendiri dan berada di tengah. */
.login-page .footer-strip.tautan { justify-content: center; }
.login-page .footer-strip.tautan .pranala { justify-content: center; }

/* ==========================================================================
   RONDE 90 — PERCANTIK, TIGA PERKARA YANG DIMINTA SEKOLAH

   Ditulis di sini, bukan dengan menyunting styles.css, karena styles.css
   masih salinan baris-per-baris dari aplikasi lama dan harus tetap dapat
   dibandingkan dengannya. Semua yang di bawah ini menimpa lewat urutan, bukan
   lewat !important.
   ========================================================================== */

/* --------------------------------------------------------------------------
   90.1  HALAMAN BERTABEL BERTUMPU PADA SATU SUMBU

   Legger dan Lembar Nilai memakai kolom lebar (min(1500px,100%)) supaya tabel
   nilai yang 1372 piksel tidak perlu digeser. Di dalam kolom itu SEMUA blok
   dipakukan ke tepi kiri — remah jejak, tombol, tabel, keterangan — kecuali
   satu: judul halaman, yang masih rata tengah warisan halaman non-tabel.

   Akibatnya terlihat jelas pada legger rombel kecil: tabelnya 745 piksel di
   layar 1440, jadi judul berdiri di 720 sementara segala sesuatu yang lain
   berdiri di 28. Bukan "kurang cantik" yang samar — dua sumbu yang berbeda
   dalam satu layar.

   Yang dipilih adalah MENURUNKAN judulnya ke sumbu kiri, bukan memusatkan
   tabelnya. Memusatkan tabel berarti tepi kirinya berpindah setiap kali
   jumlah kolomnya berubah — satu mapel ditambah, seluruh halaman bergeser —
   dan remah jejak di atasnya tidak bisa ikut, karena ia saudara .page, bukan
   anaknya, dan tidak tahu selebar apa tabel di bawahnya.

   HANYA di layar lebar. Di ponsel kolomnya selebar layar, tabelnya digulir
   di dalam wadahnya, dan judul rata tengah tetap benar di sana.
   -------------------------------------------------------------------------- */
@media (min-width: 861px) {
  .page:has(> .table-wrap) > .page-title,
  .page:has(> .table-wrap) > .page-lead,
  .page:has(> .table-wrap) > .legger-judul { text-align: left; }

  /* Judul rata kiri tidak lagi perlu jarak sebesar judul rata tengah: yang
     memisahkannya dari remah jejak sekarang tepi kiri yang sama, bukan ruang
     kosong. */
  .page:has(> .table-wrap) > .page-title { margin-top: .2rem; }

  /* Keterangan di bawah tabel dibatasi selebar bacaan, bukan selebar kolom
     1500 piksel. Baris teks sepanjang itu memaksa mata melompat balik dan
     kehilangan barisnya. */
  .page:has(> .table-wrap) > p.small { max-width: 74ch; }
}

/* --------------------------------------------------------------------------
   90.2  BAYANGAN YANG LEBIH LEMBUT

   Tiga token bayangan lama memakai dua lapis dengan sebaran pendek (16px pada
   --sh-2). Pada kartu selebar 194 piksel itu terbaca sebagai garis abu-abu di
   bawah kartu, bukan sebagai kartu yang terangkat.

   Yang dipakai sekarang tiga lapis: satu setipis rambut untuk tepinya, satu
   sedang untuk bentuknya, satu lebar dan sangat samar untuk ruangnya. Jumlah
   kepekatannya justru TURUN (sh-2: .12 menjadi .095) — lebih lembut, bukan
   lebih gelap — sementara sebarannya naik dua kali lipat.

   Warnanya tetap rgba(16,40,28): hitam kehijauan, senada dengan tinta halaman
   ini. Bayangan abu-abu netral di atas latar hijau muda terbaca kotor.
   -------------------------------------------------------------------------- */
:root {
  --sh-1: 0 1px 1px rgba(16,40,28,.04), 0 2px 6px rgba(16,40,28,.05);
  --sh-2: 0 1px 2px rgba(16,40,28,.04),
          0 5px 12px rgba(16,40,28,.05),
          0 14px 32px rgba(16,40,28,.045);
  --sh-3: 0 2px 4px rgba(16,40,28,.05),
          0 10px 24px rgba(16,40,28,.07),
          0 26px 54px rgba(16,40,28,.07);
}

/* --------------------------------------------------------------------------
   90.3  KARTU MATA PELAJARAN: SATU IRAMA UNTUK SEMUA KARTU

   Nama mapel ada yang satu baris ("MATEMATIKA") dan ada yang dua
   ("BAHASA INDONESIA"). Karena tingginya dibiarkan mengikuti isi, ikon di
   kartu bernama panjang duduk sama tinggi dengan kartu bernama pendek
   sementara namanya turun satu baris — dan yang terlihat saat dua kartu itu
   bersebelahan adalah dua kartu yang isinya tidak sejajar.

   Jatah dua baris disediakan untuk SEMUA nama, terpakai atau tidak. Kartu
   bernama pendek kehilangan sedikit ruang kosong; sebagai gantinya ikon,
   nama, cincin, dan lencana berdiri di garis yang sama di seluruh baris.

   Tidak berlaku di ponsel: di sana kartu menjadi baris mendatar (bagian 54),
   namanya di kiri dan keterangannya di kanan, jadi tidak ada yang perlu
   disejajarkan tegak.
   -------------------------------------------------------------------------- */
@media (min-width: 721px) {
  .tile.icon.berisi .label {
    display: flex; align-items: center; justify-content: center;
    min-height: calc(2 * 1.25em); line-height: 1.25;
    margin-bottom: .35rem;
  }
  /* Lencana keadaan diberi jarak dari kata "terisi" di atasnya; sebelumnya
     keduanya menempel dan terbaca sebagai satu blok abu-abu. */
  .tile.icon.berisi .isi > .badge { margin-top: .15rem; }
}

/* ==========================================================================
   RONDE 91 — LATAR SEKOLAHNYA TERLIHAT, DI SELURUH HALAMAN

   Sampai ronde 90, foto gedung sekolah hanya berdiri di halaman MASUK, dan di
   sana pun teredam sampai nyaris hilang. Halaman lain memakai gradasi hijau
   polos. Akibatnya aplikasi terasa seperti dua produk: satu halaman yang
   berwajah sekolah, lalu delapan belas halaman yang bisa milik siapa saja.

   Sekarang fotonya berdiri di seluruh aplikasi. Yang menahan diri bukan
   fotonya, melainkan SELUBUNGNYA — dan selubung itu tidak boleh ditipiskan
   "biar fotonya kelihatan":

   Judul halaman, remah jejak, dan baris keterangan berdiri LANGSUNG di atas
   latar ini, tidak beralaskan kartu. Foto gedungnya terang, berwarna
   kuning-hijau pekat, dan penuh garis diagonal. Angka yang menjaga selubung
   ini bukan selera: uji/kontras.mjs mengambil warna piksel yang SUNGGUHAN
   tergambar di belakang tiap tulisan, lalu menghitung rasionya terhadap warna
   hurufnya. Selama seluruhnya di atas 4,5 : 1, selubungnya boleh ditipiskan.
   Di bawah itu tidak — dan yang membayarnya adalah guru yang membaca layar
   ini di ruang kelas yang silau, bukan orang yang memilih angkanya.
   ----------------------------------------------------------------------- */
.app {
  background-image:
    linear-gradient(180deg,
                    rgba(244,249,245,.94) 0%,
                    rgba(244,249,245,.91) 38%,
                    rgba(240,248,242,.92) 72%,
                    rgba(234,245,237,.94) 100%),
    url('./gedung.webp');
  background-repeat: no-repeat, no-repeat;
  background-size: cover, cover;
  /* 28% supaya yang berdiri di belakang isi halaman adalah badan gedungnya,
     bukan langit kosong di atasnya — sama alasannya dengan halaman masuk. */
  background-position: center, center 28%;
  background-attachment: fixed, fixed;
}

/* Peramban seluler kerap mengabaikan background-attachment: fixed dan
   menyisakan foto yang meregang aneh saat halaman digulir. Sama seperti
   halaman masuk. */
@media (max-width: 860px), (max-height: 640px) {
  .app { background-attachment: scroll, scroll; }
}

@media print {
  .app { background-image: none; background-color: #fff; }
}

/* Kartu, panel, dan tabel WAJIB tetap buram.
   Di atas latar bergambar, satu saja yang tembus pandang membuat angka nilai
   dibaca di atas garis-garis diagonal gedung. */
.card, .panel, .kartu, .tabel-bungkus, .dialog { background-color: var(--paper); }

/* Halaman masuk: selubungnya ditipiskan, LORONG TENGAHNYA tidak.
   Yang dilonggarkan hanya lapis mendatar — bagian kiri-kanan layar, tempat
   tidak ada satu huruf pun berdiri. Lorong tengah tetap .94 karena di situlah
   "Penilaian waktu nyata — Sekolah Islam Terpadu Bina Ilmi Palembang" dan
   "Kesulitan masuk?" dibaca. */
.login-page {
  background-image:
    linear-gradient(90deg, rgba(243,251,244,0) 0%,
                    rgba(243,251,244,.84) 20%,
                    rgba(243,251,244,.94) 34%,
                    rgba(243,251,244,.94) 66%,
                    rgba(243,251,244,.84) 80%,
                    rgba(243,251,244,0) 100%),
    linear-gradient(180deg, rgba(243,251,244,.34) 0%,
                    rgba(243,251,244,.26) 30%,
                    rgba(243,251,244,.30) 62%,
                    rgba(232,244,235,.42) 100%),
    url('./gedung.webp');
}

/* ==========================================================================
   RONDE 91 — GERAK YANG TERASA HALUS, BUKAN GERAK YANG BANYAK

   Aturan yang dipakai di seluruh blok ini: satu bahasa gerak, bukan dua puluh
   animasi yang masing-masing punya selera sendiri.

     · durasi   .16s untuk sentuhan, .22s untuk sesuatu yang muncul,
                .34s untuk halaman. Lebih lama dari itu terasa lamban pada
                pekerjaan yang diulang ratusan kali sehari — dan mengisi nilai
                adalah pekerjaan seperti itu.
     · yang digerakkan hanya transform dan opacity. Keduanya tidak memaksa
       peramban menghitung ulang tata letak; menganimasikan `height` atau
       `top` pada tabel 60 baris membuat gulirannya tersendat di komputer
       sekolah, dan komputer sekolah adalah satu-satunya komputer yang penting.
     · setiap gerak di sini dimatikan seluruhnya oleh prefers-reduced-motion
       di ujung blok. Bukan diperpendek — dimatikan.
   ----------------------------------------------------------------------- */

/* Guliran halaman. Dipakai oleh "kembali ke atas", loncatan remah jejak, dan
   fokus yang berpindah ke baris jauh di bawah. */
html { scroll-behavior: smooth; }

/* --- baris tabel ------------------------------------------------------- */
/* Tabel nilai adalah layar yang paling lama ditatap. Sorotan barisnya dibuat
   dari background saja — bukan transform — karena baris yang bergeser sedikit
   saat kursor lewat membuat mata kehilangan kolom yang sedang diikuti. */
.table-wrap tbody tr, table tbody tr { transition: background-color .16s var(--ease); }
.table-wrap tbody tr:hover, table tbody tr:hover { background-color: var(--green-050); }

/* --- benda yang bisa ditekan ------------------------------------------- */
/* Umpan balik tekan yang seragam. Sebelumnya hanya .btn yang punya; chip,
   tautan tindakan, dan tombol lonceng tidak — dan bedanya terasa persis
   seperti tombol yang tidak merespons. */
.chip, .link-btn, .bell-button, .tile, .btn, .toast, .nama-buka {
  transition: transform .16s var(--ease), box-shadow .16s var(--ease),
              background-color .16s var(--ease), border-color .16s var(--ease),
              color .16s var(--ease), opacity .16s var(--ease);
}
.chip:active:not(:disabled),
.link-btn:active:not(:disabled),
.bell-button:active,
.nama-buka:active { transform: translateY(1px) scale(.985); }

/* Cincin fokus tidak ikut dianimasikan: fokus harus MUNCUL, bukan tumbuh.
   Cincin yang memudar masuk membuat orang yang berpindah dengan Tab
   kehilangan jejak posisinya. */
:focus-visible { transition: none; }

/* --- kartu dan panel --------------------------------------------------- */
/* Angkatan yang sangat kecil. .tile sudah punya -3px; kartu isi lebih besar
   dan lebih berat, jadi 2px sudah terbaca sebagai "ini bisa ditekan". */
.card, .panel { transition: box-shadow .18s var(--ease), transform .18s var(--ease); }
a.card:hover, button.card:hover,
a.panel:hover, button.panel:hover { transform: translateY(-2px); box-shadow: var(--sh-3); }

/* --- rel samping ------------------------------------------------------- */
.side a, .side button { transition: background-color .16s var(--ease), color .16s var(--ease); }

/* --- pesan sekilas ----------------------------------------------------- */
/* Toast masuk dari kanan, bukan memudar di tempat: yang muncul begitu saja di
   sudut layar sering tidak disadari sama sekali. */
.toast { animation: toast-masuk .22s var(--ease) both; }
@keyframes toast-masuk { from { opacity: 0; transform: translateX(14px) } }

/* --- lonceng ----------------------------------------------------------- */
.bell-drop:not(.hide) { animation: bell-turun .18s var(--ease) both; }
@keyframes bell-turun { from { opacity: 0; transform: translateY(-8px) scale(.99) } }

/* --- laci siswa -------------------------------------------------------- */
.laci, .drawer { transition: transform .22s var(--ease), opacity .22s var(--ease); }

/* --- gambar -----------------------------------------------------------  */
/* Foto gedung dan lambang tidak boleh "meletus" masuk saat halaman dimuat. */
.brand-logo, .brand-tagline { transition: opacity .2s var(--ease); }

/* ==========================================================================
   YANG MEMINTA GERAK DIKURANGI MENDAPAT APLIKASI YANG DIAM

   Bukan gerak yang dipercepat — dihentikan. Vestibular disorder tidak
   membaik karena animasinya 80 milidetik alih-alih 340. Yang tersisa hanya
   perubahan warna, yang tidak menggerakkan apa pun di layar.
   ----------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .page, .toast, .bell-drop:not(.hide), .dialog, .backdrop { animation: none !important; }
  .chip:active, .link-btn:active, .bell-button:active, .nama-buka:active,
  a.card:hover, button.card:hover, a.panel:hover, button.panel:hover,
  .tile:hover, .tile:active { transform: none !important; }
  .login-page::before, .login-page::after { animation: none !important; }
}

/* ==========================================================================
   RONDE 92 — TOMBOL CARI YANG MENGATAKAN APA YANG BISA DILAKUKAN

   Ctrl+K sudah berlaku sejak awal, tetapi satu-satunya petunjuknya ada di
   atribut `title` — yang hanya muncul bila kursor berhenti di atas tombol,
   dan tidak pernah muncul sama sekali di layar sentuh. Pintasan yang tidak
   diketahui sama saja dengan pintasan yang tidak ada.

   Sekarang ia tertulis di tombolnya. Yang ditambahkan hanya itu dan umpan
   balik tekan; bentuk dan warnanya tidak diubah, karena tombol ini duduk di
   kepala halaman bersama lonceng dan nama pengguna, dan tiga benda yang
   masing-masing menuntut perhatian membuat kepala halaman berisik.
   ----------------------------------------------------------------------- */
.cari-button .kbd {
  display: inline-flex; align-items: center;
  margin-left: .1rem; padding: .1rem .34rem;
  border: 1px solid var(--line-2); border-radius: 5px;
  background: var(--canvas); color: var(--ink-600);
  font: inherit; font-size: var(--t-3xs, .68rem); font-weight: 700;
  line-height: 1.5; letter-spacing: .02em;
  transition: border-color .16s var(--ease), background .16s var(--ease),
              color .16s var(--ease);
}
.cari-button:hover .kbd, .cari-button:focus-visible .kbd {
  border-color: var(--green-300); background: #fff; color: var(--green-800);
}

/* Umpan balik tekan. Tanpa ini tombolnya terasa seperti gambar: ia berubah
   warna saat disentuh kursor, lalu tidak melakukan apa-apa yang terlihat
   ketika benar-benar ditekan. */
.cari-button:active { transform: translateY(1px) scale(.985); }

/* Cincin fokus yang sama dengan tombol lain, supaya orang yang berpindah
   dengan Tab tidak kehilangan jejaknya di kepala halaman. */
.cari-button:focus-visible { outline: 2px solid var(--green-600); outline-offset: 2px; }

/* Di layar sempit: tidak ada papan ketik untuk menekan pintasannya, dan
   ruangnya diperebutkan lonceng serta nama pengguna. */
@media (max-width: 860px) {
  .cari-button .kbd { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .cari-button:active { transform: none !important; }
}
