/* ==========================================================================
   TUTOORIA — 05. AREA APLIKASI (setelah masuk)
   Dimuat hanya oleh halaman di folder app/, sesudah pages.css.
   Kanvas abu nyaris putih (--bg-canvas) untuk bingkai dan latar, kartu
   putih di atasnya — clean & minimal. Skala huruf padat dari blok .app di tokens.css. Semua nilai dari
   tokens.css — tidak ada hex atau px lepas kecuali ketebalan garis grafik.
   Kartu, panel, KPI, progres, tabel, tombol memakai komponen global di
   components.css; file ini hanya cangkang aplikasi dan tata letak isi
   yang khas dasbor.
   ========================================================================== */

/* =======================================================
   CANGKANG — sidebar kiri + konten kanan
   ======================================================= */
.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-block-size: 100dvh;
}
/* Halaman yang punya menu tingkat dua (pengaturan) memakai TIGA kolom:
   sidebar aplikasi · sidebar bagian · isi. Dulu menu bagian itu berada di
   dalam .app-main, jadi ia ikut dipusatkan bersama isinya dan tidak
   menempel penuh tinggi layar (21 Sep 2026). */
.app:has(.app-subnav) { grid-template-columns: var(--sidebar-w) var(--subnav-w) minmax(0, 1fr); }
/* Tab Pembelajaran & Jadwal menambah kolom KEEMPAT: isinya mengisi kolom
   ketiga penuh, dan rel kanan memegang daftar materi (Pembelajaran) atau
   sesi tanggal terpilih (Jadwal). Rel itu muncul-hilang mengikuti tab yang
   aktif lewat :has(), tanpa JavaScript. */
.app:has(#panel-belajar:not([hidden])),
.app:has(#panel-jadwal:not([hidden])) {
  grid-template-columns: var(--sidebar-w) var(--subnav-w) minmax(0, 1fr) var(--rail-w);
}
.app:has(#panel-belajar[hidden]) .learn-rail,
.app:has(#panel-jadwal[hidden]) .live-rail { display: none; }
/* Daftar materi FLUSH di dalam relnya: rel tidak punya jarak tepi sendiri,
   jadi garis di bawah kepala membentang penuh selebar kolom dan daftarnya
   menggulir dari tepi ke tepi. Jarak isinya dipegang .learn-side__head dan
   .learn-side__body (22 Sep 2026). */
/* .app-rail.learn-rail, bukan .learn-rail saja: keduanya berspesifisitas
   sama dan .app-rail ditulis setelahnya, jadi paddingnya akan menang. */
.app-rail.learn-rail {
  padding: 0;
  /* Yang menggulir hanya badan daftarnya, bukan relnya — dua wadah gulir
     bersarang membuat dua batang gulir muncul berdampingan. */
  overflow: hidden;
  gap: 0;
  align-content: stretch;
  grid-template-rows: auto minmax(0, 1fr);
}

.app-rail {
  position: sticky;
  inset-block-start: 0;
  block-size: 100dvh;
  display: grid;
  align-content: start;
  gap: var(--space-s);
  /* Jarak tepi sama dengan sidebar bagian di kiri dan dengan rel daftar
     materi, yang memberi jarak itu lewat kepala & badannya sendiri. */
  padding: var(--space-m);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg-canvas);
  border-inline-start: var(--border-hair) solid var(--line);
}
/* Judul tanggal memakai huruf teks, bukan Farro: ia keterangan di dalam
   panel, sejajar peran dengan judul kartu — bukan judul halaman. */
.app-rail__title {
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: var(--weight-semibold);
  letter-spacing: normal;
}
.app-rail__title:focus { outline: none; }

.app-subnav {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  block-size: 100dvh;
  /* Tepi atas disamakan dengan .app-main supaya butir pertama sejajar
     dengan judul panel di sebelahnya. */
  padding: var(--app-gutter) var(--space-m);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg-canvas);
  border-inline-end: var(--border-hair) solid var(--line);
}
/* body memakai --step-0 dari :root; di sini diambil ulang dari skala padat. */
.app,

/* <body> ikut berwarna kanvas, supaya pantulan gulir di tepi layar tidak
   memperlihatkan putih. */
body:has(> .app) { background: var(--bg-canvas); }

.app-sidebar {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  display: flex;
  flex-direction: column;
  /* 24px, bukan 32px: sejak ada kolom cari di antara logo dan menu, tiga
     blok berjarak 32px mendorong menu terlalu jauh ke bawah. Jarak ke
     .app-promo tidak terpengaruh — blok itu didorong margin auto. */
  gap: var(--space-m);
  block-size: 100dvh;
  /* Jarak ke tepi sama di keempat sisi, seperti .app-main; jarak antar-blok
     di dalamnya dipegang gap 24px. */
  padding: var(--space-m);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg-canvas);
  border-inline-end: var(--border-hair) solid var(--line);
}
.app-sidebar__logo { padding-inline: var(--space-xs); }
.app-sidebar__logo .logo__img { block-size: 1.5rem; }

/* --- Cari global ---
   Duduk antara logo dan menu: identitas dulu, lalu cara mencari, baru
   daftar tujuan. Dikecilkan ke jenjang --small lewat token, sama dengan
   butir menu di bawahnya, supaya ketiganya satu irama. */
.app-search { flex: none; }
/* Ukurannya datang dari .input--small di markup, bukan dari sini. Yang
   tersisa hanya inset kanan: ruangnya harus memuat petunjuk ⌘K, bukan
   sekadar padding kontrol. Inset kiri tetap dari .input--with-icon. */
.app-search .input { padding-inline-end: 3.25rem; }
.app-search .input-wrap__icon {
  inline-size: 1rem;
  block-size: 1rem;
}
/* Petunjuk pintasan, bukan tombol — pointer-events dimatikan supaya klik
   di atasnya tetap jatuh ke kolom isian. */
.app-search__keys {
  position: absolute;
  inset-inline-end: var(--space-2xs);
  inset-block-start: 50%;
  translate: 0 -50%;
  display: flex;
  gap: var(--space-3xs);
  pointer-events: none;
}
.app-search .kbd { min-inline-size: 1.25rem; block-size: 1.25rem; font-size: var(--step--2); }
@media (pointer: coarse) { .app-search__keys { display: none; } }

/* --- Menu utama ---
   Dua daftar: tujuan utama (Beranda, Try Out) lalu kelompok "Belajar"
   (Kelas, Perpustakaan, Copilot) dengan judulnya sendiri. */
.app-nav { display: grid; gap: var(--space-3xs); }
/* Jarak sebelum judul kelompok lebih besar daripada jarak antar butir —
   itu yang memisahkan kedua kelompok, bukan garis. */
