/* ==========================================================================
   Coaching Program — one stylesheet, five screens
   DESIGN.md: blue and white · blue means "this wants you" · a record, not a dashboard
   In Apps Script this becomes an included template.
   ========================================================================== */

:root{
  /* ---- colour: one hue, one meaning -------------------------------- */
  --b900:#0D3A57; --b700:#1B6699; --b500:#3E8FC4;
  --b200:#96CAED; --b100:#D8EAF7; --b050:#F4F8FB;
  /* THREE MEANINGS, AND NO OTHERS:
       blue  — this wants you (a session to log, a request to answer)
       green — the number is improving
       red   — the number is not (belum bergerak, turun), or the action removes
     Amber used to carry "bad" alongside red, so "belum bergerak" rendered amber
     on some screens and red on others. It is gone. */
  --up:#157F3C; --up-soft:#E6F4EA;
  --down:#C0342B; --down-soft:#FBEBE9;
  --ink:#16222B; --ink2:#55666F; --ink3:#5E7080;
  --rule:#E3EAEF; --rule2:#EFF3F6; --paper:#FFFFFF; --sel:#CFE6F7;
  --on-brand:#FFFFFF;   /* text ON a --b700 fill        */
  --on-state:#FFFFFF;   /* text ON an --up / --down fill */
  --toast-bg:#0D3A57; --toast-ink:#FFFFFF;

  /* ---- category colour, as Health does it --------------------------
     Health gives each category its own hue so you recognise a card before
     reading it — Activity red, Sleep blue, Nutrition green. Three groups here.

     The constraint is that three meanings are already spoken for: --b700 is
     "this wants you", --up is improving, --down is not. A category hue that
     collides with any of them makes the colour system argue with itself, so
     these three deliberately avoid blue, green and red. Indigo, teal and FDC's
     own magenta. They name WHICH MEASURE, never how it is doing. */
  --c-perf:#4B45C6; --c-resv:#0E7C80; --c-trmt:#B02878;
  --scrim:rgba(10,22,30,.35);
  --sheet-edge:0 -1px 0 rgba(255,255,255,.55),0 -18px 50px -12px rgba(8,26,38,.4);
  /* Kaca (3 Oktober 2026): tepi cahaya dan kilau, dipakai bilah tab, bilah
     atas, tombol bulat di bilah, dan sheet — satu bahan untuk semua kontrol
     yang melayang di atas isi. Isi sendiri (kartu, daftar, grafik) tetap pekat. */
  --kaca-tepi:rgba(255,255,255,.75);
  --kaca-kilau:rgba(255,255,255,.45);

  /* ---- spacing: a scale, not arbitrary values ---------------------- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px;

  /* ---- motion ------------------------------------------------------ */
  --t1:110ms; --t2:200ms; --t3:320ms;
  --ease:cubic-bezier(.2,.8,.2,1);
  --ease-out:cubic-bezier(.16,1,.3,1);   /* tiba dengan yakin: thumb, garis grafik, isi sheet */

  /* ---- radius: one ladder, four steps ------------------------------
     The build carried SIXTEEN distinct values (1,2,3,5,6,7,9,10,11,12,13,
     14,16,20,50%,99). Nine of them were within 5px of a neighbour and served
     no separate purpose — the "mixing radii grammars" the references name as
     a ban. Each step now means one thing, and nothing sits between two. */
  --r-1:6px;      /* chips, marks, small badges     */
  --r-2:10px;     /* buttons, inputs, rows, slots   */
  --r-3:16px;     /* cards and panels               */
  --r-4:22px;     /* the sheet                      */
  --r-pill:999px; /* pills only                     */

  --safe-t:env(safe-area-inset-top,0px);
  --safe-b:env(safe-area-inset-bottom,0px);
  --tab-dasar:max(14px, calc(var(--safe-b) - 12px));
  --tab-h:65px;                       /* tinggi bilah tab: 55 + 2×5 */
  --gut:16px;   /* 16pt seperti Apple Health (dibandingkan Wang, 3 Okt 2026); dulu 20 */
}
/* ==========================================================================
   Theme. ONE list of dark values, reached three ways:
     - the OS says dark and the reader has not overridden it
     - the reader chose dark
   A reader who chose light is never dragged into dark by the OS.

   This used to be twelve scattered @media blocks in this file and four more
   in the shell, nine of which repeated `color:#0A141C` — which is why the
   theme could not be made switchable without touching sixteen places. It is
   one place now, and --on-brand carries what those nine were saying.
   ========================================================================== */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --b900:#DCEAF5; --b700:#7CBCE8; --b500:#5B9BC9;
    --b200:#35505F; --b100:#22303B; --b050:#171F26;   /* a dim, not a blackout */
    --ink:#E8EFF4; --ink2:#A6B6C1; --ink3:#8C9CA8;
    --rule:#2E3B46; --rule2:#232F39; --paper:#1F2931; --sel:#33556E;
    /* These four were added to the light palette after this block was written
       and never followed it down, so in dark mode green sat at 2,91:1, red at
       2,65:1, and `--ink` on `--down-soft` measured 1,00:1 — the paragraph
       naming which dentists an import is about to drop was invisible, directly
       above the save button. Now 6,3:1 / 5,7:1 / 13,6:1. */
    --up:#4FBE7A; --up-soft:#16301F;
    --down:#F08078; --down-soft:#331D1B;
    --on-brand:#0A141C;
    --on-state:#0A141C;
    --toast-bg:#D6E7F3; --toast-ink:#0D1B24;
    --c-perf:#A7A2FF; --c-resv:#57CBCF; --c-trmt:#F58BC0;
    --scrim:rgba(0,0,0,.5);
    --sheet-edge:0 -1px 0 rgba(255,255,255,.07),0 -18px 50px -12px rgba(0,0,0,.6);
    --kaca-tepi:rgba(255,255,255,.22);
    --kaca-kilau:rgba(255,255,255,.10);
  }
}
:root[data-theme="dark"]{
    --b900:#DCEAF5; --b700:#7CBCE8; --b500:#5B9BC9;
    --b200:#35505F; --b100:#22303B; --b050:#171F26;   /* a dim, not a blackout */
    --ink:#E8EFF4; --ink2:#A6B6C1; --ink3:#8C9CA8;
    --rule:#2E3B46; --rule2:#232F39; --paper:#1F2931; --sel:#33556E;
    /* These four were added to the light palette after this block was written
       and never followed it down, so in dark mode green sat at 2,91:1, red at
       2,65:1, and `--ink` on `--down-soft` measured 1,00:1 — the paragraph
       naming which dentists an import is about to drop was invisible, directly
       above the save button. Now 6,3:1 / 5,7:1 / 13,6:1. */
    --up:#4FBE7A; --up-soft:#16301F;
    --down:#F08078; --down-soft:#331D1B;
    --on-brand:#0A141C;
    --on-state:#0A141C;
    --toast-bg:#D6E7F3; --toast-ink:#0D1B24;
    --c-perf:#A7A2FF; --c-resv:#57CBCF; --c-trmt:#F58BC0;
    --scrim:rgba(0,0,0,.5);
    --sheet-edge:0 -1px 0 rgba(255,255,255,.07),0 -18px 50px -12px rgba(0,0,0,.6);
    --kaca-tepi:rgba(255,255,255,.22);
    --kaca-kilau:rgba(255,255,255,.10);
}

*,*::before,*::after{box-sizing:border-box}
html{
  -webkit-tap-highlight-color:transparent;
  -webkit-text-size-adjust:100%;
  /* Tarik ke bawah dari puncak halaman = muat ulang, seperti di aplikasi lain
     (Wang, 1 Oktober 2026). Dulu `none` di kedua sumbu mematikannya. Yang
     tetap dimatikan: geser menyamping (kembali/maju tak sengaja). */
  overscroll-behavior-x:none;
}
/* Kecuali di tempat orang sedang MENULIS: lembar yang terbuka (ditutup dengan
   ditarik ke bawah — gerakan yang sama) dan ruang coaching, tempat catatan sesi
   yang belum disimpan bisa hilang oleh satu tarikan yang terlalu jauh. */
html:has(.sheet-root.on),
html:has(.ruang2){overscroll-behavior-y:none}
body{
  margin:0;background:var(--b050);color:var(--ink);
  /* 17px, not 16. Apple's one emphatic typographic choice. */
  font:400 17px/1.47 var(--sans);letter-spacing:-.022em;
  font-optical-sizing:auto;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
::selection{background:var(--sel);color:var(--ink)}
:focus-visible{outline:2px solid var(--b700);outline-offset:3px;border-radius:3px}
:where(button,a,[role=button]){
  touch-action:manipulation;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;
}
input,textarea,select{font-size:16px;font-family:inherit}
[hidden]{display:none!important}

/* ==========================================================================
   Type — Apple's ladder, measured against it rather than asserted.

   The previous block claimed to follow Apple's steps and did not. What it
   actually rendered: weight 500 on labels, which the ladder calls
   "deliberately absent"; weight 700 at 34px, 22px AND 16px, where headlines
   are 600 and 700 is reserved; body at 16px, when the one thing Apple is
   emphatic about is 17px — "the extra pixel gives the page an unmistakable
   reading, not scanning pace"; and 1.40 leading on body where the ladder says
   1.47. The sizes 22 and 15 are not steps either.

   The ladder is 400 / 600 / 700, no 500. Tracking tightens from 17px up and is
   never applied at 12px or below. Leading is context-specific: tight on
   display, 1.47 on body.
   ========================================================================== */
:root{
  --sans:-apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif;
  --sans-d:-apple-system, BlinkMacSystemFont, "SF Pro Display", system-ui, "Segoe UI", Roboto, sans-serif;
}
/* 44px, weight 700. The one place 700 is earned: Health sets its figures heavy
   and this is the number the screen exists for. Everything else is 600. */
.t-fig{font:700 44px/1.02 var(--sans-d);letter-spacing:-.022em}
/* Large Title: 34/700, DESIGN.md §6. Peran ini sempat TIDAK punya tipografi
   sama sekali — hanya margin dan warna — jadi ukurannya datang dari bawaan
   peramban untuk <h1>, yang kebetulan 32px/bold dan karenanya tidak pernah
   terlihat salah. Yang terlihat justru akibatnya: tangganya datar, karena
   judul bagian 17px berdiri di atas isi 17px tanpa satu langkah pun di antara
   mereka yang benar-benar ditentukan. */
.t-big{
  margin:0;color:var(--b900);text-wrap:balance;
  font:700 34px/1.10 var(--sans-d);letter-spacing:-.024em;
}

.t-h2 {font:600 17px/1.24 var(--sans);letter-spacing:-.022em}
.t-lbl{font:400 14px/1.43 var(--sans);letter-spacing:-.016em}
.t-met{font:400 12px/1.33 var(--sans)}
.num  {font-variant-numeric:tabular-nums}

/* ==========================================================================
   Frame
   ========================================================================== */
.wrap{max-width:600px;margin:0 auto;padding:0 var(--gut) calc(var(--s7) + var(--safe-b))}
.wrap.wide{max-width:920px}

/* ==========================================================================
   Nav bar — a behaviour, not a strip.

   The large title lives in the content and scrolls away. A compact title rises
   into the bar as it goes, and the material fades in WITH it — because a blurred
   surface with nothing behind it is just a coloured banner. Both are driven
   continuously by --nav-p (0 at rest, 1 collapsed), never by a threshold.
   ========================================================================== */
/* Sticky, not fixed: #app carries a transform for the sheet's background scale,
   and a transformed ancestor becomes the containing block for fixed children —
   which would make this scroll away instead of sticking. */
.nav{
  position:sticky;top:0;z-index:30;
  margin:0 calc(var(--gut) * -1);
  min-height:calc(var(--safe-t) + 50px);
  /* TENGAH, bukan rata bawah. Rata bawah menyamakan alas judul dengan alas
     avatar, dan karena avatar 36px sementara baris teks 20px, titik tengahnya
     terpaut persis (36-20)/2 = 8px — avatarnya duduk terlalu tinggi terhadap
     nama yang didampinginya. */
  display:flex;align-items:center;gap:var(--s2);
  /* TEGAK SIMETRIS: 7px di atas dan di bawah, jadi 7 + 36 + 7 = 50, persis
     min-height-nya. Dulu `var(--safe-t) ... 9px` — nol di atas dan 9 di bawah —
     dan akibatnya tombol setinggi 36px duduk 3px dari tepi atas layar.

     Itu terasa sempit, dan pada avatar ia lebih dari terasa: lencana yang
     menggantung di luar lingkarannya terpotong tepi layar. Selama yang duduk di
     sudut hanya avatar yang muncul saat digulung, cacat ini jarang terlihat;
     Daftar orang menaruh tombol di sana sepanjang waktu, dan ia langsung
     kentara.

     .nav-c sudah simetris (6px/6px) sejak awal dan tidak pernah begini. Dua
     bentuk bilah yang sama tingginya tidak punya alasan menaruh isinya di
     ketinggian yang berbeda. */
  padding:calc(var(--safe-t) + 7px) var(--gut) 7px;
  pointer-events:none;
}
/* A ROOT bar holds nothing at rest — no back button, no actions — so it must
   not reserve a bar's worth of height above the title. It keeps its full height
   as a sticky SURFACE, because that is what the compact title scrolls into, and
   gives most of that height back to the flow: the large title starts near the
   top of the page and passes behind the bar as it goes, which is what the
   material and the --nav-p fade were built for.

   It was 50px of empty band above every root title. On a phone with a notch the
   reserved space still clears it: what is given back is the 50px bar, never
   var(--safe-t). A pushed screen (.nav-c) keeps its height — there the bar has
   a back button in it and the content genuinely starts below. */
/* Tarikan ini ada karena bilah akar dulu TIDAK memuat apa-apa saat diam — tidak
   ada tombol kembali, tidak ada tindakan — jadi memesan setinggi bilah di atas
   tiap judul berarti 50px pita kosong. Ia memberikan sebagian besar tingginya
   kembali ke aliran halaman.

   Alasan itu hilang begitu bilahnya berisi sesuatu. Dengan avatar akun di
   dalamnya, isi yang naik 36px mendarat TEPAT di atasnya: kolom pencarian
   terbit menimpa avatar, yang terlihat seperti dua benda berebut satu tempat —
   karena memang begitu. Jadi yang berisi mempertahankan tingginya. */
.nav:not(.nav-c):not(.nav-akun){margin-bottom:-36px}
.nav > *{pointer-events:auto}
.nav::before{
  content:"";position:absolute;inset:0;z-index:-1;
  /* 86%, SAMA DENGAN .tabbar — dan itu perbaikan atas cacat yang difoto Wang.

     Dulu 72%, dan pita atas bersandar pada blur-nya untuk menyembunyikan apa
     yang lewat di bawahnya. Begitu blur-nya tidak jalan, 28% sisanya bukan
     kabut melainkan jendela: baris pertama kartu di bawahnya terbaca utuh dan
     tajam menembus pita, bertindihan dengan judul ringkas di atasnya.

     Bilah bawah memakai 86% sejak awal dan tidak pernah bermasalah. Dua pita
     dari bahan yang sama di layar yang sama tidak punya alasan memakai dua
     angka berbeda — dan yang lebih tebal adalah yang terbukti. */
  /* Bahan yang sama dengan bilah tab (3 Oktober 2026), sedikit lebih pekat:
     di sini judul ringkas duduk TEPAT di atas isi yang lewat, jadi 72% —
     cukup bening untuk terbaca kaca, cukup pekat untuk judulnya. Yang tanpa
     blur jatuh ke bahan pekat (cadangan di kaki berkas). */
  background:color-mix(in srgb,var(--b050) 72%,transparent);
  backdrop-filter:blur(26px) saturate(200%) brightness(1.04);
  -webkit-backdrop-filter:blur(26px) saturate(200%) brightness(1.04);
  box-shadow:inset 0 -.5px 0 color-mix(in srgb,var(--kaca-tepi) 60%,transparent);
  opacity:var(--nav-p,0);
}
/* TANPA BLUR, PITANYA HARUS PEKAT.

   Bahan tembus pandang ini cuma terbaca kalau blur-nya benar-benar jalan.
   Kalau tidak — peramban yang tidak mendukungnya, atau yang mematikannya demi
   hemat daya — yang tersisa hanya tirai 86% yang masih meneruskan tulisan di
   baliknya. Lebih baik pekat dan jelas daripada berlapis dan kacau.

   Aturan prefers-reduced-transparency di bawah melakukan hal yang sama untuk
   orang yang MEMINTA-nya; yang ini untuk mesin yang tidak bisa. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav::before,
  .tabbar{background:var(--b050)}
}
/* Scroll edge as a fade, never a 1px rule — and only once it is overlapping. */
.nav::after{
  content:"";position:absolute;left:0;right:0;top:100%;height:16px;z-index:-1;
  background:linear-gradient(color-mix(in srgb,var(--b050) 86%,transparent),transparent);
  opacity:var(--nav-p,0);
}
@media (prefers-reduced-transparency:reduce){
  .nav::before{background:var(--b050);backdrop-filter:none;-webkit-backdrop-filter:none}
}
/* APA PUN YANG DUDUK DI SUDUT memudar bersama judul ringkasnya. Sebelum ini ia
   satu-satunya benda yang terlihat di bilah layar akar saat istirahat — sebuah
   tombol melayang di atas pita kosong. Pada layar yang didorong --nav-p selalu
   1, jadi di sana tidak ada yang berubah.

   Dulu aturan ini menyebut `.nav-b.avatar`, karena avatar memang satu-satunya
   penghuni sudut. Daftar orang menaruh saring dan tambah di sana, dan menyalin
   ketiga aturan ini untuknya berarti dua perilaku yang harus diingat orang
   untuk diubah bersama. Yang diatur di sini adalah TEMPATNYA, bukan bendanya. */
/* Sudut kanan bilah atas SELALU terisi, saat diam maupun digulung — seperti
   Apple Health: avatar (atau tombol halaman) duduk di bilah di atas judul
   besar, tidak di samping judul itu. Wang membandingkan keduanya berdampingan,
   3 Oktober 2026. Salinan di .head-atas disembunyikan di bawah. */
.nav > .nav-sudut{opacity:1}
.nav-title{
  flex:1;text-align:center;min-width:0;
  font:600 17px/1.2 var(--sans);
  letter-spacing:-.012em;color:var(--b900);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  opacity:var(--nav-p,0);
  transform:translateY(calc(9px * (1 - var(--nav-p,0))));
}
/* The pushed screen's bar: one row, always material, title centred between two
   equal ends so it is centred on the SCREEN and not on what is left over. */
.nav.nav-c{align-items:center;padding-top:calc(var(--safe-t) + 6px);padding-bottom:6px}
.nav.nav-c::before,
.nav.nav-c::after{opacity:1}
.nav.nav-c .nav-title{opacity:1;transform:none}
.nav-b{
  -webkit-appearance:none;appearance:none;border:0;cursor:pointer;
  flex:none;width:36px;height:36px;border-radius:50%;
  display:grid;place-items:center;
  background:color-mix(in srgb,var(--ink) 7%,transparent);
  color:var(--b700);
  transition:background var(--t1) var(--ease),transform var(--t1) var(--ease);
}
/* Ikon di tombol bulat: 14px, sedikit lebih kecil dari panah kembali (15px).
   Dulu ukuran panah (9×15) berlaku untuk SEMUA ikon .nav-b, jadi saring,
   tambah, email, dan tautan di Daftar orang terjepit jadi 9px lebar. */
.nav-b svg{width:14px;height:14px}
.nav-b svg[viewBox="0 0 11 18"]{width:9px;height:15px}
.nav-b.ghost{background:none;pointer-events:none}
/* Tombol bulat DI BILAH ATAS menjadi kaca kecil, seperti avatar dan tombol
   bilah di Health. Tombol .nav-b di dalam isi (baris Daftar orang) tidak. */
.nav .nav-b:not(.ghost){
  background:color-mix(in srgb,var(--paper) 58%,transparent);
  backdrop-filter:blur(18px) saturate(190%);-webkit-backdrop-filter:blur(18px) saturate(190%);
  box-shadow:inset 0 1px 0 var(--kaca-tepi),
    inset 0 0 0 .5px color-mix(in srgb,var(--kaca-tepi) 55%,transparent),
    0 4px 12px -6px rgba(10,22,30,.32);
}
.nav .nav-b.avatar{background:color-mix(in srgb,var(--b100) 78%,transparent)}
.nav-b:active{transform:scale(.93)}
@media (hover:hover) and (pointer:fine){
  .nav-b:not(.ghost):hover{background:color-mix(in srgb,var(--ink) 12%,transparent)}
}
/* No large title above it, so the subtitle carries the top margin instead. */
.head-c{padding-top:var(--s4)}
.head-c .sub{margin:0}

.nav-back{
  -webkit-appearance:none;appearance:none;background:none;border:0;
  min-height:44px;padding:0 10px 0 0;margin-left:-10px;cursor:pointer;color:var(--b700);
  display:inline-flex;align-items:center;gap:3px;
  font:400 16px/1 var(--sans);
}
.nav-back:active{opacity:.55}
/* Selebar tombol yang digantikannya, 36px — bukan 44. Ia penyeimbang: dengan
   44 di kiri dan avatar 36 di kanan, judul yang "di tengah" duduk 4px ke kanan
   dari tengah sesungguhnya. */
.nav-spacer{width:36px;flex:none}

/* The large title, in the content where it belongs. */
.head{padding:var(--s3) 0 0}
/* Nama besar dan avatar satu baris. Rata ATAS, bukan tengah: nama yang patah
   dua baris akan menyeret avatarnya turun ke tengah kedua baris, jauh dari
   baris yang sebenarnya ia dampingi. */
.head-atas{display:flex;align-items:flex-start;gap:var(--s3)}
.head-atas .t-big{flex:1;min-width:0}
.head-atas .nav-sudut{display:none}
/* (37,4px tinggi baris judul - 36px avatar) / 2 — didudukkan pada baris
   pertama, bukan pada kotak judulnya. */
.head-atas .nav-b.avatar{flex:none;margin-top:1px;
  /* Bergantian dengan yang di bilah pada SATU ambang, bukan silang-pudar:
     bilahnya bening, jadi di tengah silang avatar yang sama tergambar dua kali,
     bergeser beberapa piksel. Pergantiannya diberi 110ms supaya terbaca sebagai
     satu benda yang berpindah, bukan berkedip. */
  transition:opacity var(--t1) var(--ease)}
:root.nav-ringkas .head-atas .nav-sudut{opacity:0}
@media (prefers-reduced-motion:reduce){
  .head-atas .nav-b.avatar,.nav > .nav-b.avatar{transition:none}
}
/* Font comes from the ladder above; this only places it. The font used to be
   restated here at 700/1.08 and, sitting later in the file, quietly overrode
   the ladder it was supposed to follow. */
.t-big{margin:0;color:var(--b900);text-wrap:balance}
.head .sub{margin:var(--s1) 0 0;color:var(--ink3)}
/* Baris kedua anak judul: fakta yang sama untuk semua orang, dipisahkan dari
   bagian yang berubah menurut pembacanya. */
.sub-b{display:block}
.head .after{margin-top:var(--s3)}

/* ==========================================================================
   The standing, inside the masthead.

   It used to sit loose on the ground below the ask, separated from everything
   by margin — which is why it read as orphaned data. Linear: "sections
   separate by lift onto surface-1 panels, not by gaps in white." It is bound
   to the person now, and the air goes AROUND the block rather than through it.
   ========================================================================== */
/* One card, three parts, hairlines between them — not three cards and not a
   card inside a card. */
.angka{
  margin:var(--s5) 0 0;border-radius:var(--r-3);
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule);
  overflow:hidden;
}
.angka-p{padding:var(--s5) var(--s4)}
.angka-p .wb{margin-top:var(--s4)}
.angka-c{border-top:1px solid var(--rule2);padding-top:var(--s4);padding-bottom:var(--s4)}
.angka-c .lede{margin:0}
.angka-go{
  display:flex;align-items:center;gap:7px;width:100%;min-height:52px;
  padding:0 var(--s4);border:0;border-top:1px solid var(--rule2);
  background:none;color:var(--b700);font:inherit;cursor:pointer;text-align:left;
  transition:background var(--t1) var(--ease);
}
.angka-go > span:first-of-type{flex:1;font:600 17px/1.24 var(--sans);letter-spacing:-.022em}
.angka-n{color:var(--ink3);font-variant-numeric:tabular-nums}
.angka-go:active,.angka-go.down{background:var(--b100)}
@media (hover:hover) and (pointer:fine){ .angka-go:hover{background:var(--b100)} }
/* Movement first — FLOW.md §5. It is the line the eye lands on after the name. */
/* The measure, named where a reader meets it — same grammar as the measure
   cards: icon, name, category hue. */
