/* ═══════════════════════════════════════════════════════════════════
   KYOTO ELEVATION — Premium UI enhancement layer
   ───────────────────────────────────────────────────────────────────
   Layer ADITIF di atas kyoto-design-system.css. Tidak menimpa token inti;
   hanya menghaluskan komponen (cards, forms, tables, progress, gamifikasi)
   dan menambah dukungan reduced-motion. Dimuat setelah design system agar
   refinement berlaku, tapi semua memakai token Kyoto yang sudah ada
   (--matcha, --kyoto-red, --gold, --shadow-*, --r-*) demi konsistensi.
   Menyebar otomatis ke semua halaman yang memuat design system.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Token tambahan: durasi & easing halus (melengkapi yg ada) ── */
:root {
  --kx-dur-fast: 140ms;
  --kx-dur: 240ms;
  --kx-dur-slow: 420ms;
  --kx-ease: cubic-bezier(0.16, 1, 0.3, 1);       /* zen ease-out */
  --kx-ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --kx-lift: 0 12px 40px rgba(107, 79, 58, .14);   /* soft hover shadow */
  --kx-ring: 0 0 0 3px rgba(200, 155, 60, .28);    /* gold focus ring */
  --kx-glass: rgba(255, 255, 255, .72);
  --kx-glass-border: rgba(255, 255, 255, .5);
}

/* ══════════════════════════════════════
   CARDS — soft lift on hover, refined edges
   ══════════════════════════════════════ */
.card {
  transition: transform var(--kx-dur) var(--kx-ease),
              box-shadow var(--kx-dur) var(--kx-ease),
              border-color var(--kx-dur) var(--kx-ease);
  will-change: transform;
}
.card:hover {
  transform: translateY(-3px);
  box-shadow: var(--kx-lift);
}
/* Kartu interaktif (link/klik) mendapat aksen halus di tepi atas */
a.card, .card-link, .card[role="button"] { position: relative; overflow: hidden; }
a.card::before, .card-link::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--kyoto-red), var(--gold));
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--kx-dur-slow) var(--kx-ease);
}
a.card:hover::before, .card-link:hover::before { transform: scaleX(1); }

/* ══════════════════════════════════════
   BUTTONS — press feedback + gold focus ring
   ══════════════════════════════════════ */
.btn {
  transition: transform var(--kx-dur-fast) var(--kx-ease-soft),
              box-shadow var(--kx-dur) var(--kx-ease),
              background var(--kx-dur) var(--kx-ease);
}
.btn:active { transform: translateY(1px) scale(.985); }
.btn:focus-visible {
  outline: none;
  box-shadow: var(--kx-ring);
}

/* ══════════════════════════════════════
   FORMS — refined inputs, clear focus
   ══════════════════════════════════════ */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
select, textarea {
  transition: border-color var(--kx-dur) var(--kx-ease),
              box-shadow var(--kx-dur) var(--kx-ease),
              background var(--kx-dur) var(--kx-ease);
}
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):focus,
select:focus, textarea:focus {
  outline: none;
  border-color: var(--gold-soft);
  box-shadow: var(--kx-ring);
}
/* Label melayang- readable */
label { transition: color var(--kx-dur) var(--kx-ease); }

/* ══════════════════════════════════════
   TABLES — zebra, hover, rounded frame
   ══════════════════════════════════════ */
table.kx-table, .kx-table table { border-collapse: separate; border-spacing: 0; width: 100%; }
.kx-table thead th {
  background: var(--kyoto-brown-pale);
  color: var(--kyoto-brown);
  font-weight: 700; font-size: .82rem; letter-spacing: .02em;
  text-align: left; padding: .8rem 1rem;
  border-bottom: 2px solid var(--border-mid);
}
.kx-table tbody td { padding: .75rem 1rem; border-bottom: 1px solid var(--border-light); }
.kx-table tbody tr { transition: background var(--kx-dur-fast) var(--kx-ease); }
.kx-table tbody tr:nth-child(even) { background: rgba(107, 79, 58, .025); }
.kx-table tbody tr:hover { background: var(--gold-pale); }
.kx-table tbody tr:last-child td { border-bottom: none; }

/* ══════════════════════════════════════
   PROGRESS — smooth fill, shimmer sweep
   ══════════════════════════════════════ */
.kx-progress { height: 10px; background: var(--border-light); border-radius: 999px; overflow: hidden; position: relative; }
.kx-progress > i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--matcha), var(--matcha-soft));
  transition: width var(--kx-dur-slow) var(--kx-ease);
  position: relative;
}
.kx-progress > i::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent);
  transform: translateX(-100%);
  animation: kx-shimmer 2.4s var(--kx-ease-soft) infinite;
}
.kx-progress.kx-red > i { background: linear-gradient(90deg, var(--kyoto-red), var(--kyoto-red-soft)); }
.kx-progress.kx-gold > i { background: linear-gradient(90deg, var(--gold), var(--gold-soft)); }
@keyframes kx-shimmer { to { transform: translateX(100%); } }

/* Radial / ring progress (untuk XP, skor) */
.kx-ring-progress { --val: 0; --sz: 96px; width: var(--sz); height: var(--sz); border-radius: 50%;
  display: grid; place-items: center;
  background: conic-gradient(var(--matcha) calc(var(--val) * 1%), var(--border-light) 0);
  transition: background var(--kx-dur-slow) var(--kx-ease); }
.kx-ring-progress::before { content: ""; position: absolute; width: calc(var(--sz) - 16px); height: calc(var(--sz) - 16px);
  background: var(--surface); border-radius: 50%; }
.kx-ring-progress > span { position: relative; font-weight: 800; color: var(--ink); font-size: 1.1rem; }

/* ══════════════════════════════════════
   GAMIFICATION — XP, streak, badges, level
   ══════════════════════════════════════ */
.kx-xp-bar { height: 14px; border-radius: 999px; background: var(--border-light); overflow: hidden; box-shadow: inset 0 1px 3px rgba(0,0,0,.06); }
.kx-xp-bar > i { display: block; height: 100%;
  background: linear-gradient(90deg, var(--gold), var(--gold-soft), var(--kyoto-red));
  background-size: 200% 100%;
  transition: width var(--kx-dur-slow) var(--kx-ease);
  animation: kx-xp-flow 3s linear infinite; }