.app-nav .menu-group { margin-block-start: var(--space-s); }
.app-nav__list {
  display: grid;
  gap: var(--space-3xs);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* = .menu-item (components.css) + selisihnya: butir menu berbaris dari kiri
   (lencana jumlah yang mendorong dirinya ke kanan lewat margin auto), tinggi
   dan radiusnya mengikuti kontrol aplikasi, dan warnanya redup sampai
   disinggahi. */
.app-nav__link {
  --menu-item-h: var(--control-h-small);
  --menu-item-radius: var(--radius-m);
  --menu-item-hover: var(--bg-canvas-strong);
  justify-content: flex-start;
  color: var(--fg-muted);
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.app-nav__link:hover { color: var(--fg); }
/* Aktif = bidang abu satu jenjang di atas kanvas + huruf tebal gelap —
   tenang, ungu disimpan untuk aksi dan sorotan di area konten. */
.app-nav__link[aria-current="page"] {
  background: var(--bg-canvas-strong);
  color: var(--fg);
  font-weight: var(--weight-semibold);
}
.app-nav__icon { display: grid; place-items: center; flex: none; }
.app-nav__icon .icon { inline-size: 1.125rem; block-size: 1.125rem; }
.app-nav__label--short { display: none; }
.app-nav__count {
  margin-inline-start: auto;
  min-inline-size: 1.375rem;
  padding: 0.05em 0.45em;
  border-radius: var(--radius-pill);
  background: var(--bg-canvas-strong);
  color: var(--fg);
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.app-nav__link:is(:hover, [aria-current="page"]) .app-nav__count { background: var(--bg-raised); }

/* --- Blok promosi paket — bidang abu cekung, bukan ungu, supaya tidak
   berebut dengan butir menu aktif. --- */
.app-promo {
  display: grid;
  gap: var(--space-2xs);
  margin-block-start: auto;
  padding: var(--space-s);
  border-radius: var(--radius-l);
  background: var(--bg-sunken);
}
.app-promo__title { font-family: var(--font-display); font-weight: var(--weight-display); font-size: var(--step-0); line-height: var(--leading-snug); }
.app-promo__body { font-size: var(--step--1); line-height: var(--leading-normal); color: var(--fg-muted); }
.app-promo .button { margin-block-start: var(--space-2xs); }

/* --- Pengguna di dasar sidebar --- */
/* Tanpa garis atas: jaraknya sudah dipegang gap .app-sidebar, dan garis
   tambahan membuat blok akun terbaca seperti bagian terpisah dari sidebar
   (21 Sep 2026). */
.app-user {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  padding-inline: var(--space-3xs);
}
.app-user .avatar { --avatar-size: 2.25rem; font-size: var(--step--1); }
/* Di sidebar desktop tombol keluar tidak ada: tempatnya di dalam Pengaturan.
   Ia hanya dipasang di bilah atas ponsel (lihat blok < lg). */
.app-user__out { display: none; }
/* Avatar + nama adalah satu tautan ke Pengaturan — pintu masuk satu-satunya,
   karena bilah tab ponsel sudah penuh dengan lima menu utama dan menu keenam
   akan memperkecil sasaran sentuh semuanya. Isinya <span>, bukan <p>, supaya
   sah berada di dalam <a>; keduanya dijadikan blok di sini. */
.app-user__me {
  flex: 1;
  min-inline-size: 0;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-3xs);
  margin-inline-start: calc(var(--space-3xs) * -1);
  border-radius: var(--radius-m);
  color: inherit;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out);
}
.app-user__me:hover { background: var(--bg-canvas-strong); text-decoration: none; }
.app-user__me[aria-current="page"] { background: var(--bg-canvas-strong); }
.app-user__text { min-inline-size: 0; flex: 1; line-height: var(--leading-snug); }
.app-user__name {
  display: block;
  overflow: hidden;
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-user__plan { display: block; font-size: var(--step--2); color: var(--fg-muted); }
/* --- Konten --- */
.app-main {
  /* Di dalam aplikasi jarak tepinya --app-gutter. Disamakan ke --gutter
     supaya komponen yang membentang sampai tepi layar (baris tab katalog,
     .row--rail) memakai margin negatif yang tepat — dulu baris tab meluber
     7px di tablet karena memakai gutter situs. */
  --gutter: var(--app-gutter);
  min-inline-size: 0;
  display: grid;
  grid-template-columns: minmax(0, var(--app-container));
  justify-content: center;
  align-content: start;
  gap: var(--space-xl);
  /* Satu nilai untuk keempat sisi: jarak ke tepi layar, ke sidebar, ke
     bilah atas, dan ke dasar halaman sama besar. */
  padding: var(--app-gutter);
}
/* Isi SEMUA halaman aplikasi dipusatkan selebar --app-container (21 Sep
   2026; dulu hanya beranda & pengaturan, halaman lain selebar layar). Di
   bawah lebar itu kolomnya tetap mengisi penuh sampai --app-gutter. */

/* =======================================================
   PONSEL & TABLET (< lg) — sidebar menjadi bilah atas putih, menu
   menjadi bilah tab di dasar layar. Satu markup, tanpa JavaScript.
   Lima menu muat sebaris, jadi tidak perlu laci tersembunyi: setiap
   tujuan tetap satu ketukan.
   ======================================================= */
@media (max-width: 63.99rem) {
  /* Termasuk halaman bersidebar kedua & keempat: spesifisitas :has() lebih
     tinggi dari .app, jadi harus ditimpa dengan pemilih yang sama. */
  .app,
  .app:has(.app-subnav),
  .app:has(#panel-belajar:not([hidden])),
  .app:has(#panel-jadwal:not([hidden])) { grid-template-columns: minmax(0, 1fr); }
  /* Bidang hitam pemutar kembali setinggi isinya, bukan setinggi layar. */
  .app-main:has(#panel-belajar:not([hidden])) { grid-template-rows: auto; }
  #panel-belajar { padding: var(--app-gutter) 0; }

  .app-rail {
    position: static;
    block-size: auto;
    /* Kolom kanan turun ke dasar halaman, di luar .app-main — jadi jarak
       untuk bilah tab yang mengambang harus dipasang di sini juga, kalau
       tidak butir terakhirnya tertutup bilah. */
    padding: 0 var(--app-gutter)
             calc(var(--app-gutter) + var(--tabbar-h) + env(safe-area-inset-bottom));
    border-inline-start: 0;
  }
  .live .calendar__grid { block-size: auto; }

  .app-sidebar {
    flex-direction: row;
    align-items: center;
    gap: var(--space-s);
    block-size: var(--appbar-h);
    padding: 0 var(--app-gutter);
    overflow: visible;
    border-inline-end: 0;
    border-block-end: var(--border-hair) solid var(--line);
  }
  .app-sidebar__logo { padding: 0; }
  .app-sidebar__logo .logo__img { block-size: 1.375rem; }
  /* Bilah atas hanya memuat logo + akun. Kolom cari disembunyikan seperti
     .app-promo: setiap halaman yang isinya bisa dicari sudah punya kolom
     carinya sendiri di badan halaman (beranda, kelas, try out,
     perpustakaan), jadi tidak ada jalan yang hilang di ponsel. */
  .app-search { display: none; }
  .app-promo { display: none; }

  /* row-reverse, bukan properti order per butir: urutan tampilnya di ponsel
     [pengaturan] [keluar] [avatar] — kebalikan urutan markup, yang disusun
     untuk sidebar desktop (avatar dulu, baru ikon). Satu aturan lebih mudah
     dipercaya daripada tiga nilai order yang harus dijaga tetap selaras. */
  .app-user { flex-direction: row-reverse; margin-inline-start: auto; padding: 0; border: 0; }
  .app-user .avatar { --avatar-size: 2rem; }
  .app-user__me { flex: none; margin-inline-start: 0; }
  .app-user__text { display: none; }
  .app-user__out { display: grid; }

  .app-nav {
    position: fixed;
    inset: auto 0 0 0;
    z-index: var(--z-header);
    padding-block-end: env(safe-area-inset-bottom);
    background: var(--bg-canvas);
    border-block-start: var(--border-hair) solid var(--line);
  }
  /* Bilah tab: keenam butir dari KEDUA daftar harus berbaris sama lebar,
     jadi daftarnya sendiri dilarutkan (display: contents) dan yang
     memegang kolom adalah .app-nav. Judul kelompoknya tidak ikut turun —
     di bilah tab tidak ada ruang untuk judul, dan urutannya sudah jelas.
     Angkanya ditulis eksplisit dan HARUS ikut berubah saat butir menu
     bertambah: butir keenam yang masuk ke grid berkolom lima jatuh ke baris
     kedua dan bilah tabnya patah. Naik 5 → 6 pada 24 Sep 2026 (Bimbingan). */
  .app-nav { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 0; }
  .app-nav__list { display: contents; }
  .app-nav .menu-group { display: none; }
  .app-nav__link {
    position: relative;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-3xs);
    min-block-size: var(--tabbar-h);
    padding: var(--space-2xs) var(--space-3xs);
    border-radius: 0;
    font-size: var(--step--2);
    letter-spacing: 0;
  }
  .app-nav__link:hover { background: none; }
  /* Pil abu di belakang ikon — bidang, bukan garis; sama dengan sidebar. */
  .app-nav__icon {
    inline-size: 3.25rem;
    block-size: 1.875rem;
    border-radius: var(--radius-pill);
    transition: background var(--dur-fast) var(--ease-out);
  }
  .app-nav__link[aria-current="page"] { background: none; color: var(--fg); }
  .app-nav__link[aria-current="page"] .app-nav__icon { background: var(--bg-canvas-strong); }
  .app-nav__label--full { display: none; }
  .app-nav__label--short { display: block; }
  /* --radius-pill (999px) membulatkan sudut sebesar SETENGAH SISI TERPENDEK,
     jadi ia hanya menghasilkan lingkaran kalau kotaknya persegi. Di sini
     tingginya tidak pernah disetel: dengan line-height 1 dan tanpa padding
     tegak, tingginya = tinggi hurufnya (12px) sementara lebarnya dipaksa
     min-inline-size ke 17,6px — 18×12, kapsul gepeng, bukan lingkaran.

     Perbaikannya tinggi dikunci sama dengan lebar minimumnya. Satu angka
     mengisi persis 18×18 (lingkaran); dua angka melebar oleh paddingnya
     sendiri dan jadi kapsul — itu memang bentuk yang benar untuk "12". */
  .app-nav__count {
    position: absolute;
    inset-block-start: var(--space-2xs);
    inset-inline-start: calc(50% + 0.6rem);
    min-inline-size: 1.125rem;
    block-size: 1.125rem;
    margin: 0;
    padding: 0 0.3em;
    background: var(--clr-gray-900);
    color: var(--clr-white);
    display: grid;
    place-items: center;
    font-size: var(--step--2);
    line-height: 1;
  }

  /* Di ponsel sisi bawah tetap sama besar, hanya ditambah tinggi bilah tab
     yang mengambang di dasar layar. */
  .app-main {
    padding-block-end: calc(var(--app-gutter) + var(--tabbar-h) + env(safe-area-inset-bottom));
    gap: var(--space-xl);
  }
  /* Halaman fokus tidak punya bilah tab, jadi tambahan tinggi itu hanya
     menyisakan ruang menganggur di dasar halaman. */
  .app--focus .app-main { padding-block-end: var(--app-gutter); }

  /* --focus-sm: fokus HANYA di ponsel. .app-sidebar memegang bilah atas dan
     bilah tab sekaligus, jadi melepasnya membuat halaman tampil penuh —
     ±136px tinggi layar kembali ke isinya. Dipakai Copilot: ruang obrolan
     yang isinya tumbuh ke bawah paling terasa sempit di ponsel. Mulai lg
     sidebarnya kembali seperti halaman app lain.
     Syaratnya sama dengan .app--focus: jalan keluarnya harus dipasang di
     dalam halaman (di sini .copilot__back). */
  .app--focus-sm > .app-sidebar { display: none; }
  .app--focus-sm .app-main { padding-block-end: var(--app-gutter); }
  .app--focus-sm .app-main--chat { block-size: 100dvh; padding-block-end: 0; }
}


/* =======================================================
   TABLET MENDATAR (lg – xl) — kerangka TIGA kolom masih muat, yang
   KEEMPAT tidak: sidebar 16.5rem + menu kelas 15rem + rel 21rem menyisakan
   ~11rem untuk isinya, dan pemutar video di tab Pembelajaran menyusut
   sampai tombol mainnya terpotong. Di lebar ini relnya turun ke bawah isi
   — selebar kolom isi, bukan selebar layar, karena dua kolom kiri masih
   berdiri. Mulai xl kolom keempat baru dipasang kembali.
   ======================================================= */
@media (min-width: 64rem) and (max-width: 79.99rem) {
  .app:has(#panel-belajar:not([hidden])),
  .app:has(#panel-jadwal:not([hidden])) {
    grid-template-columns: var(--sidebar-w) var(--subnav-w) minmax(0, 1fr);
    /* auto 1fr, bukan auto auto: sisa ruang di layar tinggi jatuh ke rel di
       baris bawah. Kalau dibagi rata, bidang hitam pemutar ikut memanjang
       dan daftar materinya terdorong keluar layar. */
    grid-template-rows: auto 1fr;
  }
  /* Relnya kini baris kedua, jadi dua kolom kiri harus dibentangkan
     melintasi kedua baris — kalau tidak, sidebar berhenti di tengah dan
     garis kanannya ikut terputus. */
  .app:has(#panel-belajar:not([hidden])) > :is(.app-sidebar, .app-subnav),
  .app:has(#panel-jadwal:not([hidden])) > :is(.app-sidebar, .app-subnav) { grid-row: 1 / -1; }

  /* Bidang hitam pemutar kembali setinggi isinya, bukan setinggi layar. */
  .app-main:has(#panel-belajar:not([hidden])) { grid-template-rows: auto; }
  #panel-belajar { padding: var(--app-gutter) 0; }

  .app-rail {
    grid-column: 3;
    position: static;
    block-size: auto;
    border-inline-start: 0;
    border-block-start: var(--border-hair) solid var(--line);
  }
  .live .calendar__grid { block-size: auto; }
}

/* =======================================================
   KEPALA HALAMAN & SEKSI
   ======================================================= */
/* Kepala halaman dasbor duduk langsung di grid .app-main, yang jaraknya
   sudah dipegang gap — jadi tanpa margin bawah sendiri. */
.app-main > .section-head { margin-block-end: 0; }

/* Kepala halaman & kepala seksi dasbor memakai .section-head bersama
   (components.css). Yang khas dasbor hanya ini: keterangan di bawah judul
   halaman tetap seukuran teks isi, bukan .lead — dasbor dipakai lama dan
   tidak butuh kalimat pembuka sebesar halaman pemasaran. */
.app-main .section-head--page .section-head__body { font-size: var(--step-0); color: var(--fg-muted); }

/* =======================================================
   SAMBUTAN BERANDA — salam di tengah, kolom cari besar, lalu tiga kolom:
   dua pintasan (label + ubin) dan daftar "Terakhir dibuka".
   ======================================================= */
.welcome { display: grid; gap: var(--space-l); padding-block-start: var(--space-m); }
.welcome__intro { display: grid; justify-items: center; gap: var(--space-s); text-align: center; }
/* Pil hitung mundur di atas salam — tautan ke persiapan try out. */
.welcome__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  /* Atas, bawah, dan kiri sama besar (4px): badge di dalamnya adalah pil
     berisi, jadi ia duduk konsentris di dalam pil luar. Tingginya ikut isi,
     bukan dipatok min-block-size — dengan tinggi tetap, celah atas-bawah
     jadi lebih besar dari celah kiri dan bentuknya terbaca miring. Kanan
     12px karena di sana ada teks dan panah, bukan bidang berisi. */
  padding: var(--space-3xs);
  padding-inline-end: var(--space-xs);
  border: var(--border-hair) solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--bg-raised);
  color: var(--fg);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.welcome__pill:hover { border-color: var(--line-strong); text-decoration: none; }
.welcome__pill > .icon { --icon-size: 1rem; color: var(--fg-muted); }
.welcome__title { font-size: var(--text-display); line-height: var(--leading-snug); text-wrap: balance; }

/* Kolom cari: kotak isian putih di dalam cincin tipis — dua lapis garis
   membuatnya terbaca sebagai titik mulai halaman, bukan isian biasa. */
/* Cangkang kotak cari = .field-shell (components.css). Di sini hanya
   perilaku yang khas beranda. */
.welcome__search { display: block; }

/* min-inline-size: 0 di kolom dan daftarnya: tanpa itu baris "Terakhir
   dibuka" (teks nowrap ber-elipsis) menolak menyusut di bawah lebar
   min-content-nya, dan kolomnya meluber keluar layar di tablet. */
.welcome__col { display: grid; align-content: start; gap: var(--space-xs); min-inline-size: 0; }
.welcome__label { font-family: var(--font-sans); font-size: var(--step--1); font-weight: var(--weight-medium); color: var(--fg-muted); }
.welcome__label a { display: inline-flex; align-items: center; gap: var(--space-3xs); color: inherit; text-decoration: none; }
.welcome__label a:hover { color: var(--fg); text-decoration: none; }
.welcome__label .icon { --icon-size: 0.875rem; }
/* Ubin pintasan = bidang abu satu jenjang di atas kanvas. */
.welcome__tile {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-block-size: 3.5rem;
  padding-inline: var(--space-s);
  border-radius: var(--radius-l);
  background: var(--bg-canvas-strong);
  color: var(--fg-muted);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.welcome__tile:hover { background: var(--clr-gray-200); color: var(--fg); text-decoration: none; }
.welcome__tile:active { background: var(--clr-gray-200); color: var(--fg); }
.welcome__tile .icon { --icon-size: 1rem; }

/* Terakhir dibuka — "Bagian / Judul": bagian redup, judul tebal. */
.recent { display: grid; min-inline-size: 0; margin: 0; padding: 0; list-style: none; }
/* Butir daftar juga dikunci: sebagai sel grid, lebar minimumnya "auto" =
   selebar teks yang tidak membungkus, dan itulah yang mendorong kolom
   meluber di tablet. */
.recent > li { min-inline-size: 0; }
.recent__item {
  display: flex;
  min-inline-size: 0;
  align-items: center;
  gap: var(--space-xs);
  min-block-size: 3rem;
  border-block-end: var(--border-hair) solid var(--line);
  color: var(--fg-muted);
  font-size: var(--step--1);
  text-decoration: none;
}
.recent li:first-child .recent__item { min-block-size: 2.5rem; padding-block-end: var(--space-2xs); }
.recent li:last-child .recent__item { border-block-end: 0; }
.recent__item:hover { color: var(--fg); text-decoration: none; }
.recent__item > .icon { --icon-size: 1rem; flex: none; }
.recent__path { flex: 1; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recent__path b { font-weight: var(--weight-medium); color: var(--fg); }
.recent__item > .icon:last-child { transition: translate var(--dur-fast) var(--ease-out); }
.recent__item:hover > .icon:last-child { translate: 0.15rem 0; }

/* Kolom dikunci minmax(0, 1fr): kolom implisit `auto` ikut melebar sampai
   selebar isi yang tidak membungkus (baris tab ±650px), mendorong halaman
   meluber di ponsel — dan bilah tab bawah yang fixed ikut melebar. */
/* =======================================================
   BILAH TAB TINGKAT HALAMAN — [data-tabs] di bawah kepala halaman, untuk
   halaman yang isinya beberapa tampilan sejajar (Try Out: Semua /
   Try out kamu / Riwayat hasil). Aturannya sama persis dengan
   .catalog-head__tabs; dipisah karena yang satu kepala katalog dan yang
   satu cangkang halaman — menumpang kelas tetangga membuat perubahan di
   salah satunya diam-diam mengubah yang lain.
   ======================================================= */
/* minmax(0, 1fr): tanpa itu kolomnya ikut melebar mengikuti lebar deret
   tab yang sebenarnya menggulir sendiri — di ponsel halamannya jadi bisa
   digeser mendatar sejauh tab yang tidak muat. */
.app-tabs-root { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-l); }
/* Garis bawah dipasang di pembungkus, bukan di .tabs: .tabs menggulir
   mendatar di layar sempit, jadi garis di dalamnya ikut terpotong. */
.app-tabs { border-block-end: var(--border-hair) solid var(--line); }
/* Ponsel & tablet: deret tab dibentangkan sampai tepi layar, tab pertama
   tetap sejajar gutter lewat padding. */
@media (max-width: 63.99rem) {
  .app-tabs { margin-inline: calc(var(--gutter) * -1); }
  .app-tabs .tabs {
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
  }
}

.app-section { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-m); min-inline-size: 0; }

/* Kepala katalog di dalam aplikasi: strukturnya sama dengan situs, hanya
   judulnya turun ke ukuran judul seksi karena <h1> halaman sudah dipegang
   .section-head--page. */
/* Tanpa margin bawah: jarak ke kisi kartu sudah dipegang gap .app-section,
   jadi satu sumber saja — dulu margin kepala + gap seksi bertumpuk. */
.app-main .catalog-head { margin-block-end: 0; }
.app-main .catalog-head__search { inline-size: min(100%, 18rem); }
/* Kolom cari TIDAK dikecilkan ke --control-h-small. Di baris ini ia berdiri
   sebaris dengan dropdown penyaring, dan semua kontrol sebaris wajib setinggi
   --control-h yang sama — kotak 32px di sebelah dropdown 40px langsung
   terbaca sebagai salah, bukan sebagai "lebih ringkas". */

/* Panel dasbor = .card.card--compact dengan .card__head/__title/__sub.
   Grid dasbor = .row: panel selebar penuh sampai xl, lalu .col-xl-*. */

/* =======================================================
   KARTU BERJALAN — satu komponen untuk "Sedang berjalan" di beranda DAN
   "Kelas aktif" di halaman kelas: .card.card--compact.now-card di dalam
   .row.row--auto. Susunan tetap: label jenis + badge · judul · foto tutor
   & namanya · progres · aksi. Baris "Pertemuan 7 · Aturan rantai" dilepas
   (22 Sep 2026): materi yang sedang dibuka sudah disebut di dalam kelasnya,
   dan barisnya membuat blok tutor terbaca sebagai dua hal yang tidak
   sederajat. Yang tersisa di sini satu baris — "bersama <nama tutor>". SEMUA kartu berlatar putih; sesi yang
   paling mendesak (hari ini) dibedakan lewat badge ungu dan tombol primary,
   bukan lewat permukaan hitam — kartu hitam di tengah deret memecah irama
   barisnya (21 Sep 2026).
   ======================================================= */
.now-card__meta { font-size: var(--step--1); line-height: var(--leading-normal); }
.now-card__who { display: flex; align-items: center; gap: var(--space-xs); }
.now-card__body { display: grid; gap: var(--space-2xs); margin-block-start: auto; }
.now-card > .button,
.now-card__actions { margin-block-start: var(--space-2xs); }
/* Dua aksi sebaris (kartu kelas aktif); kartu beranda memakai satu
   .button--block. */
.now-card__actions .button { flex: 1; }
/* Hitung mundur memakai komponen angka bersama di bawah (.figure). */

/* =======================================================
   KPI — .card.card--compact.kpi di .row.row-cols-2.row-cols-lg-4
   label · angka · perubahan. Angka memakai Inter (sans), angka
   proporsional: angka besar dengan tabular-nums terlihat renggang.
   ======================================================= */
.kpi { gap: var(--space-2xs); }
.kpi__label { font-size: var(--step--1); font-weight: var(--weight-medium); }

/* ANGKA BESAR DASBOR — satu aturan untuk KPI dan hitung mundur kartu
   "sedang berjalan"; dulu dua blok dengan isi yang sama persis. Satuan
   ("jam", "hari lagi") memakai <small> atau .kpi__unit. */
.kpi__value,
.now-card__count {
  font-size: var(--step-4);
  font-weight: var(--weight-semibold);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
}
.kpi__unit,
.now-card__count small {
  margin-inline-start: var(--space-3xs);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  letter-spacing: normal;
}
.kpi__unit { color: var(--fg-muted); }
/* Angka yang belum ada: tanda pisah redup, bukan "0". Nol adalah hasil
   pengukuran; tanda pisah berarti belum ada yang diukur. */
.kpi__value--empty { color: var(--fg-subtle); }
.kpi__delta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3xs) var(--space-2xs);
  font-size: var(--step--2);
  color: var(--fg-muted);
}
/* Arah perubahan = .badge.badge--plain: naik bidang ungu (--accent),
   turun bidang abu (--gray). Dibawa ikon + tanda +/−, bukan warna saja. */

/* =======================================================
   GRAFIK — digambar main.js dari <table> di dalamnya.
   Tabel adalah sumber data sekaligus padanan aksesibel: tanpa JS ia tampil
   apa adanya; setelah grafik terpasang ia disembunyikan secara visual tapi
   tetap dibaca pembaca layar.
   ======================================================= */
.chart { position: relative; }
.chart__plot {
  position: relative;
  block-size: var(--chart-h);
  touch-action: pan-y;
}
.chart__plot svg { display: block; inline-size: 100%; block-size: 100%; overflow: visible; }
.chart__plot:focus-visible { outline-offset: var(--space-2xs); }

.chart__grid   { stroke: var(--chart-grid); stroke-width: 1; shape-rendering: crispEdges; }
.chart__base   { stroke: var(--clr-gray-300); stroke-width: 1; shape-rendering: crispEdges; }
.chart__tick   { fill: var(--fg-muted); font-family: var(--font-sans); font-size: 11px; font-variant-numeric: tabular-nums; }
.chart__line   { fill: none; stroke: var(--chart-ink); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart__area   { fill: var(--chart-wash); opacity: 0.7; }
/* Garis target memang ambang, jadi putus-putus — satu-satunya garis putus. */
.chart__target { stroke: var(--chart-ink); stroke-width: 1; stroke-dasharray: 4 4; }
.chart__target-label { fill: var(--fg); font-family: var(--font-sans); font-size: 11px; font-weight: 600; }
.chart__dot    { fill: var(--chart-ink); stroke: var(--bg-raised); stroke-width: 2; }
.chart__dot--end { r: 5; }
.chart__value  { fill: var(--fg); font-family: var(--font-sans); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.chart__cross  { stroke: var(--chart-ink); stroke-width: 1; opacity: 0.35; shape-rendering: crispEdges; }
.chart__bar    { fill: var(--chart-ink); transition: opacity var(--dur-fast) var(--ease-out); }
.chart__bar--hl { fill: var(--accent); }
.chart__hit    { fill: transparent; cursor: default; }
.chart__plot[data-hover] .chart__bar:not([data-active]) { opacity: 0.45; }

.chart__tip {
  position: absolute;
  z-index: var(--z-sticky);
  inset-block-start: 0;
  inset-inline-start: 0;
  display: grid;
  gap: 0.1rem;
  min-inline-size: 7rem;
  padding: var(--space-2xs) var(--space-xs);
  border-radius: var(--radius-m);
  background: var(--clr-black);
  color: var(--clr-white);
  box-shadow: var(--shadow-m);
  pointer-events: none;
  white-space: nowrap;
}
.chart__tip-value { font-size: var(--step-0); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
.chart__tip-label { font-size: var(--step--2); color: color-mix(in oklab, var(--clr-white) 70%, transparent); }

/* Tanpa JS tabel ini tampil apa adanya; bentuknya = .table.table--flush
   (components.css), di sini hanya barisnya dirapatkan karena isinya angka
   pendek. Setelah grafik terpasang, tabel disembunyikan memakai resep yang
   sama dengan .visually-hidden (base.css) — tetap dibaca pembaca layar. */
.chart__table :is(th, td) { padding-block: var(--space-3xs); }
.chart__table caption { text-align: start; font-size: var(--step--2); color: var(--fg-muted); padding-block-end: var(--space-2xs); }

.chart__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs) var(--space-m);
  font-size: var(--step--2);
  color: var(--fg-muted);
}
.chart__key { display: inline-flex; align-items: center; gap: var(--space-2xs); }
.chart__key::before { content: ""; inline-size: 1rem; block-size: 2px; background: var(--chart-ink); }
.chart__key--target::before { background: repeating-linear-gradient(90deg, var(--chart-ink) 0 4px, transparent 4px 8px); block-size: 1px; }
.chart__key--hl::before { inline-size: 0.75rem; block-size: 0.75rem; border-radius: 2px; background: var(--accent); }

/* =======================================================
   METER PER SUBTES — batang = .progress.progress--lg.progress--target
   (HTML murni, tanpa JS). Isian hitam 10px, garis tegak = target.
   ======================================================= */
.meters { display: grid; gap: var(--space-s); margin: 0; padding: 0; list-style: none; }
.meter {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-2xs) var(--space-s);
  align-items: baseline;
}
.meter__label { font-size: var(--step--1); display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2xs); }
.meter__value { font-size: var(--step--1); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
.meter .progress { grid-column: 1 / -1; }
.meter .badge { padding-block: 0.2em; }

/* =======================================================
   HEATMAP AKTIVITAS — minggu = kolom, hari = baris.
   ======================================================= */
.heat { display: grid; gap: var(--space-2xs); }
.heat__scroll { overflow-x: auto; scrollbar-width: thin; }
.heat__grid {
  --cell: minmax(0.75rem, 1fr);
  display: grid;
  grid-template-columns: 1.75rem repeat(var(--weeks, 16), var(--cell));
  grid-template-rows: auto repeat(7, auto);
  gap: 3px;
  min-inline-size: 22rem;
}
.heat__month, .heat__day {
  font-size: var(--step--2);
  color: var(--fg-muted);
  line-height: 1;
  white-space: nowrap;
}
.heat__month { padding-block-end: var(--space-3xs); }
.heat__day { align-self: center; }
.heat__cell {
  aspect-ratio: 1;
  border-radius: 3px;
  background: var(--heat-0);
  transition: outline-color var(--dur-fast) var(--ease-out);
  outline: 2px solid transparent;
  outline-offset: 1px;
}
.heat__cell[data-level="1"] { background: var(--heat-1); }
.heat__cell[data-level="2"] { background: var(--heat-2); }
.heat__cell[data-level="3"] { background: var(--heat-3); }
.heat__cell[data-level="4"] { background: var(--heat-4); }
.heat__cell[data-future] { background: transparent; box-shadow: inset 0 0 0 1px var(--line); }
.heat__cell:hover, .heat__cell[data-active] { outline-color: var(--clr-black); }
.heat__legend { display: inline-flex; align-items: center; gap: 3px; }
.heat__legend .heat__cell { inline-size: 0.75rem; }
.heat__legend span:first-child { margin-inline-end: var(--space-3xs); }
.heat__legend span:last-child { margin-inline-start: var(--space-3xs); }

/* =======================================================
   AGENDA
   ======================================================= */
.agenda { display: grid; margin: 0; padding: 0; list-style: none; }
.agenda__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-s);
  padding-block: var(--space-s);
  border-block-end: var(--border-hair) solid var(--line);
}
.agenda__item:first-child { padding-block-start: 0; }
.agenda__item:last-child { border-block-end: 0; padding-block-end: 0; }
.agenda__date {
  display: grid;
  place-items: center;
  align-content: center;
  inline-size: 3.25rem;
  block-size: 3.25rem;
  border-radius: var(--radius-m);
  background: var(--bg-sunken);
  line-height: 1;
}
.agenda__date[data-is-today] { background: var(--accent); }
.agenda__day { font-family: var(--font-display); font-weight: var(--weight-display); font-size: var(--step-1); }
.agenda__mon { margin-block-start: 0.15rem; font-size: var(--step--2); font-weight: var(--weight-semibold); }
.agenda__title { font-size: var(--step--1); font-weight: var(--weight-semibold); line-height: var(--leading-snug); }
.agenda__meta { font-size: var(--step--2); color: var(--fg-muted); }
@media (max-width: 29.99rem) {
  .agenda__item { grid-template-columns: auto minmax(0, 1fr); }
  .agenda__item > .link-arrow { grid-column: 2; justify-self: start; }
}

/* Tabel riwayat try out = .table.table--flush di dalam .card (lihat
   components.css). */

/* =======================================================
   PRATINJAU KEADAAN KOSONG (app/index.html, bagian paling bawah)
   CONTOH SAJA — dipakai hanya oleh bagian pratinjau di beranda, dan
   dihapus bersama bagian itu. Tidak ada komponen lain yang memakainya.
   ======================================================= */
.empty-demo__label { margin-block-start: var(--space-s); }
.empty-demo__label + .empty-demo { margin-block-start: calc(var(--space-s) * -1 + var(--space-2xs)); }
/* Bidang data yang kosong tetap setinggi grafik aslinya, supaya baris
   kartunya tidak mengerut dan tata letak halaman tidak berubah bentuk
   begitu datanya masuk. */
.empty-demo__plot { align-content: center; min-block-size: 16rem; }

/* =======================================================
   COPILOT AI
   ======================================================= */
.app-main--chat {
  block-size: 100dvh;
  padding-block: var(--space-m) 0;
  gap: 0;
  grid-template-rows: minmax(0, 1fr);
}
@media (max-width: 63.99rem) {
  .app-main--chat {
    block-size: calc(100dvh - var(--appbar-h));
    padding-block-end: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  }
}

/* --- Riwayat percakapan: kolom kedua kerangka (.app-subnav) ---
   Sejajar dengan menu bagian di pengaturan dan menu kelas di halaman
   belajar: kolom sendiri yang menempel setinggi layar dengan garis di
   kanannya, bukan sidebar di dalam .app-main. Tepi atasnya --space-m,
   bukan --app-gutter, supaya tombol "Percakapan baru" sebaris dengan
   judul "Copilot" di bilah obrolan — .app-main--chat juga dimulai dari
   --space-m. */
.app-subnav.copilot-nav { padding-block-start: var(--space-m); }
.copilot-nav {
  display: grid;
  /* Dikunci minmax(0, 1fr): judul percakapan memakai
     .menu-item--truncate (nowrap + elipsis), dan lebar min-content-nya
     tetap sepanjang teks penuh — tanpa kunci ini kolomnya melebar
     mengikuti judul terpanjang dan isinya meluber keluar garis. */
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  gap: var(--space-m);
}
/* Di bawah lg kerangkanya satu kolom dan layarnya habis untuk obrolan, jadi
   riwayat turun menjadi LACI yang menutup layar dari kiri — bukan baris
   menggulir seperti .app-subnav lain (isinya dua kelompok berjudul, bukan
   satu deret sejajar) dan bukan dihilangkan (percakapan lama adalah isi).
   Dibuka tombol jam di bilah obrolan, ditutup dengan bidang gelap, tombol
   silang, atau Esc. */
.copilot__history-btn,
.copilot-nav__close,
.copilot-scrim { display: none; }

@media (max-width: 63.99rem) {
  /* .app-subnav.copilot-nav, bukan .copilot-nav saja: blok ponsel milik
     .app-subnav (bagian PENGATURAN di bawah) menyetel position: static dan
     block-size: auto dengan spesifisitas yang sama tapi ditulis belakangan,
     jadi lacinya akan kembali masuk alur dan menumpuk di atas obrolan. */
  .app-subnav.copilot-nav {
    position: fixed;
    block-size: auto;
    border-block-end: 0;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: var(--z-overlay);
    inline-size: min(20rem, 86vw);
    padding: var(--app-gutter);
    border-inline-end: var(--border-hair) solid var(--line);
    background: var(--bg-canvas);
    box-shadow: var(--shadow-l);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* visibility ikut dianimasikan supaya lacinya benar-benar lepas dari
       urutan Tab saat tertutup — translate saja tidak melakukan itu. */
    translate: -101% 0;
    visibility: hidden;
    transition: translate var(--dur-base) var(--ease-out),
                visibility 0s linear var(--dur-base);
  }
  .app-subnav.copilot-nav[data-open="true"] {
    translate: 0 0;
    visibility: visible;
    transition-delay: 0s;
  }
  /* Di laci, daftarnya kembali menurun. Blok ponsel .app-subnav mengubah
     .menu-list jadi baris menggulir mendatar — benar untuk menu bagian di
     pengaturan, salah untuk daftar percakapan yang panjang ke bawah. */
  .app-subnav.copilot-nav .menu-list {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-flow: row;
    gap: calc(var(--space-3xs) / 2);
    padding-inline: 0;
    overflow-x: visible;
  }
  .copilot-nav__close { display: grid; justify-self: end; }
  .copilot__history-btn { display: grid; }

  .copilot-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: var(--z-header);
    padding: 0;
    border: 0;
    background: rgb(var(--shadow-color) / 0.4);
  }
}
/* Judul kelompok = .overline.menu-group (components.css); riwayat =
   .menu-list berisi .menu-item.menu-item--truncate. */
.copilot__history-item { font-weight: var(--weight-body); }

/* --- Kolom obrolan --- */
.copilot__chat {
  display: grid;
  /* Kolom dikunci minmax(0, 1fr): tanpa itu blok rumus (white-space: pre)
     melebarkan seluruh kolom dan penyusun pesan meluber keluar layar ponsel. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-block-size: 0;
}
.copilot__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  padding-block-end: var(--space-s);
  border-block-end: var(--border-hair) solid var(--line);
}
.copilot__lead { display: flex; align-items: center; gap: var(--space-2xs); min-inline-size: 0; }
.copilot__title { font-size: var(--text-h3); display: flex; align-items: center; gap: var(--space-xs); }
/* Tombol kembali hanya ada di ponsel — di sana halaman ini tampil penuh
   tanpa bilah tab. Margin negatif menyejajarkan ikonnya dengan tepi isi.
   Penimpaannya ditulis TEPAT di bawah aturan dasarnya: media query tidak
   menambah spesifisitas, jadi yang menang adalah yang ditulis belakangan. */
.copilot__back { display: none; }
@media (max-width: 63.99rem) {
  .copilot__back { display: grid; margin-inline-start: calc(var(--space-2xs) * -1); }
}
.copilot__tools { display: flex; align-items: center; gap: var(--space-2xs); }
.copilot__subject { inline-size: 12.5rem; }
@media (max-width: 29.99rem) { .copilot__subject { inline-size: 10rem; } }

.copilot__scroll {
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scroll-behavior: smooth;
  padding-block: var(--space-l);
}
.copilot__inner { inline-size: min(100%, 46rem); margin-inline: auto; }

/* Keadaan awal */
.copilot__intro { display: grid; gap: var(--space-m); justify-items: start; padding-block: var(--space-xl) var(--space-m); }
/* Tanda Copilot = .emblem.emblem--lg; salam = judul halaman. */
.copilot__hello { font-size: var(--text-h1); }
.copilot__lede { max-inline-size: 52ch; font-size: var(--step-0); line-height: var(--leading-normal); }
/* Pemantik obrolan = .option-card di .row.row--auto. */
.starters { inline-size: 100%; margin-block-start: var(--space-s); }

/* Di ponsel bilah atas, jarak gulir, dan pembuka obrolan sebelumnya memakan
   ±210px sebelum kalimat pertama — seperempat layar habis sebelum ada isi.
   Ketiganya dirapatkan; di layar lebar jaraknya tetap lapang. */
@media (max-width: 63.99rem) {
  .app-main--chat { padding-block-start: var(--space-s); }
  .copilot__bar { gap: var(--space-2xs) var(--space-s); padding-block-end: var(--space-xs); }
  .copilot__scroll { padding-block: var(--space-s); }
  .copilot__intro { gap: var(--space-s); padding-block: var(--space-s) var(--space-2xs); }
  .copilot__hello { font-size: var(--text-h2); }
}

/* Utas pesan */
.thread { display: grid; gap: var(--space-l); margin: 0; padding: 0; list-style: none; }
.msg { display: grid; gap: var(--space-2xs); animation: msg-in var(--dur-base) var(--ease-out); }
@keyframes msg-in { from { opacity: 0; translate: 0 0.5rem; } }
/* Pesan pengguna: bidang hitam di kanan. Jawaban AI: tanpa gelembung,
   teks penuh lebar — lebih mudah dibaca untuk penjelasan panjang. */
.msg--user { justify-items: end; }
.msg--user .msg__bubble {
  max-inline-size: min(85%, 34rem);
  padding: var(--space-xs) var(--space-s);
  border-radius: var(--radius-l) var(--radius-l) var(--radius-xs) var(--radius-l);
  background: var(--clr-black);
  color: var(--clr-white);
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.msg--ai { grid-template-columns: auto minmax(0, 1fr); column-gap: var(--space-s); align-items: start; }
/* Tanda AI di kiri jawaban = .emblem. */
.msg__body { display: grid; gap: var(--space-s); padding-block-start: 0.2rem; font-size: var(--step-0); line-height: var(--leading-prose); }
.msg__body > * { margin: 0; animation: msg-in var(--dur-base) var(--ease-out); }
.msg__body ol, .msg__body ul { display: grid; gap: var(--space-2xs); padding-inline-start: 1.25rem; }
.msg__body li::marker { font-weight: var(--weight-semibold); }
.msg__formula {
  padding: var(--space-s) var(--space-m);
  border-radius: var(--radius-m);
  background: var(--bg-sunken);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  overflow-x: auto;
  white-space: pre;
}
.msg__tip {
  padding: var(--space-s) var(--space-m);
  border-radius: var(--radius-m);
  background: var(--accent-soft);
  border-inline-start: 4px solid var(--clr-black);
}
.msg__actions { grid-column: 2; }
/* Aksi pesan (salin, latihan lagi) = .button.button--outline.button--small. */

/* Soal latihan interaktif di dalam jawaban — .card.card--compact.quiz;
   pilihannya .option-card.option-card--row dengan huruf .emblem. */
.quiz { gap: var(--space-xs); }
.quiz__q { font-weight: var(--weight-semibold); }
.quiz__feedback { font-size: var(--step--1); }
.quiz__feedback:empty { display: none; }

/* Indikator mengetik */
.typing { display: inline-flex; gap: 4px; padding-block: 0.6rem; }
.typing span {
  inline-size: 6px;
  block-size: 6px;
  border-radius: var(--radius-pill);
  background: var(--clr-black);
  animation: typing 1s var(--ease-inout) infinite;
}
.typing span:nth-child(2) { animation-delay: 150ms; }
.typing span:nth-child(3) { animation-delay: 300ms; }
@keyframes typing { 0%, 60%, 100% { opacity: 0.25; translate: 0 0; } 30% { opacity: 1; translate: 0 -3px; } }

/* --- Penyusun pesan --- */
.composer {
  inline-size: min(100%, 46rem);
  margin-inline: auto;
  padding-block: var(--space-s) var(--space-m);
  background: var(--bg-canvas);
}
/* Satu baris yang bisa digeser di layar sempit — empat chip yang turun ke
   dua baris memakan ruang obrolan. fieldset direset dari bingkai bawaannya. */
.composer__modes {
  flex-wrap: nowrap;
  min-inline-size: 0;
  margin: 0 0 var(--space-xs);
  padding: 0;
  border: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.composer__modes::-webkit-scrollbar { display: none; }
/* Bentuknya dipinjam bulat-bulat dari kolom cari beranda: cangkang
   .field-shell di luar, kotak putih ber-.--shadow-s di dalam, radius
   dalam --radius-l dan luar --radius-l + jarak cangkangnya. Keduanya
   "titik mulai mengetik" di halaman masing-masing, jadi bentuknya tidak
   boleh berbeda. Jarak tepi dalamnya --control-pad, sama dengan .input,
   supaya teks isian kedua komponen mulai di garis yang sama. */
.composer__box {
  display: flex;
  align-items: end;
  gap: var(--space-2xs);
  padding: var(--space-2xs);
  padding-inline-start: var(--control-pad);
  border: var(--border-hair) solid var(--line);
  border-radius: var(--radius-l);
  background: var(--bg-raised);
  box-shadow: var(--shadow-s);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.composer__box:focus-within { border-color: var(--clr-black); box-shadow: var(--ring-field); }
.composer__input {
  flex: 1;
  min-inline-size: 0;
  max-block-size: 12rem;
  padding-block: 0.7rem;
  border: 0;
  background: none;
  resize: none;
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  field-sizing: content;
}
.composer__input:focus { outline: none; }
.composer__input::placeholder { color: var(--fg-subtle); }
/* Tombol kirim = .icon-button.icon-button--primary.icon-button--small. */
.composer__note { margin-block-start: var(--space-2xs); font-size: var(--step--2); color: var(--fg-muted); text-align: center; }
@media (pointer: coarse) { .composer__keys { display: none; } }
html:not(.js) .composer__shell,
html:not(.js) .composer__modes { display: none; }

/* =======================================================
   HALAMAN BELAJAR (app/belajar.html)
   Kerangkanya sama dengan halaman aplikasi lain, ditambah kolom kedua
   (.app-subnav) berisi identitas kelas dan empat bagiannya — sejak
   21 Sep 2026; dulu halaman ini berdiri sendiri dengan bilah atasnya
   sendiri (.learn-bar) tanpa sidebar aplikasi.
   ======================================================= */
/* Kedua sidebar halaman belajar (menu bagian & daftar materi) memakai
   jarak tepi yang sama, 24px, supaya barisnya sejajar dari kiri ke kanan.
   Halaman pengaturan tetap memakai jarak bawaan .app-subnav, yang di sana
   disamakan dengan jarak tepi area isinya. */
.app-subnav.learn-nav { padding: var(--space-m); }
.learn-nav { display: grid; align-content: start; gap: var(--space-m); }
.learn-rail .learn-side__head,
.learn-rail .learn-side__body { padding: var(--space-m); }
.learn-nav__back { --menu-item-h: 2.25rem; color: var(--fg-muted); }
.learn-nav__back:hover { color: var(--fg); }
/* Identitas kelas: foto tutor + judul kelas + namanya. */
.learn-nav__class {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding-inline: var(--space-2xs);
}
.learn-nav__text { min-inline-size: 0; display: grid; line-height: var(--leading-snug); }
/* Yang tampil hanya nama tutor — ukuran & bobotnya mengambil alih peran
   judul yang dulu dipegang nama kelas. */
.learn-nav__tutor {
  font-size: var(--step-0);
  font-weight: var(--weight-semibold);
}
/* Menu bagian: .menu-item dengan lencana jumlah pertanyaan di ujung. */
.learn-nav .tab__count { margin-inline-start: auto; }

/* Tata letak & wadahnya dari .app-main; di sini tidak ada yang khusus. */
.learn__grid { --row-gap: var(--space-l); }
.js .learn__nojs { display: none; }
html:not(.js) .learn-side { display: none; }

/* --- Daftar materi (kanan) --- */
.learn-side { gap: 0; }
.learn-side__head {
  display: grid;
  gap: var(--space-xs);
  padding: var(--space-m);
  border-block-end: var(--border-hair) solid var(--line);
}
.learn-side__body {
  display: grid;
  align-content: start;
  gap: var(--space-s);
  min-block-size: 0;
  padding: var(--space-s);
}
/* Kotak cari materi: setinggi kontrol kecil, sejajar dengan butir daftar bab
   di bawahnya. */
.learn-side__body .input {
  --control-h: var(--control-h-small);
  --control-pad: var(--control-pad-small);
  --control-font: var(--control-font-small);
}
/* Daftar materi: keadaan kosong rata tengah di dalam panel. */
/* Kelas .learn-side sudah tidak ada — relnya kini .app-rail.learn-rail
   dengan bagian __head/__body sendiri. */
.learn-rail .empty-state--compact { padding-block: var(--space-l); text-align: center; }
/* Materi yang sedang dibuka: bidang ungu muda, huruf hitam — lebih jelas
   daripada abu tipis .menu-item, karena daftarnya panjang. */
.learn-side .menu-item[aria-current="true"] { background: var(--accent-mid); }
@media (min-width: 64rem) {
  /* Di dalam rel, hanya badan daftarnya yang menggulir — kepala (progres)
     tetap terlihat. */
  .learn-rail .learn-side__body { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
}

/* --- Kolom kiri panel Pembelajaran: materi yang sedang dibuka --- */
.learn__main { display: grid; align-content: start; gap: var(--space-xl); min-inline-size: 0; }

/* --- Tab kelas (pembelajaran · jadwal · tanya jawab · profil tutor) ---
   Bilah tab memakai .tabs/.tab dari components.css; di sini hanya garis
   pemisah selebar kolom dan jarak panelnya. Panel yang tidak aktif
   disembunyikan lewat [hidden] oleh modul tabPanels(). Panel tidak diberi
   tabindex: isinya selalu memuat elemen yang bisa difokus (tombol, kalender,
   formulir), jadi papan ketik dan pembaca layar tetap sampai ke dalamnya —
   dan tautan #id tidak meninggalkan cincin fokus sebesar panel. */
/* Jadwal, tanya jawab, dan profil tutor berdiri sendiri SELEBAR WADAH
   halaman — sama dengan .app-main di halaman aplikasi lain (21 Sep 2026).
   Dulu dibatasi 56rem, sehingga lebar isi halaman belajar terlihat berbeda
   sendiri. Keterbacaan dijaga per komponen lewat --measure, bukan dengan
   menyempitkan seluruh panel. */
.learn-tabs { display: grid; gap: var(--space-m); }
/* Tinggi bilah yang menempel — dipakai sebagai jarak berhenti gulir saat
   berpindah tab, supaya baris pertama panel tidak tertutup bilah. */
/* Di bawah lg bilah atas aplikasi menempel setinggi --appbar-h, jadi
   panel yang dibuka lewat #id berhenti di bawahnya, bukan tertutup. */
.learn-tabs__panel { scroll-margin-block-start: calc(var(--appbar-h) + var(--space-m)); }
@media (min-width: 64rem) {
  .learn-tabs__panel { scroll-margin-block-start: var(--app-gutter); }
}
/* Angka pertanyaan di label tab — bidang abu kecil, bukan huruf berwarna. */
.tab__count {
  margin-inline-start: var(--space-2xs);
  padding: 0.1em 0.5em;
  border-radius: var(--radius-pill);
  background: var(--bg-canvas-strong);
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
}
.tab[aria-selected="true"] .tab__count { background: var(--accent-mid); }
.learn-tabs__panel { display: grid; gap: var(--space-s); }
/* Panel Pembelajaran berisi .row (materi + daftar materi), yang sudah
   punya jaraknya sendiri lewat --row-gap. */
.learn-tabs__panel--study { gap: 0; }
.learn-tabs__note { font-size: var(--step--1); color: var(--fg-muted); }

/* --- Kartu tutor --- */
.tutor-panel {
  display: grid;
  /* Foto besar: di panel selebar wadah, foto 5.5rem tenggelam dan sisi
     kanannya menyisakan bidang kosong. */
  grid-template-columns: 12rem minmax(0, 1fr);
  gap: var(--space-m);
  /* stretch, bukan start: foto mengisi tinggi kartu, jadi tinggi kartu
     ditentukan teksnya dan tidak ada bidang kosong di kanan foto. */
  align-items: stretch;
}
/* Foto di dalam pembungkus ber-posisi: tanpa itu tinggi alami gambar yang
   menentukan tinggi baris, teks di sebelahnya ikut diregangkan, dan
   jaraknya jadi melebar tak beraturan. Sekarang tinggi kartu ditentukan
   teksnya, foto tinggal mengisi. */
.tutor-panel__media {
  position: relative;
  min-block-size: 11rem;
  border-radius: var(--radius-m);
  overflow: hidden;
  background: var(--clr-gray-100);
}
.tutor-panel__photo {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center top;
}
.tutor-panel__text { display: grid; align-content: start; gap: var(--space-2xs); min-inline-size: 0; }
.tutor-panel__role { font-size: var(--step--1); color: var(--fg-muted); }
.tutor-panel__bio { font-size: var(--step-0); line-height: var(--leading-relaxed); max-inline-size: var(--measure); }
.tutor-panel__tags { --cluster-gap: var(--space-2xs); margin-block-start: var(--space-3xs); }
@media (max-width: 47.99rem) {
  /* Foto pindah ke atas: dua kolom di layar sempit membuat kolom teks
     tinggal ±40 karakter. */
  .tutor-panel { grid-template-columns: minmax(0, 1fr); }
  .tutor-panel__media { inline-size: 9rem; aspect-ratio: 4 / 5; }
}

/* Kelas lain bersama tutor ini. Barisnya penuh selebar kartu: emblem,
   judul + jumlah materi, lalu ajakan membuka di ujung kanan — daftar
   satu baris teks di kartu selebar ini terbaca kosong di tengahnya. */
.tutor-more { gap: var(--space-s); }
.tutor-more__item {
  --menu-item-h: 4rem;
  --menu-item-radius: var(--radius-m);
  gap: var(--space-s);
  padding-inline: var(--space-s);
  border: var(--border-hair) solid var(--line);
}
.tutor-more__item:hover { border-color: var(--line-strong); }
.tutor-more__go {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  flex: none;
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
}
.tutor-more__go .icon { --icon-size: 1rem; transition: translate var(--dur-fast) var(--ease-out); }
.tutor-more__item:hover .tutor-more__go .icon { translate: 0.15rem 0; }
@media (max-width: 29.99rem) {
  .tutor-more__go span { display: none; }
}

/* --- Jadwal kelas live: kalender mengisi kolom ketiga (penuh lebar dan
   tinggi), keterangan sesinya pindah ke kolom keempat .app-rail
   (22 Sep 2026). --- */
/* --- Tab Pembelajaran: pemutar di bidang hitam yang mengisi kolom ---
   Flush seperti kalender: tanpa jarak tepi, dengan pemutar dipusatkan di
   tengah bidang. Kelas .surface-invert di panelnya yang membalik token
   warna, jadi keterangan di dalamnya otomatis terbaca di atas hitam. */
.app-main:has(#panel-belajar:not([hidden])),
.app-main:has(#panel-jadwal:not([hidden])) {
  /* gap 0: panel yang tidak aktif tingginya nol, tapi jaraknya tetap
     dihitung — bidang hitam (atau kalender) jadi kurang 48px dari dasar. */
  gap: 0;
}
.app-main:has(#panel-belajar:not([hidden])) {
  padding: 0;
  /* Satu baris setinggi sisa ruang: tanpa ini, tinggi sisa dibagi rata
     dengan seksi panel lain yang sedang kosong, dan bidang hitamnya
     berhenti di tengah kolom. */
  grid-template-rows: minmax(0, 1fr);
}
#panel-belajar {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--space-m);
  padding: var(--app-gutter);
  block-size: 100%;
}
#panel-belajar > * { inline-size: min(100%, 64rem); }

/* Kalender FLUSH: mengisi kolomnya sampai tepi, tanpa kartu dan tanpa
   jarak tepi .app-main — bidang sebesar ini tidak perlu bingkai lagi,
   dan kotak di dalam kotak membuat kalendernya terasa mengambang
   (22 Sep 2026). Yang tetap berjarak hanya keterangan di atasnya. */
.app-main:has(#panel-jadwal:not([hidden])) { align-content: stretch; padding: 0; }
#panel-jadwal { grid-template-rows: auto minmax(0, 1fr); gap: 0; }
#panel-jadwal .learn-tabs__note { padding: var(--app-gutter) var(--app-gutter) var(--space-s); }
.live { display: grid; block-size: 100%; }
.live .calendar { block-size: 100%; grid-template-rows: auto minmax(0, 1fr) auto; gap: 0; }
.live .calendar__head { padding: 0 var(--app-gutter) var(--space-s); }
.live .calendar__legend { padding: var(--space-s) var(--app-gutter) var(--app-gutter); }
.live .calendar__grid { block-size: 100%; }
/* Garis pemisah antar-pekan menggantikan bingkai kartu: di kalender
   selebar ini, baris tanpa garis sulit dibaca mendatar. */
.live .calendar__grid tbody tr + tr td { border-block-start: var(--border-hair) solid var(--line); }
.live .calendar__grid thead th { padding-block: var(--space-2xs); border-block-end: var(--border-hair) solid var(--line); }
/* Sel setinggi baris: angka tanggal duduk di atas dan titik penanda tepat
   di bawahnya — di sel setinggi ini, angka yang dipusatkan terbaca
   melayang dan titiknya terlepas jauh dari angkanya. */
.live .calendar__cell {
  /* Tombol mengisi tinggi selnya, jadi angkanya benar-benar duduk di atas
     baris — bukan tombol pendek yang dipusatkan di baris yang tinggi. */
  block-size: 100%;
  align-content: start;
  justify-items: center;
  gap: var(--space-3xs);
  padding-block-start: var(--space-2xs);
}
.live .calendar__dot { position: static; }
/* Penanda pindah ke angkanya; selnya sendiri tetap polos. */
.live .calendar__cell[aria-pressed="true"] { background: none; color: var(--fg); }
.live .calendar__cell[aria-pressed="true"] .calendar__num { background: var(--clr-black); color: var(--clr-white); }
.live .calendar__cell[aria-pressed="true"] .calendar__dot { background: var(--accent); }
.live .calendar__cell[aria-current="date"] { box-shadow: none; }
.live .calendar__cell[aria-current="date"] .calendar__num { box-shadow: inset 0 0 0 var(--border-hair) var(--line-strong); }
.live .calendar__cell:hover { background: none; }
.live .calendar__cell:hover .calendar__num { background: var(--bg-canvas-strong); }
.live .calendar__cell[aria-pressed="true"]:hover .calendar__num { background: var(--clr-black); }
/* Sel dibuat lebih tinggi karena kolomnya kini jauh lebih lebar — sel 36px
   di kolom 140px terbaca seperti tabel yang tergencet. Angkanya ikut
   dibesarkan ke 20px: di bidang selebar ini, angka 13px membuat kalender
   terasa kosong. */
.live .calendar__cell { min-block-size: 3.25rem; font-size: var(--step-2); }
.live .calendar__num { min-inline-size: 2.5rem; block-size: 2.5rem; }
.live .calendar__grid td { padding: 0; }
.live .calendar__grid th { font-size: var(--step--1); }
.live__list { display: grid; gap: var(--space-2xs); margin: 0; padding: 0; list-style: none; }

.live-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s);
  border: var(--border-hair) solid var(--line);
  border-radius: var(--radius-m);
}
/* Sesi yang sudah diikuti: bidang ungu muda — penanda keadaan, bukan garis. */
.live-item[data-joined="true"] { background: var(--accent-soft); border-color: var(--accent-mid); }
.live-item__time { flex: none; display: grid; line-height: var(--leading-snug); }
.live-item__time b { font-size: var(--step-0); font-variant-numeric: tabular-nums; }
.live-item__time span { font-size: var(--step--2); color: var(--fg-muted); }
.live-item__text { flex: 1 1 12rem; min-inline-size: 0; }
.live-item__title { font-size: var(--step--1); font-weight: var(--weight-semibold); line-height: var(--leading-snug); }
.live-item__meta { font-size: var(--step--2); color: var(--fg-muted); }

/* Di kolom kanan yang sempit, ketiga bagian sesi selalu menumpuk. Dengan
   flex-wrap, jarak antar-baris ikut gap 16px yang aslinya untuk jarak
   mendatar — hasilnya renggang tidak beraturan. Di sini ditulis sebagai
   tumpukan dengan jaraknya sendiri: waktu (satu baris), judul + keterangan
   (rapat, karena satu kesatuan), lalu tombol selebar kartu. */
.app-rail .live-item {
  display: grid;
  gap: var(--space-s);
  padding: var(--space-m);
}
.app-rail .live-item__time {
  display: flex;
  align-items: baseline;
  gap: var(--space-2xs);
}
.app-rail .live-item__text { display: grid; gap: var(--space-3xs); }
.app-rail .live-item :is(.button, .badge) { justify-self: stretch; text-align: center; }
/* Dua tombol sesi (ikut & pengingat) menumpuk selebar kartu di kolom
   sempit, sebaris kalau ruangnya cukup. */
.live-item__actions { --cluster-gap: var(--space-2xs); }
.app-rail .live-item__actions { display: grid; }
.live-item__actions .button { justify-content: center; }
/* Keterangan kapan pengingat muncul — hanya tampil saat pengingat aktif. */
.live-item__note {
  font-size: var(--step--2);
  line-height: var(--leading-normal);
  color: var(--fg-muted);
}

/* --- Tanya jawab --- */
.qna { display: grid; gap: var(--space-m); }
/* Formulir berada DI LUAR kartu daftar pertanyaan: ia titik mulai bagian
   ini, bukan salah satu isi daftar. Kotaknya memakai cangkang yang sama
   dengan kolom cari beranda (.field-shell). */
.qna__form { display: grid; gap: var(--space-xs); }
.qna__input { min-block-size: 5.5rem; font-size: var(--step-0); resize: none; }
.qna__form-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
}
.qna__hint { font-size: var(--step--2); color: var(--fg-muted); }

.qna__list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
/* Daftar kosong tetap menyumbang satu gap .qna di atas keadaan kosongnya —
   terbaca sebagai ruang menganggur di kepala kartu. */
.qna__list:empty { display: none; }

.qna-item {
  display: grid;
  gap: var(--space-2xs);
  padding-block: var(--space-m);
  border-block-start: var(--border-hair) solid var(--line);
}
/* Butir pertama tidak bergaris: sejak formulirnya pindah ke luar kartu,
   garis itu berdiri sendiri di puncak kartu tanpa apa pun di atasnya. */
.qna-item:first-child { padding-block-start: 0; border-block-start: 0; }
.qna-item__head { display: flex; align-items: center; gap: var(--space-xs); }
.qna-item__who {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--step--1);
}
.qna-item__when { font-size: var(--step--2); font-weight: var(--weight-body); color: var(--fg-muted); }
/* Tanpa batas lebar: di panel selebar wadah, batas 60ch membuat tiap
   pertanyaan dan balasan berhenti di tengah kartu dan menyisakan ruang
   kosong besar di kanan. Utas tanya jawab dibaca seperti percakapan,
   bukan artikel. */
.qna-item__text { font-size: var(--step-0); line-height: var(--leading-relaxed); }
/* Jawaban tutor menjorok ke dalam dengan garis tegak — terbaca sebagai
   balasan, bukan pertanyaan berikutnya. */
.qna-item__reply {
  display: flex;
  gap: var(--space-xs);
  margin-block-start: var(--space-2xs);
  margin-inline-start: var(--space-s);
  padding-inline-start: var(--space-s);
  border-inline-start: var(--border-bold) solid var(--line);
}
.qna-item__reply > div { display: grid; gap: var(--space-3xs); min-inline-size: 0; }
.qna-item__waiting { margin-block-start: var(--space-3xs); }

/* --- Materi terbuka (kiri) --- */
.lesson-view { display: grid; gap: var(--space-m); }
/* Judul, jenis, dan durasi materi kini hanya untuk pembaca layar (judul
   tampil di daftar materi dan di keterangan pemutar), jadi kepala materi
   tidak perlu kotak sendiri: display: contents membuatnya tidak menyisakan
   jarak kosong di atas pemutar. Yang masih terlihat hanyalah badge
   "Selesai", dan barisnya ikut hilang saat badge itu tersembunyi. */
.lesson-view__head { display: contents; }
.lesson-view__title:focus { outline: none; }
.lesson-view__meta { --cluster-gap: var(--space-2xs); }
.lesson-view__meta:not(:has(> :not([hidden]))) { display: none; }

/* Video = bidang hitam 16:9; bacaan & latihan = bidang abu cekung. */
.lesson-view__media {
  --focus-color: var(--clr-purple);
  display: grid;
  place-items: center;
  align-content: center;
  gap: var(--space-s);
  aspect-ratio: 16 / 9;
  padding: var(--space-m);
  border-radius: var(--radius-l);
  background: var(--clr-black);
  color: var(--clr-white);
}
.lesson-view__media:not([data-type="v"]) {
  --focus-color: var(--clr-black);
  aspect-ratio: auto;
  min-block-size: 16rem;
  background: var(--bg-sunken);
  border: var(--border-hair) solid var(--line);
  color: var(--fg);
}
.lesson-view__play {
  display: grid;
  place-items: center;
  inline-size: 4.5rem;
  block-size: 4.5rem;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-fg);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.lesson-view__play:hover { transform: scale(1.06); }
.lesson-view__play .icon { --icon-size: 1.75rem; }
/* Segitiga putar diisi penuh dan digeser sedikit — pusat optisnya ada di
   kiri titik tengah kotak. */
[data-type="v"] > .lesson-view__play .icon { fill: currentColor; translate: 0.12rem 0; }
.lesson-view__media:not([data-type="v"]) .lesson-view__play { background: var(--clr-black); color: var(--clr-white); }
.lesson-view__caption { font-size: var(--step--1); font-weight: var(--weight-medium); }
.lesson-view__note { font-size: var(--step--1); color: var(--fg-muted); }
.lesson-view__note:empty { display: none; }

/* --- Materi terkunci — ajakan berlangganan. Ini SATU-SATUNYA kartu yang
   masih memakai .surface-invert: ia berdiri sendiri selebar panel, bukan
   satu di antara deretan kartu. --- */
.paywall { gap: var(--space-m); padding: var(--space-xl); }
@media (max-width: 47.99rem) { .paywall { padding: var(--space-l); } }
.paywall__title:focus { outline: none; }
.paywall__lead { max-inline-size: 52ch; }
/* Dua kelas: reset ul[role="list"] { padding: 0 } di base.css lebih
   spesifik daripada satu kelas dan menghapus padding-block ini. */
.paywall > .paywall__features {
  --row-gap: var(--space-m);
  padding-block: var(--space-m);
  border-block: var(--border-hair) solid var(--line);
}
.paywall__feature { display: flex; align-items: start; gap: var(--space-s); }
.paywall__feature-title { font-size: var(--step--1); font-weight: var(--weight-semibold); }
.paywall__feature-body { font-size: var(--step--1); color: var(--fg-muted); line-height: var(--leading-normal); }
.paywall__actions { margin-block-start: var(--space-2xs); }
/* Judul materi & ajakan berlangganan bisa panjang ("Proses isobarik,
   isokhorik, isotermal, adiabatik") — satu jenjang di bawah judul seksi. */
.lesson-view__title,
.paywall__title { font-size: var(--text-h3); }

/* =======================================================
   PENGATURAN (app/pengaturan.html)
   Dua kolom mulai lg: daftar bagian yang menempel di kiri, panel di
   kanan. Di bawah lg daftar itu menjadi satu baris yang menggulir
   mendatar di atas panel.

   Tanpa JavaScript SEMUA panel tampil bertumpuk dan daftar di kiri
   menjadi daftar jangkar biasa — halaman tetap utuh. Dengan JS hanya
   satu panel yang tampil (modul 21 di main.js); panel yang sedang
   dibuka masuk ke URL sebagai tanda pagar, jadi tautan langsung
   (…/pengaturan.html#notifikasi) dan tombol kembali tetap bekerja.
   ======================================================= */
/* Menu bagiannya kini kolom kerangka (.app-subnav), jadi di sini tinggal
   panelnya. */
.settings { display: grid; grid-template-columns: minmax(0, 1fr); }

/* Di bawah lg kerangkanya satu kolom: menu bagian jadi satu baris yang
   bisa digeser mendatar, menempel di bawah bilah atas. */
@media (max-width: 63.99rem) {
  .app-subnav {
    position: static;
    block-size: auto;
    /* Jarak tepi pindah ke deret menunya sendiri: deret itu menggulir
       mendatar, jadi butir yang belum muat harus terpotong di tepi layar —
       bukan di dalam gutter, yang terbaca seperti teks terpenggal.
       Perlakuan sama dengan .app-tabs dan .catalog-head__tabs. */
    padding: var(--space-2xs) 0;
    border-inline-end: 0;
    border-block-end: var(--border-hair) solid var(--line);
  }
  .app-subnav .menu-list {
    /* Kolom eksplisit dari .menu-list dilepas: di sini alirannya mendatar,
       dan satu kolom 1fr akan menelan seluruh lebar untuk butir pertama. */
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    gap: var(--space-2xs);
    overflow-x: auto;
    scrollbar-width: none;
    padding-inline: var(--app-gutter);
    scroll-padding-inline: var(--app-gutter);
  }
  .app-subnav .menu-list::-webkit-scrollbar { display: none; }
  .app-subnav .menu-item { white-space: nowrap; }

  /* Menu kelas (belajar.html) jarak tepinya --space-m, bukan --app-gutter,
     jadi deret tabnya dibentangkan dengan margin negatif seukuran itu. */
  .learn-nav .menu-list {
    margin-inline: calc(var(--space-m) * -1);
    padding-inline: var(--space-m);
    scroll-padding-inline: var(--space-m);
  }
}

.settings__panels { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-2xl); min-inline-size: 0; }
/* .app-section memasang display: grid, yang mengalahkan [hidden]. */
.settings-panel[hidden] { display: none; }
.settings-panel__title:focus { outline: none; }

/* --- Kartu berisi formulir --- */
.settings-fields { --row-gap: var(--space-m); }
/* .field adalah grid, dan sebagai sel .row ia ikut diregangkan setinggi
   baris — isinya lalu terbagi rata dan kotak isian di kolom bersebelahan
   tidak lagi sebaris begitu salah satunya punya keterangan dua baris. */
.settings-fields > .field { align-content: start; }
/* = .cluster.actions.actions--divided */
@media (max-width: 29.99rem) { .settings-actions > .button { flex: 1 1 100%; } }

/* --- Baris pengaturan: keterangan di kiri, kontrol di kanan ---
   Baris bersakelar ditulis sebagai <label> supaya seluruh barisnya bisa
   diketuk; baris yang kontrolnya tombol atau tautan memakai <div>. */
.setting-list { display: grid; }
.setting-list > * + * { border-block-start: var(--border-hair) solid var(--line); }
.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding-block: var(--space-s);
}
label.setting-row { cursor: pointer; }
.setting-list > :first-child { padding-block-start: 0; }
.setting-list > :last-child { padding-block-end: 0; }
.setting-row__text { display: grid; gap: var(--space-3xs); min-inline-size: 0; }
.setting-row__title { font-size: var(--step-0); font-weight: var(--weight-medium); line-height: var(--leading-snug); }
.setting-row__desc { max-inline-size: var(--measure); font-size: var(--step-0); color: var(--fg-muted); line-height: var(--leading-relaxed); }
.setting-row__control { flex: none; display: flex; align-items: center; gap: var(--space-2xs); }
/* Kontrol di dalam baris memakai tinggi kecil — sejajar dengan judul baris,
   bukan berdiri setinggi tombol formulir. Ditulis sebagai aturan berjangkauan,
   bukan kelas pengubah di markup, karena combobox (modul 11) membangun ulang
   pemicu <select> dan hanya menyalin kelas .select. */
.setting-row__control :is(.select, .input) {
  --control-h: var(--control-h-small);
  --control-pad: var(--control-pad-small);
  --control-font: var(--control-font-small);
  min-inline-size: 11rem;
}
/* Kontrol selain sakelar (select, tombol) turun ke baris sendiri di ponsel —
   sakelar tetap di kanan karena lebarnya tetap dan kecil. */
@media (max-width: 47.99rem) {
  .setting-row:not(:has(.switch)) { flex-wrap: wrap; align-items: start; }
  .setting-row:not(:has(.switch)) .setting-row__control { inline-size: 100%; }
  .setting-row:not(:has(.switch)) .setting-row__control > * { flex: 1; }
  .setting-row__control :is(.select, .input) { min-inline-size: 0; }
}

/* --- Foto profil --- */
.settings-photo { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2xs) var(--space-m); }
.settings-photo .avatar { --avatar-size: 4.5rem; }
.settings-photo__actions { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }
.settings-photo__hint { flex-basis: 100%; font-size: var(--step--2); color: var(--fg-muted); }

/* --- Perangkat yang sedang masuk --- */
.device { display: flex; align-items: center; gap: var(--space-s); padding-block: var(--space-s); }
.device__text { flex: 1; min-inline-size: 0; display: grid; gap: var(--space-3xs); }
.device__name { font-size: var(--step-0); font-weight: var(--weight-medium); }
.device__meta { font-size: var(--step--1); color: var(--fg-muted); }

/* --- Blok tindakan permanen ---
   Palet Tutooria tidak punya merah, dan menambah satu warna hanya untuk
   satu blok akan merusak seluruh sistem. Peringatannya datang dari
   bentuk: garis hitam penuh mengelilingi kartu, bukan abu 1px seperti
   kartu lain, jadi blok ini terbaca berbeda tanpa warna baru. */
.settings-danger { border-color: var(--line-strong); }

/* Dua kolom mulai xl: rincian di kiri, kartu mulai yang menempel di kanan.
   Di bawah itu satu kolom — kartu mulai naik ke atas daftar subtes lewat
   urutan markup, jadi tombolnya tetap yang pertama terlihat. */
/* =======================================================
   HALAMAN FOKUS — .app.app--focus
   Kerangka aplikasi tanpa sidebar dan tanpa bilah tab: satu kolom, isinya
   tetap dipusatkan selebar --app-container lewat .app-main. Dipakai halaman
   yang tujuannya sudah tunggal — rincian try out dan daftar penuh aktivitas
   — sehingga menu ke tujuan lain justru mengganggu. Gantinya satu baris alat
   di atas: tautan kembali di kiri, aksi halaman (kalau ada) di kanan.
   ======================================================= */
.app--focus { grid-template-columns: minmax(0, 1fr); }

.focus-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
}
/* Margin negatif seukuran jarak tepi .menu-item: tanpa itu ikon panahnya
   menjorok 12px ke kanan dari tepi isi halaman di bawahnya. */