.mast-h{display:flex;align-items:center;gap:7px;margin:0 0 var(--s3);
  font:600 15px/1.2 var(--sans);letter-spacing:-.01em}
.mast-h svg{flex:none}

.mast-g{margin:0;font:600 17px/1.24 var(--sans);
  letter-spacing:-.006em;color:var(--ink2)}
.mast-g.up{color:var(--up)} .mast-g.dn{color:var(--down)}
/* Hero figure: proportional, not tabular. Tabular is for numbers in a column;
   on a standalone value it widens the digits for no reason. */
.mast-f{margin:2px 0 0;
  font:700 44px/1.02 var(--sans-d);
  letter-spacing:-.022em;color:var(--b900)}
.mast-s{margin:var(--s2) 0 0;color:var(--ink3)}
.mast-g,.mast-f{max-width:100%}

/* Twelve weeks, drawn whole. The filled part is what has happened; the rest is
   the programme ahead, which is room and not a deficit. */
.sp-wrap{display:block}
.spark{display:block;margin:var(--s3) 0 0;overflow:visible}
.spark-axis{stroke:var(--rule);stroke-width:1;vector-effect:non-scaling-stroke}
.spark-tgt{stroke:var(--rule);stroke-width:1;stroke-dasharray:3 4;vector-effect:non-scaling-stroke}
/* The line and its current point take the colour of whatever they sit in, so a
   category's chart matches its category. */
/* Weight matched to the rest of the system: a 16px radius and 34px display
   type do not sit beside a 2px hairline. */
.spark-l{stroke:currentColor;opacity:.75;stroke-width:3;stroke-linecap:round;
  stroke-linejoin:round;vector-effect:non-scaling-stroke}
.spark-d{fill:currentColor;stroke:var(--paper);stroke-width:2;
  vector-effect:non-scaling-stroke}


/* ==========================================================================
   The reading — a figure with everything it needs to be understood
   ========================================================================== */
.reading{padding:var(--s6) 0 var(--s5)}
.reading .lbl{margin:0 0 var(--s2);color:var(--ink2)}
.reading .fig{margin:0;color:var(--b900)}
.reading .now{margin:var(--s2) 0 0;color:var(--ink3)}
.facts{
  margin:var(--s4) 0 0;padding:0;display:grid;gap:var(--s1);
}
.facts div{display:flex;align-items:baseline;gap:var(--s3);
  font-size:14px;color:var(--ink2)}
.facts dt{margin:0}
.facts dd{margin:0 0 0 auto;color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.facts .tgt dd{color:var(--ink3);font-weight:400}

.verdict{
  margin:var(--s4) 0 0;display:inline-flex;align-items:center;gap:var(--s2);
  font:600 15px/1 var(--sans);color:var(--b700);
}
.verdict.flat{color:var(--ink3)}
.verdict.stall{color:var(--down)}

/* ==========================================================================
   The line — the only chart in the product
   ========================================================================== */
.plot{margin:var(--s5) 0 0}
.plot svg{display:block;width:100%;height:auto}
.plot .ends{display:flex;justify-content:space-between;margin-top:var(--s2);
  color:var(--ink3);white-space:nowrap}

/* ==========================================================================
   Ledger — the record as one continuous thing, in its own scroll.
   Twelve sessions must not push the rest of the card off the page, and each
   one should settle into view rather than stopping halfway.
   ========================================================================== */
/* On its own surface. It sat loose between two cards, which read as something
   adrift rather than the record the programme is made of. The spine survives
   inside it — that is the object, and it is what the card now holds. */
/* Keadaan kosong catatan: sebuah kalimat di atas kertas, bukan kotak berisi
   ketiadaan. Ia menempati tempat yang sama dengan daftar yang akan
   menggantikannya, supaya halamannya tidak melompat saat sesi pertama masuk. */
/* Gambar jalan buntu. Ukurannya sengaja sedang: cukup untuk mengatakan
   halaman ini memang begini, tidak cukup untuk jadi pokok halamannya —
   kalimatnya yang membawa kabarnya, bukan gambarnya. */
.kosong-gb{display:block;width:76px;height:76px;margin:0 auto var(--s4)}

/* .resting adalah kartu yang tidak meminta apa-apa — dua-duanya jalan buntu,
   dan sekarang keduanya bergambar, jadi isinya ditengahkan seperti
   .kosong-catatan. Kartu .want-block yang MEMINTA sesuatu tidak ikut: di sana
   teks rata kiri dan tombolnya yang memimpin. */
.want-block.resting{text-align:center;padding:var(--s6) var(--s4)}

/* Di dalam kartu lain: tanpa permukaan sendiri, dan tanpa jarak atas — kartu
   induknya sudah memberi keduanya. */
.kosong-catatan.polos{margin:0;padding:var(--s5) 0 var(--s2);
  background:none;box-shadow:none}

.kosong-catatan{margin:var(--s5) 0 0;padding:var(--s6) var(--s4);
  border-radius:var(--r-3);background:var(--paper);
  box-shadow:inset 0 0 0 1px var(--rule);text-align:center}
.kosong-catatan p{margin:0;font:600 17px/1.3 var(--sans);letter-spacing:-.012em;
  color:var(--ink)}
.kosong-catatan .why{margin:var(--s2) 0 0;font:400 15px/1.47 var(--sans);
  font-weight:400;color:var(--ink3)}

.ledger{margin:var(--s5) 0 0}
.ledger.card{padding:var(--s5) var(--s4) var(--s4);border-radius:var(--r-3);
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule)}
/* The card adds its own side padding, so the spine's indent comes back by the
   same amount — otherwise the entry header lost 16px and the date wrapped onto
   two lines. Total indent is what it was before the card. */
.ledger.card .ledger-track{padding-left:var(--s6);padding-top:0;padding-bottom:var(--s3)}
.ledger.card .ent::before{left:-18px}
/* The node is a disc the thread passes behind, so it has to be filled with the
   surface it sits on. On the record screen that is the page; inside a card it
   is the card, and a page-coloured disc on a white card reads as a smudge —
   most visibly as the 4px halo around the live node. */
.ledger.card{--node:var(--paper)}
/* Track padding 32, spine at 14, badge 26 wide: 32 - 31 = 1, centre 14. */
.ledger.card .ent .no{left:-31px}
.ledger.card .btn.quiet{width:100%}
/* Tidak ada satu pun aturan margin untuk h3 di stylesheet ini — .t-h2 hanya
   mengatur hurufnya — jadi yang berlaku bawaan peramban: 1em atas dan bawah,
   17px pada ukuran ini. Di dalam kartu yang sudah memberi 24px padding, itu
   menumpuk jadi 41px di atas judul dan 29px di bawahnya, sebelum entri yang
   sendirinya berpadding 16px lagi. Terlihat "wajar" selama ada tiga entri
   mengisi kartunya; dengan satu entri, pitanya kosong dan jelas. */
.ledger > h3{display:flex;align-items:baseline;gap:var(--s2);margin:0 0 var(--s2)}
.ledger .count{font-size:12px;letter-spacing:.02em;color:var(--ink3);font-variant-numeric:tabular-nums}

.ledger-track{position:relative;padding:var(--s3) 0 var(--s5) var(--s7)}
/* The thread runs BETWEEN the nodes — it begins at the first and ends at the
   last, because what it draws is the connection, not a rule down the margin.

   It used to be one line down the whole track, held off each end by a fixed
   inset. Those insets could not know where the last node was, so under a card
   — whose padding differs from the plain track's — the line carried on about
   16px past the final node and stopped in the padding: a thread tied to
   nothing. Per entry, each segment knows its own node. */
.ent::before{
  content:"";position:absolute;left:-26px;top:0;bottom:0;width:1px;background:var(--rule);
}
.ent:first-child::before{top:24px}                  /* the first node's centre */
.ent:last-child::before{bottom:auto;height:24px}    /* stops at the last */
.ent:first-child:last-child::before{display:none}   /* one node, nothing to join */
.ent{position:relative;padding:var(--s4) 0 var(--s5)}
.ent + .ent{border-top:1px solid var(--rule2)}
/* The number IS the node on the spine — not a digit beside the line with a
   separate dot floating off it. The ground-coloured fill makes the line read
   as passing behind the marker. */
.ent .no{
  position:absolute;left:-39px;top:11px;          /* spine sits at 22px; 26/2 = 13 */
  width:26px;height:26px;border-radius:50%;
  display:grid;place-items:center;
  margin:0;
  background:var(--node, var(--b050));
  box-shadow:inset 0 0 0 1px var(--rule);
  font-variant-numeric:tabular-nums;
  color:var(--ink3);
}
.ent.live .no{
  background:var(--b700);color:var(--on-brand);font-weight:700;
  box-shadow:0 0 0 4px var(--node, var(--b050));  /* the ground clears the line around it */
}
/* Two columns while they fit, stacked when they do not. Fixed columns squeezed
   both halves into two ragged lines each — "Jumat, 18 / September" beside
   "Hasil per shift Rp / 1.531.117" — where one clean line each is what a narrow
   screen actually has room for. */
.ent .hd{display:flex;align-items:baseline;gap:0 var(--s3);flex-wrap:wrap;color:var(--ink3)}
.ent .hd > span{white-space:nowrap}
.ent .hd .amt{margin-left:auto;color:var(--ink2);font-variant-numeric:tabular-nums}
@media (max-width:420px){ .ent .hd .amt{margin-left:0} }
.ent .said{margin:var(--s3) 0 0;font-size:17px;line-height:1.45;letter-spacing:-.012em}
.ent .said em{font-style:italic;color:var(--ink)}
.state{display:inline-flex;align-items:center;gap:6px;margin-top:var(--s3);font-size:13px}
.state.done{color:var(--ink3)}
.state.want{color:var(--b700);font-weight:600}

/* ==========================================================================
   Clusters — a ledger too
   ========================================================================== */
.clusters{margin:var(--s5) 0 0;width:100%;border-collapse:collapse}
.clusters caption{text-align:left;color:var(--ink2);padding-bottom:var(--s3)}
.clusters th,.clusters td{
  padding:var(--s2) 0;border-bottom:1px solid var(--rule2);
  font-size:14px;font-weight:400;text-align:left;color:var(--ink);
}
.clusters td.n{text-align:right;font-variant-numeric:tabular-nums;color:var(--ink2);width:62px}
.clusters td.d{text-align:right;font-variant-numeric:tabular-nums;width:64px;font-weight:600}
.clusters td.d.up{color:var(--b700)}
.clusters td.d.dn{color:var(--down)}
.clusters td.d.same{color:var(--ink3);font-weight:400}
.clusters tr:last-child th,.clusters tr:last-child td{border-bottom:0}

/* ==========================================================================
   Something wants you
   ========================================================================== */
.want-block{
  margin:var(--s5) 0 0;padding:var(--s5) var(--s4);
  background:var(--paper);border-radius:var(--r-3);
  box-shadow:inset 0 0 0 1px var(--rule);
}
.want-block h2{margin:0 0 var(--s2);font:600 17px/1.35 var(--sans);
  letter-spacing:-.005em;color:var(--b900)}
.want-block .why{margin:0 0 var(--s4);font-size:14px;color:var(--ink2)}
/* A card that is not asking for anything. Same box, quieter ink and no
   trailing space reserved for a button that is not there. */
.want-block.resting h2{color:var(--ink2);font-weight:600}
.want-block.resting .why{margin-bottom:0}
/* A booked hour that stopped working for somebody who joined the session after
   it was set. It is not a bad number and it does not remove anything — it is a
   thing asking to be dealt with, so it takes the same blue as every other
   "this wants you", rather than inventing a fourth meaning. */
.clash{
  margin:0 0 var(--s4);padding:10px 12px;border-radius:var(--r-2);
  background:var(--b100);color:var(--b700);
  font-size:13.5px;line-height:1.45;font-weight:600;
}
.want-block .why:has(+ .clash){margin-bottom:8px}

/* One reading, stated as a number. No axis and no line: a chart of a single
   value implies a passage of time that has not happened. */
.satu{margin:var(--s4) 0 0;display:flex;flex-direction:column;gap:2px}
.satu-n{font:600 30px/1.1 var(--sans-d);
  letter-spacing:-.022em;color:var(--b900);font-variant-numeric:tabular-nums}
.satu-l{font-size:13px;color:var(--ink3);letter-spacing:.01em}

.quote{margin:0 0 var(--s5);padding-left:var(--s4);border-left:2px solid var(--b200);
  font-style:italic;color:var(--ink)}
.quote .by{display:block;margin-top:var(--s2);font-style:normal;color:var(--ink3);
  font-size:13px;letter-spacing:.012em}

.acts{display:grid;gap:var(--s2)}
@media (min-width:400px){.acts.two{grid-template-columns:1fr auto}}

.btn{
  -webkit-appearance:none;appearance:none;
  display:flex;align-items:center;justify-content:center;gap:var(--s2);
  width:100%;min-height:52px;padding:0 var(--s5);border:0;border-radius:var(--r-2);
  font:600 17px/1 var(--sans);letter-spacing:.004em;
  /* Ada .btn yang berupa <a> — yang menuju Google, misalnya. Tanpa ini ia
     satu-satunya tombol di aplikasi yang bergaris bawah. */
  text-decoration:none;
  background:var(--b700);color:var(--on-brand);cursor:pointer;
  transition:transform var(--t1) var(--ease),background var(--t1) var(--ease);
}
.btn:active,.btn.down{transform:scale(.975);background:var(--b900)}
.btn:active,.btn.down{background:var(--b500)}
.btn.ghost{background:transparent;color:var(--ink2);box-shadow:inset 0 0 0 1px var(--rule)}
.btn.ghost:active,.btn.ghost.down{background:var(--b100);color:var(--b900)}
.btn.quiet{min-height:46px;font-size:15px;background:transparent;color:var(--ink2);
  box-shadow:inset 0 0 0 1px var(--rule)}