@keyframes kx-xp-flow { to { background-position: 200% 0; } }

.kx-badge-earn { display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .8rem; border-radius: 999px; font-weight: 700; font-size: .8rem;
  background: var(--gold-pale); color: #8B6A1C;
  box-shadow: 0 2px 8px rgba(200, 155, 60, .2);
  transition: transform var(--kx-dur) var(--kx-ease); }
.kx-badge-earn:hover { transform: translateY(-2px) scale(1.04); }

/* Streak flame pulse */
.kx-streak { display: inline-flex; align-items: center; gap: .35rem; font-weight: 800; color: var(--kyoto-red); }
.kx-streak .kx-flame { animation: kx-flicker 1.8s var(--kx-ease-soft) infinite; }
@keyframes kx-flicker { 0%,100% { transform: scale(1); } 50% { transform: scale(1.12); } }

/* Level chip */
.kx-level { display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 34px; padding: 0 .5rem; border-radius: 10px;
  font-weight: 800; font-size: .9rem; color: #fff;
  background: linear-gradient(135deg, var(--matcha), var(--kyoto-brown));
  box-shadow: var(--shadow-sm); }

/* ══════════════════════════════════════
   ENTRANCE — subtle reveal on load
   ══════════════════════════════════════ */
@keyframes kx-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.kx-rise { animation: kx-rise var(--kx-dur-slow) var(--kx-ease) both; }
.kx-rise-1 { animation-delay: .05s; }
.kx-rise-2 { animation-delay: .12s; }
.kx-rise-3 { animation-delay: .19s; }

/* #main-content — dipakai 347/377 halaman yang memuat berkas ini sebagai
   target skip-link (id yang sama dipakai audit aksesibilitas), jadi ini
   satu-satunya wrapper konten yang genuinely nyaris universal di seluruh
   situs. Dipasang di sini (bukan ditambahkan class="kx-rise" ke tiap
   halaman) supaya "animasi keseluruhan" berlaku situs-lebar dari SATU
   baris, sama seperti .kx-rise sendiri: markup statis, tidak butuh JS
   untuk mengungkapnya, jadi tidak bisa berakhir tersembunyi permanen
   walau skrip halaman gagal. */
#main-content { animation: kx-rise var(--kx-dur-slow) var(--kx-ease) both; }

/* ══════════════════════════════════════
   PAGE TRANSITION — cross-fade antar halaman (View Transitions API)
   ──────────────────────────────────────
   `navigation: auto` mengaktifkan cross-document view transition BAWAAN
   browser untuk navigasi sesama origin -- klik link ke halaman lain
   memudar keluar/masuk alih-alih hard-reload putih polos. Progressive
   enhancement murni: browser yang belum mendukung `@view-transition`
   cuma mengabaikan at-rule yang tak dikenal dan bernavigasi normal
   seperti sekarang -- TIDAK ADA jalur gagal, tidak butuh deteksi fitur,
   tidak butuh JS. Spesifikasinya mensyaratkan KEDUA sisi navigasi (halaman
   asal & tujuan) sama-sama mendeklarasikan aturan ini -- karena 377/388
   halaman memuat berkas ini, transisi otomatis terjadi di hampir semua
   perpindahan antar halaman situs; berpindah dari/ke salah satu dari 11
   halaman yang tidak memuatnya cukup jatuh kembali ke navigasi biasa,
   bukan gagal. Timing dipetakan ke token kx-dur-slow/kx-ease yang sama dipakai
   .kx-rise, supaya terasa satu bahasa gerak dengan entrance konten. */
@view-transition {
  navigation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--kx-dur-slow);
  animation-timing-function: var(--kx-ease);
}

/* ══════════════════════════════════════
   ACCESSIBILITY — honor reduced motion (celah yg ditemukan)
   ══════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .card, .btn, input, select, textarea,
  .kx-progress > i, .kx-ring-progress, .kx-xp-bar > i,
  .kx-badge-earn, .kx-table tbody tr, a.card::before, .card-link::before {
    transition: none !important;
    animation: none !important;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
  .card:hover { transform: none; }
  .kx-rise, #main-content { animation: none; opacity: 1; transform: none; }
  .kx-progress > i::after { display: none; }
}

/* ══════════════════════════════════════
   DARK MODE — sesuaikan bayangan & kaca
   ══════════════════════════════════════ */
[data-theme="dark"] {
  --kx-lift: 0 12px 40px rgba(0, 0, 0, .5);
  --kx-glass: rgba(30, 27, 24, .72);
  --kx-glass-border: rgba(255, 255, 255, .08);
}
[data-theme="dark"] .kx-table tbody tr:nth-child(even) { background: rgba(255, 255, 255, .03); }
[data-theme="dark"] .kx-table tbody tr:hover { background: rgba(200, 155, 60, .12); }

/* ══════════════════════════════════════
   SIDEBAR — premium polish (aditif; celah v112)
   Menghaluskan .sidebar / .side-nav / .sidebar-profile yang sudah ada
   tanpa mengubah struktur/fungsi. Memakai token & warna yang sudah dipakai
   komponen sidebar (var --green, --surface, --line) agar konsisten.
   ══════════════════════════════════════ */