.focus-back {
  --menu-item-h: 2.25rem;
  inline-size: auto;
  margin-inline-start: calc(var(--space-xs) * -1);
  color: var(--fg-muted);
}
.focus-back:hover { color: var(--fg); }

/* =======================================================
   HALAMAN MULAI TRY OUT (app/tryout-mulai.html)
   ======================================================= */

/* Ini BUKAN deretan kartu, jadi bukan .row 12 kolom: isi + rel kanan adalah
   pembelahan halaman, sama seperti kolom rel di halaman belajar. Karena itu
   lebar relnya --rail-w (sama dengan rel daftar materi) dan jarak antar-kolom
   dipilih sendiri — --grid-col-gap milik .row sengaja satu nilai untuk
   seluruh produk (tokens.css) dan tidak boleh ditimpa; di sini nilainya 16px,
   terlalu rapat untuk dua bidang sebesar ini.
   Di bawah xl ketiganya menumpuk sesuai urutan markup: kepala · kartu mulai ·
   daftar subtes — aksi utama tidak terkubur di dasar halaman. */
.tryout-detail__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-xl);
}
.tryout-detail__head { display: grid; align-content: start; gap: var(--space-m); }
@media (min-width: 80rem) {
  .tryout-detail__grid {
    grid-template-columns: minmax(0, 1fr) var(--rail-w);
    column-gap: var(--space-xl);
  }
  /* Rel dibentangkan melintasi kedua baris (kepala di baris pertama, daftar
     subtes di baris kedua) supaya kartu mulainya punya ruang untuk menempel
     sepanjang halaman. */
  .tryout-detail__side { grid-column: 2; grid-row: 1 / span 2; }
}
/* Judul halaman memakai skala judul seksi, bukan judul kartu: ini satu-satunya
   judul di layar, dan .card__title terlalu kecil untuk berdiri sendiri. */