.btn.quiet:active,.btn.quiet.down{background:var(--b100);color:var(--b900)}
.btn[disabled]{opacity:.45;pointer-events:none}
@media (hover:hover) and (pointer:fine){
  .btn:hover{background:var(--b900)}
  .btn.ghost:hover,.btn.quiet:hover{background:var(--b100);color:var(--b900)}
}

/* ==========================================================================
   People — a list you read DOWN, not across.
   Every row shares one Rp 400k–800k scale, so the target tick lands at the same
   x on all of them and forms a reference line. Bar position is her level; bar
   length is how far she has moved. Neither needs reading.
   ========================================================================== */
.people{margin:var(--s6) 0 0}
.list-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s3);
  margin:0 0 var(--s3);flex-wrap:wrap}
.list-head h2{margin:0;color:var(--ink2)}
.legend{display:inline-flex;align-items:center;gap:6px;color:var(--ink3)}
.legend i{display:inline-block}
.lg-bar{width:16px;height:5px;border-radius:var(--r-pill);background:var(--b700)}
.lg-tick{width:2px;height:11px;border-radius:1px;background:var(--ink3);margin-left:6px}

.who{
  display:block;width:100%;text-align:left;
  padding:var(--s4) 0 var(--s5);border:0;border-bottom:1px solid var(--rule2);
  background:none;color:inherit;font:inherit;cursor:pointer;
  transition:background var(--t1) var(--ease);
}
.who:last-child{border-bottom:0}
.who:active,.who.down{background:var(--rule2)}
.who-top{display:flex;align-items:baseline;gap:var(--s3)}
.who .nm{margin:0;font:600 16px/1.3 var(--sans);
  letter-spacing:-.012em;color:var(--b900);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who-num{margin-left:auto;display:flex;align-items:baseline;gap:8px;flex:none;
  font-variant-numeric:tabular-nums}
.who-num b{font:600 15px/1.2 inherit;color:var(--b900)}
.who-num span{font-size:13px;font-weight:600;color:var(--b700)}
.who-num span.stall{color:var(--down)}
.who .at{display:block;margin:2px 0 0;color:var(--ink3)}
.who .st{display:block;margin:var(--s3) 0 0;font-size:14px;line-height:1.4;color:var(--ink2)}
.who .st.want{color:var(--b700);font-weight:600}
.who .st.stall{color:var(--down);font-weight:600}

.rt-track{position:relative;height:5px;margin:var(--s3) 0 0;border-radius:var(--r-pill);background:var(--rule2)}
.rt-fill{position:absolute;top:0;bottom:0;border-radius:var(--r-pill);background:var(--b700)}
.rt-fill.back{background:var(--down)}
.rt-now{position:absolute;top:50%;width:9px;height:9px;border-radius:50%;
  background:var(--b700);transform:translate(-50%,-50%);box-shadow:0 0 0 2px var(--b050)}
.rt-now.back{background:var(--down)}
.rt-target{position:absolute;top:-4px;bottom:-4px;width:2px;border-radius:1px;
  background:var(--ink3);opacity:.55;transform:translateX(-50%)}

/* ==========================================================================
   Filter chips
   ========================================================================== */
.chips{display:flex;gap:var(--s2);flex-wrap:wrap;margin:var(--s3) 0 0}
.chip{
  -webkit-appearance:none;appearance:none;
  padding:7px 14px;border:0;border-radius:var(--r-pill);cursor:pointer;
  font:600 13px/1 var(--sans);letter-spacing:.012em;
  background:transparent;color:var(--ink2);box-shadow:inset 0 0 0 1px var(--rule);
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease);
}
.chip[aria-pressed=true]{background:var(--b700);color:var(--on-brand);box-shadow:none}
.chip:active,.chip.down{transform:scale(.96)}

/* ==========================================================================
   Import grid
   ========================================================================== */
.drop{
  margin:var(--s5) 0 0;padding:var(--s7) var(--s4);text-align:center;
  border:1.5px dashed var(--b200);border-radius:var(--r-3);background:var(--paper);
}
.drop p{margin:0 0 var(--s4);color:var(--ink2)}
.report{margin:var(--s5) 0 0}
.report h2{margin:0 0 var(--s3)}
.line{display:flex;gap:var(--s3);align-items:baseline;padding:var(--s3) 0;
  border-bottom:1px solid var(--rule2);font-size:15px}
.line:last-child{border-bottom:0}
.line b{margin-left:auto;font-variant-numeric:tabular-nums;font-weight:600}
.line.warn{color:var(--down)}
.line.warn b{color:var(--down)}
.swing{margin:var(--s5) 0 0;padding:var(--s4);border-radius:var(--r-2);background:var(--down-soft)}
.swing h3{margin:0 0 var(--s2);font:700 14px/1.3 var(--sans);color:var(--down)}
.swing p{margin:0;font-size:14px;color:var(--ink)}
.swing ul{margin:var(--s3) 0 0;padding-left:18px;font-size:14px}
.swing li{margin-bottom:4px;font-variant-numeric:tabular-nums}
/* Pilihan, bukan peringatan: calon yang bisa dijadikan kartu. Merah hanya
   untuk jumlah yang mencurigakan (berkas lengkap, bukan berkas peserta). */
.swing.tawar{background:color-mix(in srgb,var(--b100) 50%,var(--paper))}
.swing.tawar h3{color:var(--b900)}
.ganti-baris{margin-top:12px;padding-top:12px;border-top:1px solid var(--rule)}
.ganti-n{margin:0 0 6px}
.ganti-baris select.field{height:auto;min-height:44px;padding:10px 12px;font-size:16px}
.ganti-akibat{margin:6px 0 0;color:var(--ink2)}

/* ==========================================================================
   Room
   ========================================================================== */
.room-grid{display:grid;gap:var(--s6)}
@media (min-width:760px){.room-grid{grid-template-columns:1fr 1fr;gap:var(--s7)}}
.inroom{display:flex;gap:var(--s2);flex-wrap:wrap;margin:var(--s3) 0 0}
.ent .inroom{margin:var(--s3) 0 0}
.face{
  display:inline-flex;align-items:center;gap:7px;padding:5px 11px 5px 5px;border-radius:var(--r-pill);
  background:var(--b100);font-size:13px;letter-spacing:.012em;color:var(--b900);
}
.face i{
  width:22px;height:22px;border-radius:50%;background:var(--b700);color:var(--on-brand);
  display:grid;place-items:center;font:700 10px/1 var(--sans);font-style:normal;
}
/* Asked, not yet answered. Grey rather than blue, because the chip is stating
   an absence — and the clock says which kind of absence it is. */
.face.wait{background:var(--rule2);color:var(--ink2)}
.face.wait i{background:var(--ink3)}
.face.wait svg{margin-left:1px;color:var(--ink3)}
/* Answered no. Same grey, no clock: nothing is pending any more. */
.face.no{background:var(--rule2);color:var(--ink3)}
.face.no i{background:var(--ink3);opacity:.7}

.writing{
  margin:var(--s4) 0 0;padding:var(--s4);border-radius:var(--r-2);
  background:var(--b100);border-left:2px solid var(--b700);
}
.writing .cap{margin:0 0 var(--s2);color:var(--ink2)}
.writing p{margin:0;font-style:italic;font-size:16px;line-height:1.5;color:var(--ink)}
.caret{display:inline-block;width:2px;height:1.05em;vertical-align:-.15em;margin-left:2px;
  background:var(--b700);animation:blink 1.1s steps(1,end) infinite}
@keyframes blink{50%{opacity:0}}

.calm{margin:var(--s5) 0 0;padding:var(--s6) var(--s4);color:var(--ink2)}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:1ms!important;animation-iteration-count:1!important;
    transition-duration:1ms!important}
}

/* ==========================================================================
   Sheet — a material, not a panel. Depth is driven by the drag, continuously.
   ========================================================================== */
.sheet-root{position:fixed;inset:0;z-index:60;display:none}
.sheet-root.on{display:block}
.sheet-scrim{position:absolute;inset:0;background:rgba(8,26,38,.44);opacity:0;
  backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
.sheet-panel{
  /* Sheet kaca seperti iOS 26: terangkat 8px dari tepi kiri, kanan, dan bawah
     dengan keempat sudutnya bulat, bening dengan tepi cahaya — bukan papan
     yang menempel ke dasar layar. */
  position:absolute;left:8px;right:8px;bottom:8px;
  max-width:600px;margin:0 auto;
  padding:0 20px calc(20px + env(safe-area-inset-bottom,0px));
  background:color-mix(in srgb,var(--paper) 76%,transparent);
  backdrop-filter:blur(34px) saturate(190%) brightness(1.03);-webkit-backdrop-filter:blur(34px) saturate(190%) brightness(1.03);
  border-radius:32px;                /* lebih bulat dari --r-4: ia mengikuti sudut layar */
  box-shadow:inset 0 1px 0 var(--kaca-tepi),
    inset 0 0 0 .5px color-mix(in srgb,var(--kaca-tepi) 55%,transparent),
    0 18px 50px -12px rgba(8,26,38,.42);
  touch-action:none;                 /* this element owns every axis while dragging */
  will-change:transform;
}
@media (prefers-reduced-transparency:reduce){
  .sheet-panel{background:var(--paper);backdrop-filter:none;-webkit-backdrop-filter:none}
  .sheet-scrim{backdrop-filter:none;-webkit-backdrop-filter:none}
}
.sheet-grab{width:38px;height:4px;border-radius:var(--r-pill);background:var(--ink3);opacity:.45;
  margin:10px auto 14px;cursor:grab}
.sheet-body h2,.sheet-kepala h2{margin:0 0 6px;font:600 19px/1.25 var(--sans);
  letter-spacing:-.012em;color:var(--b900)}
/* Isi yang lebih tinggi dari layar menggulir di dalam panel; kepalanya
   (.sheet-kepala, diangkat keluar oleh sheet.js) tetap di tempat dan menjadi
   pegangan seret. Margin negatif memberi ruang cincin fokus di tepi. */
.sheet-panel{display:flex;flex-direction:column;
  max-height:calc(100dvh - env(safe-area-inset-top,0px) - 16px)}
.sheet-kepala{flex:none;padding-bottom:var(--s2)}
.sheet-body{min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;margin:0 -6px;padding:0 6px}
.sheet-body.gulir{touch-action:pan-y}
/* Hanya paragraf TANPA kelas — yaitu kalimat pembuka sebuah sheet. Dulu ini
   berlaku untuk setiap <p> anak langsung, dan sebagai pemilih anak ia
   mengalahkan kelas mana pun: tiap paragraf berkelas yang ditambahkan
   kemudian diam-diam kehilangan margin dan ukuran hurufnya sendiri. Itulah
   yang membuat jarak di panel ruang tidak beraturan — 0px di satu tempat, 18px
   di tempat lain, tanpa sebab yang terbaca di aturannya. */
.sheet-body > p:not([class]){margin:0 0 18px;font-size:14px;color:var(--ink2)}
#app{transform-origin:50% 0}
#app.lifting{will-change:transform}

/* Meet link — only ever rendered when a calendar event exists. */
.meet{display:flex;align-items:center;gap:10px;margin:14px 0 0;padding:12px 14px;
  border-radius:var(--r-2);background:var(--b100);color:var(--b700)}
.meet code{flex:1;min-width:0;font:600 14px/1 ui-monospace,SFMono-Regular,Menlo,monospace;
  letter-spacing:.01em;color:var(--b900);overflow-wrap:anywhere}
/* Copy sits on the end of the link it copies. A 44px target inside a 12px-tall
   row, so the row keeps its height and the tap area does not. */
.meet-c{
  -webkit-appearance:none;appearance:none;border:0;background:none;cursor:pointer;
  flex:none;display:grid;place-items:center;width:44px;height:44px;margin:-12px -10px -12px 0;
  border-radius:var(--r-2);color:var(--b700);
  transition:background var(--t1) var(--ease);
}
.meet-c:active{background:color-mix(in srgb,var(--b700) 14%,transparent)}
@media (hover:hover) and (pointer:fine){
  .meet-c:hover{background:color-mix(in srgb,var(--b700) 10%,transparent)}
}

/* ==========================================================================
   Catatan sesi — the coach's private record
   ========================================================================== */
.form{margin-top:var(--s5)}
.f{border:0;padding:0;margin:0 0 var(--s6)}
.f legend{padding:0;margin:0 0 var(--s3);color:var(--ink2)}
.f.req legend::after{content:"";display:inline-block;width:5px;height:5px;border-radius:50%;
  background:var(--b700);margin-left:7px;vertical-align:middle}
.hint{margin:var(--s2) 0 0;color:var(--ink3)}
.field{width:100%;min-height:104px;padding:13px 15px;border-radius:var(--r-2);
  border:1px solid var(--rule);background:var(--paper);color:var(--ink);
  font:400 17px/1.5 var(--sans);resize:vertical;
  transition:border-color var(--t1) var(--ease),box-shadow var(--t1) var(--ease)}
/* Satu baris kalau memang satu baris. .field dibuat untuk textarea — memakai
   tinggi itu pada <input> membuat kotak email setinggi kotak karangan, dan
   bentuk sebuah kolom adalah janji tentang berapa banyak yang diharapkan. */
input.field{min-height:52px;height:52px;resize:none}
/* .field dibuat untuk catatan (104px). Pilihan satu baris setinggi isian satu baris. */
select.field{min-height:52px;height:52px;padding-top:0;padding-bottom:0}
.field.short{min-height:76px}
/* Moved out of prototipe.html's "prototype chrome" <style>, where these app
   components had quietly accumulated and were overriding this file. */
.slot{display:grid;gap:var(--s2);margin-top:var(--s3)}
.slot button{text-align:left;padding:14px 16px;border-radius:var(--r-2);border:0;cursor:pointer;
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule);color:var(--ink);
  font:400 15px/1.4 var(--sans);transition:box-shadow var(--t1) var(--ease),background var(--t1) var(--ease)}
.slot button b{display:block;font:600 15px/1.3 var(--sans);color:var(--b900);margin-bottom:2px}
.slot button[aria-pressed=true]{background:var(--b100);box-shadow:inset 0 0 0 2px var(--b700)}
.slot button:active{transform:scale(.99)}
.thumb{width:56px;height:56px;border-radius:var(--r-2);flex:none;background:var(--b100);
  box-shadow:inset 0 0 0 1px var(--rule);display:grid;place-items:center;color:var(--b700)}
.proof{display:flex;align-items:center;gap:14px;margin-top:14px}
.proof p{margin:0;font-size:14px;color:var(--ink2)}
.proof strong{display:block;font-weight:600;color:var(--ink)}
.field::placeholder{color:var(--ink3)}
.field:focus{outline:0;border-color:var(--b700);box-shadow:0 0 0 3px var(--b100)}

.seg{display:grid;grid-template-columns:1fr 1fr;gap:var(--s2)}
.segb{-webkit-appearance:none;appearance:none;min-height:48px;border:0;border-radius:var(--r-2);
  cursor:pointer;background:transparent;color:var(--ink2);
  box-shadow:inset 0 0 0 1px var(--rule);font:600 15px/1 inherit;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease)}
.segb[aria-pressed=true]{background:var(--b700);color:var(--on-brand);box-shadow:none}
.segb:active,.segb.down{transform:scale(.98)}

/* ==========================================================================
   Progress at a glance.
   The change is the second-largest thing on the screen, not a footnote under
   four numbers of equal weight. The track measures the distance she has
   TRAVELLED — thick and blue — with what remains as a hairline and the target
   as a tick. It never reads as a fraction of a target she may not reach.
   ========================================================================== */
.now-row{display:flex;align-items:flex-start;gap:var(--s4);flex-wrap:wrap;margin-top:var(--s2)}
.now-row .fig{margin:0}
.delta{margin:6px 0 0;display:flex;flex-direction:column;gap:1px}
.delta .d-big{
  font:700 19px/1.1 var(--sans);
  letter-spacing:-.015em;font-variant-numeric:tabular-nums;color:var(--ink3);
}
.delta .d-sub{font-size:12px;letter-spacing:.012em;color:var(--ink3)}
.delta.up .d-big{color:var(--b700)}
.delta.up .d-sub{color:var(--b700)}
.delta.dn .d-big,.delta.dn .d-sub{color:var(--down)}

/* The target is a fact, stated once, quiet. It replaces `.journey` — a filled
   track with a dot, which is the progress bar this product bans: measured all
   in, most seats sit below target for all twelve weeks, so the bar reported a
   fraction of a person every week to someone who was genuinely improving.
   Movement lives in `.delta` above; this line only says where the line is. */
.tgt{margin:var(--s4) 0 0;color:var(--ink3);font-variant-numeric:tabular-nums}

.mini{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4);margin-top:var(--s6);
  padding-top:var(--s4);border-top:1px solid var(--rule2)}
.m{display:flex;flex-direction:column;gap:3px;min-width:0}
.m-l{color:var(--ink2)}
.m-v{font:700 22px/1.1 var(--sans);
  letter-spacing:-.018em;font-variant-numeric:tabular-nums;color:var(--b900)}
.m-d{font-size:12px;letter-spacing:.012em;color:var(--ink3);font-variant-numeric:tabular-nums}
.m-d.up{color:var(--b700);font-weight:600}

/* Screen stack. The ghost is the other screen in the transition — behind on a
   push, on top while it leaves on a pop. */
.nav-layer{position:fixed;inset:0;z-index:15;overflow:hidden;background:var(--b050);
  pointer-events:none;contain:layout paint}
.nav-layer.over{z-index:35;box-shadow:-14px 0 40px -10px rgba(8,26,38,.22)}
.nav-layer-in{position:absolute;inset:0;overflow:hidden;will-change:transform}
.nav-layer::after{content:"";position:absolute;inset:0;background:#0A1A24;
  opacity:calc(var(--dim,0) * .10);pointer-events:none}

/* The dentist's own card speaks first, and quieter.
   Her figure is a fact among facts, not a verdict looming over the screen. */
.lede{
  margin:var(--s5) 0 0;
  font:400 17px/1.45 var(--sans);
  letter-spacing:-.004em;color:var(--ink);text-wrap:pretty;
}
.lede b{font-weight:600;color:var(--b900)}
.t-fig-soft{
  font:600 28px/1.1 var(--sans);
  letter-spacing:-.018em;font-variant-numeric:tabular-nums;color:var(--b900);
}
.t-fig-soft + .delta .d-big{font-size:16px}
.reading:has(.t-fig-soft){margin-top:var(--s5);padding-top:var(--s5);border-top:1px solid var(--rule2)}

/* ==========================================================================
   Grouped list. The section header IS the summary — a label and a count — so
   nothing needs a card above the list restating what the list already says.
   Headers stick under the nav, the way a grouped table view does.
   ========================================================================== */