.side-nav a {
  position: relative;
  transition: background var(--kx-dur) var(--kx-ease),
              color var(--kx-dur) var(--kx-ease),
              transform var(--kx-dur-fast) var(--kx-ease-soft);
}
.side-nav a:hover { transform: translateX(2px); }
.side-nav a:active { transform: translateX(1px) scale(.99); }
/* indikator aktif — batang torii di tepi kiri */
.side-nav a.is-active::before {
  content: ""; position: absolute; left: -18px; top: 50%;
  width: 3px; height: 20px; transform: translateY(-50%);
  background: linear-gradient(180deg, var(--kyoto-red, #A63A3A), var(--gold, #C89B3C));
  border-radius: 0 3px 3px 0;
}
.side-nav a > span { transition: transform var(--kx-dur) var(--kx-ease); }
.side-nav a:hover > span { transform: scale(1.08); }
/* badge angka halus */
.side-nav a b { transition: transform var(--kx-dur) var(--kx-ease); }
.side-nav a:hover b { transform: scale(1.06); }

/* Sidebar container — bayangan lembut saat sticky */
.sidebar { transition: box-shadow var(--kx-dur) var(--kx-ease); }

/* Profil sidebar — lift halus */
.sidebar-profile {
  transition: background var(--kx-dur) var(--kx-ease),
              box-shadow var(--kx-dur) var(--kx-ease);
}
.sidebar-profile:hover { box-shadow: var(--shadow-sm, 0 4px 16px rgba(107,79,58,.08)); }

/* Drawer mobile — transisi lebih halus (menimpa .2s linear default dgn ease-zen) */
@media (max-width: 980px) {
  .sidebar { transition: transform var(--kx-dur-slow) var(--kx-ease), box-shadow var(--kx-dur) var(--kx-ease) !important; }
}

@media (prefers-reduced-motion: reduce) {
  .side-nav a, .side-nav a > span, .side-nav a b, .sidebar, .sidebar-profile {
    transition: none !important;
  }
  .side-nav a:hover, .side-nav a:active { transform: none; }
  .side-nav a:hover > span { transform: none; }
}

/* ── Tombol ganti tema mengambang ─────────────────────────────────────
   294 halaman menuliskan gaya ini sebagai atribut style= pada tombolnya.
   Sebagian deklarasi di sana sebenarnya MATI: kyoto-bundle.min.css sudah
   menetapkan background, border, dan color dengan !important, termasuk
   variannya untuk [data-theme="dark"]. Yang benar-benar berlaku dari inline
   hanya posisi, ukuran, bentuk bulat, dan bayangan — itulah yang dipindahkan
   ke sini.

   Berkas ini dimuat tepat setelah kyoto-bundle.min.css, jadi width/height/
   border-radius di bawah (yang di bundle tidak ber-!important) tetap menang.

   Kelas terpisah, bukan .theme-toggle langsung, supaya tombol tema yang
   menyatu di navbar halaman lain tidak ikut menjadi fixed. */
.theme-toggle-float {
  position: fixed;
  top: 12px;
  right: 12px;
  z-index: 9999;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  font-size: 18px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
}

/* Navbar kyoto SUDAH punya tombol tema (#knThemeBtn). Pada 332 halaman
   keduanya tampil bersamaan — dan karena yang melayang dipatok top:12/right:12,
   ia mendarat tepat di atas tombol CTA navbar di lebar desktop.

   Keduanya berfungsi dan menghasilkan tema yang sama (data-theme="dark",
   latar rgb(17,17,17)); ini diperiksa sebelum aturan ini ditulis, sebab
   menyembunyikan tombol yang ternyata satu-satunya yang bekerja akan
   mematikan mode gelap di 332 halaman.

   Yang disembunyikan yang melayang, bukan yang di navbar: navbar selalu
   terlihat, sedangkan tombol melayang menutupi isi halaman. */
body:has(#knThemeBtn) .theme-toggle-float { display: none; }

/* Dua halaman (404 dan Kelas-Online) menaruh .theme-toggle telanjang —
   tanpa kelas -float — sebagai anak langsung <body> dengan position:fixed
   di pojok kanan atas, tepat menimpa tombol tema milik navbar. Dua tombol
   dengan fungsi sama, saling tumpang tindih.

   Kombinator ANAK dipakai dengan sengaja: diperiksa di seluruh 53 halaman
   akar, tidak ada satu pun .theme-toggle yang bukan anak langsung <body>,
   jadi aturan ini tidak mungkin mengenai tombol tema yang menyatu di dalam
   navbar. Itu risiko yang membuat aturan -float di atas ditulis sesempit
   itu sejak awal. */
body:has(#knThemeBtn) > .theme-toggle { display: none; }

/* ── Footer situs ─────────────────────────────────────────────────────
   126 halaman menyalin markup footer yang sama beserta gayanya sebagai
   atribut style= — 3.397 atribut, ditambah 1.058 handler onmouseover/
   onmouseout inline yang hanya mengubah warna saat hover.

   Kelas di bawah menyalin deklarasi itu PERSIS, jadi tampilannya tidak
   berubah sedikit pun. Yang berubah: warna hover kini ditangani :hover CSS,
   bukan JavaScript inline pada tiap tautan.

   Varian opasitas lain (.35, .4, .55) sengaja tidak dibuatkan kelas —
   perbedaannya kemungkinan besar hasil salin-tempel yang menyimpang, dan
   menyeragamkannya di sini akan mengubah tampilan tanpa keputusan desain. */
.site-footer {
  background: var(--ink);
  color: rgba(255, 255, 255, .7);
  padding: 3rem clamp(16px, 5vw, 72px) 2rem;
  margin-top: auto;
}
.ft-inner   { max-width: 1200px; margin: 0 auto; }
.ft-grid    { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 2rem; margin-bottom: 2.5rem; }
.ft-brand   { font-family: 'Noto Serif JP', serif; color: #fff; font-size: 20px; font-weight: 700; margin-bottom: .5rem; }
.ft-tagline { font-size: 13px; line-height: 1.75; margin-bottom: .875rem; }
.ft-chips   { display: flex; gap: .4rem; flex-wrap: wrap; }
.ft-heading { font-size: 11px; font-weight: 800; color: rgba(255, 255, 255, .4); letter-spacing: .08em; text-transform: uppercase; margin-bottom: .75rem; }
.ft-links   { display: flex; flex-direction: column; gap: .35rem; }
.ft-meta    { display: flex; gap: 1rem; font-size: 12px; }
.ft-note    { font-size: 12px; color: rgba(255, 255, 255, .35); }

.ft-link     { color: rgba(255, 255, 255, .65); font-size: 13px; text-decoration: none; transition: color .15s; }
/* Tinggi kotak kliknya hanya 21px — di bawah minimum WCAG 2.2 (24×24).
   Diukur di Chromium pada 15 halaman. Padding tegak menaikkannya ke 25px
   tanpa menggeser posisi teksnya, sebab tautan ini berdiri sendiri dalam
   kolom daftar footer. */
/* !important karena aturan tema `a[class*="link"]:not([class*="footer"])`
   ikut mengenai kelas ini — "ft-link-dim" memuat "link" tapi tidak memuat
   "footer" karena disingkat, jadi penjaganya meleset. Aturan itu ada juga
   di kyoto-bundle.min.css yang TANPA berkas sumber, sehingga memperbaiki
   kyoto-theme.css saja tidak menjangkau halaman yang memuat bundel.
   Ditelusuri dengan CDP pada 404.html: cokelat --primary di atas footer
   gelap memberi rasio 2,05.

   !important saja TIDAK CUKUP: dua deklarasi sama-sama !important, jadi
   pemenangnya kekhususan — dan .ft-link-dim (0,1,0) kalah dari
   a[class*="link"]:not([class*="footer"]) (0,2,1). Kelasnya digandakan
   supaya kekhususannya (0,3,1) menang telak. */
a.ft-link-dim.ft-link-dim,
.ft-link-dim {
  color: rgba(255, 255, 255, .6) !important;
  text-decoration: none;
  transition: color .15s;
  display: inline-block;
  padding-block: 2px;
}
.ft-link:hover,
.ft-link-dim:hover { color: #fff; }

/* ── Sel tabel materi ─────────────────────────────────────────────────
   1.812 sel di seluruh situs menuliskan gaya ini sebagai atribut style=.
   Tabelnya sendiri tidak punya kelas, jadi tidak ada aturan yang bisa
   menjangkaunya — deklarasi di bawah menyalin yang inline PERSIS. */
.tbl-cell      { padding: 8px 12px; border-bottom: 1px solid rgba(0, 0, 0, .06); font-size: .85rem; vertical-align: top; line-height: 1.6; }
.tbl-cell-sm   { padding: 7px 10px; border-bottom: 1px solid rgba(0, 0, 0, .06); }
.tbl-cell-meta { font-size: 11px; color: var(--ink-soft); }

/* ── Utilitas satu-properti ───────────────────────────────────────────
   Gaya seperti style="flex:1" atau style="margin-bottom:0" muncul ratusan
   kali pada elemen yang beragam. !important dipakai DI SINI SAJA, dan
   memang perlu: atribut style= mengalahkan setiap selektor, jadi tanpa
   !important sebuah aturan lain yang lebih spesifik (mis. `.card p`) akan
   diam-diam menghidupkan kembali margin yang tadinya sengaja dinolkan.
   Ini menyalin perilaku yang digantikan, bukan menambah kekuatan baru. */
.u-mb-0    { margin-bottom: 0 !important; }
.u-mt-0    { margin-top: 0 !important; }
.u-flex-1  { flex: 1 !important; }


/* ══════════════════════════════════════════════════════════════════════
   JALUR WIDGET MELAYANG
   ══════════════════════════════════════════════════════════════════════
   Lima benda melayang menaruh dirinya sendiri dengan offset tetap, tanpa
   satu pun tahu yang lain ada:

       kn-bottom-nav          bottom 0      navigasi utama, tinggi 62px
       .np-wa-float           bottom 24     ← mendarat DI ATAS navigasi
       .np-dark-toggle-btn    bottom 76
       .pro-floating-actions  bottom 86     baris tombol menggulir
       .nihongo-chat-launch   bottom 86     ← menimpa baris itu
       .translator-launch     bottom 88     ← menimpa juga

   Diukur di index.html pada 390px: LIMA tabrakan, dan gelembung WhatsApp
   menutupi navigasi bawah sehingga tombolnya tidak bisa ditekan.

   Penyebabnya bukan salah satu widget, melainkan tidak adanya kesepakatan
   tentang ruang di bawah layar. Aturan di bawah membuat kesepakatan itu:
   dua jalur bertingkat di atas navigasi, dengan tinggi navigasi sebagai
   satu-satunya sumber angka.

       jalur 1  baris tombol melayang (selebar layar)
       jalur 2  chat (kiri) · penerjemah (tengah-kanan) · WhatsApp (kanan)

   Lebar jalur 2 pada layar 390px: 12+90+8+132+8+52+12 = 314px — muat. */

.np-wa-float {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 500;
}

@media (max-width: 768px) {
  :root {
    --np-lane: calc(62px + env(safe-area-inset-bottom, 0px)); /* tinggi kn-bottom-nav */
    --np-bar: 0px;                                            /* bilah promo, 0 saat tersembunyi */
  }
  /* Bilah promo (.sticky-bar) meluncur masuk dengan kelas .show dan dipatok
     bottom:0 dengan z-index 90 — di BAWAH navigasi yang z-index 900. Isinya
     jadi tertutup navigasi. Ia dinaikkan ke atas navigasi, dan selama ia
     tampil seluruh jalur widget ikut naik setinggi bilahnya. */
  body:has(.sticky-bar.show) { --np-bar: 96px; }

  .sticky-bar { bottom: var(--np-lane) !important; }

  /* GENUINELY DIPERBAIKI (keluhan screenshot: strip FAB melar selebar layar
     dan menutupi teks). Aturan lama di pro-style.css/legacy-bundle.min.css
     menyetel left:12px + right:12px + flex row pada position:fixed = elemen
     MELAR selebar layar, bukan menyusut ke kiri. Elevation ini dimuat
     TERAKHIR di setiap halaman (termasuk 14 halaman legacy-bundle), jadi
     di sini posisi finalnya dipatok: kolom tetap di POJOK KANAN BAWAH,
     di atas jalur navigasi, tidak menutupi teks, tidak menabrak
     chat/WhatsApp di jalur 2. Tombol tambahan sudah dikumpulkan ke menu
     "⋯" oleh pro-app.js; menu terbuka ke atas sebagai kolom. */
  .pro-floating-actions {
    left: auto !important;
    right: 12px !important;
    bottom: calc(var(--np-lane) + var(--np-bar) + 10px) !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: flex-end !important;
    overflow: visible !important;
  }

  .nihongo-chat-launch  { bottom: calc(var(--np-lane) + var(--np-bar) + 62px) !important; left: 12px !important; }
  .translator-launch    { bottom: calc(var(--np-lane) + var(--np-bar) + 62px) !important; right: 72px !important; }
  .np-wa-float          { bottom: calc(var(--np-lane) + var(--np-bar) + 62px) !important; right: 12px !important; }

  .np-dark-toggle-btn   { bottom: calc(var(--np-lane) + var(--np-bar) + 114px) !important; }
}

/* KONTROL TEMA GANDA.
   ──────────────────────────────────────────────────────────────────────
   Aturan di atas mengasumsikan tombol tema melayang "hanya tersisa di
   halaman tanpa navbar kyoto", tapi tidak ada yang menegakkannya. Diperiksa
   di index.html: navbar kyoto hadir DAN punya tombol temanya sendiri
   (#knThemeBtn), sementara #npDarkToggle yang melayang tetap dirender --
   dua kontrol untuk satu fungsi.

   Yang melayang itu justru float yang menjangkau paling tinggi ke layar
   (bottom 176 pada 390px), jadi ia membayar ongkos ruang paling besar untuk
   fungsi yang sudah tersedia di navbar. Disembunyikan HANYA bila penggantinya
   benar-benar ada, sehingga halaman tanpa navbar kyoto tidak kehilangan
   satu-satunya cara mengganti tema. */
body:has(#knThemeBtn) .np-dark-toggle-btn { display: none !important; }

/* Pada desktop tidak ada navigasi bawah, tapi bilah promo tetap ada dan
   menabrak tombol WhatsApp serta tombol tema. Pil penerjemah juga menabrak
   kolom tombol melayang (bottom 92, tinggi 148 → puncak 240). */
@media (min-width: 769px) {
  :root { --np-bar: 0px; }
  body:has(.sticky-bar.show) { --np-bar: 88px; }

  /* SATU REL DI KANAN.
     ──────────────────────────────────────────────────────────────────
     Dulu benda melayang duduk di KEDUA sudut bawah: chat di kiri
     (left:18 bottom:92), sisanya di kanan. Diukur di index.html pada
     1440x900, pil chat (y 767-808) menutupi .btn-primary hero
     (y 786-838) -- tombol ajakan utama halaman depan tertutup widget.

     Sudut kiri-bawah adalah tempat konten hero berakhir, jadi apa pun
     yang dipatok di sana akan menabrak sesuatu. Perbaikannya bukan
     menggeser chat beberapa piksel, melainkan menyepakati bahwa SEMUA
     chrome melayang tinggal di satu rel kanan, sehingga penulis halaman
     hanya perlu menghindari satu sudut.

     Rel dari bawah ke atas (tinggi elemen dalam kurung):
         24  WhatsApp        (52) -> 24-76
         88  chat            (41) -> 88-129
        141  penerjemah      (40) -> 141-181
        193  kolom tombol   (148) -> 193-341

     Tombol tema tetap di kiri-bawah: ia kecil, x-nya 20-68, dan tidak
     bersinggungan dengan kolom tombol hero yang mulai di x=72. */
  .np-wa-float          { bottom: calc(1.5rem + var(--np-bar)); }
  .np-dark-toggle-btn   { bottom: calc(20px + var(--np-bar)) !important; }

  /* left:auto WAJIB. CSS dasar di pro-style.css menyetel left:18px; kalau
     left dan right sama-sama aktif pada position:fixed dengan lebar auto,
     elemennya melar selebar layar alih-alih pindah ke kanan. */
  .nihongo-chat-launch  { bottom: calc(88px + var(--np-bar)); left: auto; right: 18px; }
  .translator-launch    { bottom: calc(141px + var(--np-bar)); }
  .pro-floating-actions { bottom: calc(193px + var(--np-bar)); }
}


/* ══════════════════════════════════════════════════════════════════════
   TEKS DI DALAM PANEL HERO GELAP
   ══════════════════════════════════════════════════════════════════════
   Aturan ini memberi latar gelap ke setiap elemen berkelas mengandung
   "hero", dan ada di DUA tempat sekaligus:

       assets/kyoto-theme.css        dimuat  91 halaman
       assets/kyoto-bundle.min.css   dimuat 374 halaman  (tanpa berkas sumber)

   Keduanya menyetel latar #2D2D2D tapi tidak pernah menyetel warna teks,
   sehingga isinya mewarisi warna gelap halaman — gelap di atas gelap.

   Diukur di Chromium pada halaman akar:

       h1        #6B4F3A cokelat  rasio 1,84   7 halaman
       p         #6E6E6E abu      rasio 2,70   6 halaman
       .kicker   #6B4F3A          rasio 1,84   4 halaman
       .eyebrow  #6B4F3A          rasio 1,84   2 halaman

   Perbaikannya ditaruh DI SINI, bukan di kyoto-theme.css. Berkas itu hanya
   menjangkau 91 halaman; kyoto-elevation.css dimuat 375 halaman dan berada
   SESUDAH kyoto-bundle.min.css dalam urutan muat, jadi ia menang atas
   salinan di bundel yang tidak punya berkas sumber untuk disunting.

   Hanya empat jenis elemen yang disetel, bukan seluruh keturunannya:
   lencana, pil, dan tombol di dalam hero sering memakai warna aksen yang
   memang disengaja, dan `color: inherit` menyeluruh akan memucatkannya. */
:is(.hero-bar, .prem-cta,
    [class*="hero"]:not(.hero-chip):not(.hero-left):not(.hero-right))
  :is(h1, h2, p, .kicker, .eyebrow) {
  color: #EDE7DA !important;
}


/* ══════════════════════════════════════════════════════════════════════
   LABEL DI MODE GELAP
   ══════════════════════════════════════════════════════════════════════
   kyoto-bundle.min.css memuat aturan yang sangat luas:

       .label,.eyebrow,[class*="eyebrow"],[class*="label"] {
         color:#6B4F3A !important }

   [class*="label"] mengenai APA PUN yang nama kelasnya memuat "label" —
   .kn-bn-label pada navigasi bawah, .lv-label, .qr-label, .form-label,
   .stat-label — tanpa peduli latar maupun tema. Warnanya cokelat mode
   terang, jadi di mode gelap ia duduk di atas latar gelap:

       48×  rasio 2,52  di rgb(17,17,17)   21 halaman
       24×  rasio 2,32  di rgb(26,26,26)   13 halaman

   Bundel itu TIDAK punya berkas sumber, jadi tidak bisa disunting dari
   hulu. Penimpa ditaruh di sini — dimuat 375 halaman dan SESUDAH bundel.

   Kekhususan: [data-theme="dark"] (0,1,0) + :is() yang mengambil argumen
   paling spesifik [class*="label"] (0,1,0) = 0,2,0, mengalahkan aturan
   bundel yang 0,1,0.

   Emas #C89B3C adalah warna aksen yang memang sudah dipakai mode gelap di
   berkas lain, jadi labelnya tetap terbaca sebagai label, bukan teks biasa. */
[data-theme="dark"] :is(.label, .eyebrow, [class*="eyebrow"], [class*="label"]) {
  color: #C89B3C !important;
}

/* ══════════════════════════════════════════════════════════════════════
   Tautan-aksi cokelat harus jadi emas di mode gelap
   ══════════════════════════════════════════════════════════════════════
   kyoto-bundle.min.css memaksa `color:#6B4F3A !important` lewat selektor
   yang sangat luas:

       .sec-action, a[class*="action"], a[class*="more"],
       a[class*="link"]:not([class*="footer"])

   Cokelat itu benar di mode terang. Di mode gelap ia duduk di atas #1A1A1A
   dengan rasio 2,32:1 — dan karena !important, tidak ada satu pun aturan
   mode gelap di berkas lain yang bisa menyentuhnya.

   Yang paling kelihatan: dua tautan terakhir di navbar utama, "試 Ujian" dan
   "庭 Dasbor". Enam tautan navbar lainnya baik-baik saja karena mereka
   sebenarnya <button class="kn-link">, bukan <a> — jadi a[class*="link"]
   tidak menangkapnya. Hanya dua yang benar-benar <a> yang jadi cokelat,
   yang membuatnya tampak seperti dua item nav yang redup sendiri.

   Emas #C89B3C sudah jadi aksen mode gelap di seluruh situs: 6,7:1 di atas
   #1A1A1A dan 7,4:1 di atas #111111. Selektornya sengaja dicerminkan persis
   agar tepat menimpa yang itu dan bukan yang lain; kyoto-elevation.css dimuat
   sesudah bundle, jadi pada kekhususan yang sama ia yang menang. */
[data-theme="dark"] :is(.sec-action, a[class*="action"], a[class*="more"],
                        a[class*="link"]:not([class*="footer"])) {
  color: #C89B3C !important;
}

/* ══════════════════════════════════════════════════════════════════════
   Aksen merah di slab hero gelap beranda
   ══════════════════════════════════════════════════════════════════════
   Hero kiri index.html adalah slab #2D2D2D di KEDUA tema. --red #BE3428 di
   atasnya hanya 2,43:1 — lencana "✨ Platform Belajar Bahasa Jepang · Gratis"
   dan judul "Bahasa Jepang". Terbukti bukan cuma dari hitungan: potongan
   layar 2× memperlihatkan teks merah gelap yang tenggelam di arang.

   Diperbaiki di sini, bukan di sumbernya, karena dua alasan:

   1. neko-theme.css menyetel `color: var(--red) !important` lewat daftar
      selektor panjang yang juga memuat .section-label, .kicker, .tag,
      .plan-badge, .level-badge — komponen yang di halaman lain duduk di atas
      krem, dan #F2705C di sana justru cuma 2,56:1. Menyentuh aturan itu
      memindahkan bug, bukan menghapusnya.
   2. --red juga jadi LATAR tombol hero yang teksnya putih; melunturkan
      tokennya akan merusak tombol yang sekarang benar.

   kyoto-elevation.css dimuat sesudah neko-theme.css, jadi pada kekhususan
   yang sama ia yang menang. -webkit-text-fill-color ikut disetel karena
   neko-theme menyetelnya juga; tanpa itu warnanya tidak berubah. */
.hero-badge,
.hero-left h1 .accent {
  color: #F2705C !important;
  -webkit-text-fill-color: #F2705C !important;
}

/* ══════════════════════════════════════════════════════════════════════
   --ink-faint: satu nilai yang lolos di keempat lapisan token
   ══════════════════════════════════════════════════════════════════════
   Repo ini punya EMPAT lapisan token yang saling menimpa, dua di antaranya
   tanpa berkas sumber:

       kyoto-theme.css        #6E6E6E / #8A8784   (sudah diperbaiki)
       neko-theme.css         #6B6F75 / #888E96   (sudah diperbaiki)
       kyoto-bundle.min.css   tanpa sumber
       legacy-bundle.min.css  tanpa sumber — memuat SALINAN neko-theme,
                              lengkap dengan `--ink-faint:#b8c0ca !important`

   Karena salinan itu, memperbaiki neko-theme.css tidak menyentuh halaman yang
   memuat legacy-bundle: 404, AI-Sensei, Blog, dan Akun tetap memakai #b8c0ca —
   1,66:1 di atas krem. Praktis tak terbaca, dan di 404.html yang kena adalah
   satu-satunya baris identitas di kaki halaman.

   Satu pasang nilai cukup untuk semua lapisan, karena permukaan terangnya
   berkisar #f5f7fa–#FEFCF8 dan gelapnya #111111–#1f232b:
       #6E6E6E  4,85:1 di #f5f7fa   ·  5,14:1 di #FEFCF8
       #8A8784  4,67:1 di #1a1d23   ·  5,27:1 di #111111
   kyoto-elevation.css dimuat paling akhir di setiap halaman, jadi ia menang. */
:root { --ink-faint: #6E6E6E !important; }
html[data-theme="dark"] { --ink-faint: #8A8784 !important; }

/* ══════════════════════════════════════════════════════════════════════
   --red sebagai TEKS di mode gelap
   ══════════════════════════════════════════════════════════════════════
   neko-theme.css (dan salinannya di legacy-bundle.min.css) menyetel
   `--red: #be3428 !important` di blok terang DAN di blok gelap — nilainya
   tidak pernah membalik. Di atas kartu #1A1A1A rasionya 3,08:1, dan di atas
   lencana bertint emas rgba(255,182,6,.14) cuma 2,19:1.

   Tokennya tidak bisa sekadar dilunturkan, karena --red juga dipakai sebagai
   LATAR 18 tombol yang teksnya putih. Angkanya menutup pintu itu rapat-rapat:

       teks di atas #1A1A1A butuh luminans ≥ 0,293
       putih di atasnya sebagai latar butuh luminans ≤ 0,183

   Tidak ada satu warna yang memenuhi keduanya, jadi pemisahannya wajib —
   bukan pilihan gaya. Yang didaftar di sini hanya sembilan pemakaian
   sebagai-teks yang benar-benar terukur gagal; sisanya latar tombol dan
   sengaja dibiarkan.

   #EE8878: 4,95:1 di atas lencana bertint emas #3F3317 — permukaan tergelap
   yang dipakai — 6,86:1 di atas #1A1A1A, dan 7,4:1 di atas #111111. Daftar ini
   memang bisa basi kalau kelas baru ditambahkan — layout-sweep akan
   menangkapnya, karena begitulah kesembilan ini ditemukan. */
html[data-theme="dark"] :is(.lv-btn, .num, .scenario-btn, .chat-level,
                            .social-icon, .jp) {
  color: #EE8878 !important;
}

/* .btn-outline cokelat di kartu gelap
   ══════════════════════════════════
   kyoto-bundle.min.css: `.btn-outline,.plan-btn:not(.filled),.btn-white
   { color:#6B4F3A !important }`. Cokelat itu benar di atas krem, tapi di mode
   gelap tombolnya duduk di kartu #1A1A1A — 2,32:1. Emas adalah aksen mode
   gelap situs ini: 6,68:1. .btn-white tidak ikut karena latarnya justru selalu
   putih; ia sudah dipin ke merah merek di index-page.css. */
html[data-theme="dark"] :is(.btn-outline, .plan-btn:not(.filled)) {
  color: #C89B3C !important;
}

/* .stat-num beranda: --red neko tidak membalik (#be3428 di kedua tema), dan
   pil statistik menjadi kartu gelap #1A1A1A di mode gelap — 3,08:1 pada 22px
   berbobot 400, yang belum terhitung "teks besar" (butuh ≥24px, atau ≥18,66px
   dan tebal). #EE8878 mencapai 6,99:1. */
html[data-theme="dark"] .stat-num { color: #EE8878 !important; }  /* neko-theme memaksanya dengan !important */

/* Panel .error-state: latarnya --red-pale yang menggelap di mode gelap
   ══════════════════════════════════════════════════════════════════════
   kyoto-design-system.css memberi panel ini `background: var(--red-pale)`
   dengan `h3 { color: var(--kyoto-red) }` dan `p { color: var(--ink-soft) }`.
   Di mode terang tint merahnya pucat (#FAEAEA) dan --ink-soft tinggal 4,38:1;
   di mode gelap tint yang sama menumpuk jadi #291818 dan --kyoto-red #A63A3A
   jatuh ke 2,66:1. Latar dan teks membalik ke arah yang sama, jadi jaraknya
   menyempit dari dua sisi sekaligus.

   --ink-mid lebih pekat di terang (7,5:1) dan tetap terang di gelap, sehingga
   satu nilai cukup untuk paragrafnya. Judulnya perlu pasangan. */
.error-state p,
.error-state code { color: var(--ink-mid); }
.error-state h3 { color: #A63A3A; }
html[data-theme="dark"] .error-state h3 { color: #EE8878; }

/* ══════════════════════════════════════════════════════════════════════
   Sasaran sentuh tautan footer — WCAG 2.5.8 (24×24 px)
   ══════════════════════════════════════════════════════════════════════
   Footer bersama menyalin markup yang sama ke puluhan halaman, jadi tinggi
   tautannya juga sama di mana-mana: 20–21px, ditentukan murni oleh
   line-height. Di layar sentuh itu sasaran yang meleset.

   Ini BUKAN kasus pengecualian "Inline" WCAG 2.5.8 — tautan ini berdiri
   sendiri di dalam kolom dan baris meta, bukan di tengah kalimat, jadi
   tidak ada teks sekitar yang membatasi tingginya. (Tautan yang memang di
   dalam kalimat, seperti alamat surel di FAQ, sengaja tidak disentuh;
   memperbesarnya justru merusak paragrafnya.)

   inline-flex + align-items:center menaikkan tinggi tanpa menggeser garis
   dasar teksnya, jadi tata letak footer tidak berubah selain sedikit lebih
   lega. min-height, bukan padding, supaya tautan yang sudah cukup tinggi
   tidak ikut membesar.

   Daftar kelas sempat dipakai, tapi beberapa halaman lama (Privacy, Progress,
   Blog-SEO) menulis tautan footernya sebagai <a> tanpa kelas sama sekali, jadi
   13 tautan per halaman luput. `footer a` menangkap semuanya. Disisir di 53
   halaman: tepat SATU tautan footer yang berada di dalam kalimat — alamat surel
   di beranda — dan mailto: dikecualikan untuk itu. */
footer a:not([href^="mailto:"]) {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

/* ══════════════════════════════════════════════════════════════════════
   #floatingTimer — pengatur waktu belajar di 56 halaman Materi
   ══════════════════════════════════════════════════════════════════════
   Widget ini ditulis sebagai gaya inline di tiap halaman:

       position:fixed; bottom:5.5rem; left:1rem; z-index:150

   Angka 5.5rem itu dipilih tanpa menghitung widget lain, jadi ia tidak ikut
   sistem lajur di atas. Hasilnya di 1280px: timer menempati [16,679,240×133]
   dan tombol AI Chat [18,767,94×41] — keduanya bertumpuk. Lebih dari itu,
   karena `pointer-events` normal dan z-index 150, ia MENUTUPI tautan halaman
   di bawahnya: di Materi.html ia menelan "Mulai Nol" dan "Kana", tombol aksi
   kartu pertama; di Flashcard-Kanji ia menelan "Acak" dan "Reset Progres".

   Dinaikkan ke atas tombol chat (bottom 92 + tinggi 41 + jarak 12 = 145),
   memakai --np-bar yang sama supaya ikut bergeser saat bilah promo muncul.

   WARNA. Labelnya memakai color:var(--red,#be3428) inline. neko-theme tidak
   pernah membalik --red, jadi di mode gelap ia tetap #be3428 di atas kartu
   #1C1E22 — rasio 2,95. !important diperlukan justru karena gaya inline. */
#floatingTimer { bottom: calc(145px + var(--np-bar)) !important; }

@media (max-width: 768px) {
  #floatingTimer { bottom: calc(var(--np-lane) + var(--np-bar) + 114px) !important; }
}

html[data-theme="dark"] #floatingTimer > div:first-child { color: #EE8878 !important; }

/* Menaikkannya ke lajur widget menghentikan tumpukan dengan tombol chat, tapi
   tidak menghentikan penutupan isi halaman: panel 240×133 memang akan selalu
   menumpang di atas sesuatu. Dipindah ke lajur kanan justru lebih buruk —
   menutupi empat tautan dan menabrak dua widget lain.

   Yang bisa dijamin adalah ia tidak pernah MEMBLOKIR. Diukur dengan uji-tembak
   (document.elementFromPoint di titik tengah tiap kontrol), bukan perbandingan
   kotak:

       Flashcard-Kanji   4 kontrol terblokir → 0    (N5, N4, Acak, N1 Sumber…)

   Aturan ini sempat saya buang karena diuji di Materi.html, di mana satu-satunya
   yang terblokir justru tertutup TOMBOL timer sendiri — yang memang harus tetap
   bisa ditekan. Di sana angkanya 1 → 1, jadi tampak tak berguna. Halaman itu
   kebetulan kasus yang salah untuk mengujinya. */
#floatingTimer { pointer-events: none !important; }
#floatingTimer button { pointer-events: auto !important; }

/* Tombol tutup, ditambahkan pro-app.js. Wadahnya pointer-events:none supaya
   tidak memblokir tautan halaman, jadi badan panel tidak bisa lagi menerima
   klik penutup seperti dulu — tombol ini menggantikannya. 24×24 memenuhi
   WCAG 2.5.8, dan warnanya memakai token supaya ikut kedua tema. */
#floatingTimer { position: fixed; }
.np-timer-close {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-soft, #6E6E6E);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}
.np-timer-close:hover { background: rgba(128, 128, 128, .18); }

/* Tombol jeda & reset dipindah ke BARIS ATAS panel.
   ═══════════════════════════════════════════════
   Wadahnya sudah pointer-events:none, jadi badan panel meneruskan klik. Yang
   tersisa memblokir hanyalah tombol-tombolnya sendiri — dan letak bawaannya
   persis di atas baris tautan kartu pertama, sehingga "Mulai Nol" tidak bisa
   ditekan sama sekali.

   Menyempitkan panel atau membatasinya ke layar lebar tidak menolong: diukur
   di 1024–1920px, kontrol paling kiri halaman selalu ada di x=51–72. Tata
   letaknya tidak punya margin kiri, jadi panel berlabuh kiri SELALU menumpang
   isi. Yang bisa diatur hanyalah BAGIAN MANA dari panel yang menangkap klik.

   Dipindah ke atas, ketiga tombol berkumpul di baris yang sama dengan tombol
   tutup — jauh di atas baris tautan kartu — dan seluruh separuh bawah panel
   menjadi tembus klik. */
/* !important di sini bukan kemalasan: tombolnya dirender oleh skrip halaman
   dengan gaya INLINE (width, padding, background), dan gaya inline mengalahkan
   selektor apa pun tanpa !important. Tanpa ini ukurannya tetap 32×32 dan
   posisinya cuma setengah berpindah. */
#floatingTimer button:not(.np-timer-close) {
  position: absolute !important;
  top: 4px !important;
  bottom: auto !important;
  width: 24px !important;
  height: 24px !important;
  min-width: 24px !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  padding: 0 !important;
  margin: 0 !important;
  border: none !important;
  border-radius: 6px !important;
  background: transparent !important;
  color: var(--ink-soft, #6E6E6E) !important;
  font-size: 12px !important;
  line-height: 1 !important;
  cursor: pointer;
}
#floatingTimer button:not(.np-timer-close):hover { background: rgba(128, 128, 128, .18) !important; }
#floatingTimer button:not(.np-timer-close):nth-of-type(1) { right: 60px !important; }
#floatingTimer button:not(.np-timer-close):nth-of-type(2) { right: 32px !important; }

/* ══════════════════════════════════════════════════════════════════════
   #bttBtn (↑ kembali ke atas lama) — redundan bila dock FAB ada
   ══════════════════════════════════════════════════════════════════════
   pro-app.min.js membangun .pro-floating-actions yang SUDAH memuat
   tombol ↑ (data-pro-top, "Kembali ke atas"). Tombol statis #bttBtn di
   43 halaman (inline bottom:5rem;right:1.5rem) ternyata mendarat PERSIS
   di pojok yang sama dengan dock: diukur 766px ia bertumpuk 42×34px
   dengan strip FAB selebar layar (z 150 < z 520, jadi tertutup dan tidak
   bisa ditekan), dan di desktop ia menabrak dasar kolom FAB kanan.
   Dua tombol kembali-ke-atas di satu halaman = kekacauan visual.
   Disembunyikan hanya bila dock benar-benar ada (:has), supaya halaman
   tanpa pro-app tetap punya tombolnya. */
body:has(.pro-floating-actions) #bttBtn { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════
   #floatingTimer — chip ringkas untuk layar pendek
   ══════════════════════════════════════════════════════════════════════
   Di viewport pendek (mis. panel pratinjau setinggi ponsel) panel timer
   240×94 melayang tepat di atas kartu statistik/isi dan terbaca seperti
   tata letak rusak. Kelas .np-timer-mini (dikelola draggable-floats.js
   saat innerHeight <= 680px) menyisakan judul saja — chip selebar
   168px — yang diklik untuk membuka panel penuh kembali. Tombol ⏸/↺/✕
   disembunyikan karena tidak berguna di chip; labelnya yang menangkap
   klik (satu-satunya bagian yang boleh pointer-events:auto saat mini,
   persis seperti perilaku pilin lain). */
#floatingTimer.np-timer-mini {
  min-width: 168px !important;
  max-width: 168px !important;
  padding: .45rem .8rem !important;
}
#floatingTimer.np-timer-mini #studyTimerWidget,
#floatingTimer.np-timer-mini button { display: none !important; }
#floatingTimer.np-timer-mini > div:first-child {
  pointer-events: auto !important;
  cursor: pointer;
  margin-bottom: 0 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#floatingTimer.np-timer-mini > div:first-child:hover { opacity: .8; }