.tryout-detail__title {
  font-size: var(--text-h2);
  line-height: var(--leading-snug);
  text-wrap: balance;
}
.tryout-detail__ident { display: grid; gap: var(--space-2xs); }
.tryout-detail__lede { font-size: var(--step-0); line-height: var(--leading-relaxed); max-inline-size: var(--measure); }

/* Daftar subtes: baris menu bernomor, metanya rata kanan. */
.subtes-list .menu-item { --menu-item-h: 3.25rem; gap: var(--space-s); }
.subtes-list .menu-item__meta { margin-inline-start: auto; flex: none; text-align: end; }

.tryout-start { position: sticky; inset-block-start: var(--app-gutter); gap: var(--space-s); }
.tryout-start__meta { font-size: var(--step--1); color: var(--fg-muted); }

/* Papan skor: peringkat, nama + asal, lalu skor besar rata kanan. Juara
   dibedakan garis hitam, bukan warna — emas tidak ada dalam palet. */
.papan-item {
  flex-direction: row;
  align-items: center;
  gap: var(--space-s);
}
.papan-item[data-top] { border-color: var(--clr-black); }
/* Nama + asal sekolah dibaca sebagai SATU identitas, jadi keduanya memakai
   leading rapat. Tanpa ini keduanya mewarisi leading prosa (1,6) dan jarak
   antar-barisnya jadi ±12px — terbaca seperti dua keterangan lepas, bukan
   satu blok nama. Pola yang sama dipakai blok akun di sidebar. */