.sec{
  position:sticky;top:calc(var(--safe-t) + 50px);z-index:10;
  display:flex;align-items:baseline;gap:var(--s2);
  margin:var(--s5) calc(var(--gut) * -1) 0;
  padding:var(--s2) var(--gut) var(--s2);
  font:600 13px/1.4 var(--sans);
  letter-spacing:.006em;color:var(--ink2);
  background:color-mix(in srgb,var(--b050) 88%,transparent);
  backdrop-filter:blur(14px) saturate(160%);
  -webkit-backdrop-filter:blur(14px) saturate(160%);
}
@media (prefers-reduced-transparency:reduce){ .sec{background:var(--b050);backdrop-filter:none} }
.sec-n{
  margin-left:auto;min-width:21px;height:21px;padding:0 6px;border-radius:var(--r-pill);
  display:inline-flex;align-items:center;justify-content:center;
  font:600 12px/1 inherit;font-variant-numeric:tabular-nums;
  background:var(--rule);color:var(--ink2);
}
.sec-n.want{background:var(--b700);color:var(--on-brand)}

/* A request is a decision, not a summary — so it keeps a surface, but a tight
   one, and it disappears the moment it is answered. */
/* `req` does double duty: this padded box, and a modifier on .f marking a
   required answer. Without the :not() the box wrapped the fieldset and its top
   edge struck straight through the legend. */
.req:not(.f){margin:var(--s4) 0 0;padding:var(--s4);border-radius:var(--r-2);
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--b200)}
.req-t{margin:0 0 var(--s3);font-size:15px;line-height:1.45;color:var(--ink)}

/* A secondary action is a way out, not a rival to the primary one. */
.btn.plain{
  min-height:40px;margin-top:var(--s2);
  background:transparent;color:var(--ink2);box-shadow:none;font-weight:600;font-size:15px;
}
.btn.plain:active,.btn.plain.down{background:var(--rule2);color:var(--ink)}

/* The photo control is a <label> wrapping a hidden file input, so one tap opens
   the camera with no picker in between. It has to behave as a button: same box,
   same press, and the input itself never visible. */
label.btn{display:flex;align-items:center;justify-content:center;gap:8px}
label.btn input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
label.btn:has(input:disabled),label.btn.is-busy{opacity:.6;pointer-events:none}
label.btn:focus-within{outline:2px solid var(--b700);outline-offset:3px}
.thumb img{width:56px;height:56px;border-radius:var(--r-2);object-fit:cover;display:block}
@media (hover:hover) and (pointer:fine){ .btn.plain:hover{background:var(--rule2);color:var(--ink)} }

/* ==========================================================================
   Beranda — the queue. Short by design; empty on a good day, and it says so.
   ========================================================================== */
.sec.flat{position:static;background:none;backdrop-filter:none;-webkit-backdrop-filter:none;
  margin:var(--s6) 0 var(--s2);padding:0}
.queue{display:grid;gap:var(--s2)}
.q-item{
  display:flex;align-items:center;gap:var(--s3);width:100%;text-align:left;
  padding:var(--s4);border:0;border-radius:var(--r-2);cursor:pointer;
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule);
  color:inherit;font:inherit;
  transition:background var(--t1) var(--ease),transform var(--t1) var(--ease);
}
.q-item:active,.q-item.down{transform:scale(.99);background:var(--b100)}
.q-item.decide{display:block;box-shadow:inset 0 0 0 1px var(--b200);cursor:default}
.q-item.decide:active{transform:none;background:var(--paper)}
.q-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.q-t{font:600 16px/1.35 var(--sans);
  letter-spacing:-.01em;color:var(--b900)}
.q-t.warn{color:var(--down)}
.q-s{font-size:14px;line-height:1.4;color:var(--ink2)}
.q-item.decide .q-t{margin:0}
.q-item.decide .q-s{margin:0 0 var(--s4)}
.q-item .chev{color:var(--ink3);flex:none}
.q-empty{padding:var(--s6) var(--s4);text-align:center;color:var(--ink2)}
.q-empty p{margin:0}

.rowlink{
  display:flex;align-items:center;gap:var(--s3);width:100%;
  margin-top:var(--s6);padding:var(--s4) 0;border:0;border-top:1px solid var(--rule);
  background:none;color:var(--b700);cursor:pointer;
  font:600 16px/1.3 var(--sans);
  letter-spacing:-.008em;
}
.rowlink-n{margin-left:auto;color:var(--ink3);font-weight:400;font-variant-numeric:tabular-nums}
.rowlink .chev{color:var(--ink3)}
.rowlink:active,.rowlink.down{opacity:.6}

/* ---- search ------------------------------------------------------------- */
.search{
  display:flex;align-items:center;gap:8px;margin:var(--s4) 0 0;
  min-height:44px;padding:0 12px;border-radius:var(--r-2);background:var(--rule2);color:var(--ink3);
}
.search input{
  flex:1;min-width:0;align-self:stretch;border:0;background:none;outline:0;color:var(--ink);
  font:400 17px/1.3 inherit;
}
.search input::placeholder{color:var(--ink3)}
.search input::-webkit-search-cancel-button{display:none}
.search-x{border:0;background:none;color:var(--ink2);font-size:20px;line-height:1;
  cursor:pointer;min-width:36px;min-height:36px;padding:0;margin-right:-8px;border-radius:50%}
.search-x:active{background:var(--rule2)}
.no-hit{margin:var(--s5) 0 0;text-align:center;color:var(--ink3)}

/* ==========================================================================
   Calendar. A month reads as a month — days the coach cannot make stay in
   place, dimmed, rather than being removed and leaving a grid full of holes.
   ========================================================================== */
.cal{margin:var(--s5) 0 0}
.cal-head{display:flex;align-items:center;gap:var(--s2);margin:0 0 var(--s3)}
.cal-title{flex:1;text-align:center;
  font:600 17px/1.2 var(--sans);
  letter-spacing:-.012em;color:var(--b900)}
.cal-nav{-webkit-appearance:none;appearance:none;border:0;background:none;cursor:pointer;
  color:var(--b700);padding:8px 10px;border-radius:var(--r-2);line-height:0}
.cal-nav:active{background:var(--rule2)}
.cal-wd,.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.cal-wd span{text-align:center;padding:0 0 6px;
  font:600 11px/1 var(--sans);letter-spacing:.03em;color:var(--ink3)}
.cal-d,.cal-x{
  aspect-ratio:1;display:grid;place-items:center;border:0;border-radius:50%;
  background:none;color:var(--ink);cursor:pointer;
  font:400 15px/1 var(--sans);
  font-variant-numeric:tabular-nums;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease);
}
.cal-x{visibility:hidden}
.cal-d.off{color:var(--ink3);opacity:.38;cursor:default}
.cal-d.today{font-weight:700;color:var(--b700)}
.cal-d:not(.off):active,.cal-d.down{background:var(--b100)}
.cal-d.on{background:var(--b700);color:var(--on-brand);font-weight:600}
.cal-d.on.today{color:var(--on-brand)}
@media (hover:hover) and (pointer:fine){ .cal-d:not(.off):not(.on):hover{background:var(--b100)} }

.times{margin:var(--s5) 0 0;display:grid;gap:var(--s2)}
.times-h{margin:0 0 var(--s1);color:var(--ink2)}
.times-hint{margin:var(--s5) 0 0;text-align:center;color:var(--ink3)}
/* Siapa yang jam sibuknya belum terbaca. Kecil dan tenang: ini keterangan
   tentang jam yang ditawarkan, bukan galat — jadwalnya tetap bisa dipilih. */
.sibuk-catat{margin:var(--s2) 0 0;font-size:13px;line-height:1.45;color:var(--ink2)}
.sibuk-catat a{color:var(--b700);font-weight:600}
.time{
  display:flex;align-items:baseline;gap:var(--s3);width:100%;text-align:left;
  padding:13px 16px;border:0;border-radius:var(--r-2);cursor:pointer;
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule);color:inherit;font:inherit;
  transition:box-shadow var(--t1) var(--ease),background var(--t1) var(--ease);
}
.time b{font:600 16px/1.2 inherit;color:var(--b900);font-variant-numeric:tabular-nums}
.time span{margin-left:auto;font-size:13px;color:var(--ink3)}
.time:active,.time.down{background:var(--b100);box-shadow:inset 0 0 0 2px var(--b700)}

/* The three soonest mutual times, offered before the grid. These are the
   answer; the month below is the fallback. So they carry the blue — this is
   what wants you — and the grid that follows is quiet. */
.soon{display:grid;gap:var(--s2);margin:var(--s4) 0 0}
.soon-b{
  display:flex;align-items:baseline;gap:var(--s3);width:100%;min-height:52px;text-align:left;
  padding:11px 16px;border:0;border-radius:var(--r-2);cursor:pointer;
  background:var(--b100);box-shadow:inset 0 0 0 1px var(--b200);color:inherit;font:inherit;
  transition:box-shadow var(--t1) var(--ease),background var(--t1) var(--ease);
}
.soon-b b{font:600 16px/1.2 inherit;color:var(--b900);letter-spacing:-.008em}
.soon-b span{margin-left:auto;font:600 16px/1.2 inherit;color:var(--b700);
  font-variant-numeric:tabular-nums}
.soon-b:active,.soon-b.down{box-shadow:inset 0 0 0 2px var(--b700)}
.soon-alt{margin:var(--s5) 0 var(--s2);color:var(--ink3)}

/* ==========================================================================
   Jump-to-session. Appears only when there IS a session and it is below the
   fold; bobs three times, then rests.
   ========================================================================== */
.jump{
  position:fixed;left:50%;z-index:40;
  bottom:calc(20px + env(safe-area-inset-bottom,0px));
  display:inline-flex;align-items:center;gap:7px;
  min-height:44px;padding:0 16px 0 18px;border:0;border-radius:var(--r-pill);cursor:pointer;
  background:var(--b700);color:var(--on-brand);
  font:600 14px/1 var(--sans);
  letter-spacing:-.006em;
  box-shadow:0 8px 22px -10px rgba(10,22,30,.4);
  opacity:0;transform:translate(-50%,10px) scale(.92);pointer-events:none;
  transition:opacity var(--t2) var(--ease),transform var(--t2) var(--ease);
}
.jump.on{opacity:1;transform:translate(-50%,0) scale(1);pointer-events:auto}
.jump:active{transform:translate(-50%,0) scale(.95)}
.jump.on.bob{animation:bob 1.1s var(--ease) 3}
@keyframes bob{
  0%,100%{transform:translate(-50%,0) scale(1)}
  38%{transform:translate(-50%,5px) scale(1)}
}
@media (prefers-reduced-motion:reduce){ .jump.on.bob{animation:none} }

/* Owed. Red because nothing else in this product is — a debt is the one state
   that should not read as part of the furniture. The pill turns to paper so the
   dot is the loudest thing on it. */
.jump-dot{display:none}
.jump.owed .jump-dot{display:block;width:7px;height:7px;border-radius:50%;
  background:var(--down);flex:none}
.jump.owed{background:var(--paper);color:var(--b900);
  box-shadow:0 0 0 1px var(--rule), 0 10px 26px -10px rgba(10,22,30,.4)}

/* The arrow, and the bob, follow whichever way the target actually lies. */
.jump.up .jump-a{transform:rotate(180deg)}
.jump.on.bob.up{animation:bobUp 1.1s var(--ease) 3}
@keyframes bobUp{
  0%,100%{transform:translate(-50%,0) scale(1)}
  38%{transform:translate(-50%,-5px) scale(1)}
}

/* Rungs already in the session — accountability accumulates, so they stack. */

/* Notes from everyone in the case, stacked under the session they belong to. */
.notes{margin:var(--s3) 0 0;display:grid;gap:var(--s2);
  padding-left:var(--s3);border-left:2px solid var(--rule)}
.note{margin:0;font-size:14px;line-height:1.5;color:var(--ink2)}
.note-by{display:block;font-size:12px;letter-spacing:.012em;color:var(--ink3);margin-bottom:2px}

/* ==========================================================================
   BPE's overview — one measure, repeated at one width.
   The programme's own bar, then the same bar per area, all left-aligned to the
   same edge so the red tails line up and can be compared by eye.
   ========================================================================== */
.whole{margin:var(--s5) 0 0}
.bar{display:flex;gap:2px;height:7px;border-radius:var(--r-pill);overflow:hidden;background:var(--rule2)}
.bar.wide{height:11px}
.bseg{display:block;min-width:2px}
.bseg.ok{background:var(--b700)}
.bseg.mid{background:var(--b200)}
.bseg.warn{background:var(--down)}

.keys{display:flex;flex-wrap:wrap;gap:var(--s3);margin:var(--s3) 0 0;color:var(--ink3)}
.k{display:inline-flex;align-items:center;gap:6px}
.k::before{content:"";width:8px;height:8px;border-radius:2px;background:var(--rule)}
.k.ok::before{background:var(--b700)}
.k.mid::before{background:var(--b200)}
.k.warn::before{background:var(--down)}
.k.warn{color:var(--down);font-weight:600}