.papan-item__who {
  min-inline-size: 0;
  display: grid;
  gap: var(--space-3xs);
  line-height: var(--leading-snug);
}
.papan-item__name { font-weight: var(--weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.papan-item__score {
  margin-inline-start: auto;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--step-3);
  line-height: 1;
}

@media (max-width: 79.99rem) {
  /* Kartu mulai berhenti menempel begitu kolomnya tidak lagi berdampingan —
     elemen sticky selebar layar hanya menutupi isinya sendiri. */
  .tryout-start { position: static; }
}

/* =======================================================
   RUANG LATIHAN (app/tryout-soal.html)
   Halaman fokus tanpa sidebar: satu bilah atas (keluar · waktu · navigasi)
   lalu dua kolom — soal di kiri, jawaban di kanan. Semua tujuan lain
   sengaja tidak ada; satu-satunya jalan keluar adalah tombol tutup.
   ======================================================= */
.quiz-room {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr);
  min-block-size: 100dvh;
  background: var(--bg-canvas);
}
.quiz-bar {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  gap: var(--space-m);
  padding: var(--space-s) var(--app-gutter);
  background: var(--bg-canvas);
  border-block-end: var(--border-hair) solid var(--line);
}
/* Waktu dan kemajuan duduk di tengah bilah, selebar kolom isi — ia penanda
   keadaan, bukan tombol, jadi tidak ikut berebut tepi layar. */
.quiz-bar__center {
  flex: 1;
  min-inline-size: 0;
  display: grid;
  justify-items: center;
  gap: var(--space-2xs);
  /* max-inline-size + margin auto, bukan inline-size: flex-basis dari
     flex: 1 akan menimpa lebar tetap, sedangkan margin auto pada butir
     flex menyerap sisa ruang sehingga waktunya benar-benar di tengah. */
  max-inline-size: 34rem;
  margin-inline: auto;
}
.quiz-bar__progress { inline-size: 100%; }
.quiz-timer {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--step-1);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
}
.quiz-bar__nav { flex: none; gap: var(--space-2xs); }

/* Daftar nomor menggantung di bawah bilah, rata kanan dengan tombolnya. */
.quiz-jump {
  position: absolute;
  inset-block-start: calc(var(--appbar-h) + var(--space-2xs));
  inset-inline-end: var(--app-gutter);
  z-index: var(--z-overlay);
  inline-size: min(22rem, calc(100vw - var(--app-gutter) * 2));
  padding: var(--space-s);
}
.quiz-jump__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(2.5rem, 1fr));
  gap: var(--space-2xs);
  margin: 0;
  padding: 0;
  list-style: none;
}
.quiz-jump__item {
  inline-size: 100%;
  min-block-size: 2.5rem;
  padding: 0;
  border: var(--border-hair) solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg-raised);
  color: var(--fg);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.quiz-jump__item:hover { border-color: var(--line-strong); }