.alist{margin:var(--s6) 0 0}
.arow{
  display:grid;grid-template-columns:96px 1fr auto;align-items:center;gap:var(--s4);
  width:100%;text-align:left;padding:var(--s4) 0;
  border:0;border-bottom:1px solid var(--rule2);background:none;color:inherit;font:inherit;
  cursor:pointer;transition:background var(--t1) var(--ease);
}
.arow:last-child{border-bottom:0}
.arow:active,.arow.down{background:var(--rule2)}
.arow-n{font:600 14px/1.29 var(--sans);
  letter-spacing:-.008em;color:var(--b900);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.arow-c{display:flex;align-items:center;gap:7px;font-size:13px;color:var(--ink3);
  font-variant-numeric:tabular-nums}
.arow-c b{min-width:19px;height:19px;padding:0 5px;border-radius:var(--r-pill);display:inline-flex;
  align-items:center;justify-content:center;background:var(--down);color:var(--on-state);font:600 11px/1 inherit}

/* Say what a list contains. "Perlu perhatian" is ambiguous on its own — it
   could mean everyone in scope or only the reader's own mentees. */
.scope{margin:var(--s4) 0 0;padding:11px 13px;border-radius:var(--r-2);
  background:var(--rule2);color:var(--ink2);line-height:1.45}
.scope strong{color:var(--ink);font-weight:600}

/* ==========================================================================
   Inset grouped list — the actual iOS list.
   One rounded group on the ground; rows inside it; hairlines inset from the
   left so they start under the text, never under the whole row; a chevron on
   anything that navigates. Separation comes from the group and the spacing,
   not from an outline around every item.
   ========================================================================== */
.group{
  margin:var(--s4) 0 0;border-radius:var(--r-3);background:var(--paper);overflow:hidden;
  box-shadow:inset 0 0 0 1px var(--rule);
}
.group + .group{margin-top:var(--s5)}
.glabel{
  margin:var(--s6) 0 var(--s2) var(--s1);
  font:600 13px/1.3 var(--sans);
  letter-spacing:.006em;color:var(--ink3);
}
.glabel + .group{margin-top:0}

.row{
  display:flex;align-items:center;gap:var(--s4);width:100%;
  min-height:58px;padding:11px var(--s4);
  border:0;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer;
  position:relative;transition:background var(--t1) var(--ease);
}
.row + .row::before{
  content:"";position:absolute;left:var(--s4);right:0;top:0;height:1px;background:var(--rule2);
}
.row:active,.row.down{background:var(--rule2)}
.row-b{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.row-t{font:400 17px/1.3 var(--sans);
  letter-spacing:-.012em;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row-t.want{font-weight:600;color:var(--b900)}
.row-s{font-size:13px;line-height:1.35;color:var(--ink3)}
.row-s.warn{color:var(--down);font-weight:600}
.row-s.want{color:var(--b700);font-weight:600}
.row-v{margin-left:auto;display:flex;align-items:center;gap:8px;flex:none;
  font-size:15px;color:var(--ink3);font-variant-numeric:tabular-nums}
.row-v .chev{color:var(--ink3);opacity:.5}
.row-w{min-width:22px;height:22px;padding:0 7px;border-radius:var(--r-pill);display:inline-flex;
  align-items:center;justify-content:center;background:var(--down);color:var(--on-state);
  font:600 12px/1 inherit}

.state-s{margin:var(--s2) 0 0;font-size:14px;line-height:1.43;letter-spacing:-.016em;color:var(--ink2)}
.mini-bar{display:flex;gap:2px;height:6px;border-radius:var(--r-pill);overflow:hidden;
  background:var(--rule2);margin:var(--s4) 0 0;width:100%}

/* Stocks-form row: name and clinic on the left, the current figure and its
   change stacked on the right. Green up, red down — the sign carries the same
   meaning, so nothing depends on colour alone. No chevron: the pill already
   holds the right edge and the whole row is the target. */
.srow{align-items:center;gap:var(--s3);padding:11px var(--s4)}
.srow-b{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
/* Was nowrap + ellipsis, which clipped 3 of the DPA's 6 rows and took the
   "Pelatih" badge with them. A person's name is the one thing on the row that
   must arrive whole; two lines cost less than a truncated name. */
.srow-n{font:600 17px/1.25 var(--sans);
  letter-spacing:-.014em;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  text-wrap:pretty}
.srow-s{font-size:13px;line-height:1.3;color:var(--ink3);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.srow-s.warn{color:var(--down);font-weight:600}
.srow-s.want{color:var(--b700);font-weight:600}

.srow-r{display:flex;flex-direction:column;align-items:flex-end;gap:3px;flex:none}
/* Movement as words, in the tone of its direction — the same sentence shape the
   dentist's own card uses, so coach and dentist read one fact one way. */
.srow-d{font:600 13px/1.2 var(--sans);letter-spacing:-.006em;
  font-variant-numeric:tabular-nums;color:var(--ink3)}
.srow-d.up{color:var(--up)}
.srow-d.dn{color:var(--down)}
.srow-v{font:600 17px/1.2 var(--sans);
  letter-spacing:-.01em;font-variant-numeric:tabular-nums;color:var(--ink)}
.pill{
  min-width:52px;padding:3px 7px;border-radius:var(--r-1);text-align:center;
  background:var(--up);color:var(--on-state);
  font:600 12px/1.25 var(--sans);
  letter-spacing:.002em;font-variant-numeric:tabular-nums;
}
.pill.dn{background:var(--down)}
.pill.flat{background:var(--rule);color:var(--ink2)}
.gn{margin-left:7px;color:var(--ink3);font-weight:400;font-variant-numeric:tabular-nums}
.state-s .legend{display:none}

/* Removing someone from a case is the destructive direction — red, and as a
   text button rather than a filled one, so it never outweighs the primary
   action sitting above it. */
.btn.plain.danger{color:var(--down)}
.btn.plain.danger:active,.btn.plain.danger.down{background:var(--down-soft);color:var(--down)}
@media (hover:hover) and (pointer:fine){
  .btn.plain.danger:hover{background:var(--down-soft);color:var(--down)}
}

/* What this card is to the person reading it: mine to coach, one I joined, or
   one I only watch. The third gets no tag — absence is the quiet state. */
.tag{
  display:inline-block;margin-left:7px;padding:2px 6px;border-radius:var(--r-1);
  font:600 11px/1.35 var(--sans);
  letter-spacing:.02em;vertical-align:1.5px;
}
.tag.mine{background:var(--b700);color:var(--on-brand)}
.tag.join{background:var(--b100);color:var(--b700)}
/* Who runs the card, for a reader above the coach. Quiet: it is context, not a
   claim on the reader. */
/* `.tag.read` is gone with the bare role badge it styled. */
.tag.none{background:none;color:var(--down);box-shadow:inset 0 0 0 1px currentColor}
/* Program selesai (032/033): tenang dan pasti, bukan peringatan. */
.tag.done{background:var(--up-soft);color:var(--up)}
.selesai-tanda{margin:0 0 6px;font:600 13px/1.3 var(--sans);color:var(--up);display:flex;align-items:center;gap:6px}
.tag-pasang{display:inline-flex;flex-wrap:wrap;gap:4px;margin-left:7px;vertical-align:1.5px}
.tag-pasang .tag{margin-left:0;vertical-align:0}
.srow-t{display:flex;flex-wrap:wrap;gap:4px;margin:3px 0 2px}
.srow-t .tag,.srow-t .tag-pasang{margin-left:0;vertical-align:0}
/* Booked. The same dot in both places, so "scheduled" reads as one idea across
   the area list and the rows inside it. */
.tag-d{display:inline-block;width:5px;height:5px;margin-right:5px;border-radius:50%;
  background:currentColor;vertical-align:1.5px}

.dot-due{display:inline-block;width:7px;height:7px;margin-right:2px;border-radius:50%;
  background:var(--down)}
.dot-jadwal{display:inline-block;width:6px;height:6px;margin-right:8px;border-radius:50%;
  background:var(--b700);vertical-align:1px}

/* ==========================================================================
   Measure cards — category, sentence, rule, evidence.
   The sentence is the headline. The chart is the content, not decoration, so
   it is drawn large and labels its own values.
   ========================================================================== */
.ucards{display:grid;gap:var(--s3);margin:var(--s5) 0 0}

.ucard{
  display:block;width:100%;text-align:left;padding:var(--s4);
  border:0;border-radius:var(--r-3);cursor:pointer;background:var(--paper);color:inherit;font:inherit;
  /* Depth by surface ladder and a hairline, not by a drop shadow. A card is
     --paper standing on --b050; the step in lightness IS the lift. This also
     means light and dark behave identically instead of one having shadows
     the other has to switch off. */
  box-shadow:inset 0 0 0 1px var(--rule);
  transition:transform var(--t1) var(--ease),background var(--t1) var(--ease);
}
.ucard:active,.ucard.down{transform:scale(.988);background:var(--b100)}

/* Each category carries its own colour, as Health does. */
.ucard-h{display:flex;align-items:center;gap:7px;color:var(--b700)}
.g-perf{color:var(--c-perf)} .g-resv{color:var(--c-resv)} .g-trmt{color:var(--c-trmt)}
/* The group heading is gone — the hue and the icon on each card already say
   which category it belongs to. The runs still follow group order, so they need
   a gap between them, not a label. */
.ucards + .ucards{margin-top:var(--s3)}

/* The debt, stated on the card that leads to where it is paid. */
/* Named `hutang`, not `owed` — `.jump.owed` is the shortcut's own state class
   and the two were colliding in every querySelector. */
/* Her two places, floating off the bottom edge rather than welded to it — the
   page runs under it, which is what says the bar belongs to the app and not to
   the page. One material, one ring, and the live tab on its own raised pill. */
.tabbar{
  position:fixed;left:50%;transform:translateX(-50%);z-index:45;
  /* Seperti bilah tab iOS: ~21pt dari tepi bawah di iPhone dengan garis home
     (safe area 34 - 12 = 22), 14px di layar tanpa garis home. Dulu safe area
     dihitung DUA KALI (--chrome sudah memuatnya, lalu + --safe-b), jadi
     bilahnya melayang ~80pt di atas tepi. --tab-dasar ditimpa prototipe untuk
     duduk di atas bilah peran. */
  bottom:var(--tab-dasar);
  /* EQUAL columns, each as wide as the widest label. A grid of `1fr` columns in
     a bar sized to its content does exactly that: "Area" gets the same width as
     "Catatan sesi" without the bar stretching to the full gutter. `flex:1` in a
     fixed-width bar was the other way round — equal thirds NARROWER than the
     words, so the longest tab was clipped by its own pill. */
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
  gap:2px;padding:5px;border-radius:var(--r-pill);
  width:max-content;max-width:calc(100vw - var(--gut) * 2);
  /* Kaca seperti bilah tab iOS 26 (Wang, 3 Oktober 2026: "glass visual like
     health"). Tiga hal yang membuat kaca terbaca sebagai kaca, bukan sekadar
     buram: (1) cukup bening untuk meneruskan warna di baliknya — 52%, dulu 86%
     yang terbaca pekat; (2) buram dan saturasi kuat supaya tulisan di belakang
     menjadi warna, bukan huruf; (3) tepi cahaya — garis terang di sisi atas
     dan kilau lembut dari atas (::before). Bayangan luar tetap, supaya bilah
     melayang di atas isi. */
  --kaca-isi:color-mix(in srgb,var(--b050) 52%,transparent);
  background:var(--kaca-isi);
  backdrop-filter:blur(26px) saturate(200%) brightness(1.04);
  -webkit-backdrop-filter:blur(26px) saturate(200%) brightness(1.04);
  box-shadow:
    inset 0 1px 0 var(--kaca-tepi),
    inset 0 0 0 .5px color-mix(in srgb,var(--kaca-tepi) 55%,transparent),
    inset 0 -1px 0 rgba(255,255,255,.12),
    0 0 0 .5px rgba(10,22,30,.08),
    0 12px 32px -12px rgba(10,22,30,.38);
  isolation:isolate;
}
.tabbar::before{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:-1;
  background:linear-gradient(180deg,var(--kaca-kilau) 0%,transparent 55%);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .tabbar{--kaca-isi:color-mix(in srgb,var(--b050) 58%,transparent)}
}
:root[data-theme="dark"] .tabbar{--kaca-isi:color-mix(in srgb,var(--b050) 58%,transparent)}
@media (prefers-reduced-transparency:reduce){
  .tabbar{background:var(--b050);backdrop-filter:none;-webkit-backdrop-filter:none}
  .tabbar::before{display:none}
}
.tabb{
  -webkit-appearance:none;appearance:none;border:0;background:none;cursor:pointer;
  /* Proporsi bilah tab iOS: ikon ~24pt, label 10pt — ikonnya yang dibaca,
     labelnya pendamping. Dulu ikon 20 dan label 12, bilahnya lebar dan tinggi. */
  /* +15% dari ukuran iOS (Wang, 3 Oktober 2026): 55/16/28/11.5 dari 48/14/24/10. */
  min-height:55px;padding:5px 16px;border-radius:var(--r-pill);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  color:var(--ink3);font:600 11.5px/1.15 var(--sans);letter-spacing:.01em;
  white-space:nowrap;overflow:hidden;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease);
}
.tabb{grid-row:1;position:relative;z-index:1}
.tabb.on{color:var(--b700)}
/* Isian tab yang terpilih sebagai benda sendiri, di bawah tombolnya. Saat tab
   berganti ia meluncur dengan pegas (actions.js, geserTab) dan sedikit meregang
   searah geraknya — bilah tab iOS 26. Diam di sel tab yang aktif. */
.tab-lensa{grid-row:1;align-self:stretch;justify-self:stretch;z-index:0;pointer-events:none;
  border-radius:var(--r-pill);will-change:transform;
  /* Lensa kaca di atas kaca: lebih terang dari bilahnya, dengan tepi cahaya
     sendiri — seperti tab terpilih di iOS 26, bukan pil putih pekat. */
  background:color-mix(in srgb,var(--paper) 62%,transparent);
  box-shadow:inset 0 1px 0 var(--kaca-tepi),inset 0 0 0 .5px color-mix(in srgb,var(--kaca-tepi) 60%,transparent),
    0 2px 8px -3px rgba(10,22,30,.22)}
@media (prefers-reduced-transparency:reduce){ .tab-lensa{background:var(--paper)} }
/* The debt mark, on the icon rather than beside the label: it has to read at a
   glance from the corner of the eye, and a number set into the word makes the
   word longer instead of making the tab louder. Red, like the upload's mark and
   the shortcut's — one colour for "this is owed". */
.tabi{position:relative;display:grid;place-items:center}
.tabi svg{width:28px;height:28px}
.tabi .tabn{left:14px}            /* lencana ikut ikon yang membesar */
.tabn{
  position:absolute;top:-5px;left:11px;
  min-width:17px;height:17px;padding:0 4px;border-radius:var(--r-pill);
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--down);color:var(--on-state);
  font:700 10px/1 var(--sans);font-variant-numeric:tabular-nums;
  box-shadow:0 0 0 2px var(--b050);
}
.tabb.on .tabn{box-shadow:0 0 0 2px var(--paper)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.tabb:active{transform:scale(.97)}
@media (hover:hover) and (pointer:fine){
  .tabb:not(.on):hover{color:var(--ink2)}
}
/* The page ends above the bar, never behind it. */
/* Semua diukur dari bilah tab: dasarnya (--tab-dasar) + tingginya (--tab-h). */
.wrap.has-tabs{padding-bottom:calc(var(--tab-dasar) + var(--tab-h) + 20px)}
/* And so does everything else pinned to that edge. */
:root.tabs .jump{bottom:calc(var(--tab-dasar) + var(--tab-h) + 12px)}
/* Kabar singkat di bawah layar — dipakai aplikasi dan Daftar orang. */
.toast{position:fixed;left:50%;bottom:92px;transform:translateX(-50%) translateY(8px);z-index:90;
  background:var(--toast-bg);color:var(--toast-ink);padding:12px 18px;border-radius:10px;font-size:14px;
  box-shadow:0 10px 30px -8px rgba(0,0,0,.35);opacity:0;pointer-events:none;
  transition:opacity 160ms var(--ease),transform 160ms var(--ease);max-width:86vw}
/* Masuk 240ms dengan perlambatan, keluar 160ms: kabar tiba dengan yakin dan
   pergi tanpa menahan mata. */
.toast.on{opacity:1;transform:translateX(-50%) translateY(0);pointer-events:auto;
  transition:opacity 240ms var(--ease-out),transform 240ms var(--ease-out)}
@media (prefers-reduced-motion:reduce){ .toast,.toast.on{transform:translateX(-50%)} }
.toast-undo{-webkit-appearance:none;appearance:none;margin-left:14px;min-height:32px;
  padding:0 10px;border:0;border-radius:var(--r-1);cursor:pointer;
  background:color-mix(in srgb,var(--toast-ink) 16%,transparent);
  color:inherit;font:600 14px/1 inherit}
.toast-undo:active{background:color-mix(in srgb,var(--toast-ink) 28%,transparent)}
:root.tabs .toast{bottom:calc(var(--tab-dasar) + var(--tab-h) + 12px)}
:root.tabs .tulis-dock{bottom:calc(var(--tab-dasar) + var(--tab-h) + 6px)}
/* BPE's search-and-upload dock rides above the bar too. */
:root.tabs .dock{bottom:calc(var(--tab-dasar) + var(--tab-h) + 6px)}
:root.tabs .wrap.has-dock{padding-bottom:calc(var(--tab-dasar) + var(--tab-h) + 100px + var(--kb,0px))}
/* With the keyboard up the bar is behind it; the writing sheet takes the edge. */
:root.kb-up .tabbar{display:none}
:root.tabs.kb-up .tulis-dock{bottom:0}

/* The card's heading, with its own actions at the end of it. */
.want-h{display:flex;align-items:flex-start;gap:var(--s3)}
.want-h h2{flex:1;min-width:0}
.titik{
  -webkit-appearance:none;appearance:none;border:0;cursor:pointer;flex:none;
  width:36px;height:36px;margin:-6px -6px 0 0;border-radius:50%;
  display:grid;place-items:center;background:none;color:var(--ink3);
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease);
}
.titik:active{background:color-mix(in srgb,var(--ink) 10%,transparent)}
@media (hover:hover) and (pointer:fine){
  .titik:hover{background:color-mix(in srgb,var(--ink) 7%,transparent);color:var(--ink2)}
}

/* The menu behind them: full-width rows, one job each. */
.menu{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s2);margin-top:var(--s5)}   /* minmax: keterangan panjang dipotong, tidak mendorong barisnya keluar layar */
/* Keluar berdiri sendiri, dijauhkan dari yang di atasnya. */
.menu.menu-akhir{margin-top:var(--s7)}
.menu-i{
  -webkit-appearance:none;appearance:none;border:0;cursor:pointer;width:100%;
  min-height:52px;padding:0 var(--s4);border-radius:var(--r-2);text-align:left;
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule);
  color:var(--b900);font:600 17px/1.3 var(--sans);letter-spacing:-.012em;
  transition:background var(--t1) var(--ease);
}
.menu-i:active{background:var(--b100)}
.menu-i.danger{color:var(--down)}
@media (hover:hover) and (pointer:fine){ .menu-i:hover{background:var(--b100)} }
/* Sebagian baris menu adalah <a>. .menu-i dibuat untuk <button>, jadi ia tidak
   membawa aturan tinggi maupun perataan yang dibutuhkan sebuah tautan. */
a.menu-i{display:flex;align-items:center;text-decoration:none}

/* Judul kecil di atas sekelompok baris. Bukan <h3>: ia memberi nama sebuah
   kelompok, bukan membuka bagian baru dari sebuah dokumen. */
/* Judul kelompok: ruang lebih banyak di ATAS daripada di bawah, supaya ia
   terbaca sebagai milik yang di bawahnya — aturan yang sama dipakai seluruh
   panel ini. */
.menu-judul{margin:var(--s6) 0 var(--s3);font:600 13px/1.38 var(--sans);
  letter-spacing:.02em;text-transform:uppercase;color:var(--ink3)}

/* Tiga pilihan yang saling meniadakan, jadi satu baris tersegmen — bukan tiga
   baris menu, yang akan terbaca sebagai tiga tindakan berbeda. */
.tema{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;margin-top:0;
  padding:4px;border-radius:var(--r-2);background:var(--rule2)}
.tema-b{
  -webkit-appearance:none;appearance:none;border:0;cursor:pointer;
  min-height:44px;border-radius:calc(var(--r-2) - 4px);
  background:none;color:var(--ink2);
  font:600 15px/1 var(--sans);letter-spacing:-.012em;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease);
}
.tema-b[aria-pressed="true"]{color:var(--b900)}
@media (hover:hover) and (pointer:fine){
  .tema-b:not([aria-pressed="true"]):hover{color:var(--ink)}
}

/* Nama dan versi, sekecil yang masih terbaca. Ia ada untuk satu saat: ketika
   sesuatu salah dan seseorang perlu menyebutkan versi mana yang ia lihat. */
/* Panduan (panduan.js): strip cara pakai yang bisa ditutup, tombol ? untuk
   membukanya lagi, dan tur per peran. */
.panduan{display:grid;grid-template-columns:auto 1fr auto;align-items:start;gap:10px;margin:0 0 16px;
  padding:12px 14px;border-radius:var(--r-2);background:var(--b100);color:var(--b900)}
.panduan svg{margin-top:3px;color:var(--b700)}
.panduan p{margin:0;font-size:14px;line-height:1.5;color:var(--ink)}
.panduan-tutup{-webkit-appearance:none;appearance:none;border:0;background:none;cursor:pointer;border-radius:8px;
  font:600 13px/1.2 var(--sans);color:var(--b700);padding:0 8px;min-height:36px;margin:-8px -8px -8px 0}
.panduan-tutup:active{background:color-mix(in srgb,var(--b700) 12%,transparent)}
.panduan-buka{-webkit-appearance:none;appearance:none;display:inline-flex;align-items:center;gap:4px;
  margin:0 0 12px;min-height:32px;padding:0 12px;border:0;border-radius:var(--r-pill);cursor:pointer;
  background:color-mix(in srgb,var(--ink) 7%,transparent);color:var(--ink2);font:600 13px/1 var(--sans)}
/* Strip yang baru ditutup atau dibuka lagi: datang pelan di tempatnya. */
.panduan.panduan-baru,.panduan-buka.panduan-baru{animation:panduan-muncul 200ms var(--ease-out) both}
@keyframes panduan-muncul{from{opacity:0;transform:translateY(-3px)}}
@media (prefers-reduced-motion:reduce){ .panduan.panduan-baru,.panduan-buka.panduan-baru{animation:panduan-pudar 120ms linear both} }
@keyframes panduan-pudar{from{opacity:0}}
.panduan-tutup:focus-visible,.panduan-buka:focus-visible{outline:2px solid var(--b700);outline-offset:2px}
/* Tur sorot: layar diredupkan oleh bayangan raksasa di sekeliling lubang,
   jadi tombol yang dijelaskan tetap terang dan pada tempatnya sendiri. */
.jelajah{position:fixed;inset:0;z-index:90}
.jelajah.penuh{background:rgba(8,26,38,.62);display:grid;place-items:center;padding:16px}
.jelajah-lubang{position:fixed;border-radius:14px;box-shadow:0 0 0 9999px rgba(8,26,38,.62),0 0 0 2px var(--b500, #96CAED);
  pointer-events:none}
.jelajah-gelembung{position:fixed;z-index:1;width:min(340px,calc(100vw - 32px));padding:16px 16px 14px;border-radius:16px;
  background:var(--paper);color:var(--ink);box-shadow:0 18px 50px -12px rgba(8,26,38,.5),inset 0 0 0 1px var(--rule)}
.jelajah.penuh .jelajah-gelembung{position:relative;left:auto;top:auto;width:min(380px,100%)}
.jelajah-gelembung[data-arah]::before{content:"";position:absolute;left:calc(var(--panah,50%) - 8px);width:16px;height:16px;
  background:var(--paper);transform:rotate(45deg);box-shadow:inset 1px 1px 0 var(--rule)}
.jelajah-gelembung[data-arah="atas"]::before{top:-8px}
.jelajah-gelembung[data-arah="bawah"]::before{bottom:-8px;box-shadow:inset -1px -1px 0 var(--rule)}
.jelajah-kepala{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:-4px 0 2px}
.jelajah-ke{margin:0;font:600 12px/1.3 var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--ink3)}
.jelajah-gelembung h2{margin:0 0 6px;font:600 19px/1.25 var(--sans);letter-spacing:-.012em;color:var(--b900)}
.jelajah-gelembung p{margin:0;font-size:15px;line-height:1.55;color:var(--ink2)}
.jelajah-aksi{display:flex;align-items:center;gap:8px;margin-top:14px}
.jelajah-aksi .btn{width:auto;min-height:42px;padding:0 14px;white-space:nowrap}
.jelajah-sela{flex:1}
.jelajah-lewati{border:0;background:none;cursor:pointer;min-height:40px;padding:0 8px;margin:-6px -8px -6px 0;border-radius:8px;
  font:600 14px/1.2 var(--sans);color:var(--ink3)}