.quiz-jump__item[data-answered] { background: var(--bg-canvas-strong); }
.quiz-jump__item[aria-current] { background: var(--clr-black); color: var(--clr-white); border-color: var(--clr-black); }

.quiz-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  gap: var(--space-xl);
  inline-size: min(100%, var(--app-container));
  margin-inline: auto;
  padding: var(--app-gutter);
}
@media (min-width: 64rem) {
  /* Soal di kiri, jawaban di kanan — keduanya terbaca sekaligus, tanpa
     menggulir bolak-balik seperti kalau ditumpuk. */
  .quiz-main { grid-template-columns: minmax(0, 1fr) minmax(0, 27rem); gap: var(--space-2xl); }
}

.quiz-q { display: grid; align-content: start; gap: var(--space-s); }
.quiz-q__num { font-size: var(--step-2); color: var(--fg-muted); }
.quiz-q__num b { font-weight: var(--weight-semibold); color: var(--fg); }
.quiz-q__total { font-variant-numeric: tabular-nums; }
.quiz-q__text {
  font-size: var(--step-1);
  line-height: var(--leading-relaxed);
  max-inline-size: var(--measure);
}
.quiz-q__source { font-size: var(--step--1); font-style: italic; color: var(--fg-muted); }
.quiz-q__source:empty { display: none; }
.quiz-q__note { font-size: var(--step--2); color: var(--fg-subtle); }

.quiz-answers { position: sticky; inset-block-start: calc(var(--appbar-h) + var(--app-gutter)); gap: var(--space-m); }
.quiz-answers__hint { font-size: var(--step--1); color: var(--fg-muted); }
.quiz-answers__hint:empty { display: none; }
@media (max-width: 63.99rem) { .quiz-answers { position: static; } }

/* --- Hasil --- */
.quiz-result {
  display: grid;
  gap: var(--space-2xl);
  inline-size: min(100%, var(--app-container));
  margin-inline: auto;
  padding: var(--app-gutter);
}
.quiz-result__card { gap: var(--space-s); }
.quiz-result__score { display: flex; align-items: baseline; gap: var(--space-2xs); }
.quiz-result__score b {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--step-5);
  line-height: 1;
}
.quiz-result__score span { color: var(--fg-muted); }

.quiz-review { display: grid; gap: var(--space-s); margin: 0; padding: 0; list-style: none; }
/* Benar/salah ditandai garis tebal di tepi awal, bukan latar berwarna:
   kartunya masih harus terbaca sebagai teks panjang. */
.quiz-review__item { gap: var(--space-2xs); border-inline-start: 3px solid var(--line-strong); }
.quiz-review__item[data-result="right"] { border-inline-start-color: var(--accent); }
.quiz-review__item[data-result="wrong"] { border-inline-start-color: var(--clr-black); }
.quiz-review__q { font-weight: var(--weight-semibold); line-height: var(--leading-snug); }
.quiz-review__mine,
.quiz-review__key { font-size: var(--step--1); }
.quiz-review__mine { color: var(--fg-muted); }
.quiz-review__why { font-size: var(--step--1); line-height: var(--leading-relaxed); color: var(--fg-muted); }

/* Rumus KaTeX di dalam soal: ukurannya disamakan dengan teks di sekitarnya
   (KaTeX bawaannya 1,21em — terbaca seperti kutipan, bukan bagian kalimat),
   dan rumus panjang boleh digulir mendatar sendiri daripada melebarkan
   kolomnya. */
.katex { font-size: 1.05em; }
.quiz-q__text .katex-display,
.quiz-review__item .katex-display { margin-block: var(--space-2xs); overflow-x: auto; overflow-y: hidden; }
/* Huruf pilihan (A–E) tidak boleh ikut mengecil karena rumus di sampingnya. */
.option-card .katex { font-size: 1em; }

/* =======================================================
   RUANG LATIHAN DI PONSEL
   Semua jarak di halaman soal disetel untuk layar lebar, tempat blok soal
   dan kartu jawaban berdiri BERDAMPINGAN — di situ 48px adalah jarak
   antar-kolom. Di ponsel keduanya menumpuk, dan jarak yang sama berubah
   arti jadi jurang tegak: satu layar 390px hanya memuat soal dan dua
   pilihan pertama, sisanya harus digulir.

   Tangga barunya: 8px di dalam satu blok, 16px antar-blok di dalam blok
   jawaban, 24px antara blok soal dan blok jawaban. Blok jawaban di ponsel
   tidak lagi berbentuk kartu — lihat .quiz-answers di bawah.
   ======================================================= */
@media (max-width: 47.99rem) {
  .quiz-main { gap: var(--space-m); }          /* 48px → 24px */

  /* Overline, "1 / 5", soal, sumber, dan catatan adalah SATU keterangan
     tentang satu soal. Berjarak 16px kelimanya terbaca sebagai lima blok
     lepas; .quiz-q__text sendiri sudah punya leading-relaxed, jadi udara
     di antara barisnya tidak hilang. */
  .quiz-q { gap: var(--space-2xs); }           /* 16px → 8px */

  /* Kartu jawaban melepas bingkainya. Di layar lebar kartu itu ada gunanya:
     ia berdiri sebagai kolom kedua di sebelah soal dan bingkainya yang
     memisahkan keduanya. Di ponsel tidak ada kolom kedua — yang tersisa
     hanya kotak bergaris berisi lima kotak bergaris, dua lapis bingkai
     yang menandai hal yang sama.

     Sekalian ini mengembalikan ~32px lebar: pilihan dan tombol kini
     selebar blok soal di atasnya, bukan menjorok ke dalam oleh padding
     kartu. Pilihan tetap putih di atas kanvas abu, jadi batas tiap kotak
     justru lebih tegas daripada waktu putih di atas putih. */
  .quiz-answers {
    gap: var(--space-s);                       /* 24px → 16px */
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
  }

  /* Kotak pilihan TIDAK dirapatkan. .option-card--row sudah berpadding
     8/16px, dan tingginya dipegang min-block-size: var(--control-h) — 44px
     di peranti sentuh, batas sasaran ketuk WCAG 2.5.5. Merapatkan
     paddingnya tidak memendekkan kotaknya sama sekali; yang terjadi hanya
     hurufnya jadi lebih mepet garis tepi. */

  /* Bilah atas menempel di puncak layar sepanjang ujian, jadi tiap piksel
     di situ dibayar oleh setiap soal. */
  .quiz-bar { padding-block: var(--space-xs); }

  /* Layar hasil memakai --space-2xl (56px di ponsel) — jarak seksi halaman
     pemasaran, bukan jarak layar yang dibaca sekali lalu ditutup. */
  .quiz-result { gap: var(--space-m); }        /* 56px → 24px */
}

/* =======================================================
   SAKLAR TINGKAT HALAMAN — dua seksi jadi dua tab, HANYA di ponsel
   Dipakai halaman Kelas (Kelas aktif / Jelajahi kelas) dan Pustaka
   (Lanjutkan / Semua materi). Keduanya berbentuk sama: satu daftar pendek
   yang sedang berjalan, lalu satu katalog panjang. Di layar sempit
   katalognya mendorong daftar pendek itu jauh ke atas layar — padahal
   itulah yang paling sering dituju dari menu.

   Markup & ARIA-nya sama persis dengan halaman Try Out; yang khas di sini
   hanya ambang lebarnya (lihat [data-tabs-until] di main.js).
   ======================================================= */
.page-tabs__panel { min-inline-size: 0; }

/* Mulai md pembungkusnya LENYAP dari tata letak (display: contents), jadi
   kedua panel kembali jadi anak langsung .app-main dan memakai jarak
   antar-seksi halaman — bukan gap .app-tabs-root yang lebih rapat. Tanpa
   ini kedua halaman ini akan punya irama tegak sendiri, beda dari halaman
   aplikasi lain. */
@media (min-width: 48rem) {
  .page-tabs { display: contents; }
  .page-tabs > .tabs { display: none; }
}

@media (max-width: 47.99rem) {
  .page-tabs { gap: var(--space-s); }

  /* Label tabnya sudah menyebut nama kedua bagian — judul seksi di bawahnya
     tinggal pengulangan. Disembunyikan dengan resep .visually-hidden, bukan
     display: none, supaya aria-labelledby tiap seksi tetap menemukan
     judulnya. position: absolute-nya sekaligus mengeluarkan elemen dari
     alur kisi, jadi tidak ada jarak kosong yang tertinggal di tempatnya.

     Dua sasaran, dipilih dari STRUKTURNYA, bukan dari id halaman tertentu:
       1. kepala yang menempel langsung di .app-section hanya berisi judul,
          jadi seluruh kepalanya yang disembunyikan;
       2. kepala di dalam .catalog-head masih memuat kotak cari, dropdown,
          dan baris penyaring — di situ HANYA judulnya. */
  .page-tabs__panel > .app-section > .section-head,
  .page-tabs__panel .catalog-head .section-head > h2 {
    position: absolute;
    width: 1px; height: 1px;
    clip-path: inset(50%);
    overflow: hidden;
    white-space: nowrap;
  }
}

/* =======================================================
   KEPALA KATALOG DI PONSEL
   Dua perbaikan yang berpasangan; keduanya hanya terlihat di layar sempit.
   ======================================================= */
@media (max-width: 47.99rem) {
  /* 1. Kolom cari dibiarkan selebar penuh. Batas 18rem di atas dipasang
        untuk saat kotak cari duduk DI SAMPING judul katalog; di ponsel ia
        turun ke barisnya sendiri, dan di situ batas itu menyisakan ~74px
        ruang kosong di kanannya — kotaknya jadi terbaca lebih sempit dari
        kartu-kartu di bawahnya, seperti salah potong.
        .section-head__tools > * sudah menyuruh selebar penuh, tapi kalah
        oleh .app-main .catalog-head__search yang dua kelas. */
  .app-main .catalog-head__search,
  .catalog-head__search,
  .catalog-head__select { inline-size: 100%; }

  /* 2. Jarak di dalam kepala katalog: 24px antara kotak cari dan baris
        penyaring terbaca sebagai dua blok terpisah, padahal keduanya satu
        alat yang sama. */
  .catalog-head { gap: var(--space-s); }

  /* Tinggi baris tab: 16px atas-bawah membuat bilah penyaring setinggi
     52px. 12px menyisakan 44px — masih persis sasaran ketuk minimum
     (WCAG 2.5.5), tanpa baris kosong di atas dan bawah hurufnya. */
  .tab { padding-block: var(--space-xs); }
}

/* =======================================================
   IRAMA TEGAK DI PONSEL
   Jarak antar-seksi (--space-xl, 48px) dan antara kepala seksi dengan
   isinya (--space-m, 24px) disetel untuk layar lebar, tempat mata
   menyapu mendatar dan butuh jeda besar untuk memisah blok. Di layar
   390px tidak ada sapuan mendatar: blok sudah terpisah karena menumpuk
   satu per satu, jadi jeda sebesar itu hanya menambah gulir.

   Tangga jaraknya di ponsel jadi 8 / 12 / 24: antar-kartu 8px (--grid-gap
   & --grid-col-gap, tokens.css), kepala seksi ke isinya 12px, antar-seksi
   24px. Dulu 16 / 24 / 48.

   Jenjang teratas sengaja TIDAK ikut turun ke 16px. Dua jenjang bawah
   rapat justru membuat satu seksi terbaca sebagai satu benda padat; yang
   memisahkan seksi dari seksi berikutnya tinggal jarak ini sendirian,
   karena tidak ada garis atau latar yang membatasinya.
   ======================================================= */
@media (max-width: 47.99rem) {
  .app-main { gap: var(--space-m); }        /* 48px → 24px */
  .app-section { gap: var(--space-xs); }    /* 24px → 12px */
  .welcome { gap: var(--space-s); padding-block-start: var(--space-2xs); }

  /* Dua pintasan (Kelas, Copilot AI) berdampingan, bukan bertumpuk: dua
     ubin setinggi 56px selebar layar penuh adalah dua baris yang hampir
     tidak membawa apa-apa. Kolom ketiga, "Terakhir dibuka", TIDAK ikut —
     ia daftar empat baris teks berjenjang ("Perpustakaan / Genetika…"),
     dan pada separuh lebar layar tiap barisnya patah tiga kali. Jadi ia
     mengambil barisnya sendiri selebar kisi. */
  .welcome__grid > .welcome__col:last-child { grid-column: 1 / -1; }
  /* Ubin di kolom separuh: padding mendatar dikecilkan dan teks yang patah
     dua baris tetap rata tengah. */
  .welcome__grid .welcome__tile { padding-inline: var(--space-xs); text-align: center; }
}


/* =======================================================
   RAK GESER DI DALAM APLIKASI — .app-main .shelf
   Komponen .shelf (components.css) dipakai apa adanya; yang perlu ditimpa
   hanya titik tempelnya. Bawaannya menempel di --header-h, yaitu tinggi
   bilah atas SITUS — dan bilah itu tidak ada di sini. Di desktop kepala rak
   menempel di tepi atas wadah gulirnya (0); di bawah lg bilah aplikasi
   setinggi --appbar-h ada di atas, jadi kepala rak turun sebanyak itu.
   Tanpa timpaan ini kepala rak berhenti 72px terlalu rendah dan judul
   kategorinya tertutup kartu yang lewat.
   ======================================================= */
.app-main .shelf__head { inset-block-start: 0; }
@media (max-width: 63.99rem) {
  .app-main .shelf__head { inset-block-start: var(--appbar-h); }
}