.jelajah-lewati:active{background:var(--rule2)}
.jelajah-lewati:focus-visible{outline:2px solid var(--b700);outline-offset:2px}
/* GERAKNYA (7 Oktober 2026). Satu momen yang dirancang: lubang sorot meluncur
   ke tombol berikutnya, lalu gelembungnya datang dari arah panahnya — sesudah
   lubangnya hampir tiba, bukan bersamaan, supaya mata mengikuti satu benda
   dulu baru membaca.
     .pindah  antar-langkah: gelembung pudar keluar cepat; lubang TETAP terlihat
              dan meluncur.
     .layar   tur sedang membuka tab atau masuk ke ruang: lubangnya ikut
              disembunyikan sampai layar barunya diam, layar tetap redup.
     .keluar  tur ditutup: semuanya pudar, lebih cepat dari masuknya. */
.jelajah{animation:jelajah-masuk 240ms var(--ease-out) backwards}
.jelajah.jelajah-keluar{opacity:0;pointer-events:none;transition:opacity 160ms var(--ease)}
@keyframes jelajah-masuk{from{opacity:0}}
.jelajah-gelembung{--masuk:translateY(6px);
  transition:opacity 220ms var(--ease-out) 90ms,transform 280ms var(--ease-out) 90ms}
.jelajah-gelembung[data-arah="atas"]{--masuk:translateY(-6px)}
.jelajah.penuh .jelajah-gelembung{--masuk:scale(.96)}
.jelajah.pindah .jelajah-gelembung{opacity:0;transform:var(--masuk);transition:opacity 110ms var(--ease),transform 0s 110ms}
.jelajah.layar{background:rgba(8,26,38,.62)}
.jelajah.layar .jelajah-lubang{opacity:0}
.jelajah-lubang{transition:opacity 160ms var(--ease)}
/* Daftar bagian (dibuka lagi dari menu akun): satu baris per menu. */
.jelajah-bab{display:grid;gap:8px;margin-top:14px}
.jelajah-bab button{display:grid;gap:2px;text-align:left;padding:12px 14px;border:0;border-radius:12px;cursor:pointer;
  background:var(--b050, #F4F8F3);box-shadow:inset 0 0 0 1px var(--rule);color:var(--ink);font:inherit}
.jelajah-bab button:hover{box-shadow:inset 0 0 0 1px var(--b500, #5ABA4F)}
.jelajah-bab button:focus-visible{outline:2px solid var(--b700);outline-offset:2px}
.jelajah-bab button[data-bab="semua"]{background:var(--b100);box-shadow:none}
.jelajah-bab-n{font:600 16px/1.3 var(--sans);color:var(--b900)}
.jelajah-bab-s{font-size:14px;line-height:1.45;color:var(--ink2)}
.jelajah-tindak{margin-top:14px;padding:12px 14px;border-radius:12px;background:var(--b100, #CDE8C0)}
.jelajah-tindak p{color:var(--ink)}
.jelajah-tindak .btn{width:auto;min-height:40px;margin-top:10px;padding:0 14px}
.jelajah.penuh .jelajah-gelembung{max-height:calc(100dvh - 32px);overflow:auto}
@media (prefers-reduced-motion:no-preference){
  .jelajah-lubang{transition:left 320ms var(--ease-out),top 320ms var(--ease-out),width 320ms var(--ease-out),
    height 320ms var(--ease-out),opacity 160ms var(--ease)}
}
/* Gerak dikurangi: tanpa luncur dan tanpa geser — hanya pudar, lebih singkat. */
@media (prefers-reduced-motion:reduce){
  .jelajah{animation-duration:120ms}
  .jelajah-gelembung,.jelajah.penuh .jelajah-gelembung,.jelajah-gelembung[data-arah="atas"]{--masuk:none;
    transition:opacity 120ms var(--ease)}
}
/* Prompt rating platform VCS (platform.js): lima angka sejajar, sama besar. */
.rating-baris{display:grid;grid-template-columns:repeat(5,1fr);gap:var(--s2);margin-top:var(--s3)}
.rating-n{min-width:0;width:100%;padding:0;min-height:52px;font:600 19px/1 var(--sans);font-variant-numeric:tabular-nums}
.menu-kaki{margin:var(--s5) 0 0;padding-top:var(--s4);
  box-shadow:inset 0 1px 0 var(--rule);
  font:400 12px/1.45 var(--sans);color:var(--ink3);text-align:center}
.menu-kaki a{color:var(--b700)}
.menu-kaki span{display:block;margin-top:2px}

/* Baris menu yang membawa keadaannya. Nama di atas, keadaan di bawahnya dalam
   warna yang lebih pelan — sama seperti baris dokter di daftar mana pun. */
.menu-k{display:flex;align-items:center;gap:var(--s3);padding-top:11px;padding-bottom:11px}
.menu-t{flex:1;min-width:0}
.menu-s{display:block;font:400 13px/1.38 var(--sans);color:var(--ink3);
  font-weight:400;letter-spacing:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.menu-k .tabn{position:static;flex:none;box-shadow:none}

/* Ikon baris menu: petak 36px, satu bahasa garis dengan ikon bilah. Dipakai
   menu akun dan menu ⋯ Daftar orang, jadi tinggal di sini, bukan di halaman. */
.menu-ik{flex:none;width:36px;height:36px;border-radius:10px;display:grid;place-items:center;
  background:var(--b100);color:var(--b700)}
.menu-ik svg{width:14px;height:14px}   /* sama dengan ikon .nav-b (Wang, 2 Oktober) */
.menu-i.danger .menu-ik{background:color-mix(in srgb,var(--down) 14%,transparent);color:var(--down)}
.menu-panah{flex:none;color:var(--ink3);margin-right:-4px}

/* Kelompok baris (Refero: lembar akun iOS). Satu wadah, garis pemisah yang
   mulai sejajar teks — bukan dari tepi — seperti daftar bawaan iOS. */
.menu.grup{gap:0;border-radius:var(--r-2);background:var(--paper);
  box-shadow:inset 0 0 0 1px var(--rule);overflow:hidden}
.menu.grup .menu-i{background:none;box-shadow:none;border-radius:0;position:relative}
.menu.grup .menu-i + .menu-i::before{content:"";position:absolute;top:0;right:0;
  left:calc(var(--s4) + 36px + var(--s3));height:1px;background:var(--rule)}
.menu.grup .menu-i:active{background:var(--b100)}
@media (hover:hover) and (pointer:fine){ .menu.grup .menu-i:hover{background:var(--b100)} }
.menu.grup.menu-akhir{margin-top:var(--s6)}

/* Kepala lembar akun: inisial, nama, peran dan email. */
.akun-kepala{display:flex;align-items:center;gap:var(--s3)}
.akun-ava{flex:none;width:48px;height:48px;border-radius:50%;display:grid;place-items:center;
  background:var(--b100);color:var(--b900);font:600 17px/1 var(--sans);letter-spacing:.01em}
.akun-nama{min-width:0}
.akun-nama h2{margin:0}
.akun-nama p{margin:2px 0 0;font:400 15px/1.35 var(--sans);color:var(--ink3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Tombol Google: permukaan NETRAL dengan lambang berwarna. Bukan biru —
   pedoman merek Google meminta begitu, dan lagi pula keutamaan di sini datang
   dari urutan dan dari yang lain disembunyikan, bukan dari warna. Dipindahkan
   ke sini dari <style> halaman masuk, karena sekarang dua tempat memakainya
   dan dua salinan akan berbeda suatu hari. */
.btn.google{background:var(--paper);color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--rule);gap:var(--s3)}
.btn.google:active,.btn.google.down{background:var(--rule2)}
@media (hover:hover) and (pointer:fine){ .btn.google:hover{background:var(--rule2)} }
.g-mark{flex:none;width:18px;height:18px}

/* Kembali, di pojok kiri atas panel — sebuah layar yang didorong punya jalan
   pulang di tempat yang sama dengan seluruh aplikasi, bukan sebuah baris di
   dasar daftar yang harus digulung dulu. */
.sheet-atas{margin:0 0 var(--s3)}
.sheet-atas .nav-b{background:color-mix(in srgb,var(--ink) 7%,transparent);color:var(--b700)}
@media (hover:hover) and (pointer:fine){
  .sheet-atas .nav-b:hover{background:color-mix(in srgb,var(--ink) 12%,transparent)}
}

.menu-bantu{margin:var(--s2) 0 0;font:400 13px/1.45 var(--sans);color:var(--ink3)}
.menu-bantu b{color:var(--ink2);font-weight:600}

/* Panel ruang. */
/* Nama sendiri, bukan `.titik` — nama itu sudah dipakai sebuah TOMBOL di file
   ini, dan memakainya lagi membuat titik status mewarisi gaya tombol. */
.ruang-titik{display:inline-block;width:8px;height:8px;border-radius:50%;
  margin-right:var(--s2);vertical-align:middle}
.ruang-titik.hidup{background:var(--up)} .ruang-titik.mati{background:var(--ink3)}

/* IRAMA PANEL. Satu tangga, bukan margin yang ditempel satu per satu di tiap
   elemen — itulah yang membuat jaraknya dulu tidak sebanding: beberapa bagian
   berjarak 24px, yang lain 12px, tanpa sebab selain urutan penulisan.

   Sekarang: antar-blok 24px (--s5), di dalam blok 12px (--s3), dan sebuah
   judul selalu punya ruang lebih di ATASnya daripada di bawahnya. */
/* Kabar hasil sebuah tindakan: berhasil atau gagalnya penyambungan kalender,
   tersimpan atau tidaknya tautan ruang. Sampai sekarang kelas ini tidak punya
   satu pun aturan di sini — warnanya cuma ada di dalam <style> halaman masuk,
   jadi di dalam sheet ia tampil sebagai teks biasa: kalimat terpenting di
   seluruh alur ini, yang justru tidak pernah terlihat sebagai kabar. */
.pesan{margin:var(--s5) 0 0;font:600 15px/1.45 var(--sans);color:var(--ink2)}
/* Kosong: tidak memakan ruang, tapi TETAP digambar. `display:none` pada
   sebuah aria-live membuat sebagian pembaca layar tidak mengumumkan isinya
   ketika ia muncul, dan kabar yang tidak terbaca sama saja dengan tidak ada. */
.pesan:empty{margin:0}
.pesan.bagus{color:var(--up)}
.pesan.buruk{color:var(--down)}

.ruang-ok{margin:var(--s5) 0 0;font:400 15px/1.47 var(--sans);color:var(--ink2)}
/* Keterangan MILIK tombol di atasnya, jadi ia menempel rapat — 8px, bukan 24.
   Jarak yang sama di kedua sisi akan membuatnya terbaca sebagai blok lepas. */
.ruang-ok b{color:var(--ink);font-weight:600;word-break:break-all}
.ruang-apa{margin:var(--s3) 0 0;padding-left:1.15em;display:grid;gap:var(--s2)}
.ruang-apa li{font:400 15px/1.47 var(--sans);color:var(--ink2)}
.ruang-blok{margin:var(--s5) 0 0}

/* Ruang Meet sendiri: tersembunyi sampai diminta, tapi ringkasannya berbentuk
   tombol — ia tindakan, bukan catatan kaki. */
.ruang-buat{margin:var(--s5) 0 0}
/* Ringkasan yang bisa dibuka DAN ditutup: nama di kiri, keadaan dan anak panah
   di kanan. Bawaan .btn menengahkan isinya, yang benar untuk sebuah tombol dan
   salah untuk sebuah gagang — gagang perlu sisi tetap supaya anak panahnya ada
   di tempat yang sama, terbuka maupun tertutup. */
.ruang-buat > summary{justify-content:space-between;gap:var(--s3);padding:0 var(--s4)}
.ruang-sum-t{text-align:left}
.ruang-sum-k{flex:none;display:inline-flex;align-items:center;gap:var(--s3)}
.ruang-sum-k .chevd{color:var(--ink3);transition:transform var(--t2) var(--ease)}
.ruang-buat[open] > summary .chevd{transform:rotate(180deg)}
@media (prefers-reduced-motion:reduce){.ruang-sum-k .chevd{transition:none}}

/* Mana yang benar-benar dipakai. Keduanya bisa terisi sekaligus, dan yang
   terisi belum tentu yang berlaku. */
.ruang-tag{flex:none;padding:3px 9px;border-radius:var(--r-pill);
  font:600 12px/1.35 var(--sans);letter-spacing:.002em;white-space:nowrap}
/* Yang berlaku memakai isian penuh, sama seperti .pill di atas — tata bahasa
   yang sudah dipakai aplikasi ini untuk "keadaan baik, dinyatakan sebagai
   keping kecil", lengkap dengan warna teks yang ikut berubah di mode gelap.
   --up di atas --up-soft terukur 4,47:1: di bawah ambang AA untuk 12px, dan
   sebuah penanda yang tidak terbaca tidak menandai apa pun.
   Yang menganggur sengaja tetap redup: bedanya bukan cuma kata-katanya. */
.ruang-tag.pakai{background:var(--up);color:var(--on-state)}
.ruang-tag.diam{background:var(--rule2);color:var(--ink3)}

/* Nama sebuah pilihan, dengan keadaannya di ujung kanan. */
.ruang-opsi{display:flex;align-items:center;gap:var(--s2);margin:0 0 var(--s3);
  font:400 15px/1.4 var(--sans);color:var(--ink2)}
.ruang-opsi b{color:var(--ink);font-weight:600}
.ruang-opsi .ruang-tag{margin-left:auto}
.ruang-buat > summary{list-style:none;cursor:pointer}
.ruang-buat > summary::-webkit-details-marker{display:none}
.ruang-isi{margin:var(--s4) 0 0;padding:var(--s4);border-radius:var(--r-3);
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule)}
.ruang-isi > :first-child{margin-top:0}
.ruang-langkah{margin:0;padding-left:1.25em;display:grid;gap:var(--s2)}
.ruang-langkah li{font:400 15px/1.47 var(--sans);color:var(--ink2)}
.ruang-isi form{margin:var(--s4) 0 0}
.ruang-isi .field{width:100%;caret-color:var(--b700)}
.ruang-isi .field:focus{border-color:var(--b500);outline:none;
  box-shadow:0 0 0 3px var(--b100)}
.ruang-isi .acts{margin:var(--s3) 0 0}
.ruang-isi .pesan{margin:var(--s3) 0 0}

/* Avatar: mengisi slot kanan bilah yang memang sudah ada — 36px, sama dengan
   tombol kembali di kiri, jadi judulnya tetap tepat di tengah. */
/* position:relative supaya lencana berangka bisa menempel padanya — .tabn
   diposisikan absolut terhadap induknya. */
.nav-b.avatar{background:var(--b100);color:var(--b900);position:relative;width:40px;height:40px}   /* 40pt, ukuran avatar Health */
/* Lencana DI DALAM kotak avatar, bukan menggantung keluar 3px. Di bilah
   ringkas avatar hanya 12px dari tepi atas bilah, dan tepi atas bilah adalah
   tepi atas layar: menggantung ke atas memberi cincinnya 7px sisa, yang di
   layar sungguhan terbaca seperti terpotong. Di dalam kotak ia tidak pernah
   bergantung pada tinggi bilah atau pada seberapa dalam takik ponselnya. */
.nav-b.avatar .tabn{top:0;left:auto;right:0}
.nav-b.avatar i{font:600 13px/1 var(--sans);letter-spacing:.01em;font-style:normal}
@media (hover:hover) and (pointer:fine){
  .nav-b.avatar:hover{background:var(--b200)}
}

/* Attendance already in: a state, not a task, so it reads as quiet confirmation
   under the session it belongs to. */
/* Search, wherever it sits: the top of a kartu, or BPE's dock. */
/* Above the title, so it reads as a way past this person rather than part of
   their heading. */
.head + .cari,
.nav + .cari,
.nav + .no-hit ~ .cari{margin-top:var(--s3)}
.cari{display:flex;align-items:center;gap:var(--s2);margin:var(--s3) 0 var(--s2);
  padding:0 var(--s3);border-radius:var(--r-2);background:var(--paper);
  box-shadow:inset 0 0 0 1px var(--rule);color:var(--ink3)}
.cari svg{flex:none}
.cari input{flex:1;min-width:0;min-height:44px;border:0;background:none;outline:0;
  color:var(--ink);font:400 17px/1 var(--sans)}
.cari input::placeholder{color:var(--ink3)}
/* Satu tombol hapus saja: × bawaan peramban disembunyikan, yang dipakai milik
   kita (.search-x) — dua tanda silang berdampingan (Wang, 7 Oktober 2026). */
.cari input::-webkit-search-cancel-button,.cari input::-webkit-search-decoration{-webkit-appearance:none;appearance:none;display:none}
.cari:focus-within{box-shadow:inset 0 0 0 2px var(--b700)}
/* One round button, so no scrim: the full-width gradient behind the dock is
   there to separate a search field from the list running under it, and with a
   single button in the corner it washed out the rows either side of it while
   separating nothing. */
.dock-one{justify-content:flex-end}
.dock-one::before{display:none}

/* Two sub-pages under one tab. A segmented control, not a second tab bar: it
   scrolls with the page because it belongs to the list, and the bar at the
   bottom belongs to the app. */
.subbar{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:2px;
  margin:var(--s4) 0 var(--s2);padding:3px;border-radius:var(--r-pill);
  background:var(--rule2)}
.subb{-webkit-appearance:none;appearance:none;border:0;background:none;cursor:pointer;
  min-height:38px;padding:0 var(--s3);border-radius:var(--r-pill);
  display:flex;align-items:center;justify-content:center;gap:7px;
  color:var(--ink2);font:600 14px/1 var(--sans);letter-spacing:-.006em;white-space:nowrap;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease)}
.subb.on{color:var(--b900)}
.subb:active{transform:scale(.98)}
/* The same red as the tab's mark and the upload's: it is the same debt, and a
   reader should not have to learn that grey here means red there. */
.subn{min-width:19px;height:19px;padding:0 5px;border-radius:var(--r-pill);
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--down);color:var(--on-state);
  font:700 11px/1 var(--sans);font-variant-numeric:tabular-nums}

/* Unfolded in place, inset so it reads as belonging to the row above it. */
.kl-isi{background:var(--b050);box-shadow:inset 0 0 0 1px var(--rule)}
/* Indented, but not by a whole step: the inset background and hairline already
   say this list is nested, and --s6 took enough width off the sub-line that
   "Sesi 7 · Sen 14 Sep · 09.00" wrapped to a second line inside a clinic while
   fitting everywhere else. */
.kl-isi .srow{padding-left:calc(var(--s4) + 10px)}
.krow-c{display:inline-flex;transition:transform var(--t2) var(--ease)}
.krow-c.on{transform:rotate(90deg)}
@media (prefers-reduced-motion:reduce){ .krow-c{transition:none} }

/* A clinic row: the place on the line, the people in it underneath. Two lines
   of different weight, so the names read as belonging to the clinic rather than
   as a second heading. */
.krow{display:flex;align-items:center;gap:var(--s3);width:100%;
  padding:var(--s4);border:0;background:none;cursor:pointer;text-align:left}
.krow .row-b{flex:1;min-width:0}
.krow .row-t{font:600 17px/1.3 var(--sans);letter-spacing:-.012em;color:var(--b900)}
.krow .row-s{font-size:14px;line-height:1.4;color:var(--ink2)}
.krow .row-v{flex:none;display:flex;align-items:center;gap:var(--s2);color:var(--ink3)}

.sudah{display:flex;align-items:center;gap:7px;margin:var(--s3) 0 0;
  font-size:14px;line-height:1.45;color:var(--ink2)}
.sudah svg{flex:none}

/* Langkah bukti kehadiran: satu tombol yang berdiri langsung di badan sheet,
   sesudah kalimat yang memintanya. Anak LANGSUNG, bukan keturunan — sebagai
   keturunan ia juga mengenai tombol di dalam .acts dan di dalam formulir, yang
   sudah mengatur jaraknya sendiri, dan 16px itu menumpuk di atas jarak yang
   sudah benar tanpa ada yang menuliskannya. */
.sheet-body > .btn{margin-top:var(--s4)}
.ucard-h svg{flex:none}
.ucard-t{flex:1;font:700 17px/1.3 var(--sans);
  letter-spacing:-.008em}
.ucard-h .chev{opacity:.55;flex:none}
/* Health puts the time at the right of the category line; here it is which week
   the reading belongs to, which is the same job — it dates the number. */
.ucard-when{color:var(--ink3);flex:none}

/* The reading. Health's Activity card is three figures on one line divided by
   a rule; ours is the measure and, where one exists, the figure that gives it
   context. The number is ink, never the category colour — colour lives in the
   header and says which category this is, not whether the number is good. */
.ucard-read{display:flex;align-items:flex-end;gap:var(--s4);margin-top:var(--s5);
  min-height:76px}
.ucard-n{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.ucard-n b{font:700 26px/1.15 var(--sans-d);letter-spacing:-.018em;color:var(--b900)}
.ucard-n i{font-style:normal;font-size:14px;color:var(--ink2)}
.ucard-n i.up{color:var(--up)} .ucard-n i.dn{color:var(--down)}
.ucard-side{display:flex;flex-direction:column;gap:2px;justify-content:flex-end;
  padding-left:var(--s4);border-left:1px solid var(--rule2);flex:none;max-width:44%}
.ucard-side b{font:600 20px/1.2 var(--sans);letter-spacing:-.014em;color:var(--ink)}
.ucard-side i{font-style:normal;font-size:13px;color:var(--ink3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* The comparison, when one exists. Health's Highlights carry the chart inside
   the card because the comparison IS the content; a card with a single reading
   has nothing to compare and shows none. */
/* The chart takes the right of the body, aligned to the value's baseline area
   rather than banded off beneath it. No rule between them: they are one row. */
.ucard-plot{display:flex;align-items:center;flex:1.1;min-width:0;align-self:stretch}
.ucard-plot .spark{margin:0;width:100%;height:auto}
/* The count's card chart. Same grammar as the detail's bars — zero baseline,
   newest bar at full strength, the rest receding — at the sparkline's height so
   the two kinds of card sit at the same weight beside each other. */
.sparkb{display:flex;align-items:flex-end;gap:3px;width:100%;height:56px;
  color:inherit}
.sparkb-b{flex:1;min-width:0;background:currentColor;opacity:.45;
  border-radius:3px 3px 0 0}
.sparkb-b.on{opacity:1}

.ucard-say{
  display:block;margin:var(--s3) 0 var(--s4);
  font:400 17px/1.4 var(--sans);
  letter-spacing:-.012em;color:var(--ink);text-wrap:pretty;
}
.ucard-body{display:block;padding-top:var(--s4);border-top:1px solid var(--rule2)}

.plotc{display:block;color:var(--b700);overflow:visible}
.plotc.up{color:var(--up)} .plotc.dn{color:var(--down)} .plotc.flat{color:var(--b500)}
.plotl{font:600 12px/1 var(--sans);fill:var(--ink3);
  letter-spacing:.012em}
.plotl.on{fill:currentColor}

.bars{display:grid;gap:var(--s3)}
.brow{display:block}
.bval{margin:0 0 5px;font:700 22px/1 var(--sans-d);
  letter-spacing:-.018em;font-variant-numeric:tabular-nums;color:var(--ink)}
.bval span{margin-left:7px;font:400 13px/1 inherit;letter-spacing:.008em;color:var(--ink3)}
.bbar{height:26px;border-radius:var(--r-1);background:var(--rule);display:flex;align-items:center;
  justify-content:flex-end;padding:0 9px;min-width:44px}
.bbar.up{background:var(--up)}
.bbar span{font:600 12px/1 var(--sans);color:var(--on-state);
  font-variant-numeric:tabular-nums}
.bbar:not(.up) span{color:var(--ink2)}

.detail{margin:var(--s5) 0 0;padding:var(--s5) var(--s4);border-radius:var(--r-3);background:var(--paper);
  box-shadow:inset 0 0 0 1px var(--rule)}

.state-d{margin-left:10px;font:600 17px/1 inherit;letter-spacing:0;color:var(--ink3)}
.state-d.up{color:var(--up)} .state-d.dn{color:var(--down)}
.plotc-c{fill:var(--ink3);font-weight:400}

/* The record as a card, matching the measures around it. */
.ucard.as-card{padding:var(--s4) var(--s4) var(--s2);cursor:default}
.ucard.as-card:active{transform:none;background:var(--paper)}
/* Was padding:0, so the row rendered 24px tall — a full-width control a finger
   misses. The card already carries the horizontal padding, so the height is
   bought vertically and pulled back out with a negative margin: the row grows
   to 44px without the card growing with it. */
.ucard-h.lg{
  display:flex;align-items:center;gap:7px;width:100%;min-height:44px;
  padding:0;margin:-10px 0;border:0;background:none;
  color:var(--b700);font:inherit;cursor:pointer;text-align:left;
}
.ucard-h.lg[disabled]{cursor:default}
.lg-n{font:400 15px/1 inherit;color:var(--ink3);font-variant-numeric:tabular-nums}
.ucard.as-card .ucard-body{margin-top:var(--s3)}
.ucard.as-card .ledger-track{padding-left:var(--s7)}
.ucard.as-card .ent:last-child{padding-bottom:var(--s3)}

/* Rows with a leading icon, Folders-style. The hairline starts after the icon
   so the icons form one clean edge down the group. */
.irow{gap:var(--s3);padding-left:var(--s4)}
.irow + .irow::before{left:56px}
.irow-i{flex:none;display:grid;place-items:center;width:22px;height:22px;color:var(--b700)}
.irow-i.warn{color:var(--down)}
.irow-n{font-variant-numeric:tabular-nums;color:var(--ink3)}

/* The dock: search where the thumb is, with the one action beside it.
   Fixed, not sticky — sticky only sticks once there is something to scroll, so
   on a short page it just sat wherever it happened to fall in the flow. */
.dock{
  position:fixed;left:0;right:0;bottom:0;z-index:35;
  /* Lifted clear of the keyboard. Transform, not `bottom`, so it composites. */
  transform:translateY(calc(-1 * var(--kb,0px)));
  transition:transform .22s cubic-bezier(.32,.72,0,1);
  display:flex;align-items:center;gap:var(--s3);
  max-width:600px;margin:0 auto;
  padding:10px var(--gut) calc(10px + env(safe-area-inset-bottom,0px));
}
.dock::before{
  content:"";position:absolute;inset:-18px 0 0;z-index:-1;pointer-events:none;
  background:linear-gradient(transparent,var(--b050) 28%);
}
@media (prefers-reduced-motion:reduce){ .dock{transition:none} }
/* Under the keyboard the home indicator is hidden, so its inset is dead space. */
:root.kb-up .dock{padding-bottom:10px}

/* The dock floats over the page, so the page has to end above it — otherwise
   the last row of a list sits under the search field and cannot be reached.
   The keyboard's height joins the same sum when it is up. */
.wrap.has-dock{padding-bottom:calc(76px + var(--kb,0px) + env(safe-area-inset-bottom,0px))}

.dock .search{flex:1;margin:0;background:color-mix(in srgb,var(--rule2) 94%,transparent);
  backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}
@media (prefers-reduced-transparency:reduce){ .dock .search{background:var(--rule2);backdrop-filter:none} }
.dock-b{
  flex:none;width:44px;height:44px;border:0;border-radius:50%;cursor:pointer;
  display:grid;place-items:center;background:var(--b700);color:var(--on-brand);position:relative;
  box-shadow:0 4px 12px -6px rgba(10,22,30,.3);
  transition:transform var(--t1) var(--ease);
}
.dock-b:active,.dock-b.down{transform:scale(.93)}
/* A dot, not a number — the dock says "there is something", the screen says what. */
.dock-b.due::after{
  content:"";position:absolute;top:1px;right:1px;width:11px;height:11px;border-radius:50%;
  background:var(--down);box-shadow:0 0 0 2.5px var(--b050);
}

/* ==========================================================================
   Footer. The gallery has no theme-control pattern to borrow — only two of its
   seventy-four entries mention one, and Binance's says outright that toggle
   behaviour is "product behavior, not extracted from the marketing surfaces".
   What the gallery IS consistent about is footers: Linear runs caption type in
   ink-subtle; Apple lets the footer be the one dense place on the page, at
   fine-print size and heavily muted.

   So the theme lives here as quiet text, not as a control. It is not part of
   the coaching work and should not look like it is — no track, no pill, no
   segmented switch competing with the buttons that actually do something.
   ========================================================================== */
.kaki{
  margin:var(--s7) 0 0;padding-top:var(--s4);
  border-top:1px solid var(--rule2);
  display:flex;flex-wrap:wrap;gap:var(--s2) var(--s4);align-items:baseline;
  color:var(--ink3);
}
.kaki-n{margin:0;flex:1;min-width:0}
.kaki-t{margin:0;display:flex;align-items:baseline;gap:var(--s2)}
.kaki-t > span{color:var(--ink3)}
/* Quiet text that still clears 44px: the box is bought with padding and given
   back with margin, so the footer does not inflate around it. */
.kaki-b{
  -webkit-appearance:none;appearance:none;border:0;background:none;cursor:pointer;
  font:inherit;color:var(--ink3);
  min-height:44px;padding:0 2px;margin:-14px 0;
  transition:color var(--t1) var(--ease);
}
.kaki-b[aria-pressed="true"]{color:var(--ink);font-weight:600}
@media (hover:hover) and (pointer:fine){ .kaki-b:hover{color:var(--ink2)} }

/* ==========================================================================
   The measure's own screen — Health's detail shape.
   ========================================================================== */
/* Headline: the label small and muted, the figure large, the unit INLINE
   beside it rather than stacked under, then the span it covers. */
.lead{margin:var(--s5) 0 0}
.lead-l{margin:0;color:var(--ink3);letter-spacing:.06em}
.lead-n{margin:2px 0 0;text-wrap:balance;font:700 34px/1.1 var(--sans-d);letter-spacing:-.022em;color:var(--b900)}
/* Was `font:400 17px/1 inherit` — `inherit` is not a valid family inside the
   font shorthand, so the whole declaration was dropped and the unit rendered
   at the figure's own 34px/700. That is the disproportion. */
.lead-n i{font-style:normal;font-weight:400;font-size:17px;line-height:1;
  color:var(--ink3);margin-left:8px;letter-spacing:-.022em}
.lead-r{margin:var(--s2) 0 0;color:var(--ink3)}

/* Health's detail chart furniture: y-labels on the RIGHT, ONE rule across the
   middle, period labels beneath. The mark itself is a line here because these
   measures are levels, not counts. */
/* The range picker: Health's segmented control, scoped to 2H / M / B. */
.rentang{display:inline-flex;gap:2px;margin:var(--s5) 0 0;padding:2px;
  border-radius:var(--r-2);background:var(--rule2)}
.rb{-webkit-appearance:none;appearance:none;min-width:52px;min-height:36px;border:0;
  border-radius:calc(var(--r-2) - 2px);cursor:pointer;background:none;color:var(--ink2);
  font:600 14px/1 var(--sans);transition:background var(--t1) var(--ease),color var(--t1) var(--ease)}
.rb.on{color:var(--b900)}

.wb{margin:var(--s4) 0 0}
/* Bars: a zero baseline, one rule across the middle, the current period in the
   hue and the rest receding — Health's steps chart. Each bar is its own touch
   target, so scrubbing a count works the way scrubbing a line does. */
.wb-bars-plot{height:170px}
.wbb{position:absolute;inset:0 74px 0 0;display:flex;align-items:flex-end;gap:3px}
.wbb-mid{position:absolute;left:0;right:0;top:50%;height:1px;background:var(--rule2)}
.wbb-b{flex:1;min-width:0;border:0;padding:0;cursor:pointer;
  /* A button does not inherit colour from its parent, so currentColor here
     resolved to the UA default and every bar rendered white. */
  color:inherit;
  background:currentColor;opacity:.45;border-radius:3px 3px 0 0;
  transition:opacity var(--t1) var(--ease)}
.wbb-b.on,.wbb-b.sel{opacity:1}
.wbb-b:active{opacity:.85}
@media (hover:hover) and (pointer:fine){ .wbb-b:hover{opacity:.7} }

/* A cluster row is a way in, not a dead number. */
.kl-row{cursor:pointer}
.kl-row .kl-c{width:18px;color:var(--ink3);opacity:.6;text-align:right}
.kl-row:active{background:var(--rule2)}
@media (hover:hover) and (pointer:fine){ .kl-row:hover{background:var(--rule2)} }
.wb-plot{position:relative;padding-right:74px}
.wb-plot svg{display:block;width:100%;height:auto;overflow:visible}
.wb-mid{stroke:var(--rule2);stroke-width:1;vector-effect:non-scaling-stroke}
.wb-tgt{stroke:var(--rule);stroke-width:1;stroke-dasharray:3 4;vector-effect:non-scaling-stroke}
.wb-line{stroke:currentColor;opacity:.75;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;
  vector-effect:non-scaling-stroke}
.wb-pt{fill:currentColor;opacity:.75}
.wb-now{fill:currentColor;stroke:var(--b050);stroke-width:2.5;vector-effect:non-scaling-stroke}
.wb-sel{fill:currentColor;stroke:var(--b050);stroke-width:2.5;vector-effect:non-scaling-stroke}
.wb-cross{stroke:var(--b200);stroke-width:1;vector-effect:non-scaling-stroke}
.wb-hit{fill:transparent;cursor:pointer}
.lead-x{-webkit-appearance:none;appearance:none;border:0;background:none;padding:0;
  min-height:44px;margin:-14px 0;cursor:pointer;color:var(--b700);font:inherit}
.wb-y{position:absolute;top:0;right:0;bottom:0;width:70px;
  display:flex;flex-direction:column;justify-content:space-between;
  align-items:flex-end;color:var(--ink3);font-variant-numeric:tabular-nums}
.wb-x{display:flex;justify-content:space-between;margin:var(--s2) 74px 0 0;
  color:var(--ink3)}

/* Health's Trend row: an outlined pill stating one fact. */
.tren{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  margin:var(--s4) 0 0;min-height:52px;padding:0 var(--s4);
  border-radius:var(--r-2);box-shadow:inset 0 0 0 1px var(--rule);color:var(--ink2)}
.tren b{color:var(--b900);font-weight:600;font-variant-numeric:tabular-nums}

.sec-h{margin:var(--s7) 0 var(--s3);color:var(--ink2)}

/* The finding: sentence first, evidence second. Two bars, value above, period
   named INSIDE the bar — the one being read in the hue, the one it is measured
   against in grey. */
.bd{border-radius:var(--r-3);background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule);
  padding:var(--s4)}
.bd-h{margin:0;color:inherit;font:600 14px/1.29 var(--sans)}
.bd-s{margin:var(--s2) 0 0;font-size:17px;line-height:1.4;color:var(--ink)}
.bd-body{margin-top:var(--s4);padding-top:var(--s4);border-top:1px solid var(--rule2)}
.bd-v{margin:0 0 4px;font:600 21px/1.19 var(--sans);letter-spacing:-.016em;color:var(--b900);
  font-variant-numeric:tabular-nums}
.bd-bar{display:flex;align-items:center;min-height:34px;padding:0 var(--s3);
  margin-bottom:var(--s4);border-radius:var(--r-1);background:var(--rule2);color:var(--ink2)}
.bd-bar:last-child{margin-bottom:0}
.bd-bar.on{background:currentColor}
.bd-bar.on span{color:var(--paper);mix-blend-mode:normal}
.bd-bar span{font:600 14px/1 inherit;white-space:nowrap}

/* ==========================================================================
   Temuan — what is going well, and what is worth working on.
   Two halves of one card so neither can be read without the other.
   ========================================================================== */
/* The reading, in a card under the numbers it reads. */
.ringkas{margin:var(--s5) 0 0;border-radius:var(--r-3);overflow:hidden;
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule)}
/* The last thing on her card: what she agreed to do, in her coach's words, set
   the way the record sets them. It closes the page on an action rather than on
   a shortfall. */
.ringkas-next{
  margin:0;padding:var(--s4);border-top:1px solid var(--rule2);
  display:flex;flex-direction:column;gap:var(--s2);
}
.ringkas-next em{font-style:italic;font-size:17px;line-height:1.45;
  letter-spacing:-.012em;color:var(--ink)}
.ringkas > h3{margin:0;padding:var(--s4) var(--s4) 0}
.ringkas-p{padding:var(--s3) var(--s4) 0}
.ringkas-p .lede{margin:0}
/* A section of that card, not a card inside a card. */
.ringkas .tm{margin:0;border-radius:0;background:none;box-shadow:none}
.ringkas .tm-p{padding:var(--s4) var(--s4) 0}
.ringkas .tm-p:last-child{padding-bottom:var(--s4)}
.tm{margin:var(--s5) 0 0;border-radius:var(--r-3);overflow:hidden;
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule)}
.tm-p{padding:var(--s4)}
.tm-next{border-top:1px solid var(--rule2)}
.ringkas .tm-next{border-top:0}
.tm-h{margin:0 0 var(--s3);color:var(--ink3);letter-spacing:.02em}
.tm-l{margin:0;padding:0;list-style:none;display:grid;gap:var(--s3)}
.tm-i{display:grid;grid-template-columns:auto 1fr;gap:var(--s3);align-items:start}
/* A dot in the category's hue, so a finding is attributable at a glance without
   repeating the measure's name as a heading. */
.tm-d{width:9px;height:9px;border-radius:50%;background:currentColor;margin-top:7px}
.tm-s{font-size:17px;line-height:1.4;color:var(--ink)}

/* ==========================================================================
   The coaching room — a live conversation, not a page to read.

   One tap between the three groups, the whole group on one screen, and the
   writing block pinned so the coach can capture the moment something is agreed
   without navigating away from what they were both looking at.
   ========================================================================== */
.rtabs{
  /* Empat sejak catatan ikut duduk di sini. Di 375px empat label 15px tidak
     muat, jadi hurufnya mengecil bersama-sama — bukan satu label dipendekkan,
     yang membuat satu tab terbaca lain jenis dari tiga tetangganya. */
  display:grid;grid-template-columns:repeat(4,1fr);gap:2px;
  margin:var(--s5) 0 0;padding:2px;border-radius:var(--r-2);background:var(--rule2);
  position:sticky;top:calc(var(--safe-t) + 44px);z-index:20;
}
.rtab{
  -webkit-appearance:none;appearance:none;min-height:44px;border:0;cursor:pointer;
  border-radius:calc(var(--r-2) - 2px);background:none;color:var(--ink2);
  font:600 14px/1 var(--sans);letter-spacing:-.014em;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease);
}
.rtab.on{background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule)}
.rtab.on.g-perf{color:var(--c-perf)} .rtab.on.g-resv{color:var(--c-resv)}
.rtab.on.g-trmt{color:var(--c-trmt)}
.rentang.room{margin:var(--s3) 0 0}
.kartu-rentang{margin:var(--s3) 0 var(--s2)}
.rpanel{min-height:340px}
.rpanel .angka{margin-top:var(--s4)}

/* Pinned. The coach types the moment something is agreed; recall after the
   session is where things get lost. The whole session record lives here now —
   there is no separate catat screen. Both people are on this screen, so the
   kesepakatan is the only part the dentist is shown; hambatan and training sit
   behind the disclosure and are rendered for the coach, DPA and BPE only. */
.tulis-dock{
  position:fixed;left:0;right:0;bottom:0;z-index:35;
  background:color-mix(in srgb,var(--b050) 92%,transparent);
  backdrop-filter:blur(20px) saturate(180%);
  -webkit-backdrop-filter:blur(20px) saturate(180%);
  box-shadow:0 -1px 0 var(--rule);
}
@media (prefers-reduced-transparency:reduce){
  .tulis-dock{background:var(--b050);backdrop-filter:none;-webkit-backdrop-filter:none}
}
.tulis-in{
  max-width:920px;margin:0 auto;
  padding:var(--s3) var(--gut) calc(var(--s3) + var(--safe-b));
}
/* A label belongs to the thing UNDER it. With 8px below and nothing above,
   every caption was pushed off its own value and pressed against the previous
   one — "Yang dibicarakan" floated free while "Kesepakatannya" read as the tail
   of the sentence above it. Tight to its value, spaced from the pair before. */
.tulis-in .cap{margin:0 0 3px;color:var(--ink3)}
.tulis-in .tulis-baca + .cap{margin-top:var(--s4)}
/* One height for every box in the form. They were 64 / 76 / 104 depending on
   which rule won, so three questions of the same kind looked like three
   different kinds of question. */
.tulis-in .field{min-height:88px;margin-bottom:0;resize:none}
/* The field the save stopped on. A hairline on the control, a sentence under
   it — where the person is already looking — instead of a toast at the far edge
   of the screen. */
.f-err{margin:var(--s2) 0 0;font:600 14px/1.4 var(--sans);color:var(--down)}
.f.bad .field{border-color:var(--down);box-shadow:0 0 0 3px var(--down-soft)}
.f.bad .seg .segb,.f.bad .skala .skb{box-shadow:inset 0 0 0 1px var(--down)}
/* Tindak lanjut sesudah sesi: tiga pilihan bertumpuk, satu baris per pilihan —
   labelnya terlalu panjang untuk dua kolom .seg di layar HP. */
.pilih-tindak{display:grid;gap:var(--s2)}
.pilih-tindak .segb{text-align:left;padding:0 var(--s4)}
.f.bad .pilih-tindak .segb{box-shadow:inset 0 0 0 1px var(--down)}
.f.bad .t-lbl{color:var(--down)}

/* Antusiasme, 1–5. Five equal targets, each a 44px tap, so the scale reads as
   one control rather than five buttons that happen to be adjacent. */
.skala{display:grid;grid-template-columns:repeat(5,1fr);gap:var(--s2)}
.skb{
  -webkit-appearance:none;appearance:none;min-height:44px;cursor:pointer;
  border:0;border-radius:var(--r-2);background:var(--paper);
  box-shadow:inset 0 0 0 1px var(--rule);color:var(--ink2);
  font:600 17px/1 var(--sans);font-variant-numeric:tabular-nums;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease),
             box-shadow var(--t1) var(--ease);
}
.skb.on{background:var(--b700);color:var(--on-brand);box-shadow:none}
.skb:active{transform:scale(.97)}
.skala-l{margin:var(--s2) 0 0;color:var(--ink3)}

/* The trend, under the question it answers. */
.ant{margin:var(--s3) 0 0;padding:var(--s3) 0 0;border-top:1px solid var(--rule)}
.ant-s{display:block;overflow:visible;color:var(--b700)}
.ant-g{stroke:var(--rule);stroke-width:1;vector-effect:non-scaling-stroke}
.ant-l{stroke:currentColor;opacity:.75;stroke-width:2.5;stroke-linecap:round;
  stroke-linejoin:round;vector-effect:non-scaling-stroke}
.ant-d{fill:currentColor;opacity:.55}
.ant-d.kini{opacity:1;stroke:var(--b050);stroke-width:2;vector-effect:non-scaling-stroke}
.ant-x{display:flex;justify-content:space-between;margin:var(--s2) 0 0;color:var(--ink3)}
.tulis-in .f{margin:0 0 var(--s3);padding:0;border:0}
.tulis-in .f legend{padding:0;margin:0 0 var(--s2);color:var(--ink3)}
.tulis-in .f:last-of-type{margin-bottom:var(--s3)}
.tulis-in > :last-child{margin-bottom:0}
.tulis-in .seg{margin-bottom:0}
/* The note that says who can see a field sits tight under that field, so the
   two read as one thing. A gap here is what let the old footer note look like
   it covered everything above it. */
.tulis-in .tulis-n{margin:var(--s2) 0 0}
/* Only the note that follows a field pulls up — a field carries its own 8px
   below it, and without this the pair read as two separate things. */
.tulis-in .field + .tulis-n{margin-top:calc(-1 * var(--s1))}
/* Read before anything private is typed, so it leads the disclosure and carries
   a rule to show where the private part begins. */
.tulis-in .tulis-priv{margin:var(--s3) 0 var(--s3);padding-top:var(--s3);
  border-top:1px solid var(--rule);color:var(--ink2)}
/* DPA and BPE read the private part; nobody but the coach types in it. */
.tulis-priv-baca .cap{margin:var(--s3) 0 3px}
.tulis-priv-baca .cap:first-of-type{margin-top:0}
.tulis-baca.sekunder{font-size:15px;color:var(--ink2)}
/* What was discussed, above the agreement it led to. Plain prose, so the
   quotation marks stay with the thing that was actually promised. Each caption
   hugs its own text and is spaced from the pair before — the same rule as the
   sheet, for the same reason: unlabelled, the two read as one paragraph and
   nothing said which was the record and which the commitment. */
.ent .bahas{margin:0;font-size:15px;line-height:1.5;color:var(--ink2)}
.ent .cap{margin:var(--s3) 0 3px;color:var(--ink3)}
.ent .hd + .cap{margin-top:var(--s2)}
.ent .said{margin:0}

/* Tiga ukuran di KEPALA halaman Semua sesi — sekali, bukan di tiap entri.
   Satu kartu, tiga baris, masing-masing dalam warna kelompoknya sendiri. */
.ukur-atas{margin:var(--s5) 0 0;padding:var(--s5) var(--s4);
  background:var(--paper);border-radius:var(--r-3);
  box-shadow:inset 0 0 0 1px var(--rule)}
.ua + .ua{margin-top:var(--s5);padding-top:var(--s5);
  box-shadow:inset 0 1px 0 var(--rule2)}
.ua-h{display:flex;align-items:center;gap:7px;margin:0;
  font:600 14px/1.25 var(--sans);letter-spacing:-.006em}
.ua-h svg{flex:none}
.ua-v{margin:var(--s2) 0 0;font:700 24px/1.1 var(--sans-d);letter-spacing:-.02em;
  color:var(--ink);font-variant-numeric:tabular-nums}
.ua .ent-ug{display:block;width:100%;height:60px;margin-top:var(--s3)}

/* Bentuk gerak, dipakai di dua ukuran: kepala halaman dan — dulu — tiap entri.
   Garis untuk yang mengalir, batang untuk yang dihitung. */
.ent-ug{flex:none;width:76px;height:26px}
.ent-ug polyline{fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;opacity:.9}
.ent-ug circle{fill:currentColor}
.ent-ug rect{fill:currentColor;opacity:.32}
.ent-ug rect.on{opacity:1}

/* Antusiasme: sebuah tingkat, bukan uang — jadi ia tidak memakai tipografi
   angka rupiah, dan skalanya selalu disebut. "4" sendirian tidak berarti apa
   pun; "4 dari 5 — Antusias" berarti. */
.ent-ant{margin:0 0 var(--s3);font:400 15px/1.45 var(--sans);color:var(--ink2)}
.ent-ant-n{font-weight:600;color:var(--ink)}

/* What was discussed, on her card: read as plain prose above the agreement,
   which keeps the quote mark for the agreement alone. */
.said-p{margin:0 0 var(--s4);font-size:15px;line-height:1.5;color:var(--ink2)}
.want-block .cap{margin:var(--s3) 0 var(--s1);color:var(--ink3)}
.tulis-in .btn{min-height:46px}
.tulis-n{margin:var(--s2) 0 0;color:var(--ink3)}
.tulis-baca{margin:0;font-size:17px;line-height:1.4;color:var(--ink)}
/* The page has to clear the dock, and grow with the keyboard the way the
   search dock does. */
/* Hanya .has-tulis. Dulu .wide ikut memesan ruangnya, dan .wide berarti "ini
   layar ruang", bukan "ada laci tulis di bawahnya". Keduanya sama artinya
   selama setiap pembaca punya laci; begitu pembaca tanpa peran tidak lagi
   punya, yang tersisa 104px kosong di kaki halamannya. */
.wrap.has-tulis{padding-bottom:calc(104px + var(--kb,0px) + var(--safe-b))}
/* Was a CSS transform; Tulis sets an inline one, which beats it. The lift is
   folded into paint() there so the two cannot fight. */
.tulis-dock{transition:transform .22s cubic-bezier(.32,.72,0,1)}
/* Expanding the disclosure made the dock 809px tall in an 812px viewport: it
   covered the data it is meant to sit beside, and — being pinned by its BOTTOM
   edge, it grew straight off the top of the screen, putting the attendance
   control and the kesepakatan field out of reach entirely. Cap it and let it
   scroll inside itself, the way a bottom sheet does. */
.tulis-dock{max-height:76vh;display:flex;flex-direction:column}
/* The measure, in the handle, so it survives every detent — the sheet covers
   the card at 375px and this is what the coach is writing about. */
.tulis-grab-n{display:flex;flex-direction:column;align-items:flex-end;gap:1px;
  flex:none;margin-left:auto;padding-left:var(--s3);text-align:right}
.tulis-grab-nl{font:400 11px/1.2 var(--sans);letter-spacing:.02em;color:var(--ink3)}
.tulis-grab-nv{font:600 14px/1.2 var(--sans);letter-spacing:-.012em;
  font-variant-numeric:tabular-nums;color:var(--b900)}
.tulis-in{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch}
/* The primary never scrolls away. A hairline above it so the scrolling body
   ends against something rather than sliding under it unannounced. */
.tulis-foot{flex:none;padding:var(--s3) var(--gut) calc(var(--s3) + var(--safe-b));
  box-shadow:0 -1px 0 var(--rule)}
.tulis-foot .btn{margin:0;min-height:50px}

/* The handle IS the sheet when it is down: a full-width button carrying the
   label and how far along the record is, so nothing has to be opened to find
   out. `--lift` is driven by the drag, so the shadow and the corners belong to
   a raised object and flatten as it returns to the floor. */
.tulis-grab{
  position:relative;
  -webkit-appearance:none;appearance:none;border:0;background:none;width:100%;
  display:flex;align-items:center;gap:var(--s3);cursor:grab;
  padding:var(--s2) var(--gut) var(--s3);text-align:left;color:inherit;
  touch-action:none;flex:none;
}
.tulis-grab:active{cursor:grabbing}
.tulis-bar{
  position:absolute;top:6px;left:50%;transform:translateX(-50%);
  width:38px;height:4px;border-radius:var(--r-pill);background:var(--ink3);opacity:.45;
}
.tulis-grab-r{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.tulis-grab-t{font:600 15px/1.2 var(--sans);letter-spacing:-.01em;color:var(--b900)}
.tulis-grab-s{font-size:13px;line-height:1.3;color:var(--ink3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tulis-grab-c{display:inline-flex;flex:none;color:var(--b700);
  transform:rotate(-90deg);transition:transform var(--t2) var(--ease)}
.tulis-grab[aria-expanded="true"] .tulis-grab-c{transform:rotate(90deg)}
@media (prefers-reduced-motion:reduce){ .tulis-grab-c{transition:none} }
/* Expanding the disclosure made the dock 809px tall in an 812px viewport: it
   covered the data it is meant to sit beside, and — being pinned by its BOTTOM
   edge — grew straight off the top of the screen, putting the attendance
   control and the kesepakatan field out of reach entirely. Cap it and let it
   scroll inside itself, the way a bottom sheet does. */


/* Phone: one column, the writing pinned to the bottom.
   760px and up: two columns, and the writing stops being a dock — it becomes a
   sticky panel beside the data, because there is room for both. */
@media (min-width:760px){
  .ruang2{display:grid;grid-template-columns:1fr minmax(300px,380px);
    gap:var(--s6);align-items:start}
  /* Sticky, so the same thing applies: a panel taller than the viewport can
     never be scrolled to the end of. Scoped to the room: it is the two-column
     layout that makes a column possible, and the detail screens have no second
     column, so there the record stays a sheet at every width. */
  .ruang2 > .tulis-dock{position:sticky;top:calc(var(--safe-t) + 60px);bottom:auto;
    max-height:calc(100vh - var(--safe-t) - 80px);
    background:var(--paper);backdrop-filter:none;-webkit-backdrop-filter:none;
    border-radius:var(--r-3);box-shadow:inset 0 0 0 1px var(--rule);
    transform:none}
  /* There is room for both columns here, so the record is simply present. A
     handle would be an open/close affordance for something already open. */
  .ruang2 > .tulis-dock .tulis-grab{display:none}
  .ruang2 > .tulis-dock .tulis-in{padding:var(--s4)}
  .ruang2 > .tulis-dock .tulis-in .field{min-height:120px}
  .wrap.wide{padding-bottom:calc(var(--s7) + var(--safe-b))}
  .rtabs{top:calc(var(--safe-t) + 44px)}
}


/* The agreement as already written, shown rather than asked for again. */
.sepakat{margin:var(--s5) 0 0;padding:var(--s4);border-radius:var(--r-3);
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--rule)}
.sepakat .cap{margin:0 0 var(--s2);color:var(--ink3)}
.sepakat .quote{margin:0 0 var(--s3)}
.sepakat .btn.quiet{width:auto;padding:0 var(--s4)}

/* The `.lagi` disclosure is gone: the session form is one page now. */.pindah-c{display:flex;align-items:center;gap:10px;min-height:44px;font-size:15px;color:var(--ink)}
.pindah-c input{width:20px;height:20px;accent-color:var(--b700);margin:0}


/* ==========================================================================
   Gerak, 2 Oktober 2026 — empat celah yang ditambal, bukan sistem baru.
   Sistemnya sudah ada (pegas di nav.js dan sheet.js); yang di bawah hanya
   menutup tempat-tempat yang masih melompat.
   ========================================================================== */

/* 1. Thumb kontrol tersegmen (2H/M/B, Area/Sesi, Terang/Gelap/Ikuti HP).
   Isian "yang terpilih" pindah dari tombolnya ke ::before, supaya bisa MELUNCUR
   dari pilihan lama ke yang baru alih-alih berkedip di tempat. Posisi lamanya
   dicatat sebelum layar digambar ulang (geserSegmen di actions.js). */
.rb,.subb,.tema-b{position:relative;isolation:isolate}
.rb.on::before,.subb.on::before,.tema-b[aria-pressed="true"]::before{
  content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;
  background:var(--paper);transform-origin:0 50%}
.rb.on::before{box-shadow:inset 0 0 0 1px var(--rule)}
.subb.on::before{box-shadow:0 1px 3px -1px rgba(10,22,30,.22), inset 0 0 0 1px var(--rule)}
.geser::before{animation:geser var(--t2) var(--ease-out)}
@keyframes geser{from{transform:translateX(var(--dx,0)) scaleX(var(--sx,1))}}

/* 2. Garis grafik digambar ulang dari kiri ke kanan saat rentangnya diganti —
   urutan waktu, bukan sekadar berkedip. Batang tumbuh dari garis nolnya. */
/* Kurva lebih landai dari --ease-out: sapuannya harus terlihat berjalan, bukan muncul. */
.segar .wb-plot > svg,.segar .spark{animation:gambar 340ms cubic-bezier(.33,1,.68,1)}
@keyframes gambar{from{clip-path:inset(-8px 100% -8px 0)}to{clip-path:inset(-8px 0 -8px 0)}}
.segar .wbb-b{animation:tumbuh var(--t2) var(--ease-out);transform-origin:50% 100%}
@keyframes tumbuh{from{transform:scaleY(.2);opacity:.2}}

/* 3. Isi sheet yang berganti (menu → formulir) memudar masuk; tingginya
   diluncurkan di sheet.js. */
.sheet-body.ganti{animation:muncul var(--t2) var(--ease-out)}
@keyframes muncul{from{opacity:0;transform:translateY(4px)}}

@media (prefers-reduced-motion:reduce){
  .geser::before,.segar .wb-plot > svg,.segar .spark,.segar .wbb-b,.sheet-body.ganti{animation:none}
}

/* Kaca tanpa blur bukan kaca, melainkan jendela: tulisan di belakang terbaca
   tajam menembus kontrolnya. Peramban yang tidak menjalankan backdrop-filter,
   dan orang yang meminta transparansi dikurangi, mendapat bahan pekat. Di kaki
   berkas supaya menang atas aturan kaca di atas. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .tabbar,.nav::before{background:var(--b050)}
  .sheet-panel{background:var(--paper)}
  .nav .nav-b:not(.ghost){background:var(--rule2)}
  .tab-lensa{background:var(--paper)}
}
@media (prefers-reduced-transparency:reduce){
  .nav .nav-b:not(.ghost){background:var(--rule2);backdrop-filter:none;-webkit-backdrop-filter:none}
  .nav .nav-b.avatar{background:var(--b100)}
}

/* Buka kembali di kepala kartu tertutup (038) — tombol teks, bukan blok. */
.t-buka{border:0;background:none;cursor:pointer;margin-left:10px;padding:6px 2px;font:600 14px/1.3 var(--sans);color:var(--b700)}
.t-buka:hover{text-decoration:underline}
.t-buka:focus-visible{outline:2px solid var(--b700);outline-offset:2px;border-radius:4px}
