*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* Every icon is an inline svg with no width attribute. Without this floor an
   icon dropped into a container that has no rule for it stretches to the whole
   box, which is where the page filling warning triangle came from. Any
   .class svg rule below beats this one, so nothing intentional changes. */
svg{width:15px;height:15px;flex:none;vertical-align:middle}
/* One visible focus ring for the keyboard, and only for the keyboard.
   Three controls used to remove the outline and put nothing back — the role
   switcher, the chat member search and the Inspector search — which left them
   at 2.02:1 against their own field. :focus-visible never fires on a mouse
   click, so nothing changes for anybody using one. */
/* --lg-focus, bukan --lime. Cincin fokus adalah afordansi tingkat sistem —
   satu aplikasi tidak boleh punya dua warnanya — dan cincin ini dulu memakai
   --lime, aksen dashboard lama.

   Sejak --lime sendiri ikut jadi #7FA6FF, keduanya kebetulan warna yang sama.
   Tetap ditulis sebagai --lg-focus dan bukan --lime: kalau suatu hari aksennya
   bergerak lagi, cincin fokus tidak ikut bergerak bersamanya.

   Custom property diselesaikan pada waktu computed-value, bukan waktu parse,
   jadi tidak masalah bahwa --lg-focus dideklarasikan di blok :root jauh di
   bawah baris ini. */
:focus-visible{outline:2px solid var(--lg-focus);outline-offset:2px;border-radius:4px}
:root{
--bg:#101012;--sf:#18181B;--sf2:#1F1F23;--sf3:#28282D;
--bd:#2A2A30;--bds:#3A3A42;--bdl:#4A4A54;
--tx:#F4F4F5;--tx2:#C8C8CE;--tx3:#8E8E96;
/* #7FA6FF sejak 2026-09-15, atas permintaan Joe: aksen dashboard lama
   menyerah pada Cobalt/300 milik design system baru, supaya layar lama dan
   layar baru berhenti memakai dua warna aksi. Nama tokennya sengaja TIDAK
   diganti — 70 tempat memakainya dan mengganti namanya akan jadi diff yang
   menyembunyikan perubahan sebenarnya di dalam perubahan nama. Ia sekarang
   nama tanpa arti, dan itu lebih murah daripada nama yang salah di 70 baris.

   #101012 di atasnya menghitung 8.0:1 — jauh di atas AA, turun dari 16.3:1
   waktu masih limau, jadi tidak ada tombol yang jatuh di bawah ambang. */
--lime:#7FA6FF;--limeBg:rgba(127,166,255,.12);
--gr:#4ADE80;--grBg:rgba(74,222,128,.12);
--rd:#FF7070;--rdBg:rgba(255,112,112,.12);
--am:#FFC46B;--amBg:rgba(255,196,107,.12);
--bl:#7DB8FF;--blBg:rgba(125,184,255,.12);
--r:7px;--rl:10px;
--fd:'Archivo',system-ui,sans-serif;
--fu:'Inter',system-ui,sans-serif;
--fm:'JetBrains Mono',ui-monospace,monospace}
html,body{height:100%;overflow:hidden;background:var(--bg);font-family:var(--fu);font-size:14px;color:var(--tx);-webkit-font-smoothing:antialiased}
/* vh first, dvh second: dvh wins where it is understood, vh is the fallback.
   100vh on Android Chrome and Brave is the toolbar-RETRACTED height, and
   html/body are overflow:hidden, so the bottom ~56px of every page sat
   permanently underneath the browser toolbar with no way to scroll to it. On
   the Feedback tab that band is exactly where Post lives. */
/* shell di Figma: padding 16 di keempat sisi dan gap 16 antara sidebar dan
   area kerja. Sidebar duduk di x=16 dan main mulai di x=300 — 16 + 268 + 16.
   Jaraknya milik shell, jadi .sb tidak lagi membawa margin sendiri. */
#app{display:flex;gap:16px;padding:16px;height:100vh;height:100dvh}

/* Padding 16 itu milik shell dashboard — jarak antara sidebar, area kerja dan
   tepi jendela. Layar yang MENGGANTIKAN shell tidak punya sidebar untuk diberi
   jarak, dan padding itu justru menyisakan pita hitam 16px mengelilinginya:
   foto hero berhenti sebelum tepi layar, dan latar ambient terpotong.

   Tiga layar yang begitu. Dua menulis .lgpage langsung ke #app — Sign in
   (showLogin) dan Pick a Brand (showBrandPicker). Yang ketiga .lgboot, dan ia
   yang paling mudah terlewat: ia tidak ditulis app.js sama sekali, ia sudah ada
   di dalam #app di public/index.html supaya ada sesuatu di layar sebelum
   app.js selesai dimuat. Jadi ia layar PERTAMA yang dilihat orang, dan
   pita 16px itu justru paling terlihat di sana.

   :has() memagarinya ke saat salah satunya sedang tampil; kalau :has() tidak
   didukung, yang tersisa perilaku lama yang cuma kurang rapat, bukan tata
   letak yang rusak. */
#app:has(.lgpage),#app:has(.lgboot){padding:0;gap:0}

/* sidebar */
.sb{width:236px;background:#131316;border-right:1px solid var(--bd);display:flex;flex-direction:column;flex-shrink:0;overflow-y:auto;padding-bottom:20px}
.sb-lg{padding:22px 20px 18px;border-bottom:1px solid var(--bd);margin-bottom:10px}
.sb-lg h1{font-family:var(--fd);font-size:16px;font-weight:700}
.sb-lg span{font-family:var(--fm);font-size:10px;color:var(--tx3);display:block;margin-top:5px;letter-spacing:.12em;text-transform:uppercase}
/* Experimental multi-brand demo: a workspace-switcher chip in place of the
   static sidebar header — same slot, same border-bottom, so nothing around
   it has to change when a database has no brand tables and this never
   renders at all. */
.brandsw{display:flex;align-items:center;gap:11px;padding:16px 20px;border-bottom:1px solid var(--bd);margin-bottom:10px;cursor:pointer;transition:background .12s}
.brandsw:hover{background:var(--sf2)}
.brandsw-txt{min-width:0;flex:1}
.brandsw-name{font-family:var(--fd);font-weight:700;font-size:14.5px;color:var(--tx);display:flex;align-items:center;gap:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.brandsw-chev{color:var(--tx3);font-size:11px}
.brandsw-role{font-family:var(--fm);font-size:10px;color:var(--tx3);margin-top:2px;letter-spacing:.1em;text-transform:uppercase}
/* The row for the brand already active in the switch modal — same subdued
   "selected" language as .roopt.pick and .titleopt.sel elsewhere (a tinted
   background and a lime border, not a solid fill), because a solid --lime
   button here reads as "press this to do something" rather than "you are
   already here". */
.brandrow-cur{border-color:var(--lime);background:var(--limeBg);color:var(--tx)}
.nsec{font-family:var(--fm);font-size:10px;color:var(--tx3);padding:16px 20px 6px;letter-spacing:.12em;text-transform:uppercase}
.ni{display:flex;align-items:center;gap:11px;padding:9px 20px;font-size:13.5px;color:var(--tx2);cursor:pointer;border-left:3px solid transparent;transition:all .12s}
.ni svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.7;fill:none;flex-shrink:0}
.ni:hover{background:var(--sf);color:var(--tx)}
.ni.on{background:var(--sf2);color:var(--tx);border-left-color:var(--lime);font-weight:600}
.ni.on svg{color:var(--lime)}
.nb{margin-left:auto;font-family:var(--fm);background:var(--sf3);color:var(--tx2);font-size:11px;font-weight:600;border-radius:4px;padding:2px 7px;min-width:24px;text-align:center}
.nb.q{background:var(--amBg);color:var(--am)}
.ni.on .nb,.ni.on .nb.q{background:var(--lime);color:#101012}

/* shell */
.ma{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
/* min-width:0 on the trail and flex-shrink:0 on the controls: without the pair,
   a long trail refuses to give way and pushes Sign Out off the right edge
   instead of shortening itself. */
.tb{height:56px;background:#131316;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between;padding:0 28px;flex-shrink:0;gap:12px}
.crumb{font-family:var(--fm);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--tx3);min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.crumb b{color:var(--lime);font-weight:500}
.tbr{display:flex;align-items:center;gap:12px;flex-shrink:0}
/* step back and step forward, kept together and set apart from the bell so the
   pair reads as one control rather than two more icons in a row */
/* role editor: jobs across the top, permissions grouped underneath */
.tplgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:9px}
.tpljob{border:1px solid var(--bd);border-radius:var(--rl);padding:11px 12px;background:var(--sf)}
.tpljl{font-size:13px;font-weight:600;margin-bottom:9px}
.tpljl span{display:block;font-weight:400;font-size:11.5px;color:var(--tx3);margin-top:2px}
.tpllv{display:flex;gap:6px;flex-wrap:wrap}
.capbox{max-height:340px;overflow:auto;border:1px solid var(--bd);border-radius:var(--rl);padding:4px 12px 12px}
.capgrp{margin-top:14px}
.capgl{font-family:var(--fm);font-size:10px;letter-spacing:.11em;text-transform:uppercase;color:var(--tx3);margin-bottom:8px}
.caprow{display:flex;gap:9px;align-items:flex-start;cursor:pointer;font-size:13px;padding:4px 0}
.caprow input{width:auto;margin-top:3px;flex-shrink:0}
.ubar{display:flex;align-items:center;gap:2px;padding-right:10px;border-right:1px solid var(--bd)}
.ubar .pbtn{width:30px;height:30px}
.ubar .pbtn:not(.off):hover{background:var(--sf2);color:var(--tx)}
.ubar .pbtn.off{color:var(--bdl);cursor:default}
.rsw{display:flex;align-items:center;gap:9px;padding:6px 12px 6px 6px;border:1px solid var(--bds);border-radius:40px;background:var(--sf)}
.rsw select{border:none;background:none;font-size:12.5px;font-family:var(--fu);color:var(--tx2);cursor:pointer}
.rsw select option{background:var(--sf);color:var(--tx)}
/* Rata dengan topbar. Di Figma, topbar dan isi sama-sama selebar penuh `main`
   tanpa padding horizontal di dalamnya — jarak ke tepi jendela dan ke sidebar
   datang dari shell. Padding 28 di sini membuat judul dan kartunya tidak
   pernah segaris. */
/* Jarak 24 ke topbar adalah gap milik frame 'main' di Figma (89:12450,
   VERTICAL, itemSpacing 24) — bukan angka yang dipilih dengan mata. Di sana
   blok judul rata bawah dengan topbar, jadi 24 itu juga yang terlihat antara
   subjudul dan baris filter. Sebelumnya 16, dan itu terbaca berdempetan. */
.pg{flex:1;overflow-y:auto;padding:24px 0 48px}
/* The review page is a fixed-height frame, which is right on a desktop and
   wrong everywhere else: on a phone .rvpage becomes height:auto and grows past
   the viewport, and overflow:hidden then cut off the feedback rail and the box
   you type into. The x axis stays clipped; only y opens up. */
.pg.tight{padding:18px 22px 18px;overflow:hidden;overflow-y:auto}
/* chat fills the page: header, scrolling messages, box pinned at the bottom */
.chatpage{display:flex;flex-direction:column;height:calc(100vh - 112px);height:calc(100dvh - 112px);min-height:420px}
.chathead{flex-shrink:0;padding-bottom:12px;border-bottom:1px solid var(--bd);margin-bottom:14px}
.chathead .am2{font-size:12.5px;color:var(--tx3);margin-top:6px}
.chatpage .chatlist{flex:1;overflow-y:auto;padding-right:6px;min-height:0}
.chatbar{flex-shrink:0;display:flex;gap:10px;align-items:flex-end;padding-top:12px;border-top:1px solid var(--bd)}
.chatbar textarea{flex:1;min-height:44px;max-height:140px;resize:none}

/* page header */
.ph{margin-bottom:26px}
.eyeb{font-family:var(--fm);font-size:11px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--lime);margin-bottom:12px}
.ph h2{font-family:var(--fd);font-size:30px;font-weight:700;letter-spacing:-.02em;line-height:1.15}
.ph p{font-size:14px;color:var(--tx2);margin-top:11px;max-width:780px;line-height:1.65}

/* pipeline strip */
.flow{display:grid;grid-template-columns:repeat(var(--flowcols,5),1fr);gap:10px;margin-bottom:24px}
.fl{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:16px 17px;cursor:pointer;transition:border-color .12s}
.fl:hover{border-color:var(--bds)}
.fl .k{font-family:var(--fm);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-bottom:10px}
.fl .n{font-family:var(--fd);font-size:28px;font-weight:700;line-height:1}
.fl .t{background:var(--sf3);height:5px;border-radius:3px;margin-top:12px;overflow:hidden}
.fl .t i{display:block;height:100%;border-radius:3px}
.fl .s{font-size:12px;color:var(--tx3);margin-top:9px}

/* stats */
/* as many columns as there are cards, so dropping one does not leave a gap */
.stats{display:grid;grid-template-columns:repeat(var(--statcols,5),1fr);gap:10px;margin-bottom:24px}
.st{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:18px 19px}
.st .v{font-family:var(--fd);font-size:32px;font-weight:700;line-height:1;color:var(--lime)}
.st .l{font-family:var(--fm);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-top:12px}
.st .s{font-size:12.5px;color:var(--tx2);margin-top:8px}
.st.alert{border-color:rgba(255,112,112,.35)}
.st.alert .v,.st.alert .s{color:var(--rd)}

.sl{font-family:var(--fm);font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--tx3);margin:28px 0 13px;padding-bottom:10px;border-bottom:1px solid var(--bd)}
.sl:first-child{margin-top:0}

/* pills */
.pill{display:inline-flex;align-items:center;font-size:11.5px;font-weight:600;padding:4px 11px;border-radius:5px;white-space:nowrap}
.pl-n{background:var(--sf3);color:var(--tx2)}
.pl-a{background:var(--amBg);color:var(--am)}
.pl-p{background:var(--blBg);color:var(--bl)}
/* pl-e dulu lime, dan lime adalah aksen dashboard lama yang design system
   ganti dengan Cobalt. Ia satu-satunya sumber hijau-neon di pil tahap, dan
   di setiap artboard baru warna itu tidak ada.

   Diubah di sini saja — hanya pil tahap, bukan --lime itu sendiri. Token itu
   masih menggerakkan tombol, cincin fokus dan keadaan aktif di seluruh
   aplikasi, dan menukarnya menyeluruh adalah keputusan tim yang sudah
   tercatat di tasks/open-questions-fe-revamp.md, bukan efek samping dari
   memperbaiki satu kartu. */
.pl-e{background:rgba(20,84,219,.16);color:#8FB3FF}
.pl-s{background:var(--grBg);color:var(--gr)}
.pl-d{background:var(--rdBg);color:var(--rd)}
.age{font-family:var(--fm);font-size:12px;color:var(--tx3);white-space:nowrap}
.age.warn{color:var(--am);font-weight:500}
.age.bad{color:var(--rd);font-weight:600}

/* rows */
.qrow{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:15px 17px;margin-bottom:9px;transition:border-color .12s}
.qrow:hover{border-color:var(--bds)}
.qtop{display:flex;align-items:center;gap:14px}
.qid{font-family:var(--fm);font-size:11.5px;font-weight:600;color:var(--tx3)}
.qinfo{flex:1;min-width:0}
.qt{font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.qm{font-size:12.5px;color:var(--tx3);margin-top:4px}
.qright{display:flex;align-items:center;gap:11px;flex-shrink:0}
.qacts{display:flex;gap:8px;margin-top:13px;flex-wrap:wrap;padding-top:13px;border-top:1px solid var(--bd)}

/* avatars */
.av{width:26px;height:26px;border-radius:50%;font-family:var(--fm);font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.av-ed{background:#3A4A10;color:#D8FF7A}
.av-pm{background:#0F3D26;color:#7BEBAA}
.av-cd{background:#4A3208;color:#FFD9A0}
.av-sd{background:#12325A;color:#A8CFFF}
.av-am{background:#4A3208;color:#FFD9A0}
.av-wr{background:#33333A;color:#D4D4DA}
.av-ow{background:var(--lime);color:#101012}
.av-lg{width:32px;height:32px;font-size:11px}

/* buttons */
.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 15px;border-radius:var(--r);border:1px solid var(--bds);background:var(--sf2);color:var(--tx2);font-size:12.5px;font-weight:500;cursor:pointer;font-family:var(--fu);transition:all .12s;white-space:nowrap}
.btn:hover{background:var(--sf3);color:var(--tx);border-color:var(--bdl)}
.btn svg{width:14px;height:14px;stroke:currentColor;stroke-width:1.8;fill:none}
.btn-p{background:var(--lime);color:#101012;border-color:var(--lime);font-weight:600}
/* Cobalt/200, satu nada lebih terang dari aksennya — peran yang sama yang
   dipegang #DBFF6B terhadap limau. */
.btn-p:hover{background:#8FB2FF;color:#101012}
.btn-s{background:var(--grBg);color:var(--gr);border-color:rgba(74,222,128,.4)}
.btn-s:hover{background:rgba(74,222,128,.2);color:var(--gr);border-color:var(--gr)}
.btn-d{background:var(--rdBg);color:var(--rd);border-color:rgba(255,112,112,.4)}
.btn-d:hover{background:rgba(255,112,112,.2);color:var(--rd);border-color:var(--rd)}
.btn-sm{padding:6px 12px;font-size:12px}
.btn:disabled{opacity:.35;cursor:not-allowed}
/* A file input hidden with display:none is not reliably clickable from script:
   Safari can refuse to open the picker for an element that is not in the render
   tree, which looked exactly like a dead button. These stay rendered but
   invisible, and a <label for> opens them natively in every browser. */
.fileinput{position:absolute;width:1px;height:1px;padding:0;margin:-1px;border:0;opacity:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
label.btn{cursor:pointer}

/* forms */
input[type=text],input[type=email],input[type=password],textarea,select{width:100%;padding:10px 12px;border:1px solid var(--bds);border-radius:var(--r);background:var(--sf2);color:var(--tx);font-size:13.5px;font-family:var(--fu);transition:border-color .12s}
input::placeholder,textarea::placeholder{color:var(--tx3)}
input:focus,textarea:focus,select:focus{border-color:var(--lime)}
textarea{resize:vertical;min-height:72px;line-height:1.6}
select option{background:var(--sf);color:var(--tx)}
.fg{margin-bottom:14px}
.fg label{font-family:var(--fm);font-size:10.5px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);display:block;margin-bottom:8px}

/* Pick-one rows for the QC4 approve dialog — a full-width card per take
   rather than the .vchip/.ochip pill row Title And Post uses for the same
   choice, because that pill has to fit several to a line and truncates a
   long take name to fit; here there is exactly one column of them and
   room to just say the whole name. Native radio+label read as an
   afterthought bolted onto a confirm dialog; this is sized and colored
   like the rest of the app's own controls instead. */
.qcopts{display:flex;flex-direction:column;gap:8px}
.qcopt{display:flex;align-items:center;gap:11px;padding:11px 13px;border-radius:var(--r);border:1px solid var(--bd);background:var(--sf2);cursor:pointer;transition:border-color .12s,background-color .12s}
.qcopt:hover{border-color:var(--bds)}
.qcopt.on{background:var(--blBg);border-color:rgba(125,184,255,.5)}
.qcopt-mark{width:17px;height:17px;border-radius:50%;border:2px solid var(--tx3);flex-shrink:0;position:relative;transition:border-color .12s}
.qcopt.on .qcopt-mark{border-color:var(--bl)}
.qcopt.on .qcopt-mark::after{content:'';position:absolute;inset:3px;border-radius:50%;background:var(--bl)}
.qcopt-label{font-size:13.5px;color:var(--tx2);line-height:1.4}
.qcopt.on .qcopt-label{color:var(--tx)}
.qcopt-label b{color:var(--tx);font-weight:600}
.qcopt.on .qcopt-label b{color:var(--bl)}

.card{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:18px 20px}
.warnbox{background:var(--amBg);border-left:3px solid var(--am);border-radius:0 var(--r) var(--r) 0;padding:13px 16px;font-size:13px;color:var(--am);margin-bottom:18px;line-height:1.65}
/* the same shape saying the opposite, for a check that came back clean —
   "nothing is wrong" in amber reads as a warning at a glance */
.okbox{background:var(--grBg);border-left:3px solid var(--gr);border-radius:0 var(--r) var(--r) 0;padding:13px 16px;font-size:13px;color:var(--gr);margin-bottom:18px;line-height:1.65}
.warnbox strong{color:#FFE0B0}
.ftbox.rejbar{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.ftbox{background:var(--rdBg);border-left:3px solid var(--rd);border-radius:0 var(--r) var(--r) 0;padding:11px 15px;font-size:12.5px;color:var(--rd);line-height:1.6;margin-top:13px}

/* tables */
.tbl{width:100%;font-size:13.5px;border-collapse:collapse}
.tbl th{text-align:left;padding:0 14px 12px 0;font-family:var(--fm);font-weight:500;color:var(--tx3);border-bottom:1px solid var(--bds);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase}
.tbl td{padding:14px 14px 14px 0;border-bottom:1px solid var(--bd);vertical-align:middle}
.tbl tfoot td{border-bottom:none;border-top:1px solid var(--bds);font-family:var(--fm);font-size:12.5px}
.tbl .num{font-family:var(--fm);font-size:13px}
.lbrow{cursor:pointer;transition:background .12s}
.lbrow:hover{background:var(--sf2)!important}
/* A row somebody cannot open still answers when it is clicked — it says why.
   The cursor is the one thing that can say so before the click, so it does. */
.lbshut{cursor:default}
.bar{height:6px;background:var(--sf3);border-radius:3px;overflow:hidden;width:90px;display:inline-block;vertical-align:middle}
.bar i{display:block;height:100%;background:var(--lime);border-radius:3px}

/* filters */
.filt{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px;align-items:center}
.chip{padding:6px 14px;border-radius:40px;font-size:12.5px;cursor:pointer;border:1px solid var(--bds);color:var(--tx2);background:var(--sf);transition:all .12s}
.chip:hover{border-color:var(--bdl);color:var(--tx)}
.chip.on{background:var(--lime);border-color:var(--lime);color:#101012;font-weight:600}
.chip b{font-family:var(--fm);font-weight:600;margin-left:5px;opacity:.65}

/* pool */
.pool{display:grid;grid-template-columns:repeat(auto-fill,minmax(284px,1fr));gap:11px}
.pc{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:17px 18px;display:flex;flex-direction:column;gap:12px;transition:border-color .12s}
.pc:hover{border-color:var(--bds)}
.pc .pct{font-size:14.5px;font-weight:600;line-height:1.4}
.pc .pcm{font-size:12.5px;color:var(--tx3);line-height:1.55}
/* The editor a writer had in mind. Muted, at the size of the writer line
   directly above it, because on somebody else's card this is context and not
   an instruction — an editor scanning seventy cards should read past it. */
.pc .pcsug{font-size:12.5px;color:var(--tx3);line-height:1.55;font-family:var(--fm)}
/* The same card when it is yours. A lime border and a tinted ground rather
   than a solid fill, the same treatment .brandrow-cur uses for "this is the
   one you are on" — solid lime here would out-shout the Claim button, which
   is the only thing on this card anybody presses.
   The line inside turns to full text colour: on your own card it stops being
   context and becomes the reason the card is at the top. */
.pc.pc-sug{border-color:var(--lime);background:var(--limeBg)}
.pc.pc-sug .pcsug{color:var(--tx);font-weight:600}
/* ---------- the half of the pool that has not arrived yet ----------
   A topic on its way is a different kind of card from one that is ready, and
   the design has to say so before anybody reads a word: the assets are not
   made, there is nothing to listen to, and the only thing to do is read and
   decide. So it is quieter than a pool card -- the surface steps back to the
   page ground and the border stays plain -- and it earns colour only once
   somebody has acted on it. A ready card is an offer; this is a notice. */
.pc.pc-up{background:var(--bg);border-style:dashed}
.pc.pc-up:hover{border-color:var(--bds)}
/* Where it actually is right now, beside the code. Small, monospaced and in
   the muted tone, because it is the one fact on the card that changes on its
   own while nobody is looking -- it answers "how close is this" and nothing
   more. */
.pc-up .upstage{font-family:var(--fm);font-size:10.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--tx3);margin-left:9px}
/* Spoken for. Blue rather than lime: lime on this page already means "written
   for you" on a card you can claim right now, and two greens a card apart
   would be two different promises wearing one colour. */
.pcbook{font-size:12.5px;font-family:var(--fm);color:var(--tx3);
  display:flex;align-items:center;gap:7px}
.pcbook svg{width:14px;height:14px;flex:none}
.pc.pc-booked{opacity:.72}
.pc.pc-booked-me{border-color:var(--bl);border-style:solid;background:var(--blBg);opacity:1}
.pc.pc-booked-me .pcbook{color:var(--tx);font-weight:600}
/* Why this one is back in the pool. Amber rather than lime: it is a caution to
   read before claiming, not another thing to click. */
/* A note on a pool card, not a warning about one. It used to be amber with a
   warning triangle and editors read that as damage and skipped the card — see
   poolNote(). Same words, said quietly: the left rule keeps it distinct from
   the description above it without claiming anything is wrong. */
/* ---------- pool search ----------
   The snippet is why a card is in the results: the line of the script the
   words were actually on. Marked rather than bolded, because bold is already
   the writer's name on the card above it. */
.poolsearch{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;margin:0 0 14px}
.poolsearch .tcode{color:var(--tx3);padding-bottom:9px;white-space:nowrap}
.pcsnip{display:flex;gap:7px;align-items:flex-start;margin:2px 0 0;padding:8px 10px;background:var(--sf3);border-radius:8px;font-size:12px;line-height:1.5;color:var(--tx2)}
.pcsnip svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.7;fill:none;flex-shrink:0;margin-top:2px;opacity:.7}
.pcsnip mark{background:var(--limeBg);color:var(--lime);border-radius:3px;padding:0 2px}
.pcnote{font-size:12px;line-height:1.55;color:var(--tx2);background:var(--sf2);
  border-left:2px solid var(--bds);border-radius:0 var(--r) var(--r) 0;padding:8px 11px}
/* flex-wrap:wrap because .btn is white-space:nowrap and flex items do not
   shrink a nowrap label below its own text width — three buttons ("Feedback",
   "Move To Animation Pool", "Duplicate") measured 402px of content in a
   281px row, and with nothing to wrap to the overflow pushed straight
   through the card's edge and, on the cards nearest the right side of the
   grid, past the edge of the screen itself: a sliver of the Duplicate
   button sitting outside the page with nothing visibly attached to it. */
.pcrow{display:flex;gap:8px;flex-wrap:wrap}
.pcrow .btn{flex:1 1 auto;justify-content:center;min-width:110px}
.pcl{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--lime);cursor:pointer}
.pcl:hover{text-decoration:underline}
.pcl svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.8;fill:none}

/* storyboard + animation production */
.sblist{display:flex;flex-direction:column;gap:11px;margin-bottom:16px}
.sfcard{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:16px 18px;display:flex;flex-direction:column;gap:10px}
.sfhead{display:flex;align-items:center;justify-content:space-between;gap:10px}
.sfrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}
.sfslot{background:var(--sf2);border:1px dashed var(--bds);border-radius:var(--r);padding:10px;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:6px;min-height:64px}
.sfslot img,.sfslot video{border:1px solid var(--bd)}

/* detail + chat */
.dtl{display:grid;grid-template-columns:320px 1fr;border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden;height:calc(100vh - 56px - 180px);min-height:480px}
.dl{background:var(--sf);border-right:1px solid var(--bd);display:flex;flex-direction:column;overflow-y:auto}
.dprev{height:170px;background:#0C0C0E;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.dprev svg{width:34px;height:34px;stroke:var(--tx3);stroke-width:1.4;fill:none}
.dsec{padding:16px 18px;border-bottom:1px solid var(--bd)}
.dsec .lb{font-family:var(--fm);font-size:10px;color:var(--tx3);margin-bottom:11px;text-transform:uppercase;letter-spacing:.12em}
.dsec .dt{font-size:14.5px;font-weight:600;line-height:1.4}
.dsec .dm{font-size:12.5px;color:var(--tx3);margin-top:6px}
.mrow{display:flex;align-items:center;gap:10px;margin-bottom:10px;font-size:13px;color:var(--tx2)}
.mrow .rl{margin-left:auto;font-family:var(--fm);font-size:10.5px;color:var(--tx3)}
.dr{display:flex;flex-direction:column;overflow:hidden;min-width:0;background:var(--sf)}
.drh{padding:0 18px;border-bottom:1px solid var(--bd);display:flex;align-items:center;gap:4px;flex-shrink:0}
.dtab{font-family:var(--fm);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);padding:14px 14px 12px;cursor:pointer;border-bottom:2px solid transparent;font-weight:500}
.dtab:hover{color:var(--tx2)}
.dtab.on{color:var(--lime);border-bottom-color:var(--lime)}
.cl{flex:1;overflow-y:auto;padding:18px;display:flex;flex-direction:column;gap:18px}
.cm{display:flex;gap:11px}
.cmb{flex:1;min-width:0}
.cmt{display:flex;align-items:center;gap:8px;margin-bottom:6px;flex-wrap:wrap}
.cma{font-size:13px;font-weight:600}
.cmtm{font-family:var(--fm);font-size:11px;color:var(--tx3)}
.tsb{font-family:var(--fm);font-size:11px;font-weight:600;padding:2px 8px;border-radius:4px;background:var(--limeBg);color:var(--lime);cursor:pointer}
.stg{font-family:var(--fm);font-size:10px;font-weight:500;padding:2px 8px;border-radius:4px;background:var(--sf3);color:var(--tx3);letter-spacing:.06em;text-transform:uppercase}
.rb{font-size:10.5px;font-weight:600;padding:3px 9px;border-radius:4px;background:var(--grBg);color:var(--gr)}
.ob{font-size:10.5px;font-weight:600;padding:3px 9px;border-radius:4px;background:var(--rdBg);color:var(--rd)}
.chb{font-size:10.5px;font-weight:600;padding:3px 9px;border-radius:4px;background:var(--blBg);color:var(--bl)}
/* A pasted link ran off the right edge with the rest of the sentence behind
   it, and every line break someone typed collapsed into one paragraph.
   overflow-wrap:anywhere rather than break-word on purpose: it also lowers the
   min-content width, which is what stops one long link widening the whole rail. */
.cmx{font-size:13px;color:var(--tx2);line-height:1.65;white-space:pre-wrap;overflow-wrap:anywhere}
/* Every other place a person's typed text is rendered, for the same reason and
   with the same keyword: `anywhere` also lowers min-content width, so one
   pasted Drive link cannot widen a pool card, a queue row, or the page itself.
   A link in a description used to pan Title And Publish 262px sideways. */
.rotext,.descbox,.pcnote,.sbtext,.scriptread p,.sr-open,
.ftbox,.qm,.roopt{overflow-wrap:anywhere}
.mn{color:var(--lime);font-weight:500}
.cac{display:flex;gap:14px;margin-top:8px}
.ca{font-family:var(--fm);font-size:10.5px;color:var(--tx3);cursor:pointer;background:none;border:none;display:flex;align-items:center;gap:5px;letter-spacing:.06em;text-transform:uppercase}
.ca:hover{color:var(--lime)}
.ca svg{width:12px;height:12px;stroke:currentColor;stroke-width:1.8;fill:none}
.rth{margin-top:14px;padding-left:15px;border-left:2px solid var(--bds);display:flex;flex-direction:column;gap:14px}
.cmp{border-top:1px solid var(--bd);padding:14px 18px;flex-shrink:0;background:#131316}
.cmpt{display:flex;align-items:center;gap:9px;margin-bottom:10px;flex-wrap:wrap}
.cmpt svg{width:13px;height:13px;stroke:var(--tx3);stroke-width:1.8;fill:none}
.cmpt label{font-family:var(--fm);font-size:10px;color:var(--tx3);letter-spacing:.1em;text-transform:uppercase}
.tsi{width:66px!important;text-align:center;font-family:var(--fm)!important;font-size:12px!important;padding:6px!important}
.cma2{width:100%;border:1px solid var(--bds);border-radius:var(--r);padding:10px 12px;font-size:13px;font-family:var(--fu);color:var(--tx);background:var(--sf2);resize:none;height:56px;line-height:1.6}
.cma2:focus{border-color:var(--lime)}
/* flex-end plus margin-right:auto on the leading child reproduces
   space-between exactly while everything is on one row, but when the row wraps
   the trailing Post lands under the attach button instead of stranded at the
   far left. It already wrapped on the desktop too — three children need ~348px
   in a 343px rail — so this is not only a phone fix.
   .stg is scoped because it is used outside .cmf as well. */
/* Shown only where Enter actually posts — see cmKey(). On a phone Return
   makes a new line, so the hint would be a lie. */
.enterhint{display:none}
@media (any-pointer:fine){.enterhint{display:inline;font-family:var(--fm);font-size:10.5px;color:var(--tx3);margin-right:2px}}
.cmf{display:flex;align-items:center;justify-content:flex-end;margin-top:9px;gap:10px;flex-wrap:wrap}
.cmf .marktype,.cmf .stg{margin-right:auto}
.marktype{display:flex;gap:6px}
.mt{font-size:11px;padding:5px 11px;border-radius:40px;border:1px solid var(--bds);background:var(--sf2);color:var(--tx3);cursor:pointer;font-family:var(--fu);font-weight:500}
.mt.on-note{background:var(--sf3);color:var(--tx);border-color:var(--bdl)}
.mt.on-change{background:var(--blBg);color:var(--bl);border-color:var(--bl)}
.mt.on-fix{background:var(--grBg);color:var(--gr);border-color:var(--gr)}

/* chat bubbles */
.chatlist{flex:1;overflow-y:auto;padding:18px;display:flex;flex-direction:column;gap:12px}
.msg{max-width:78%;display:flex;gap:9px}
.msg.me{align-self:flex-end;flex-direction:row-reverse}
.msg .bub{background:var(--sf2);border:1px solid var(--bd);border-radius:12px;padding:9px 13px;font-size:13px;color:var(--tx2);line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere}
.msg.me .bub{background:var(--limeBg);border-color:rgba(127,166,255,.25);color:var(--tx)}
.msg .who{font-family:var(--fm);font-size:10px;color:var(--tx3);margin-bottom:4px}
.msg.me .who{text-align:right}
.chatlock{font-size:12px;color:var(--tx3);text-align:center;padding:10px;border-top:1px solid var(--bd);background:#131316}

/* activity */
.hist{display:flex;flex-direction:column}
.hi{display:flex;gap:12px;padding:11px 0;border-bottom:1px solid var(--bd);font-size:13px;align-items:flex-start}
.hi:last-child{border-bottom:none}
.hid{width:6px;height:6px;border-radius:50%;background:var(--lime);margin-top:7px;flex-shrink:0}
.hid.rej{background:var(--rd)}
/* flex:1 without min-width:0 keeps its automatic minimum, so a long link in a
   reject reason made this box wider than the rail instead of wrapping inside it */
.hi .ht{flex:1;min-width:0;color:var(--tx2);line-height:1.55;overflow-wrap:anywhere}
.hi .hm{font-family:var(--fm);font-size:11px;color:var(--tx3);white-space:nowrap}

/* sfx + avatar bank */
.sfxg{display:grid;grid-template-columns:repeat(auto-fill,minmax(188px,1fr));gap:10px;margin-bottom:10px}
.sfc{background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);padding:14px 15px;transition:border-color .12s}
.sfc:hover{border-color:var(--bds)}
.sfn{font-size:13px;font-weight:600}
.sfk{font-family:var(--fm);font-size:10.5px;color:var(--tx3);margin-top:7px;letter-spacing:.08em;text-transform:uppercase}
/* the folder label, now something you can press to see the rest of that folder */
.sfk-f{cursor:pointer;display:inline-block;padding:2px 8px;margin-top:6px;border-radius:40px;
  background:var(--sf2);border:1px solid var(--bd);transition:all .12s}
.sfk-f:hover{border-color:var(--bdl);color:var(--tx2)}
/* ---------- avatar bank ----------
   A shelf of renders, so the render is the card. Narrower columns than before
   because the poster carries the recognition now and the text underneath is
   only confirming it — more of them fit on a screen, which is the whole point
   of a bank you are picking from. */
.avbank{display:grid;grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:12px}
.avc2{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden;
  transition:border-color .12s,transform .12s;cursor:pointer}
.avc2:hover{border-color:var(--bdl);transform:translateY(-1px)}
.avc2:active{transform:none}
/* Avatars are shot vertical. 3:4 shows the face without giving one card a third
   of the screen — the full 9:16 made a row of eight taller than the viewport. */
.avc2 .prev{position:relative;aspect-ratio:3/4;background:#0C0C0E;border-bottom:1px solid var(--bd)}
.avc2 .prev video{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}
.avc2 .prevnone{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:8px;color:var(--tx3)}
.avc2 .prevnone svg{width:26px;height:26px;stroke:var(--tx3);stroke-width:1.4;fill:none}
.avc2 .prevnone span{font-family:var(--fm);font-size:10px;letter-spacing:.06em}
/* Over the poster rather than under it: costs no height, and a gradient keeps
   it readable whatever frame the render happens to open on. */
.avc2 .prevtop{position:absolute;left:0;right:0;top:0;display:flex;align-items:center;gap:6px;
  padding:8px 9px;flex-wrap:wrap;
  background:linear-gradient(180deg,rgba(0,0,0,.72) 0%,rgba(0,0,0,.35) 60%,rgba(0,0,0,0) 100%)}
.avc2 .body{padding:11px 12px 12px}
.avc2 .nm{font-size:13px;font-weight:600;line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.avc2 .mt2{font-size:11.5px;color:var(--tx3);margin-top:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.avc2 .avscript{margin-top:9px;font-size:11.5px}

/* leaderboard */
.lb1{background:linear-gradient(135deg,rgba(127,166,255,.14),transparent 60%);border-color:rgba(127,166,255,.35)!important}
.rank{font-family:var(--fd);font-size:20px;font-weight:700;color:var(--tx3);width:34px}
.rank.r1{color:var(--lime)}
.rank.r2{color:var(--tx)}
.rank.r3{color:var(--am)}
.medal{font-size:15px}

/* team panels */
.tops{display:grid;grid-template-columns:1fr 1fr;gap:11px;margin-bottom:10px}
.topt{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);padding:18px 20px}
.topt h4{font-family:var(--fm);font-size:10.5px;font-weight:500;margin-bottom:15px;color:var(--tx3);letter-spacing:.12em;text-transform:uppercase}
.trow{display:flex;align-items:center;gap:12px;padding:8px 0;font-size:13px}
.trow .tn{flex:1;color:var(--tx2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.trow .tv{font-family:var(--fm);font-size:12.5px;color:var(--tx);font-weight:600;min-width:20px;text-align:right}

/* title picker + platforms */
.titleopt{display:flex;align-items:center;gap:12px;padding:13px 15px;border:1px solid var(--bd);border-radius:var(--r);margin-bottom:8px;cursor:pointer;font-size:13.5px;background:var(--sf2);transition:all .12s;line-height:1.45}
.titleopt:hover{border-color:var(--bds)}
.titleopt.sel{border-color:var(--lime);background:var(--limeBg);color:var(--lime);font-weight:600}
.titleopt .rad{width:14px;height:14px;border-radius:50%;border:2px solid var(--bds);flex-shrink:0}
.titleopt.sel .rad{border-color:var(--lime);border-width:5px}
.plat{display:flex;gap:9px;flex-wrap:wrap;margin-top:13px}
.pb{display:flex;align-items:center;gap:8px;padding:9px 15px;border-radius:var(--r);border:1px solid var(--bds);background:var(--sf2);color:var(--tx2);font-size:12.5px;font-weight:500;cursor:pointer;font-family:var(--fu);transition:all .12s}
.pb:hover{background:var(--grBg);border-color:var(--gr);color:var(--gr)}
.pb.dn{background:var(--grBg);border-color:rgba(74,222,128,.45);color:var(--gr)}
.pb svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none}

/* admin */
.cred{font-family:var(--fm);font-size:12px;background:var(--sf2);border:1px solid var(--bds);border-radius:5px;padding:4px 9px;color:var(--tx2);display:inline-block}
.onb{counter-reset:step}
.onb .os{display:flex;gap:15px;padding:15px 0;border-bottom:1px solid var(--bd);align-items:flex-start}
.onb .os:last-child{border-bottom:none}
.onb .os::before{counter-increment:step;content:counter(step);font-family:var(--fd);font-size:17px;font-weight:700;color:var(--lime);min-width:26px}
.onb .os .ot{font-size:14px;font-weight:600;margin-bottom:5px}
.onb .os .od{font-size:13px;color:var(--tx2);line-height:1.6}

/* script panel for audio production */
.scriptbox{margin-top:13px;border:1px solid var(--bd);border-radius:var(--r);overflow:hidden;background:#0E0E10}
.sbh{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid var(--bd);background:var(--sf2)}
.sbcode{font-family:var(--fm);font-size:12px;font-weight:700;color:var(--lime);letter-spacing:.06em}
.sbtext{padding:16px 18px;font-size:14px;line-height:1.85;color:var(--tx2);max-height:230px;overflow-y:auto;white-space:pre-wrap}

/* asset bundle */
.bundle{display:flex;gap:9px;margin-top:13px;flex-wrap:wrap}
.bundle.col{flex-direction:column;gap:8px;margin-top:0}
.bundle.sm{margin-top:0}
.bi{display:flex;align-items:center;gap:9px;padding:9px 13px;border-radius:var(--r);border:1px solid var(--bd);background:var(--sf2);flex:1;min-width:130px}
.bundle.sm .bi{padding:7px 10px;min-width:0;flex:1}
.bi svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.7;fill:none;flex-shrink:0}
.bi.ok{color:var(--gr);border-color:rgba(74,222,128,.3)}
/* A badge that opens something. Without this they look identical to the ones
   that do nothing, which is why people clicked them and wondered why the audio
   would not play. */
/* Amber, not red: this is not a fault, it is a route that carries different
   consequences and the person signing it off should know before they do. */
.lanebadge{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.5;color:var(--am);background:var(--amBg);border:1px solid rgba(255,196,107,.3);border-radius:var(--r);padding:9px 12px;margin-top:13px}
.lanebadge svg{flex:none;margin-top:2px}
.bi.tap{cursor:pointer}
.bi.tap:hover{border-color:var(--gr);background:rgba(74,222,128,.08)}
.bi.pend{color:var(--am);border-color:rgba(255,196,107,.35)}
.bi .bn{font-size:12.5px;font-weight:600;color:var(--tx)}
.bundle.sm .bi .bn{font-size:11.5px}
.bi .bs{font-size:11px;color:var(--tx3);margin-top:2px}

/* upload zone */
.upzone{margin-top:13px;padding:13px 15px;border:1px dashed var(--bds);border-radius:var(--r);display:flex;align-items:center;gap:12px;background:#0E0E10}
/* the "what is different about this take" box, inside an upload slot */
.upzone input[type=text]{background:var(--sf);border:1px solid var(--bds);border-radius:var(--r);
  color:var(--tx);padding:7px 11px;font-size:12.5px;width:100%}
.upzone input[type=text]:focus{border-color:var(--bdl)}
/* a file dragged over any drop zone — an .upzone, or a whole composer footer
   that has no border of its own to swap the color of, hence the inset ring
   instead of border-color there */
.upzone.dragover{border-color:var(--lime);border-style:solid;background:var(--limeBg)}
.cmp.dragover{box-shadow:inset 0 0 0 2px var(--lime);background:var(--limeBg)}
.upnote{font-size:12px;color:var(--tx3)}
.upok{margin-top:13px;padding:11px 15px;border-radius:var(--r);background:var(--grBg);color:var(--gr);font-size:12.5px;font-weight:500;display:flex;align-items:center;gap:8px}

/* per-platform views, on a video's own Views tab */
.pvlist{display:flex;flex-direction:column;gap:1px;border:1px solid var(--bd);border-radius:var(--r);overflow:hidden}
.pvrow{display:flex;align-items:center;gap:12px;padding:12px 15px;background:#0E0E10}
.pvrow+.pvrow{border-top:1px solid var(--bd)}
.pvname{width:78px;flex:none;font-weight:600;font-size:13px}
.pvstat{flex:1;font-size:12.5px}

/* audio player mock */
.audioplayer{margin-top:13px;display:flex;align-items:center;gap:13px;padding:13px 15px;border:1px solid var(--bd);border-radius:var(--r);background:#0E0E10}
.apicon{width:34px;height:34px;border-radius:50%;background:var(--blBg);color:var(--bl);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.apicon svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none}
.apbody{flex:1;min-width:0}
.apname{font-size:12.5px;color:var(--tx2);margin-bottom:7px}
.apbar{height:5px;background:var(--sf3);border-radius:3px;overflow:hidden}
.apfill{width:38%;height:100%;background:var(--bl);border-radius:3px}

/* resource links list */
.reslist{margin-top:13px;border:1px solid var(--bd);border-radius:var(--r);overflow:hidden}
/* the margin above is for the cards, where this sits under other text. Inside
   the review scroller it is the first thing in a column that already spaces its
   children with gap, so the two would stack into an odd 31px gutter.

   flex-shrink:0 because .cl is itself a flex column, so this became a flex item
   the moment it moved inside — and .reslist is overflow:hidden, which means
   shrinking does not scroll it, it clips it. Measured: squeezed to 52px with
   209px of links inside, so 157px of what the writer supplied was gone from the
   screen with nothing to say so. The same property would be wrong one level up,
   as a sibling of .cl, where it would pin 196px that .cl needs; here the
   scrollbar is what absorbs the height instead. */
.cl>.reslist{margin-top:0;flex-shrink:0}
.reslist.empty2{padding:13px 15px;font-size:12.5px;color:var(--tx3)}
.reshead{padding:10px 15px;background:var(--sf2);font-family:var(--fm);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--tx3);display:flex;align-items:center;gap:7px;border-bottom:1px solid var(--bd)}
.reshead svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.8;fill:none}
/* One line each, ellipsised rather than wrapped — a raw URL with tracking
   params ran to 3-4 lines under word-break:break-all, and a card's height
   ended up decided by whichever link on it happened to be longest rather
   than by its actual content. Nobody reads a URL character by character
   anyway; they click it or press Copy above. The full address is still on
   the anchor's title (see resHtml() in app.js) for whoever hovers to check
   it, and still in the href for the click and the Copy button either way. */
.resitem{display:block;padding:10px 15px;font-size:12.5px;color:var(--bl);border-bottom:1px solid var(--bd);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.resitem:last-child{border-bottom:none}
.resitem:hover{background:var(--sf2);text-decoration:underline}

/* script document editor */
.docwrap{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden}
/* Was a hardcoded 1fr 1fr 1fr, which is the kind of number that is correct
   until somebody adds a fourth thing — the Suggested editor column landed on a
   row of its own, alone, under three. auto-fit counts the columns itself, so
   the next one costs nothing here, and on a narrow window they wrap in pairs
   instead of squeezing four selects into a phone width. */
.dochead{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));border-bottom:1px solid var(--bd)}
.dhcol{padding:14px 18px;border-right:1px solid var(--bd)}
.dhcol:last-child{border-right:none}
.dhcol label{font-family:var(--fm);font-size:10px;color:var(--tx3);letter-spacing:.1em;text-transform:uppercase;display:block;margin-bottom:7px}
.dhval{font-size:13.5px;font-weight:600}
.dhcol select{padding:6px 9px;font-size:13px}
.docsec{padding:18px 20px;border-bottom:1px solid var(--bd)}
.docsl{font-family:var(--fm);font-size:10.5px;color:var(--tx3);letter-spacing:.1em;text-transform:uppercase;margin-bottom:10px;display:flex;align-items:center;justify-content:space-between}
.wc{font-family:var(--fm);font-size:10.5px;color:var(--tx3);text-transform:none;letter-spacing:0;font-weight:400}
.doctitle{margin-bottom:0}
.titlerow{display:flex;gap:8px;margin-bottom:8px;align-items:center}
.titlerow input{flex:1}
.titlerow .ca{background:var(--sf2);padding:8px;border-radius:var(--r);border:1px solid var(--bds);flex-shrink:0}
.doctitle:last-child{margin-bottom:0}
.docarea{width:100%;padding:11px 13px;border:1px solid var(--bds);border-radius:var(--r);background:var(--sf2);color:var(--tx);font-size:13.5px;font-family:var(--fu);line-height:1.7;resize:vertical;}
.docarea:focus{border-color:var(--lime)}
.docbody{min-height:220px}
.docacts{display:flex;align-items:center;gap:12px;margin-top:12px}
.gcount{font-family:var(--fm);font-size:11px;color:var(--tx3)}
.gclean{margin-top:12px;padding:10px 14px;background:var(--grBg);color:var(--gr);border-radius:var(--r);font-size:12.5px;font-weight:500;display:flex;align-items:center;gap:8px}
.gissues{margin-top:12px;display:flex;flex-direction:column;gap:7px}
/* Styled by SEVERITY, not by rule name.

   It used to be by rule name — .gitem-spelling and .gitem-repeat — and the
   checker emitted six names, so four of the six drew unstyled. Adding a rule
   meant remembering to add a colour, and forgetting was invisible until
   somebody made that particular mistake. Three severities cover every rule
   there will ever be. */
.gitem{padding:8px 12px;border-radius:var(--r);font-size:12.5px;border-left:3px solid var(--am);background:var(--amBg);color:var(--am);
  display:flex;align-items:flex-start;gap:9px;cursor:pointer;text-align:left;width:100%;border-top:0;border-right:0;border-bottom:0;font-family:inherit;line-height:1.45}
.gitem:hover{filter:brightness(1.06)}
.gitem-error{border-color:var(--rd);background:var(--rdBg);color:var(--rd)}
.gitem-warning{border-color:var(--am);background:var(--amBg);color:var(--am)}
.gitem-style{border-color:var(--bd);background:var(--bg2);color:var(--tx2)}
/* The word itself, so the writer can see what is being talked about without
   reading the sentence explaining it. */
.gword{font-family:var(--fm);font-size:11.5px;font-weight:600;background:rgba(0,0,0,.10);padding:1px 6px;border-radius:5px;white-space:nowrap;flex-shrink:0;max-width:180px;overflow:hidden;text-overflow:ellipsis}
.gfix{font-family:var(--fm);font-size:11px;opacity:.85;flex-shrink:0}
.gtabs{display:flex;gap:6px;margin-top:12px;flex-wrap:wrap}
.gtab{font-size:11.5px;font-family:var(--fm);padding:4px 10px;border-radius:999px;border:1px solid var(--bd);background:var(--bg2);color:var(--tx2);cursor:pointer}
.gtab.on{background:var(--tx);color:var(--bg);border-color:var(--tx)}
.gtab[disabled]{opacity:.4;cursor:default}
.linkrow{display:flex;gap:8px;margin-bottom:8px;align-items:center}
.linkrow input{flex:1}
.linkrow .ca{background:var(--sf2);padding:8px;border-radius:var(--r);border:1px solid var(--bds)}
.docfoot{padding:18px 20px;display:flex;justify-content:flex-end}

/* notification bell + panel */
.bellwrap{position:relative;display:flex;align-items:center;cursor:pointer;color:var(--tx2);padding:6px;border-radius:50%;transition:background .12s}
.bellwrap:hover{background:var(--sf2);color:var(--tx)}
.bellwrap svg{width:19px;height:19px;stroke:currentColor;stroke-width:1.7;fill:none}
.belldot{position:absolute;top:-3px;right:-3px;background:var(--rd);color:#101012;font-family:var(--fm);font-size:10px;font-weight:700;border-radius:10px;min-width:17px;height:17px;display:flex;align-items:center;justify-content:center;padding:0 4px}
/* two max-heights on purpose: dvh wins where it is understood, vh is the
   fallback. min() resolves to 420px on anything taller than 496px, so a desktop
   and a portrait phone are unchanged; a landscape phone stops overflowing. */
.notifpanel{position:absolute;top:56px;right:28px;width:340px;max-height:min(420px,calc(100vh - 76px));max-height:min(420px,calc(100dvh - 76px));overflow-y:auto;background:var(--sf);border:1px solid var(--bds);border-radius:var(--rl);box-shadow:0 12px 32px rgba(0,0,0,.5);z-index:200}
.npHead{padding:14px 16px;font-family:var(--fm);font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--tx2);border-bottom:1px solid var(--bd);display:flex;align-items:center;gap:8px}
.npcount{background:var(--sf3);color:var(--tx3);font-size:10.5px;padding:1px 7px;border-radius:10px}
.npList{display:flex;flex-direction:column}
.npItem{display:flex;gap:11px;padding:13px 16px;border-bottom:1px solid var(--bd);cursor:pointer;transition:background .12s}
.npItem:last-child{border-bottom:none}
.npItem:hover{background:var(--sf2)}
.npItem.unread{background:rgba(127,166,255,.05)}
.npDot{width:7px;height:7px;border-radius:50%;background:var(--lime);margin-top:5px;flex-shrink:0}
.npDot.read{background:var(--bds)}
.npBody{flex:1;min-width:0}
.npMsg{font-size:12.5px;color:var(--tx2);line-height:1.5;overflow-wrap:anywhere}
.npTime{font-family:var(--fm);font-size:10.5px;color:var(--tx3);margin-top:5px}
.npEmpty{padding:24px 16px;text-align:center;font-size:12.5px;color:var(--tx3)}

/* empty */
.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:52px 20px;text-align:center;border:1px dashed var(--bds);border-radius:var(--rl)}
.empty svg{width:28px;height:28px;stroke:var(--tx3);stroke-width:1.4;fill:none;margin-bottom:15px}
.empty h3{font-size:14px;font-weight:600;color:var(--tx2);margin-bottom:6px}
.empty p{font-size:13px;color:var(--tx3)}

/* modal */
/* z-index 150, dan angkanya perlu dibaca bersama tetangganya.

   Tangganya, dari bawah:

     100  .sb        sidebar layar sempit
     120  .navfly / .ovpw / .notifpanel      menu yang menggantung
     130  .ovamenu / .bsw                    pemilih orang, pemilih brand
     140  .idw       laci Idea QC / Script QC
     150  .ov        SETIAP dialog  <- baris ini
     200  .selp      panel pemilih DI DALAM sebuah dialog
     320  #peek      dialog di atas dialog (baca skrip saat menolak)
     400  toast, spanduk notifikasi, palang versi baru

   Sebelumnya .ov di 100 — di bawah lacinya. Gejalanya cuma muncul di separuh
   tombol dan itu yang membuatnya bertahan lama: askConfirm() menambal dirinya
   sendiri dengan style.zIndex='300' inline, jadi Approve tergambar di depan
   laci sementara Reject — yang lewat mod(), tanpa tambalan itu — tergambar di
   belakangnya. Dua jalan ke satu gagasan, dan hanya satu yang tahu jawabannya.

   Tambalan inline itu sekarang dicabut: yang menentukan tinggi sebuah dialog
   adalah baris ini, satu tempat. .selp sengaja tetap DI ATAS .ov — sebuah
   dropdown yang dibuka dari dalam dialog harus menutupi dialognya sendiri. */
.ov{position:fixed;inset:0;background:rgba(0,0,0,.75);display:flex;align-items:center;justify-content:center;z-index:150;padding:24px;backdrop-filter:blur(4px)}
.mod{background:var(--sf);border:1px solid var(--bds);border-radius:var(--rl);padding:26px;width:540px;max-width:100%;max-height:88vh;overflow-y:auto}
.mod h2{font-family:var(--fd);font-size:20px;font-weight:700;margin-bottom:7px}
.mod .msub{font-size:13px;color:var(--tx3);margin-bottom:20px;line-height:1.6}
.mf{display:flex;justify-content:flex-end;gap:9px;margin-top:22px}

/* ---------- review workspace, Frame.io style ---------- */
.astrip{display:flex;align-items:center;gap:11px;flex-wrap:wrap;padding:12px 16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);margin-bottom:12px}
.astrip .sep{width:1px;height:17px;background:var(--bd)}
.astrip .am2{font-size:12.5px;color:var(--tx3)}
.astrip .am2 b{color:var(--tx2);font-weight:500}
/* the review page owns the whole viewport, so the video gets the space instead
   of a stack of headers pushing it down */
.rvpage{display:flex;flex-direction:column;height:calc(100vh - 112px);min-height:520px}
.rvbar{display:flex;align-items:center;gap:11px;flex-wrap:wrap;padding:0 0 11px;flex-shrink:0}
.rvbar .rvtitle{font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:38vw}
/* ---- remah, panah dan waktu kirim ----  artboard Final QC, 19 September

   Remahnya satu kontrol, bukan tombol Back plus judul: segmen pertamanya yang
   bisa ditekan dan ia yang kembali. Itu yang membuat baris ini tetap SATU
   baris — dan baris ini memang harus satu, karena tiga baris chrome yang
   mendorong videonya turun adalah persis keadaan yang dibongkar waktu .rvbar
   dibuat. */
/* Remahnya yang melar DAN menyusut, menggantikan pengganjal flex yang dulu
   berdiri di antara judul dan tombolnya. Itu bedanya antara satu baris dan
   dua: pengganjal memberi remahnya lebar penuh dan memaksa sisanya membungkus;
   ini membuat judulnya yang menyerah lebih dulu, dengan elipsis. */
/* flex-basis 120, BUKAN auto, dan itu bedanya antara satu baris dan dua.

   Di flex yang membungkus, pemutusan baris dihitung dari flex-basis SEBELUM
   penyusutan: flex-shrink mengecilkan barang di dalam baris yang sudah
   terbentuk, ia tidak pernah mencegah baris itu pecah. Dengan basis `auto`
   remah ini masuk hitungan selebar judul penuhnya — 609px pada judul biasa —
   jadi barisnya pecah lebih dulu dan grow-nya kemudian malah memakai sisa
   ruang baris pertama, membuatnya lebih lebar lagi. Terukur persis begitu.

   Basis 120 membuat hitungannya muat, dan grow mengembalikan sisanya. */
.rvcrumb{flex:1 1 120px;display:flex;align-items:center;gap:8px;min-width:0}
/* max-width dilepas di dalam remah: 38vw adalah batas untuk judul yang berdiri
   sendiri, dan di sini yang membatasinya adalah ruang yang tersisa. */
.rvcrumb .rvtitle{max-width:none;flex:1;min-width:0}
.rvback{display:inline-flex;align-items:center;gap:7px;border:0;background:transparent;padding:0;
  cursor:pointer;font-family:var(--fu);font-size:14px;color:var(--lg-tx3);white-space:nowrap}
.rvback:hover{color:var(--lg-tx)}
.rvci{width:15px;height:15px;fill:currentColor;flex-shrink:0}
.rvcrumb i{color:var(--lg-tx4);font-style:normal;flex:none}
/* Waktu kirim: monospasi dan tenang. Ia fakta rujukan, bukan sesuatu yang
   dibaca tiap kali — dan angka proporsional di sebelah tombol terbaca sebagai
   label tombol itu. */
/* Inter, bukan mono. Ini kalimat yang dibaca ("Submitted 12 Sep 2026 02:48"),
   bukan angka yang dibandingkan kolom demi kolom. */
.rvsent{font-family:var(--fu);font-size:12px;color:var(--lg-tx3);white-space:nowrap}
.rvacts{display:flex;align-items:center;gap:11px;flex-wrap:wrap}
.rvw{display:grid;grid-template-columns:1fr 380px;border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden;flex:1;min-height:0}
/* a 9:16 cut needs height, not width — give the whole workspace more room */
/* the workspace already fills the page, so vertical only changes nothing here */
.rvw.vert{}
.rvm{background:#0A0A0C;display:flex;flex-direction:column;overflow:hidden;min-width:0}
.rvm.doc{background:var(--sf)}
.rvs{background:var(--sf);border-left:1px solid var(--bd);display:flex;flex-direction:column;overflow:hidden;min-width:0;position:relative}
/* dropping anywhere in the panel — the note list, the tab headers, not just
   the composer strip — highlights the whole thing, the way a chat app does */
.rvs.dragover{box-shadow:inset 0 0 0 2px var(--lime)}
.chatpage.dragover{box-shadow:inset 0 0 0 2px var(--lime);border-radius:10px}
.rvtop{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid #1C1C20;background:#0E0E10;flex-wrap:wrap}
/* "Version" dan "Take" adalah label biasa di sebelah kiri deretan tabnya, bukan
   kapital kecil bersepasi seperti dulu — itu kosakata artboard lama. */
.rvtop .tl{font-family:var(--fu);font-size:13px;font-weight:500;letter-spacing:0;
  text-transform:none;color:var(--lg-tx2);margin-right:3px}
/* .vchip dan .ochip TIDAK didefinisikan di sini lagi. Joe, 19 September:
   "tombol version menggunakan style tab" — jadi ia memakai resep .idwtb yang
   sama persis, dan resep itu ditulis satu kali di bagian laci. Ditulis ulang di
   sini, dua deret tab yang seharusnya identik punya dua sumber yang akan
   menyimpang. Urutan cascade-nya juga ada di sana, dan itu bukan kebetulan:
   .ochip.on harus datang SESUDAH .vchip.on untuk menang.

   Pil status "In Review" (.rvst) yang dulu mengunci ujung kanan baris ini
   sudah dilepas — fullscreen yang menempatinya sekarang. */
.rvstage{flex:1;position:relative;background:#000;display:flex;align-items:stretch;justify-content:center;min-height:0;overflow:hidden}
.rvstage.split{display:grid;grid-template-columns:1fr 1fr;gap:2px;background:var(--bd)}
/* height:100% + min-height:0 stops a tall 1080x1920 source from stretching the
   pane to its own intrinsic height and blowing the workspace open */
.rvpane{position:relative;flex:1;height:100%;display:flex;align-items:center;justify-content:center;background:#000;overflow:hidden;min-width:0;min-height:0}
/* contain, never crop or stretch: works for 1080x1920, 4K landscape, anything */
.rvpane video{width:100%;height:100%;object-fit:contain;display:block;background:#000}
.rvlabel{position:absolute;top:9px;left:9px;font-family:var(--fm);font-size:10px;letter-spacing:.1em;text-transform:uppercase;background:rgba(0,0,0,.7);color:var(--tx2);padding:3px 9px;border-radius:4px;z-index:5}
.rvblank{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:11px;color:var(--tx3);text-align:center;padding:24px}
.rvblank svg{width:38px;height:38px;stroke:currentColor;stroke-width:1.2;fill:none}
.rvblank .bt{font-family:var(--fm);font-size:12px;letter-spacing:.08em;color:var(--tx2)}
.rvblank .bs{font-size:12.5px;max-width:330px;line-height:1.65}
/* both are positioned by JS onto the exact rectangle the frame occupies,
   so a drawing lands on the picture and not on the black letterbox bars */
/* touch-action, not preventDefault: once the compositor has started a pan it
   cannot be cancelled from script, so without this a finger on the frame
   scrolls the page instead of drawing. Inert for a mouse. */
.rvcv{position:absolute;inset:0;cursor:crosshair;z-index:4;touch-action:none}
.rvanno{position:absolute;inset:0;pointer-events:none;z-index:3;object-fit:contain}
.rvsz{position:absolute;pointer-events:none;z-index:2}
/* A dark hatch reads fine over bright footage and disappears over dark
   footage — exactly the frames a reviewer most needs this on, since a busy
   or dark composition is the one worth double-checking against the crop.
   Tinted red instead, low opacity: visible against anything, and it reads
   as "this area is claimed" at a glance without repainting the whole frame. */
.szband{position:absolute;background:repeating-linear-gradient(135deg,rgba(255,112,112,.22) 0 8px,rgba(255,112,112,.1) 8px 16px)}
.szband-top,.szband-bottom{left:0;right:0}
.szband-top{top:0;border-bottom:1.5px dashed rgba(255,112,112,.75)}
.szband-bottom{bottom:0;border-top:1.5px dashed rgba(255,112,112,.75)}
.szband-right{right:0;border-left:1.5px dashed rgba(255,112,112,.75)}
/* Bottom corner, not top: the top-left already carries the pane's own "V1
   Current" label, and on a narrow phone-width pane the two ran into each
   other — the bottom band has no competing label anywhere near it, at any
   width. */
.szlabel{position:absolute;bottom:8px;right:8px;background:rgba(18,18,21,.9);color:var(--tx2);font-family:var(--fm);font-size:10.5px;padding:4px 8px;border-radius:6px;border:1px solid var(--bds);white-space:nowrap;max-width:calc(100% - 16px);overflow:hidden;text-overflow:ellipsis}
/* Why the picture is not moving, over the picture that is not moving.

   z-index sits under the drawing canvas and the tool bar so it can never take
   a pointer away from them, and the overlay itself is pointer-events:none so
   click-to-play still reaches the video underneath. Only the pill inside it
   takes clicks, because it has a button on it. */
.rvbuf{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:5;pointer-events:none;padding:16px}
.rvbufi{pointer-events:auto;display:flex;align-items:center;gap:10px;max-width:100%;
  background:rgba(18,18,21,.94);border:1px solid var(--bds);border-radius:10px;
  padding:11px 15px;font-size:13px;color:var(--tx2);box-shadow:0 8px 30px rgba(0,0,0,.5)}
.rvbufi.bad{border-color:var(--rd);color:var(--rd)}
.rvbufi svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.8;flex-shrink:0}
.rvspin{width:14px;height:14px;border:2px solid var(--bds);border-top-color:var(--lime);
  border-radius:50%;animation:rvspin .7s linear infinite;flex-shrink:0}
@keyframes rvspin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.rvspin{animation-duration:2.4s}}

.rvtools{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:9px;background:rgba(18,18,21,.96);border:1px solid var(--bds);border-radius:40px;padding:7px 13px;z-index:6}
.swatch{width:17px;height:17px;border-radius:50%;cursor:pointer;border:2px solid transparent}
.swatch.on{border-color:#fff}
.rvscrub{padding:16px 16px 2px;background:#0E0E10;border-top:1px solid #1C1C20}
/* pan-y, not none: the browser keeps vertical panning — this bar sits inside
   the one page scroller that has to reach Post — and the app gets the
   horizontal drag. Effective touch-action is computed across ancestors, so
   putting it on the base rule also covers the .rvmark children. */
.rvtrack{position:relative;height:6px;background:var(--sf3);border-radius:999px;cursor:pointer;touch-action:pan-y}
/* Gradien Progress dari design system, resep yang sama dengan .ovtrack i di
   kartu Our Project Movement — keluarga Gradients mendaftarkannya dengan
   catatan "scrubber fill", jadi bilah inilah yang memang dimaksudnya.

   ARAHNYA SAMA DENGAN .ovtrack i, dan itu yang benar. Sempat terpasang 270deg
   pada 19 September karena saya menuliskannya persis seperti kalimat Joe;
   arahnya dibalik jam itu juga sesudah dicocokkan dengan design system. Dua
   bilah biru di aplikasi ini mengalir ke arah yang sama, dari satu resep.

   UJUNGNYA BULAT PENUH, dan tidak ada garis putih di atasnya: ujung bilah ini
   sendiri yang jadi kepala putarnya. .rvhead dihapus bersamanya, begitu juga
   baris di fioTick() yang menggesernya. */
.rvplayed{position:absolute;left:0;top:0;bottom:0;border-radius:999px;pointer-events:none;
  background:linear-gradient(90deg,#0F41A8 0%,#4D87FF 100%)}
.rvrange{position:absolute;top:-2px;bottom:-2px;background:rgba(125,184,255,.25);border-left:2px solid var(--bl);border-right:2px solid var(--bl);pointer-events:none}
.rvmark{position:absolute;top:-8px;width:13px;height:13px;border-radius:50%;margin-left:-6px;border:2px solid #0E0E10;cursor:pointer;z-index:2;transition:transform .1s}
.rvmark.open{background:var(--am)}
.rvmark.done{background:var(--gr)}
.rvmark:hover{transform:scale(1.3)}
/* ---------- animation board ----------
   A sheet, but grouped: the numbers row across the top answers "is anything
   stuck" before a single row is read, and each animator's block is its own
   small table under their name. */
.anbstats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin:0 0 16px}
.anbstat{background:var(--sf2);border:1px solid var(--bd);border-radius:10px;padding:12px 14px}
.anbstat label{display:block;font-family:var(--fm);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-bottom:6px}
.anbstat b{font-size:20px;font-weight:600}
.anbstat b.warn{color:var(--am)}
.anbstat b.bad{color:var(--rd)}
.anbbar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 10px}
.anbpct{display:flex;align-items:center;gap:8px}
.anbpct .bar{flex:1;min-width:70px}
.anbpct .tcode{color:var(--tx2);min-width:34px;text-align:right}
/* ---------- the cut's audio on a timeline ----------
   The whole cut as sound: a wave with a ruler under it, and nothing else. The
   wave is a canvas of decoded numbers — no video pixels — so the signed R2
   url cannot taint it the way it tainted the pen tool and the old
   freeze-frame. */
.rvtl{background:#0E0E10;padding:0 16px 14px}
.rvtlbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 0 8px}
.rvtlwave{position:relative;height:88px;background:var(--sf2);border:1px solid var(--bds);border-radius:8px;overflow:hidden;cursor:crosshair;touch-action:pan-y}
.rvtlwave canvas{display:block;width:100%;height:100%}
.rvtlhead{position:absolute;top:0;bottom:0;width:2px;margin-left:-1px;background:var(--lime);pointer-events:none}
.rvtlhint{font-family:var(--fm);font-size:11px;color:var(--tx3);padding:2px 0 6px}
.rvctrl{display:flex;align-items:center;gap:9px;padding:11px 16px 14px;background:#0E0E10;flex-wrap:wrap}
.pbtn{width:32px;height:32px;border-radius:6px;border:1px solid transparent;background:none;color:var(--tx2);cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.pbtn:hover{background:var(--sf2);color:var(--tx)}
.pbtn.on{background:var(--limeBg);color:var(--lime)}
.pbtn svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none;pointer-events:none}
/* The review player's volume. Same shape as the sound library's .trkvol input
   and deliberately NOT that class: .trkvol is display:none below 900px, which
   is right for a music bar squeezed onto a phone and wrong for the one control
   that decides whether a reviewer can hear the cut at all. flex-shrink:0
   because .rvctrl wraps, and a slider squeezed to 20px is worse than a slider
   on the next line. */
.rvvol{width:78px;flex-shrink:0;accent-color:var(--lime);cursor:pointer;background:none;padding:0;border:0;height:4px}
/* #1454DB dengan segitiga putih — Cobalt/500, tombol primer design system.
   Dulu var(--lime) (#7FA6FF) dengan glif gelap: biru muda dengan tulisan gelap
   adalah pasangan tombol SEKUNDER, dan satu-satunya tombol yang paling sering
   ditekan di layar ini tidak boleh membaca sebagai yang kedua. */
.pbtn.big{width:38px;height:38px;background:var(--lg-blue);color:#fff}
.pbtn.big:hover{background:var(--lg-blue-h)}
.pbtn.big svg{width:16px;height:16px;fill:currentColor;stroke:none;pointer-events:none}
/* ---------- Admin › Inspector ----------
   A database table is a different object from the rest of this app's tables.
   Everywhere else a row is a person or a video and the columns were chosen to
   fit a screen; here the columns are whatever the schema has — twenty of them,
   most empty on any given row — and the reader is looking something up rather
   than reading across. So: one line per row, never wrapped, and the two things
   you lose your place without pinned in place. */
.inspbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0 0 12px}
.inspbar .grow{flex:1}
.inspttl{font-family:var(--fm);font-size:13px;color:var(--tx);letter-spacing:.02em}
.inspttl span{color:var(--tx3);margin-left:8px}
.inspwrap{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);
  overflow:auto;max-height:min(68vh,720px);position:relative}
.inspt{border-collapse:separate;border-spacing:0;font-size:12.5px;
  font-family:var(--fm);width:max-content;min-width:100%}
/* The header has to stay put through a vertical scroll or column nineteen is
   an unlabelled column of dashes. Sticky needs a solid background of its own:
   the page wallpaper shows through anything transparent. */
.inspt thead th{position:sticky;top:0;z-index:2;background:var(--sf2);
  text-align:left;white-space:nowrap;padding:10px 14px;font-size:10px;font-weight:500;
  letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);
  border-bottom:1px solid var(--bds)}
.inspt td{padding:9px 14px;border-bottom:1px solid var(--bd);white-space:nowrap;
  max-width:320px;overflow:hidden;text-overflow:ellipsis;color:var(--tx2);vertical-align:top}
/* the first column is the id you are matching everything else against, so it
   survives scrolling sideways past the twelfth column */
.inspt td:first-child,.inspt th:first-child{position:sticky;left:0;background:var(--sf);
  border-right:1px solid var(--bd);color:var(--tx);font-weight:500}
.inspt th:first-child{background:var(--sf2);z-index:3}
.inspt tbody tr:hover td{background:var(--sf2)}
.inspt tbody tr:hover td:first-child{background:var(--sf3)}
.inspt tbody tr:last-child td{border-bottom:0}
/* ---------- the audit log reads as lines, not as a grid ----------
   Every other view here is a table because every other view is rows of short
   values you scan down a column. This one is sentences, and a sentence in a
   cell that clips at 320px is a sentence nobody reads -- the fact is in the
   half that got cut off. So it keeps the same wrapper, the same search and the
   same pager, and lays each row out as one line that wraps. */
.audlist{display:flex;flex-direction:column}
.audrow{display:grid;grid-template-columns:96px 150px 84px 1fr;gap:10px;
  align-items:baseline;padding:9px 14px;border-bottom:1px solid var(--bd);font-size:12.5px}
.audrow:last-child{border-bottom:0}
.audrow:hover{background:var(--sf2)}
.audat{font-family:var(--fm);font-size:11.5px;color:var(--tx3);white-space:nowrap}
.audwho{color:var(--tx);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.audwho i{display:block;font-style:normal;font-size:10px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tx3);font-weight:400}
.auddet{color:var(--tx2);line-height:1.45;overflow-wrap:anywhere}
.audact{font-family:var(--fm);font-size:10.5px;color:var(--tx3);margin-left:6px}
/* Narrow enough that four columns do not fit: the timestamp and who did it
   stay on one line above the sentence rather than each taking a third of the
   width and wrapping twice. */
@media (max-width:760px){
  .audrow{grid-template-columns:auto auto 1fr;row-gap:4px}
  .auddet{grid-column:1/-1}
  .audwho i{display:inline;margin-left:6px}
}
.inspnil{color:#55555C}
.inspnum{color:var(--tx)}
/* a value the cell had to cut short; the whole thing is in the title attribute */
.inspcut{border-bottom:1px dotted var(--bds);cursor:help}
.inspfoot{display:flex;gap:8px;align-items:center;margin-top:12px;flex-wrap:wrap}
.inspsrch{background:var(--sf);border:1px solid var(--bds);border-radius:var(--r);
  color:var(--tx);padding:6px 12px;font-size:12.5px;font-family:var(--fm);min-width:220px}
.inspsrch:focus{border-color:var(--bdl)}
.tcode{font-family:var(--fm);font-size:12px;color:var(--tx3);letter-spacing:.03em;white-space:nowrap}
.tcode b{color:var(--lime);font-weight:600}
/* Transport memakai SATU huruf untuk waktu dan untuk pemilih di ujungnya —
   JetBrains Mono, huruf yang sama dengan seluruh angka teknis di aplikasi ini.
   Keduanya memang sudah memakainya; yang berbeda dari artboard adalah warnanya.

   Posisi berjalan berhenti hijau-limau dan jadi putih. Limau adalah aksen, dan
   aksen yang dipakai untuk angka yang berubah dua puluh kali sedetik berhenti
   menandai apa pun — di baris ini satu-satunya yang boleh menarik mata adalah
   tombol play. Durasi di belakang garis miring tetap redup: ia tidak berubah. */
/* Inter juga, atas permintaan Joe — tapi DENGAN tabular-nums, dan itu bukan
   hiasan. Angka Inter lebarnya berbeda-beda; sebuah penanda waktu yang berubah
   dua puluh kali sedetik dengan angka proporsional membuat seluruh baris ini
   bergeser tiap frame, dan tombol di kanannya ikut bergoyang. Mono tidak punya
   masalah itu, jadi masalahnya datang bersama perpindahan hurufnya. */
.rvctrl .tcode{font-family:var(--fu);font-size:13px;color:var(--lg-tx3);
  font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1}
.rvctrl .tcode b{color:var(--lg-tx);font-weight:600}
/* Pemilih safe zone: plat #212429 yang sama dengan tombol fullscreen di baris
   Version dan dengan field di design system, setinggi 32 seperti tombol di
   sebelahnya. Sebelumnya ia bergaris kotak 22px — satu-satunya kontrol di
   transport yang terlihat seperti form. */
.rvsel{background:#212429;border:0;color:var(--lg-tx2);font-family:var(--fu);font-size:13px;
  border-radius:8px;height:32px;padding:0 10px;cursor:pointer;width:auto;}
.rvsel:hover{background:#2A2E35;color:var(--lg-tx)}
.rvfilt{display:flex;align-items:center;gap:6px;padding:10px 14px;border-bottom:1px solid var(--bd);flex-wrap:wrap}
.fchip{font-size:11px;padding:4px 11px;border-radius:40px;border:1px solid var(--bds);color:var(--tx3);cursor:pointer;background:var(--sf2)}
.fchip:hover{color:var(--tx2)}
.fchip.on{background:var(--sf3);color:var(--tx);border-color:var(--bdl)}
.cm.act .cmb{background:rgba(127,166,255,.05);border-radius:var(--r);outline:1px solid rgba(127,166,255,.2);outline-offset:7px}
.annoth{margin-top:9px;border:1px solid var(--bds);border-radius:var(--r);overflow:hidden;cursor:pointer;background:#000;max-width:190px}
/* bounded on both axes so a portrait grab does not run down the whole rail */
.annoth img{display:block;width:100%;max-height:240px;object-fit:contain;background:#000}
.annoth .al{font-family:var(--fm);font-size:9.5px;color:var(--tx3);padding:4px 8px;background:var(--sf2)}
.verb{font-family:var(--fm);font-size:9.5px;padding:2px 6px;border-radius:3px;background:var(--sf3);color:var(--tx3)}
/* who is at their desk, in Admin > Members. A dot AND a word: the dot alone is
   not readable to everybody, and the word alone is missed when the eye is
   running down a column of names. */
.onlnow{display:inline-flex;align-items:center;gap:5px;font-family:var(--fm);font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--gr);white-space:nowrap}
.onlnow::before{content:'';width:7px;height:7px;border-radius:50%;background:var(--gr);flex:0 0 auto}
.rngb{font-family:var(--fm);font-size:11px;color:var(--bl);background:var(--blBg);padding:3px 9px;border-radius:4px}
/* the frame Freeze Frame grabbed, sitting right above the box you are typing
   the note into — so what gets posted is visible before it is posted */
/* width:max-content, because the picture inside is capped by its own aspect
   ratio (frameFit caps the WIDTH so the ratio survives) and this box was a
   plain block filling the rail. A 9:16 still is about 112px wide inside a
   220px box, and the other 108px were painted black — a second empty panel
   next to the frame, which is what it looked like. */
.freezeshot{margin-top:9px;border:1px solid var(--bds);border-radius:var(--r);overflow:hidden;background:#000;width:max-content;max-width:220px}
.freezeshot img{display:block;width:100%;max-height:200px;object-fit:contain;background:#000}
/* A pinned frame is our own cut seeked to one second, not a copy of it, so the
   still is a <video> and the pen strokes ride over it as vectors. That keeps
   the frame at full quality and keeps the drawing sharp at any size — and it
   is the whole reason nothing has to be copied out of the video any more. */
/* The box takes the video's own aspect ratio the moment its metadata arrives
   (frameFit in app.js), so the video fills it exactly and there is no
   letterboxing for the ink overlay to be wrong about. The ratio below is only
   what it holds for the instant before that — portrait, because every cut here
   is. No max-height: capping height on a box with an aspect ratio breaks the
   ratio and puts the letterboxing back. */
.frameshot{position:relative;background:#000;line-height:0;aspect-ratio:9/16;max-width:113px}
.frameshot.th{max-width:135px}
.frameshot video{display:block;width:100%;height:100%;object-fit:cover;background:#000;pointer-events:none}
.frameink{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
/* Which cut is being posted, on the card that hands the file over. Same chips
   as the player's version row, so the choice made in review and the choice made
   at posting look like the same choice. */
.postpick{display:flex;gap:7px;align-items:center;flex-wrap:wrap;margin:0 0 12px;
  padding:9px 12px;background:var(--sf2);border:1px solid var(--bd);border-radius:var(--r)}
.freezeshot .ca{width:100%;justify-content:center;padding:6px 8px;background:var(--sf2);border-top:1px solid var(--bds)}
/* read only script document */
.rodoc{flex:1;overflow-y:auto;padding:0}
/* the same sections are rendered inside Read Script as well, where they had no
   padding at all and ran edge to edge */
.rosec{padding:18px 22px;border-bottom:1px solid var(--bd)}
.rosec:last-child{border-bottom:none}
.rolist{display:flex;flex-direction:column;gap:7px}
.roopt{display:flex;gap:11px;padding:11px 14px;border:1px solid var(--bd);border-radius:var(--r);background:var(--sf2);font-size:13.5px;line-height:1.5}
.roopt .n{font-family:var(--fm);font-size:11px;color:var(--tx3);flex-shrink:0;padding-top:2px}
.roopt.pick{border-color:var(--lime);background:var(--limeBg);color:var(--lime);font-weight:600}
.rotext{font-size:14px;line-height:1.85;color:var(--tx2);white-space:pre-wrap}
.rohook{font-size:15px;line-height:1.7;color:var(--tx);font-weight:500}
.rolink{display:block;font-size:12.5px;color:var(--bl);padding:9px 13px;border:1px solid var(--bd);border-radius:var(--r);background:var(--sf2);margin-bottom:7px;word-break:break-all}
.rolink:hover{border-color:var(--bl);text-decoration:none}
.descbox{font-size:13px;color:var(--tx2);line-height:1.65;padding:11px 14px;background:var(--sf2);border:1px solid var(--bd);border-radius:var(--r);margin-bottom:11px}
.descbox .dl2{font-family:var(--fm);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-bottom:6px}

::-webkit-scrollbar{width:9px;height:9px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--sf3);border-radius:5px;border:2px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:var(--bds)}

/* ---------- sign in ---------- */
/* #app is a flex row, so without flex:1 this box only took its own width and
   the whole sign in screen sat pinned to the left edge */
.loginwrap{flex:1;width:100%;min-height:100vh;height:100vh;min-height:100dvh;height:100dvh;display:flex;align-items:center;justify-content:center;padding:24px;background:radial-gradient(1000px 600px at 50% -10%,rgba(127,166,255,.06),transparent 70%)}
.loginbox{width:380px;max-width:100%;background:var(--sf);border:1px solid var(--bd);border-radius:14px;padding:30px 28px}
.lgbrand{font-family:var(--fd);font-size:26px;font-weight:700;letter-spacing:-.02em;margin-bottom:26px;line-height:1.1}
.lgbrand span{display:block;font-family:var(--fm);font-size:10.5px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--lime);margin-top:8px}
.lghint{font-size:12px;color:var(--tx3);line-height:1.6;margin-top:18px}

/* ---------- signed in user chip ---------- */
.rsw{cursor:pointer}
.uname{font-size:12.5px;color:var(--tx);line-height:1.25;font-weight:500}
.uname span{display:block;font-family:var(--fm);font-size:9.5px;color:var(--tx3);letter-spacing:.08em;text-transform:uppercase;margin-top:2px}

/* ---------- toast ---------- */
#toast{position:fixed;left:50%;bottom:26px;transform:translate(-50%,20px);background:var(--sf3);border:1px solid var(--bdl);color:var(--tx);
font-size:13px;padding:12px 18px;border-radius:9px;z-index:400;opacity:0;pointer-events:none;transition:all .18s;max-width:520px;box-shadow:0 10px 30px rgba(0,0,0,.55)}
/* clickable only while it is showing, so the Undo button actually works */
#toast.show{opacity:1;transform:translate(-50%,0);pointer-events:auto}

/* ---------- credentials shown once ---------- */
.credbox{background:#0E0E10;border:1px solid var(--bds);border-radius:var(--r);padding:16px 18px;display:flex;flex-direction:column;gap:12px}
.credbox b{font-family:var(--fm);font-size:15px;color:var(--lime);letter-spacing:.02em;user-select:all}
.cl2{display:block;font-family:var(--fm);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-bottom:4px}
.cred{font-family:var(--fm);font-size:12.5px;color:var(--tx2)}

/* ---------- media attached to a video ---------- */
.mediabox{border:1px solid var(--bd);border-radius:var(--r);padding:11px 12px;background:#0E0E10}
.mediabox audio{filter:invert(.92) hue-rotate(180deg)}
.filemeta{display:flex;align-items:center;gap:8px;font-family:var(--fm);font-size:11px;color:var(--tx3);margin-top:9px;flex-wrap:wrap}
.filemeta svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.7;fill:none}
.upnote b{color:var(--lime);font-family:var(--fm)}

/* report date range */
.rpbar{display:flex;gap:8px;margin-bottom:20px;flex-wrap:wrap;align-items:center}
.rprange{display:flex;align-items:center;gap:7px;background:var(--sf);border:1px solid var(--bds);border-radius:var(--r);padding:4px 5px 4px 9px}
.rprange input[type=date]{width:auto;padding:5px 7px;font-family:var(--fm);font-size:12px;border:none;background:var(--sf2);color-scheme:dark}
.rpto{font-size:12px;color:var(--tx3)}

/* sound suggestions under a review note */
.sfxsug{margin-top:9px}
.sfxhead{display:flex;align-items:center;gap:6px;font-family:var(--fm);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-bottom:6px}
.sfxhead svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:1.8}
.sfxrow{display:flex;align-items:center;gap:8px;padding:5px 8px;border:1px solid var(--bd);border-radius:6px;margin-bottom:5px;background:var(--sf2)}
.sfxn{font-size:12.5px;font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sfxk{font-family:var(--fm);font-size:10px;color:var(--tx3);text-transform:uppercase}

/* ================= phones and small screens ================= */
.burger{display:none;flex-direction:column;gap:4px;width:34px;height:34px;align-items:center;justify-content:center;background:none;border:1px solid var(--bds);border-radius:7px;cursor:pointer;flex-shrink:0;margin-right:12px}
.burger span{display:block;width:16px;height:1.5px;background:var(--tx2);border-radius:2px}
.scrim{display:none;position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:90}

@media (max-width:900px){
  .burger{display:flex}
  .sb{position:fixed;left:0;top:0;bottom:0;z-index:100;transform:translateX(-100%);transition:transform .18s ease;width:270px}
  body.navopen .sb{transform:none}
  body.navopen .scrim{display:block}
  .tb{padding:0 14px;height:52px}
  .crumb{font-size:10px}
  /* the trail wrapped onto three lines and burst the 52px bar. Only the page
     you are on survives here; the root and the back step are what go. */
  .crumb-up{display:none}
  .pg{padding:24px 14px 40px}   /* 24 yang sama; artboard mobile tidak ada, jadi jaraknya tidak diubah-ubah */
  .pg.tight{padding:12px 12px 28px}

  /* stacks: never squeeze five columns onto a phone */
  .stats,.flow{grid-template-columns:repeat(2,1fr);gap:8px}
  .st{padding:13px 14px}
  .st .v{font-size:24px}
  .ph h2{font-size:21px}
  .ph p{font-size:13px}
  .sfxg{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
  .pool{grid-template-columns:1fr}
  .tops{grid-template-columns:1fr}

  /* review workspace becomes one column: video first, notes under it */
  .rvpage{height:auto;min-height:0}
  .rvw{grid-template-columns:1fr;height:auto}
  .rvm{min-height:52vh;min-height:52dvh}
  /* One scroller on a phone: the page. The rail used to be capped at 70vh with
     its own scrollbar inside a page that also scrolled, which left a couple of
     hundred pixels of comments to read through a slot and needed the drag to
     start inside it. It grows now, and the page carries it. */
  .rvs{border-left:none;border-top:1px solid var(--bd);max-height:none}
  .rvs .cl,.rvs .chatlist{overflow:visible;flex:none}
  .rvbar .rvtitle{max-width:100%;white-space:normal}
  .rvctrl{flex-wrap:wrap;gap:6px}
  .rvstage{min-height:44vh;min-height:44dvh}
  .tcode{font-size:10px}
  /* was display:none, which hid playback speed AND both Compare selects on
     every phone. The base .rvsel{width:auto} beats the global select{width:100%}
     element selector, so no width fix is needed. */
  /* Tinggi 32 dari plat barunya dilepas di sini: 34 adalah lantai sasaran
     sentuh, dan sebuah select setinggi 32 px di telepon meleset. */
  .rvsel{font-size:12px;height:auto;padding:0 10px;min-height:34px;max-width:100%}

  /* chat fills the screen properly on a phone */
  /* the mandatory other half of the #app change: this sized itself against a
     100vh parent with ~42px of slack, which is the only reason Team Chat's Send
     is reachable today. Shrink the parent to dvh without this and Send goes
     under the fold — fixing Post and breaking Send in the same commit. */
  .chatpage{height:calc(100vh - 150px);height:calc(100dvh - 150px)}

  /* wide tables scroll sideways instead of breaking the layout */
  .tbl{display:block;overflow-x:auto;white-space:nowrap}
  /* the header labels are what balloon these tables, not the data */
  .tbl th{white-space:normal}
  /* The Members action column was pinned here for one day and it was a bad
     call: Reset Password and Remove together are ~420px, so on a 390px phone
     the pinned cell covered the whole screen and painted over the names it was
     supposed to sit beside. Narrow the table instead of pinning it — Password
     Set is the one column nobody acts on, and the two labels lose nothing by
     being shorter where there is no room. */
  .tbl-act th:nth-child(4),.tbl-act td:nth-child(4){display:none}
  /* width:100% on a select resolves against a shrink-to-fit column in an auto
     table layout, which is nothing — the role picker collapsed to its chevron
     and the role it was showing became unreadable. A min-width gives the column
     something real to size to. */
  .tbl select{min-width:150px}
  .docwrap,.mod{max-width:100%}
  .mod{padding:20px 16px;max-height:88vh;overflow-y:auto}
  .ov{padding:10px}
  .rpbar{gap:6px}
  .rprange{flex-wrap:wrap}
  .qacts .btn{flex:1;justify-content:center}
  .dochead{grid-template-columns:1fr 1fr}
}

@media (max-width:560px){
  .stats,.flow{grid-template-columns:1fr}
  .dochead{grid-template-columns:1fr}
  .rvfilt{overflow-x:auto;flex-wrap:nowrap;padding-bottom:4px}
  .ph h2{font-size:19px}
  .btn{padding:8px 12px}
  .tbr{gap:7px}
  .uname{display:none}
  /* with the name gone the pill is just a ring around an avatar; drop the ring
     so the row is the avatar, the bell and Sign Out, which all have to stay */
  .rsw{border:none;background:none;padding:0}
  .notifpanel{width:calc(100vw - 28px);right:-8px}
  /* one row of mark-type pills, then a full-width Post. margin-right:0 cancels
     the auto margin from the base rule so the 100% row is not pushed around */
  .cmf{gap:8px}
  .cmf .marktype{flex:1 1 100%;flex-wrap:wrap;margin-right:0}
  .cmf .stg{flex:1 1 auto}
  .cmf .btn.btn-p{flex:1;justify-content:center}
}

/* touch: make the timeline and buttons big enough to hit */
@media (hover:none){
  .pbtn{width:38px;height:38px}
  .pbtn.big{width:44px;height:44px}
  .rvtrack{height:10px}
  /* a 36px tall invisible band to grab. 13px of clearance exists below the bar,
     so -12px stays 1px clear of the play button; .rvmark keeps its own taps
     through its z-index. */
  .rvtrack::before{content:'';position:absolute;left:0;right:0;top:-14px;bottom:-12px}
  .rvmark{width:16px;height:16px;top:-10px;margin-left:-8px}
  .pcl,.ca{padding:6px 4px}
}

#toast{display:flex;align-items:center;gap:14px}
.tundo{background:none;border:none;color:var(--lime);font-weight:600;font-size:12.5px;cursor:pointer;font-family:var(--fu);padding:0;white-space:nowrap}
.tundo:hover{text-decoration:underline}

/* notification banners, top right, one per new item */
#nbanners{position:fixed;top:14px;right:14px;z-index:400;display:flex;flex-direction:column;gap:9px;max-width:340px}
.nbanner{display:flex;gap:10px;align-items:flex-start;background:var(--sf2);border:1px solid var(--bds);
border-left:3px solid var(--lime);border-radius:var(--rl);padding:11px 12px;cursor:pointer;
box-shadow:0 10px 30px rgba(0,0,0,.45);animation:nbin .22s ease}
.nbanner.out{opacity:0;transform:translateX(12px);transition:all .24s ease}
.nbanner:hover{border-color:var(--bdl)}
.nbi{color:var(--lime);margin-top:1px}
.nbb{flex:1;min-width:0}
.nbt{font-family:var(--fm);font-size:10px;letter-spacing:.11em;text-transform:uppercase;color:var(--tx3)}
.nbm{font-size:13px;line-height:1.5;color:var(--tx);margin-top:3px;word-break:break-word}
.nbx{background:none;border:0;color:var(--tx3);font-size:17px;line-height:1;cursor:pointer;padding:0 2px}
.nbx:hover{color:var(--tx)}
@keyframes nbin{from{opacity:0;transform:translateX(14px)}to{opacity:1;transform:none}}
@media (max-width:560px){#nbanners{left:10px;right:10px;max-width:none}}
.crumbl{cursor:pointer;color:var(--tx3)}
.crumbl:hover{color:var(--lime);text-decoration:underline}
.nb.new{background:var(--lime);color:#101012}

/* mention picker */
.mnpick{position:absolute;bottom:100%;left:0;right:0;margin-bottom:6px;background:var(--sf2);border:1px solid var(--bds);
border-radius:var(--rl);max-height:260px;overflow-y:auto;z-index:120;box-shadow:0 12px 34px rgba(0,0,0,.5)}
.mnrow{display:flex;align-items:center;gap:10px;padding:8px 11px;cursor:pointer}
.mnrow.on,.mnrow:hover{background:var(--sf3)}
.mnn{font-size:13px;font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mnr{font-family:var(--fm);font-size:10.5px;color:var(--tx3);white-space:nowrap}
.mn{color:var(--lime);font-weight:500;cursor:pointer;background:var(--limeBg);border-radius:4px;padding:0 4px}
.mn.me{background:var(--lime);color:#101012}

/* attachments on a message */
.attb{display:block;margin-top:7px}
.attb img{max-width:230px;max-height:230px;border-radius:8px;display:block}
.atta{display:block;margin-top:7px;width:230px;height:32px}
.attv{display:block;margin-top:7px;max-width:260px;border-radius:8px;background:#000}
.attf{display:inline-flex;align-items:center;gap:6px;margin-top:7px;font-size:12.5px;color:var(--bl);text-decoration:none}
.attf:hover{text-decoration:underline}
.attchip{position:absolute;bottom:100%;left:0;margin-bottom:6px;background:var(--sf2);border:1px solid var(--bds);
border-radius:var(--r);padding:6px 10px;font-size:12px;color:var(--tx2);display:flex;align-items:center;gap:8px}
.whol{cursor:pointer}
.whol:hover{color:var(--lime);text-decoration:underline}

/* profile card */
.profhead{display:flex;align-items:center;gap:14px;margin-bottom:16px}
.pphoto{width:56px;height:56px;border-radius:50%;object-fit:cover;flex:none}
.pphoto.ph2{font-size:17px}
.proflist{border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden}
.prow{display:flex;gap:14px;padding:11px 14px;border-bottom:1px solid var(--bd);font-size:13px}
.prow:last-child{border-bottom:0}
.pk{font-family:var(--fm);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--tx3);width:130px;flex:none;padding-top:2px}
.pv{color:var(--tx);word-break:break-word}

/* Monday to Sunday desk window */
.wkbar{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:14px}
.wklab{display:inline-flex;align-items:center;gap:7px;font-family:var(--fm);font-size:12px;color:var(--tx2);
background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);padding:7px 12px}
.wkdate{background:var(--sf);border:1px solid var(--bds);color:var(--tx2);border-radius:var(--r);
padding:6px 10px;font-size:12.5px;font-family:var(--fu);color-scheme:dark}

/* the new pieces on a phone */
@media (max-width:560px){
  .wkbar{gap:7px}
  .wklab{font-size:11px;padding:6px 9px;flex:1;min-width:0}
  .prow{flex-direction:column;gap:3px}
  .pk{width:auto}
  .profhead{gap:11px}
  .pphoto{width:46px;height:46px}
  .mnpick{max-height:180px}
  .attb img,.attv,.atta{max-width:100%}
  .rvstage.split{grid-template-columns:1fr;grid-template-rows:1fr 1fr}
}
.sfby{font-family:var(--fm);font-size:10.5px;color:var(--tx3);margin-top:8px}
/* the photo sits on top of the initials and covers them completely, so the
   initials are the fallback for a picture that is slow, missing or refused */
.avimg{position:absolute;inset:0;width:100%;height:100%;border-radius:50%;object-fit:cover;display:block;background:inherit}
.av{overflow:hidden;position:relative}
/* the download control is a link so the browser saves rather than navigates */
a.pbtn{text-decoration:none;display:inline-flex;align-items:center;justify-content:center}

/* the script, laid out to be read aloud rather than skimmed */
.scriptread{max-width:66ch}
.scriptread p{font-size:15px;line-height:1.85;color:var(--tx);margin-bottom:15px;white-space:pre-wrap}
.scriptread p:last-child{margin-bottom:0}
.sr-open{font-size:18px;line-height:1.6;font-weight:600;color:var(--tx);padding-left:13px;border-left:3px solid var(--lime)}
.srbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px;padding-top:13px;border-top:1px solid var(--bd)}
/* A sentence somebody has left a note on. An underline and a wash rather than a
   block of colour on purpose: this screen is read aloud from, and a solid
   highlight across a third of a paragraph makes the words harder to read, which
   is the one thing this page must never do. */
mark.sanchor{background:var(--amBg);color:inherit;border-bottom:2px solid var(--am);border-radius:2px;cursor:pointer}
mark.sanchor:hover{background:var(--am);color:var(--bg)}
/* What the note being composed is pinned to. Always present on the script side,
   empty-stated rather than appearing from nowhere, so the affordance is
   discoverable before the first highlight rather than after it. */
.sanchor-chip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px;padding:7px 10px;border:1px dashed var(--bd);border-radius:8px}
.sanchor-chip.on{border-style:solid;border-color:var(--am);background:var(--amBg)}
.sanchor-chip .sanchor-q{font-size:13px;color:var(--tx);overflow-wrap:anywhere}
.sanchor-chip .ca{margin-left:auto}
/* A note that has outlived its sentence. Dimmed, not hidden. */
.cmrow.lost{opacity:.75;border-left:2px solid var(--am);padding-left:10px}
/* Where the click landed. A brief ring rather than a lasting colour: it answers
   "this one" and then gets out of the way, because the thing worth reading is
   the sentence or the note, not the highlight around it. */
.sanchor-hit{outline:2px solid var(--am);outline-offset:3px;border-radius:4px;transition:outline-color .5s}
.cm[data-cm]{cursor:pointer}
@media (max-width:560px){.scriptread p{font-size:14.5px;line-height:1.8}.sr-open{font-size:16px}}

/* bulk library add */
.bklist{max-height:230px;overflow-y:auto;margin-bottom:12px}
.bkhead{font-family:var(--fm);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--tx3);margin:6px 0 8px}
.bkrow{display:flex;align-items:center;gap:9px;padding:5px 0}
.bkrow input{flex:1;min-width:0;padding:6px 9px;font-size:12.5px}
.bksz{font-family:var(--fm);font-size:10.5px;color:var(--tx3);white-space:nowrap}
.bkst{font-family:var(--fm);font-size:10.5px;color:var(--tx3);width:52px;text-align:right;white-space:nowrap}
.bkst.ok{color:var(--gr)}
.bkst.bad{color:var(--rd)}
@media (max-width:560px){.bkrow{flex-wrap:wrap}.bkrow input{flex:1 1 100%}}

/* On a phone every tappable thing needs to be a real target. The library and
   report pages were full of 20px chips and text buttons that missed by a hair. */
@media (hover:none){
  .ca,.fchip,.chip,.pcl,.mt,.vchip,.tsb{min-height:32px;display:inline-flex;align-items:center}
  .btn.btn-sm{min-height:34px}
  .bkrow input{min-height:34px}
  .wkdate{min-height:34px}
  .nbx{min-width:32px;min-height:32px}
}

/* iOS Safari magnifies the page whenever a focused field is under 16px, and
   never zooms back — the layout viewport then stays wider than the screen and
   the right-hand end of the composer row, where Post lands after it wraps, is
   pushed off. 16px is the browser's threshold, not a preference. Last in the
   file on purpose: it has to beat .bkrow input and .docarea on source order.
   select is excluded — it opens a picker, not a keyboard, so it never zooms.
   NOT fixed with maximum-scale=1, which would kill pinch zoom for everybody. */
@media (max-width:900px){
  input[type=text],input[type=email],input[type=password],textarea,
  .cma2,.docarea,.bkrow input{font-size:16px}
  /* 56px showed a line and a half at 13px; at 16px it shows barely one */
  .cma2{height:72px}
  /* 16px mono needs ~77px of content box for M:SS:FF, not 52px. The width
     !important is also what beats the inline style on the element. */
  .tsi{font-size:16px!important;width:96px!important}
}

/* ---------- chat group membership ----------
   A list of people you tick, which is the whole of what a group is.

   Every selector here is scoped under .fg because the field-group label rule
   above (.fg label) is display:block and uppercase, and these rows ARE labels —
   a label is the right element when the whole row should tick the box, and it
   arrived wearing the form-label styling with the avatar stacked over the name.
   Scoped, not renamed: the rows should stay labels. */
.fg .gmwrap{border:1px solid var(--bds);border-radius:var(--r);background:var(--sf);overflow:hidden}
.fg .gmtop{display:flex;align-items:center;gap:10px;padding:9px 11px;border-bottom:1px solid var(--bd);background:var(--sf2)}
.fg .gmtop input[type=text]{flex:1;background:transparent;border:0;color:var(--tx);font-size:13.5px;padding:4px 0;min-width:0}
.fg .gmtop input[type=text]:focus{}
.fg .gmtop input[type=text]::placeholder{color:var(--tx3)}
.gmcount{font-family:var(--fm);font-size:10.5px;letter-spacing:.06em;color:var(--lime);white-space:nowrap}
.gmcount.thin{color:var(--tx3)}

.fg .gmlist{max-height:44vh;overflow-y:auto;overscroll-behavior:contain}
.fg .gmlist label.gmrow{
  display:flex;align-items:center;gap:11px;
  padding:0 12px;min-height:46px;cursor:pointer;
  border-bottom:1px solid var(--bd);
  /* the four properties .fg label sets that have no business here */
  font-family:inherit;font-size:14px;font-weight:400;letter-spacing:normal;
  text-transform:none;color:var(--tx);margin:0}
.fg .gmlist label.gmrow:last-of-type{border-bottom:0}
.fg .gmlist label.gmrow:hover{background:var(--sf2)}
.fg .gmlist label.gmrow.on{background:var(--limeBg)}
.fg .gmlist .gmrow .av{width:28px;height:28px;font-size:11px;flex:none}
.gmn{flex:1;font-size:14px;color:var(--tx);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gmr{font-family:var(--fm);font-size:10px;color:var(--tx3);letter-spacing:.08em;
  text-transform:uppercase;white-space:nowrap}
/* last, so the eye reads name then role then state — and so the tick lands
   under the thumb on a phone rather than at the far left */
.fg .gmlist .gmrow input[type=checkbox]{width:19px;height:19px;accent-color:var(--lime);flex:none;margin:0;cursor:pointer}
.gmnone{display:none;padding:20px 12px;font-size:13px;color:var(--tx3);text-align:center}

/* ---------- dragging the sidebar into your own order ----------
   The row lifts rather than floats: it stays in the list and the list
   rearranges around it, which is what makes the drop position obvious without
   a placeholder line to interpret. */
.ni{touch-action:pan-y}
body.navdragging{cursor:grabbing;user-select:none}
/* while a drag is on, no row should light up under the pointer as it passes */
body.navdragging .ni:hover{background:transparent}
body.navdragging .ni{touch-action:none}
.ni.dragging{background:var(--sf2);border-radius:var(--r);
  box-shadow:0 8px 24px rgba(0,0,0,.45),inset 0 0 0 1px var(--bds);
  opacity:.97;cursor:grabbing;position:relative;z-index:5}
.ni.dragging:hover{background:var(--sf2)}
.sb-reset{margin:10px 12px 0;padding:7px 10px;font-family:var(--fm);font-size:10.5px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--tx3);cursor:pointer;
  border:1px dashed var(--bd);border-radius:var(--r);text-align:center}
.sb-reset:hover{color:var(--tx2);border-color:var(--bds)}

/* ---------- text read the way it was written ----------
   Blank lines become paragraphs. A description is a caption that gets pasted
   somewhere that keeps its shape, so flattening it here would misrepresent what
   is about to be published — and make a long one a wall.

   No rule down the left edge. That mark belongs to the script's opening line
   and means "this is what has to stop the scroll"; on everything it means
   nothing. */
.readpara{max-width:74ch}
.readpara p{font-size:14px;line-height:1.85;color:var(--tx2);margin-bottom:13px;white-space:pre-wrap;overflow-wrap:anywhere}
.readpara p:last-child{margin-bottom:0}

/* a field that is as tall as what is in it */
textarea.grow{resize:none;overflow:hidden}

/* Inside a posting card the description sits alongside hashtags and tags at
   13px; the paragraph block keeps its shape but takes the card's scale, so one
   field does not read a size larger than the two beside it. */
.descbox .readpara{max-width:none}
.descbox .readpara p{font-size:13px;line-height:1.65;margin-bottom:9px}

/* ---------- a table that becomes a list of cards on a narrow screen ----------

   Every other wide table in this app scrolls sideways below 900px. That works
   when the columns nobody needs are on the right; it fails on Hiring, where the
   status and the referral are the two things somebody came to read and both sit
   past the fold. A sideways scrollbar also hides that there is anything there
   at all — the first thing anyone would do is not scroll.

   One markup, two shapes: the cells carry data-l and the label is printed from
   it, so the HTML does not know which shape it is in and there is no second
   copy of the row to keep in step.

   700px rather than the house's 900px on purpose. An iPad in portrait is 768
   and has room for all seven columns; treating it as a phone is what the 900px
   rule already gets wrong elsewhere, and there was no reason to repeat it here. */
.tblwrap{width:100%}
@media (max-width:700px){
  .tblcard,.tblcard tbody,.tblcard tr,.tblcard td{display:block;width:auto}
  .tblcard{white-space:normal;overflow:visible}
  .tblcard thead{display:none}
  .tblcard tr{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);
    padding:14px 16px;margin-bottom:10px}
  .tblcard tr:hover{border-color:var(--bds)}
  .tblcard td{border:0;padding:5px 0;display:flex;gap:14px;align-items:baseline;
    justify-content:space-between;text-align:right}
  .tblcard td::before{content:attr(data-l);font-family:var(--fm);font-size:9.5px;
    letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);
    flex:0 0 auto;text-align:left}
  /* the first cell is the name, and a name does not want a label beside it */
  .tblcard td.tcmain{display:block;text-align:left;padding:0 0 8px;
    border-bottom:1px solid var(--bd);margin-bottom:8px}
  .tblcard td.tcmain::before{content:none}
  /* the 4th-column trick belongs to the sideways-scrolling shape; in a card
     there is room for everything and hiding one line would only puzzle */
  .tblcard.tbl-act td:nth-child(4),.tblcard.tbl-act th:nth-child(4){display:flex}
  .tblcard .num{font-size:12.5px}
}

/* A view count typed where it is read — the Views column on an editor's page.
   Right-aligned and monospaced so a column of them lines up, and sized to the
   number rather than to the container so it does not look like a form. */
.viewsin{width:96px;padding:6px 9px;text-align:right;font-family:var(--fm);font-size:12.5px}
@media (max-width:900px){.viewsin{font-size:16px;width:110px}}

/* ---------- External Metrics: four platforms, two numbers each ----------
   A grid rather than eleven labelled boxes in one column. Whoever fills this in
   is reading one platform's dashboard at a time and typing both of its numbers
   before moving on, so the rows follow the job. Below 560px the two numbers
   stack under the platform name, which is the only shape that fits a phone
   without a horizontal scrollbar inside a modal. */
.emtop{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0 14px}
.emgrid{border:1px solid var(--bd);border-radius:var(--r);overflow:hidden;margin-bottom:16px}
.emrow{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;align-items:center;
  padding:9px 13px;border-bottom:1px solid var(--bd)}
.emrow:last-child{border-bottom:none}
.emhead{background:var(--sf2);font-family:var(--fm);font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--tx3);padding:8px 13px}
.emname{font-size:13px;font-weight:600;display:flex;flex-direction:column;gap:2px}
.emname span{font-family:var(--fm);font-size:9.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tx3);font-weight:400}
.emcell input{width:100%}
@media (max-width:560px){
  .emrow{grid-template-columns:1fr;gap:7px;padding:11px 13px}
  /* the column headings mean nothing once the cells are stacked */
  .emhead{display:none}
  .emcell::before{content:attr(data-l);display:block;font-family:var(--fm);font-size:9.5px;
    letter-spacing:.1em;text-transform:uppercase;color:var(--tx3);margin-bottom:5px}
}

/* The sum under a table of people. Set apart from the rows above it — it is a
   different kind of statement, and reading it as one more person is the mistake
   worth designing out. */
.tblsum td{border-top:1px solid var(--bds);border-bottom:none;font-family:var(--fm);
  font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--tx3);padding-top:12px}
.tblsum td.num{font-size:13.5px;letter-spacing:0;text-transform:none;color:var(--tx2)}

/* "the page you are looking at was loaded before the last deploy". Bottom
   centre, above everything, and dismissible — it is information, not an alarm,
   and reloading under somebody mid-sentence is its own small betrayal. */
.newbuild{position:fixed;left:50%;transform:translateX(-50%);bottom:18px;z-index:400;
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;max-width:min(620px,calc(100vw - 24px));
  background:var(--sf2);border:1px solid var(--bds);border-radius:var(--rl);
  padding:12px 15px;font-size:12.5px;color:var(--tx2);box-shadow:0 10px 30px rgba(0,0,0,.45)}
.newbuild span{display:flex;align-items:center;gap:8px;flex:1;min-width:220px}
.newbuild svg{width:15px;height:15px;stroke:var(--am);stroke-width:1.8;fill:none;flex:0 0 auto}

/* ---------- the audio shelf, as a list rather than a wall of cards ----------

   The library was a grid of 188px cards, each with a native <audio> in it. At
   358 items that is 358 sets of browser player chrome on one screen, every one
   of them a different width from the last, and the only way to hear anything
   was to hunt for a 20px play button inside a card. Garv asked for it to work
   the way Epidemic Sound does, which is a list: one row per track, the name
   where the eye already is, the wave taking the width, and a single player at
   the bottom that keeps playing while you keep looking.

   The row is a grid rather than flexbox so every column lines up down the whole
   list — a wave that starts at a different x on each row is the thing that made
   the old shelf unreadable. */
.trkl{border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden;background:var(--sf)}
/* The wave column is capped rather than allowed to take every spare pixel.
   On a 2000px monitor the old 2.4fr made it about 1600px wide, which pushed
   the name hard left and the time hard right with a field of nothing between
   them — and stretched the waveform so thin it read as dots. Capped, the row
   keeps the shape it was designed at and the shelf looks the same on a laptop
   and on a studio monitor. */
.trk{display:grid;grid-template-columns:42px minmax(150px,1.4fr) minmax(0,2.2fr) 54px auto;
  gap:16px;align-items:center;padding:10px 14px;border-bottom:1px solid var(--bd);
  transition:background .12s}
.trk>.trkw,.trkhead>span:nth-child(3){max-width:720px;width:100%;justify-self:start}
/* The one thin line of labels over the list. Matched to .trk's grid column
   for column, because a header that drifts from its own table reads as two
   unrelated things stacked. */
.trkhead{display:grid;grid-template-columns:42px minmax(150px,1.4fr) minmax(0,2.2fr) 54px auto;
  gap:16px;align-items:center;padding:4px 14px 8px;
  font-family:var(--fm);font-size:10px;color:var(--tx3);letter-spacing:.12em;text-transform:uppercase}
/* The cover tile, and the play control that lives on it — the artwork slot on
   Epidemic, minus the artwork these files will never have. The gradient comes
   inline off the track's own name, so it is stable and costs no image. */
.trkart{width:42px;height:42px;border-radius:8px;position:relative;flex-shrink:0;overflow:hidden;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.06)}
.trkart svg{width:15px;height:15px;color:rgba(255,255,255,.5);
  stroke:currentColor;stroke-width:1.8;fill:none}
.trkart .trkb{width:26px;height:26px;border:0;background:rgba(10,10,12,.55);color:#fff;
  backdrop-filter:blur(2px)}
.trkart .trkb svg{width:11px;height:11px;color:#fff;fill:currentColor;stroke:none}
.trkart:hover .trkb{background:var(--lime);color:#101012}
.trkart:hover .trkb svg{color:#101012}
.trk.on .trkart .trkb{background:var(--lime)}
.trk.on .trkart .trkb svg{color:#101012}
.trkart.off{cursor:default;opacity:.45}
.trkart.bar{width:36px;height:36px;cursor:default}
.trk:last-child{border-bottom:0}
.trk:hover{background:var(--sf2)}
/* The row that is playing keeps a lime edge, so somewhere-in-a-long-list is
   answerable by looking rather than by scrolling and listening. */
.trk.on{background:var(--sf2);box-shadow:inset 3px 0 0 var(--lime)}
.trk.on .trkn{color:var(--lime)}

.trkb{width:34px;height:34px;border-radius:50%;border:1px solid var(--bds);background:var(--sf2);
  color:var(--tx2);display:flex;align-items:center;justify-content:center;cursor:pointer;
  flex-shrink:0;transition:background .12s,border-color .12s,color .12s}
.trkb svg{width:14px;height:14px;fill:currentColor;stroke:none;margin-left:1px}
.trkb:hover{border-color:var(--lime);color:var(--lime)}
.trk.on .trkb{background:var(--lime);border-color:var(--lime);color:#101012}
.trkb.pause svg{margin-left:0}

.trkm{min-width:0}
.trkn{font-size:14px;font-weight:600;letter-spacing:-.005em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.trksub{display:flex;align-items:center;gap:8px;margin-top:4px;font-family:var(--fm);
  font-size:10px;color:var(--tx3);letter-spacing:.06em;text-transform:uppercase}
.trkfold{cursor:pointer;padding:1px 7px;border-radius:40px;border:1px solid var(--bd);
  color:var(--tx3);transition:border-color .12s,color .12s;white-space:nowrap}
.trkfold:hover{border-color:var(--bdl);color:var(--tx2)}

/* The wave. Two copies of the same path, the lit one clipped to how far the
   track has played — so progress costs one attribute write per frame rather
   than a repaint, and the whole thing stays sharp at any zoom because it is
   vector rather than a canvas sized to one device pixel ratio. */
.trkw{position:relative;height:34px;cursor:pointer}
.trkw svg,.trkbar-w svg{display:block;width:100%;height:100%}
/* Not scoped to .trkw: the same wave is drawn a second time inside the player
   bar, and a rule that only reached the row left the bar's copy with no stroke
   at all — a blank gap where the wave should be. */
/* Lifted from --bdl/.75 to a flat --bdl: on the dark row the old value was
   sitting at roughly a fifth of the contrast of the lime played part, so the
   unplayed half of a wave barely existed. */
.wbase{stroke:var(--bdl)}
.wlit{stroke:var(--lime)}
.trkw.flat{display:flex;align-items:center}
/* Only for a row with no FILE at all — there is no position to show, so there
   is nothing to draw but a rule. A track that has a file but no waveform yet
   draws a real progress rail from libWaveSvg instead, which is a different
   thing and says so by moving. */
.trkw.flat i{display:block;height:0;width:100%;border-top:2px dashed var(--bd);opacity:.85}
.trk:hover .trkw.flat i{border-color:var(--bds)}
/* The rail reads quieter than a wave on purpose: it is scaffolding, not data,
   and it must not be mistaken for a flat-lining track. */
.trkw.rail .wbase,.trkbar-w.rail .wbase{stroke:var(--bd)}
.trk:hover .trkw.rail .wbase{stroke:var(--bds)}
.trk:hover .wbase{opacity:1}

.trkd{font-family:var(--fm);font-size:12px;color:var(--tx2);text-align:right;white-space:nowrap;
  font-variant-numeric:tabular-nums}
/* Always there, dimmed — not hidden until hover.

   Reveal-on-hover is a pattern for a row you already know how to use. On a
   shelf of 358 tracks the three things you came to do (file it, download it,
   take it off) were invisible until the pointer happened to land on the right
   row, so from across the screen the list looked like it had no controls at
   all. Dimmed says "here, not shouting"; hidden says "not here". */
.trka{display:flex;align-items:center;gap:2px;opacity:.68;padding-left:6px;
  border-left:1px solid var(--bd);margin-left:2px;transition:opacity .12s}
.trk:hover .trka,.trk.on .trka,.trka:focus-within{opacity:1}
.trkab{width:30px;height:30px;border-radius:6px;border:1px solid transparent;background:none;
  color:var(--tx2);display:flex;align-items:center;justify-content:center;cursor:pointer;
  transition:background .12s,color .12s,border-color .12s}
/* stroke, not fill.

   Every icon in config.js is a feather-style outline drawing — paths with no
   fill of their own — so each class that carries one has always had to say
   `stroke:currentColor; fill:none` for itself. The global svg{} rule sets size
   and nothing else, so an icon in a class that forgets falls back to the
   browser default, which is fill:black and stroke:none: the outline vanishes
   and what is left is the silhouette. Download and Remove were rendering as
   two solid blobs for exactly this reason, and Move only looked survivable
   because it is drawn from rects and a filled rect is still a square.

   The play/pause glyphs below are the deliberate exception — those two ARE
   solid shapes and say so on their own line. */
.trkab svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none}
.trkab:hover{background:var(--sf3);border-color:var(--bd);color:var(--tx)}
.trkab.danger:hover{color:var(--rd);border-color:var(--rd)}
/* Touch has no hover, so the actions would never appear at all. */
@media (hover:none){.trka{opacity:1}}

/* The one player, at the foot of the screen, outside the list it is playing
   from. It is what lets somebody keep scrolling — and keep searching — while a
   track runs, which every card-with-its-own-player layout makes impossible. */
/* The list gets room underneath it so the last track can be scrolled clear of
   the bar. Without this the bar sits on top of the final row for good, and the
   one thing you cannot do is press play on it. */
.trkl{margin-bottom:8px}
.hasbar .trkl{margin-bottom:76px}
.trkbar{position:sticky;bottom:0;z-index:6;margin:14px -2px -4px;padding:11px 16px;
  background:rgba(24,24,27,.94);backdrop-filter:blur(12px);
  border:1px solid var(--bds);border-radius:var(--rl);
  display:grid;grid-template-columns:auto auto minmax(120px,1fr) auto minmax(0,2.4fr) auto auto;
  gap:16px;align-items:center;box-shadow:0 -8px 28px rgba(0,0,0,.42)}
.trkbar>.trkbar-w{max-width:760px;width:100%;justify-self:start}
.trkbar .trkn{font-size:13px}
/* One line, always. The folder name wrapping under the track name grew the bar
   by a whole row on a phone, which pushed the list under it out of reach. */
.trkbar .trksub{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
.trkbar-t{font-family:var(--fm);font-size:11px;color:var(--tx3);white-space:nowrap}
.trkbar-w{position:relative;height:30px;cursor:pointer}
.trkbar-w svg{display:block;width:100%;height:100%}
.trkvol{display:flex;align-items:center;gap:7px;color:var(--tx3)}
.trkvol svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none}
.trkvol input{width:78px;accent-color:var(--lime);cursor:pointer;background:none;padding:0;border:0;height:4px}

/* On a phone the wave is the first thing to go. Squeezed into ninety pixels it
   is not a shape anybody can read, and it was taking the width the name needed
   — the row read "Marimba Susp…" next to a grey smudge. The name, the length
   and the three actions are what is left, which is what the row is for. */
@media(max-width:900px){
  .trk{grid-template-columns:38px minmax(0,1fr) 44px auto;gap:11px;padding:10px 12px}
  .trkart{width:38px;height:38px}
  .trk>.trkw{display:none}
  .trkhead{display:none}
  .trkby{display:none}
  /* Every pixel the three actions give back goes to the track name, which on
     a phone was truncating at "J making se…" while a divider and 14px of
     padding sat beside it doing nothing a thumb needs. */
  .trka{gap:0;padding-left:2px;border-left:0;margin-left:0}
  .trkab{width:28px;height:28px}
  .trkab svg{width:14px;height:14px}
  .trkd{font-size:11.5px}
  .trkbar{grid-template-columns:auto minmax(0,1fr) auto auto;gap:10px;padding:10px 12px}
  .trkbar .trkart{display:none}
  .trkbar-w,.trkvol{display:none}
  .hasbar .trkl{margin-bottom:84px}
}

/* ---------- the two rails in Analysis's Day By Day ----------
   One bar per column rather than one bar for the row. They are never added
   together and never share a scale: a day's scripts are measured against the
   busiest scripting day of that week and the videos against the busiest
   posting day, because those are two different questions and a reader given
   one bar for both cannot tell which one moved. */
.dbars{display:flex;flex-direction:column;gap:4px;width:100%}
.dbar{display:block;height:4px;width:100%;background:var(--sf3);border-radius:3px;overflow:hidden}
.dbar i{display:block;height:100%;border-radius:3px;transition:width .3s ease}
/* The key sits in the header cell, so the colours are explained where they
   are first seen rather than in a caption underneath the table. */
.dbkey{float:right;display:inline-flex;align-items:center;gap:5px;
  font-family:var(--fm);font-size:9.5px;color:var(--tx3);letter-spacing:.06em;text-transform:none}
.dbkey b{width:8px;height:3px;border-radius:2px;display:inline-block;margin-left:6px}
.dbkey b:first-child{margin-left:0}
@media(max-width:720px){.dbkey{display:none}}

/* ---------- the views contest ----------
   Its own small set rather than bending the leaderboard's table styles: a
   podium and a prize strip are the two things this page has that no other
   screen does, and both are read at a glance from a phone. Everything else on
   the page reuses .card, .tbl and .bar unchanged. */
.cprz{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:20px}
.cprzc{display:flex;align-items:center;gap:9px;background:var(--sf2);border:1px solid var(--bd);
  border-radius:var(--r);padding:10px 14px;flex:1 1 190px;min-width:0}
.cprzm{font-size:17px;line-height:1;flex-shrink:0}
.cprzp{font-size:13px;font-weight:600;color:var(--tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.cpod{display:grid;grid-template-columns:repeat(3,1fr);gap:11px;margin-bottom:22px}
/* On a phone the three-up podium is unreadable — a six-figure number wraps and
   the medal sits beside nothing — but stacking the same tall cards is worse: it
   measured 506px of podium and 1014px before the standings table, so the thing
   everybody opens this page for was a page and a quarter down. So the cards
   turn into rows: medal, name and prize on the left, the number on the right,
   one line each. Same information, a third of the height. */
@media (max-width:560px){
  .cpod{grid-template-columns:1fr;gap:8px}
  .cpodc{display:grid;grid-template-columns:auto 1fr auto;align-items:center;
    gap:12px;text-align:left;padding:12px 14px}
  .cpodm{font-size:19px;margin:0}
  .cpodn{grid-column:2;font-size:13.5px}
  .cpodv{grid-column:3;grid-row:1;margin:0;font-size:18px;text-align:right}
  .cpodl{display:none}
  /* The prize is already named, three times, in the strip directly above. A
     second copy on the podium is what made each card two lines instead of one
     — and two lines times three cards is the difference between the standings
     being on screen and being a scroll away. */
  .cpodp{display:none}
  /* Same reason, one row up: three stacked prize chips at 44px each is most of
     a phone screen for three short strings. */
  .cprz{gap:6px}
  .cprzc{flex:1 1 100%;padding:8px 12px}
}
.cpodc{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rl);
  padding:16px 14px;text-align:center;min-width:0}
.cpodc.first{border-color:var(--lime);background:var(--limeBg)}
.cpodc.mine{box-shadow:inset 0 0 0 1px var(--bdl)}
.cpodm{font-size:22px;line-height:1;margin-bottom:8px}
.cpodn{font-size:14px;font-weight:600;color:var(--tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cpodv{font-family:var(--fm);font-size:23px;font-weight:700;color:var(--tx);margin-top:7px;line-height:1.1}
.cpodc.first .cpodv{color:var(--lime)}
.cpodl{font-family:var(--fm);font-size:10.5px;color:var(--tx3);letter-spacing:.06em;margin-top:3px}
.cpodp{font-size:11.5px;font-weight:600;color:var(--tx2);margin-top:10px;padding-top:9px;
  border-top:1px solid var(--bd);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cpodc.first .cpodp{border-top-color:rgba(127,166,255,.28)}

.crow-me{background:var(--limeBg)}
.cbest{font-size:11px;color:var(--tx3);margin-top:2px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;max-width:34ch}
.cunl{font-family:var(--fm);font-size:10px;color:var(--am);margin-left:7px;white-space:nowrap}
.cfoot{font-size:12px;color:var(--tx3);line-height:1.7;margin-top:16px}

/* The standings table measured 474px against a 321px card on a 375px phone, so
   its last columns were simply unreachable — the page does not scroll sideways
   and nothing said the rest was there. The bar is the column to lose: it is a
   picture of the number in the column beside it, so dropping it costs nothing
   a reader cannot already see. The overflow rule underneath is the backstop for
   a name long enough to push the rest out anyway. */
.ctbl{overflow-x:auto}
@media (max-width:560px){
  .ctbl .cbar{display:none}
  .ctbl .tbl td,.ctbl .tbl th{padding-right:8px;padding-top:11px;padding-bottom:11px}
  /* The avatar is the widest thing carrying the least: the name is right beside
     it and the row is already numbered. */
  .ctbl .av{display:none}
  .cbest{max-width:17ch}
}

/* ---------- Admin → Integrations, folded ----------
   Four platforms were configured through three credential cards of equal
   weight, one of them marked "optional" and duplicating another — which is
   what made the screen read as a maze rather than a setup. The two that are
   read once (a Graph API walkthrough, a Google Cloud walkthrough) fold shut,
   and the summary line still answers the only question somebody scanning has:
   is this one set up or not. */
.intg-more{padding-top:14px}
.intg-more>summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.intg-more>summary::-webkit-details-marker{display:none}
.intg-more>summary::before{content:'\25B8';color:var(--tx3);font-size:11px;transition:transform .15s}
.intg-more[open]>summary::before{transform:rotate(90deg)}
.intg-sum{font-size:12px;color:var(--tx3);line-height:1.5}
.intg-more>summary:hover .intg-sum{color:var(--tx2)}

/* ---------- Admin → Integrations, after the 2026-09-01 cut-down ----------
   Three strips: what to press, what is missing, what is broken. The panel
   used to be three credential cards and four stat grids all open at once. */
.intg-act-b{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.intg-act .btn-p{font-size:15px;padding:11px 18px}

/* One line per platform. The dot carries the state so the line beside it can
   stay a plain sentence — colour alone is never the only signal, the words
   after it say the same thing. */
.intg-h{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--bd)}
.intg-h:last-of-type{border-bottom:none}
.intg-dot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--tx3)}
.intg-h-n{font-weight:600;font-size:14px;min-width:96px}
.intg-h-s{font-size:13px;color:var(--tx2)}
.intg-h-ok .intg-dot{background:var(--gr)}
.intg-h-warn .intg-dot{background:var(--am)}
.intg-h-bad .intg-dot{background:var(--rd)}
.intg-h-bad .intg-h-s{color:var(--rd)}
.intg-h-off{opacity:.55}

/* The contest's unlinked videos, named. Prefixed intg- rather than the
   obvious .cunl, which is already taken: the contest board uses that for the
   little amber "+3 unlinked" tag beside a row's video count, and it carries
   white-space:nowrap. Reusing the name inherited that nowrap into a paragraph
   and pushed the card a thousand pixels wide. */
.intg-unl{border-color:var(--am)}
.intg-unl-p{display:inline-block;font-size:11px;font-weight:600;padding:2px 7px;border-radius:5px;
  background:var(--amBg);color:var(--am);margin-right:4px;white-space:nowrap}
.intg-unl-ok{display:flex;align-items:flex-start;gap:8px;color:var(--gr);line-height:1.55}
.intg-unl-ok svg{width:17px;height:17px;flex:none;margin-top:2px}

/* One contested post, and the sides claiming it. A block per pair rather than
   one long table: the two claimants are read against each other, and rows from a
   different post sitting directly underneath read as a third claimant of the
   same one. Which is what the old single table did, with nothing between pairs
   to say where one ended. */
.intg-cont{border:1px solid var(--bd);border-radius:9px;padding:12px 13px;margin-bottom:12px}
.intg-cont:last-child{margin-bottom:0}
.intg-cont-h{margin-bottom:10px;line-height:1.5}
/* The side the rule would keep, marked on the row and not only in the sentence
   under the table -- the sentence is what somebody reads after they have already
   started looking for the button. */
.intg-cont-w{background:var(--grBg)}
.intg-cont-r{border-top:1px solid var(--bd);margin-top:10px;padding-top:10px;line-height:1.5}

@media(max-width:640px){
  .intg-act .btn-p,.intg-act .btn-sm{width:100%;justify-content:center}
  .intg-h{flex-wrap:wrap}
  .intg-h-n{min-width:0}
  .intg-h-s{width:100%;padding-left:19px}
}

/* Two contest boards on one page. Enough air between them that the second
   reads as its own board and not as a continuation of the first's table. */
.csec{margin-top:26px}
.csec:first-of-type{margin-top:8px}
.csec-h{margin-top:0}

/* Two videos claiming one post: the one thing on the contest page somebody
   has to act on, so it is allowed to be louder than the boards under it. */
.cdup{border-color:var(--rd);color:var(--tx2)}
.cdup strong{color:var(--rd)}
.cdup .tbl td b{font-family:var(--fm)}

/* Admin → Top Videos. A sortable header cell says so before it is pressed. */
.tvsort{cursor:pointer;user-select:none}
.tvsort:hover{color:var(--tx)}
.tvarr{opacity:.35;margin-left:4px;font-size:11px}
.tvarr.on{opacity:1;color:var(--lime)}

/* Per-platform freshness. Nothing refreshes on a timer here on purpose, so
   the age of each number is the thing that has to be readable. */
.intg-age{display:inline-block;font-family:var(--fm);font-size:10.5px;color:var(--tx2);
  background:var(--bg);border:1px solid var(--bd);border-radius:5px;
  padding:2px 7px;margin:0 6px 6px 0;white-space:nowrap}
.intg-age.none{color:var(--tx3);border-style:dashed}

/* Top Videos: the selection bar. Its own row so ticking a box repaints one
   line instead of rebuilding the table under the mouse. */
.tvbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:0 0 8px}

/* ---------- one horizontal bar per writer, toward the week's target ----------
   A row of bars is only readable if the bars share a baseline and the names
   share a width, so the name column is fixed and the track takes the rest. */
.tchart{display:flex;flex-direction:column;gap:9px}
.trow{display:flex;align-items:center;gap:12px;cursor:pointer;padding:4px 6px;margin:0 -6px;border-radius:var(--r)}
.trow:hover{background:var(--sf2)}
.tname{display:flex;align-items:center;gap:9px;width:190px;flex-shrink:0;font-size:13px;font-weight:500;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tname>span{overflow:hidden;text-overflow:ellipsis}
.ttrack{flex:1;min-width:0;display:flex;align-items:center}
/* The bar itself is .bar, sized inline so the widths compare across rows.
   Taller here than the 6px it is elsewhere: this one is the point of the
   block rather than a hint beside a number. */
.ttrack .bar{height:12px;border-radius:6px;max-width:100%}
.ttrack .bar i{border-radius:6px}
.tnum{width:180px;flex-shrink:0;text-align:right;font-family:var(--fm);font-size:12.5px;font-weight:600}
@media (max-width:760px){
  .trow{flex-wrap:wrap;gap:6px}
  .tname{width:100%}
  .tnum{width:auto;text-align:left}
  .ttrack{flex-basis:100%}
}

/* ---------- script versions ----------
   The strip reuses .rvtop and .vchip from the cut player above, on purpose:
   picking V2 of a script and picking V2 of a cut are the same action and a
   second set of chips that looked almost the same would be worse than one.
   What is new here is the text side — .rvstage is black and sized for video,
   which a column of words is not. */
.svtop{border:1px solid var(--bd);border-bottom:none;border-radius:var(--rl) var(--rl) 0 0}
.svsplit{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media (max-width:760px){.svsplit{grid-template-columns:1fr}}
.svpane{min-width:0}
.svph{font-family:var(--fm);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--tx3);margin-bottom:9px}
/* One line per line, so a changed line can be marked without the paragraph
   spacing of .scriptread p moving around it. */
.svline{white-space:pre-wrap;font-size:14.5px;line-height:1.75;color:var(--tx);padding:1px 8px;border-radius:4px;margin:0}
.svline.add{background:var(--limeBg);box-shadow:inset 2px 0 0 var(--lime)}
.svline.del{background:var(--rdBg);box-shadow:inset 2px 0 0 var(--rd)}

/* ================= a tall screen is not a small screen =================

   Garv reviews on a 4K monitor turned 9:16 — about 1080x1920 of CSS pixels.
   Every breakpoint above is a WIDTH, so that viewport reads as a desktop and
   gets the desktop layout: sidebar, then a workspace split into a video column
   and a 380px rail. Measured at 1080x1920, that put the cut in a 397px column
   inside a 1421px-tall stage — a 397x706 picture with SEVEN HUNDRED PIXELS of
   black above and below it, beside a rail squeezed to 380px on a screen with
   1920 to spare. His words: "UI bada ajeeb sa lag raha hai ... isko bahut
   chhota karna pad raha hai."

   Zooming out fixes it because zooming out buys CSS width, which is the thing
   that was actually missing. So the rule is SHAPE, not width: when the viewport
   is much taller than it is wide there is no width to spare and plenty of
   height, and two columns side by side is the wrong shape for the screen.

   4/5 rather than plain portrait, and that was measured rather than picked. At
   1000x1100 — portrait, but only just — stacking makes the picture SMALLER than
   the two-column layout does, because the height freed by moving the rail
   underneath is less than the height the rail then takes. The crossover is
   somewhere near a fifth taller than wide, so that is where the switch is. His
   monitor is 0.56, nowhere near the line.

   Above 900px only. Below that the phone rules already stack everything and
   put the sidebar behind a burger, and they must keep winning there. */
@media (max-aspect-ratio:4/5) and (min-width:901px){
  /* The workspace stacks: the cut gets the full width, the notes get the full
     width under it. The rail keeps a floor so it cannot be crushed to nothing
     by a very tall stage, and takes whatever is left over above that.

     --stage-h is one number used by all three stage rules below, so the height
     of the picture and the room reserved for everything else cannot drift
     apart. The second half of its min() is the guard for a portrait window that
     is not tall: it keeps back enough height for the player's own chrome and
     the rail's floor, so a short window shrinks the picture rather than
     overflowing the workspace. Measured at 1000x1100 and 1200x1500: no
     overflow at either. */
  .rvw{grid-template-columns:1fr;grid-template-rows:auto minmax(300px,1fr);
       --stage-h:min(52vh, calc(100dvh - 640px))}

  /* ---- and then somebody watched an editor actually use it ----

     The rule above made the PICTURE right and left the notes wherever they
     landed. Measured at 1080x1920 with the sources block already gone: stage
     998, player chrome 172, tabs 43, filters 45, composer 194 — and the notes
     themselves 253 pixels, thirteen per cent of the screen, three and a half
     notes at 68px each. Garv, watching editors work: they cannot read the
     notes without scrolling, over and over, and reading the notes is the whole
     reason this screen exists.

     So the split moves. 46vh instead of 52 and a 420px floor under the rail
     puts the notes at about 400px — six notes instead of three and a half —
     and the cut at 497x883, which is still far bigger than the 397x706 that
     started this whole section. The picture gives up eleven per cent of its
     width; the notes gain nearly sixty.

     Vertical ONLY, and that is the point of scoping it to .vert rather than
     changing the numbers above. A landscape cut is 16/9 across the full width,
     608px tall at this viewport, so --stage-h never binds for it and its rail
     is already the larger half — nothing about that screen needs adjusting and
     nothing here touches it. */
  /* The second half of the min() is the short-window guard, and it has to be
     arithmetic rather than a round number, because it is the only thing
     stopping the workspace overflowing its own page.

       100dvh - 132   .rvpage
              -  80   .rvbar
              - 172   the player's own chrome: rvtop 43, scrubber 25, controls 104
              - 420   the rail floor on the line above
       = 100dvh - 804

     It said 700 when the floor was raised from 300 to 420, which reserved
     104px less than the layout now needs — so on a portrait window under about
     1600px tall the control row was pushed off the bottom of the workspace.
     Measured, not guessed; the numbers above are the ones read off the DOM. */
  .rvw.vert{grid-template-rows:auto minmax(420px,1fr);
            --stage-h:min(46vh, calc(100dvh - 804px))}

  /* The composer holds 194px open all day so that a note can be typed into it
     for ten seconds. Its floor drops to one line while nobody is writing and
     comes back the moment it has focus, which is the only time the room is
     worth anything. resize:vertical still applies, so dragging it taller by
     hand keeps working. */
  .rvw.vert .cmp .cma2{min-height:44px}
  .rvw.vert .cmp:focus-within .cma2{min-height:96px}
  .rvs{border-left:none;border-top:1px solid var(--bd)}

  /* The stage takes the SHAPE OF THE CUT instead of whatever space is left.

     This is what removes the black. A 9:16 stage 52vh tall is 29vh wide, so the
     picture fills its own box edge to edge and the pane IS the picture — no
     letterboxing on either axis, and the height left over goes to the notes
     rather than to a black rectangle. Measured at 1080x1920: the cut goes from
     397x706 to 561x998, twice the picture, with nothing black around it.

     align-self:center is load-bearing: .rvm is a column flex container, so
     without it the stage stretches to the full width and aspect-ratio computes
     a height from that instead of a width from the height. */
  .rvw .rvstage{flex:none;align-self:center;width:auto;height:var(--stage-h);aspect-ratio:9/16;max-width:100%}
  /* A landscape cut is the same idea the other way up. fioShape() sets this
     class from the file's own dimensions once it reports them; detail() starts
     it vertical, which is what this channel ships. */
  .rvw:not(.vert) .rvstage{aspect-ratio:16/9;height:auto;width:100%;max-height:var(--stage-h);align-self:stretch}
  /* Comparing two versions is a two-column grid inside the stage, so it takes
     the full width back and keeps only the height. align-self has to be undone
     with it — the base rule centres a box that is sized by its own aspect, and
     this one is sized by the pane. */
  .rvw .rvstage.split{width:100%;aspect-ratio:auto;height:var(--stage-h);align-self:stretch}

  /* A long script would otherwise take the whole workspace and leave the notes
     on their 300px floor — a composer, and about a hundred pixels of comments
     read through a slot. The document keeps the larger share and scrolls inside
     itself, which it already knows how to do (.rodoc has its own overflow), and
     the rail keeps enough height to be a list rather than a peephole.

     Only the document side. A cut's pane is sized by its stage above and is not
     a .doc at all. */
  .rvw .rvm.doc{max-height:calc(100dvh - 760px)}

  /* The page is a viewport-height column, and 112px of it was chrome measured
     against a landscape window. dvh so an address bar that comes and goes does
     not leave the workspace overflowing by exactly its height. */
  .rvpage{height:calc(100dvh - 132px)}

  /* Five KPI columns across 779px is 144px each for a 24px number and a label
     under it. Three reads; five is a row of clipped words. */
  .stats,.flow{grid-template-columns:repeat(3,1fr)}
  /* Two 380px cards on a screen with no width to give them. */
  .tops{grid-template-columns:1fr}
  /* No table rule here, and that is a measurement rather than an oversight. An
     eleven-column table was built at this width to see whether it pushed the
     page sideways, with and without the phone rules' display:block — both gave
     788px and no horizontal scroll anywhere, because width:100% with automatic
     table layout shrinks the columns and the text wraps. Adding display:block
     would have been a real change (a table stops being a table box and becomes
     a scroll container) shipped with a comment about a problem that did not
     happen. If a real table is ever found overflowing here, this is the place. */
  /* The title had 38vw to sit in — 410px here — and then ellipsed. With the
     bar no longer competing with a 380px rail it can have the room. */
  .rvbar .rvtitle{max-width:60vw}
}

/* The pre-render checklist's group headings. Fifteen unbroken checkboxes is a
   wall somebody ticks without reading; six short groups is a list. */
.pfg{font-family:var(--fm);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--tx3);margin:12px 0 6px}
.pfg:first-child{margin-top:0}

/* ---------- the checklist in the rail ----------

   Garv, 2026-09-11: it belongs beside Chat and Activity, where the person
   checking the video already is. Built as rows rather than as <label> plus a
   native checkbox: a native box is 13px, unstyleable across browsers, and the
   thing being asked for here is a target somebody hits fifteen times in a row
   while watching a video. */
.pfhead{display:flex;align-items:center;gap:11px;padding:13px 18px;border-bottom:1px solid var(--bd);flex-shrink:0}
.pfnum{font-family:var(--fm);font-size:12px;color:var(--tx3);white-space:nowrap}
.pfnum b{color:var(--lime);font-size:15px;font-weight:700}
.bar.pfbar{flex:1;width:auto;height:5px}
.pfclr{margin-left:auto;flex-shrink:0}
.pfnote{padding:9px 18px 0;margin:0;flex-shrink:0}
.pfwrap{overflow-y:auto;padding:4px 12px 18px;flex:1;min-height:0}
/* In the rail the pane owns the height and scrolls inside itself; in the
   Submit Cut dialog the dialog already scrolls, so it must not grow a second
   scrollbar inside the first. */
.pfpane{display:flex;flex-direction:column;min-height:0;flex:1}
.pfmod{border:1px solid var(--bd);border-radius:var(--rl);overflow:hidden}
.pfmod .pfwrap{overflow:visible;max-height:none}
.pfmod .pfhead{padding:11px 14px}
.pfmod .pfnote{padding:8px 14px 0}
.pfrow{display:flex;align-items:flex-start;gap:11px;padding:9px 10px;border-radius:8px;
  cursor:pointer;transition:background .12s}
.pfrow:hover{background:var(--sf2)}
.pfrow.on .pft{color:var(--tx3);text-decoration:line-through;text-decoration-color:var(--bds)}
.pfbox{width:19px;height:19px;flex-shrink:0;margin-top:1px;border:1.5px solid var(--bds);
  border-radius:6px;display:flex;align-items:center;justify-content:center;transition:all .12s}
.pfrow:hover .pfbox{border-color:var(--tx3)}
.pfrow.on .pfbox{background:var(--lime);border-color:var(--lime)}
.pfbox svg{width:12px;height:12px;stroke:#0B0B0D;fill:none;stroke-width:3}
.pft{font-size:13.5px;line-height:1.5;color:var(--tx)}
/* Five tabs do not fit a 380px rail, and measuring said so: the row wanted
   617px of the 379 it had. Scrolling was the first answer and it was the wrong
   one — the tab that fell off the end was Check, the one just added, so the
   feature was invisible on every landscape screen and reachable only by
   dragging a row nobody knows drags. It wraps instead. Two short rows, nothing
   hidden, and the padding comes in to pay for the second line. */
.drh{flex-wrap:wrap;row-gap:0}
.dtab{white-space:nowrap;flex-shrink:0;padding:12px 11px 10px}

/* The post-link box on the posting card. The one field that stops a video
   arriving in the unlinked list a week later. */
.plink{margin:10px 0 4px}
.plink input{font-size:12.5px}
.pfaint{font-family:var(--fm);font-size:9.5px;letter-spacing:.06em;color:var(--tx3);
  text-transform:none;margin-left:6px}

/* A video with no platform row at all. Said in words rather than as a zero:
   nobody linked it is a different fact from somebody measured nothing, and
   the table was printing the same thing for both. */
.nolink{font-family:var(--fm);font-size:11px;color:var(--am);letter-spacing:.04em}

/* Dua blok yang sama-sama ditambahkan di ujung berkas ini, dari dua arah.
   Bukan benturan isi: git hanya tidak bisa menebak urutannya. Diperiksa —
   240 kelas di blok FE dan 101 di blok dashboard, dan satu-satunya nama
   yang dipakai keduanya adalah .on, yang di kedua sisi selalu majemuk
   (.pfrow.on, .nptab.on) dan tidak pernah berdiri sendiri. Jadi urutannya
   tidak mengubah apa pun yang menang.

   Blok dashboard ditaruh lebih dulu supaya blok FE tetap yang terakhir —
   itu memang tempatnya sejak awal, supaya empat branch frontend berikutnya
   bertemu konflik di sini, bukan di dalam aturan yang juga mereka sunting. */
/* ==========================================================================
   FE — Auth: the sign in screen
   ==========================================================================

   Every value below was read out of the Figma file through the REST API —
   fills[].color, style.fontSize, cornerRadius, paddingTop/Right/Bottom/Left,
   absoluteBoundingBox — from the frame the designer marked LOGIN [WENKA] ✅
   (#126:3130). Nothing here was measured off a screenshot. DESIGN_SYSTEM.md
   records the same numbers with their provenance.

   The colours are local custom properties on .lgpage rather than the :root
   tokens. Those tokens are the dashboard's and this screen is what somebody
   sees before there is a dashboard, so keeping them apart means restyling one
   cannot reach the other. The names below mirror the palette's own families
   (Ink, Content, Cobalt) so a value can be traced back to its tone.

   The identity, password and button rules are scoped two classes deep
   (.lgpage .lgf input) because the global input rule at the top of this file
   is input[type=text] — the same specificity — and a later-wins tie is a thing
   somebody reorders by accident.

   Written as its own block at the end of the file so the four other frontend
   branches touching this stylesheet meet a conflict here at worst, rather than
   inside a rule they are also editing. */

/* Neue Montreal carries the wordmark fallback. Not a Google font, so it cannot
   ride the CDN request in index.html — the files have to sit in public/fonts/.
   Both faces are declared and neither is required: a src that 404s makes the
   face unusable and the stack falls through to Inter, which is what every
   other line on this screen is set in anyway. */
@font-face{font-family:'Neue Montreal';src:url('/fonts/NeueMontreal-Bold.woff2') format('woff2');
  font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'Neue Montreal';src:url('/fonts/NeueMontreal-BoldItalic.woff2') format('woff2');
  font-weight:700;font-style:italic;font-display:swap}
:root{--fnm:'Neue Montreal',var(--fu),system-ui,sans-serif}

/* Naik ke :root.

   Semula token ini hanya di .lgpage, lalu dibagi ke .psmod, dan alasan
   menahannya di luar :root adalah supaya gaya layar login tidak bisa merembet
   ke aplikasi. Alasan itu habis begitu sidebar ikut memakainya — sidebar
   ADALAH aplikasinya, dan palang aksennya tidak berwarna karena var() di sana
   tidak menemukan apa-apa.

   Awalan --lg- sekarang salah nama; ia bukan lagi milik login saja. Mengganti
   nama seratusan rujukan tidak menambah apa pun hari ini, jadi namanya
   dibiarkan dan alasannya ditulis di sini. */
:root{
  /* Neutrals — Ink */
  --lg-canvas:#0B0C10;        /* 900 · app background */
  --lg-panel:#121418;         /* 800 · panel, and the card */
  --lg-field:#212429;         /* 300 · field fill */
  --lg-line:#212429;          /* 300 · card hairline */
  --lg-off:#1E1F21;           /* 400 · disabled ground */
  /* Neutrals — Content */
  --lg-tx:#FFFFFF;
  --lg-tx2:#98A1B5;           /* 500 · the workhorse; labels and the subtitle */
  --lg-tx3:#868EA3;           /* 600 · the floor for body text; helper, placeholder */
  --lg-tx4:#464D5D;           /* disabled */
  /* Cobalt */
  --lg-blue:#1454DB;          /* 500 · primary */
  --lg-blue-h:#6BA1FF;        /* hover */
  --lg-blue-p:#0F41A8;        /* pressed */
  --lg-focus:#7FA6FF;         /* focus ring on a field */
  --lg-link:#8FB2FF;          /* 200 · the forgot link */
  --lg-error:#FF6B6B;
  /* Glass — White alpha. "The glass system. White alpha over the panel ground
     — never a solid." The tone number IS the opacity percent, and each names
     its own job in the palette, so these are not shades picked by eye. */
  --lg-glass02:rgba(255,255,255,.02);   /* glass fill, low stop */
  --lg-glass04:rgba(255,255,255,.04);   /* ghost button plate */
  --lg-glass06:rgba(255,255,255,.06);   /* glass fill, high stop */
  --lg-glass07:rgba(255,255,255,.07);   /* dividers, panel border */
  --lg-glass09:rgba(255,255,255,.09);   /* inner top highlight on glass */
  --lg-glass14:rgba(255,255,255,.14);   /* primary button ring — from the audit */
  --lg-glass16:rgba(255,255,255,.16);   /* hover lift */
  /* Depth s2 — "workspace drop — 0 20 56 -28" */
  --lg-s2:0 20px 56px -28px rgba(0,0,0,.8);
}
/* Hanya tokennya yang dibagi. Tata letak halaman penuh di bawah ini milik
   .lgpage sendiri — menggabungkannya ke selektor di atas membuat .psmod ikut
   jadi display:flex dan isinya berjajar menyamping. */
.lgpage{flex:1;display:flex;min-width:0;min-height:0;background:var(--lg-canvas)}

/* ---------- left: the photograph ---------- */
/* Exactly half the width in the artboard: 720 of 1440. The gradient is not a
   placeholder for the photo, it is what the panel is when the photo has not
   been uploaded or fails to load — the img removes itself on error. */
.lghero{flex:0 0 50%;position:relative;overflow:hidden;
  background:linear-gradient(160deg,#0A3AA8 0%,#1454DB 55%,#6BA1FF 100%)}
.lghero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 42%;display:block}

/* The caption panel spans the full width of the photograph and is rounded on
   its top two corners only — rectangleCornerRadii [44,44,0,0]. Fill is black
   at 14%, and the legibility comes from a background blur rather than from the
   fill: Figma has BACKGROUND_BLUR radius 104 on this node.

   Figma's blur radius and CSS backdrop-filter blur are not the same unit, and
   this is where that was settled by looking rather than by arithmetic. Carried
   over literally, blur(104px) erases the photograph behind the panel into a
   smear; Figma's own render keeps the shapes readable. Halved, it matches.

   So 52px is the eyeballed value and 104 is its provenance — the one number on
   this screen that is not a direct transcription. */
.lghero-tx{position:absolute;left:0;right:0;bottom:0;color:#fff;
  padding:44px;border-radius:44px 44px 0 0;
  background:rgba(0,0,0,.14);
  -webkit-backdrop-filter:blur(52px);backdrop-filter:blur(52px)}
/* The edge is what makes this read as glass rather than as a dark box, and it
   was the property missed on the first pass. Figma has a 1.5px stroke on this
   node, aligned OUTSIDE, and it is not a flat colour — it is a linear gradient
   running from #D5D5D5 to #282B31 along handles (0, 0.064) to (1, 0.915). In a
   720x267 box that is 720 across and 227 down, so 107.5deg in CSS terms, where
   0deg points up and 90deg points right.

   Drawn as a masked pseudo-element because border-image ignores border-radius
   and a gradient cannot be a border-color. inset:-1.5px is strokeAlign
   OUTSIDE; the radius grows by the same 1.5 so the outer curve stays concentric
   with the panel's. The xor/exclude composite punches out the middle, leaving
   only the ring. */
.lghero-tx::before{content:'';position:absolute;inset:-1.5px;pointer-events:none;
  border-radius:45.5px 45.5px 0 0;padding:1.5px;
  background:linear-gradient(107.5deg,#D5D5D5 0%,#282B31 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude}
.lghero-tx h2{font-family:var(--fu);font-size:40px;line-height:48px;font-weight:600;letter-spacing:0}
.lghero-tx p{font-size:20px;line-height:30px;margin-top:24px;color:rgba(255,255,255,.75)}

/* ---------- right: the form ---------- */
/* A violet ellipse 747 across, blurred, centred on the bottom right corner of
   the frame — its centre works out to 99.7% / 96.9% of a 1440x1024 artboard.
   A radial gradient rather than an image: it costs nothing and scales. */
.lgpanel{flex:1;min-width:0;display:flex;align-items:center;justify-content:center;
  overflow-y:auto;padding:40px 28px;position:relative;
  background:
    radial-gradient(560px 560px at 100% 97%,rgba(153,73,229,.20),transparent 70%),
    var(--lg-canvas)}
.lgcol{width:100%;max-width:540px;position:relative}

/* Brand block sits 45 above the card. */
.lgmark{text-align:center;margin-bottom:45px}
/* 474x75 in the artboard, which is also the artwork's own width, so this is a
   ceiling rather than a target: it shrinks on a narrow screen and is never
   blown up past what was drawn. */
.lgmark-img{max-width:474px;width:100%;height:auto;display:block;margin:0 auto}
.lgmark-fb{font-family:var(--fnm);font-size:38px;font-weight:700;font-style:italic;
  letter-spacing:-.025em;color:var(--lg-tx);line-height:1.1}
.lgmark-b{display:flex;align-items:center;justify-content:center;gap:13px;margin-top:5px;
  font-size:20px;font-weight:400;color:var(--lg-tx)}
.lgmark-b img{height:27px;width:auto;display:block}

/* The card IS the Popup component from the design system, not a card invented
   for this screen — same fill, hairline, radius, padding and effects. The two
   shadows are Figma's own: an inner white highlight at 9% on the top edge, and
   a drop shadow offset 20 with radius 56 and spread -28.

   It carries .lgedge as well, which is the point of that class existing. The
   Popup node has BOTH a flat #212429 stroke and the GLASS effect, so the two
   belong together rather than in place of one another: the ring sits exactly
   over the 1px border — inset:0 on a border-box — and composites white over
   it, so the hairline stays #212429 at the two dark corners and lifts towards
   Glass 14 along the sides. One border, lit unevenly, which is what glass
   does. */
.lgcard{position:relative;background:var(--lg-panel);border:1px solid var(--lg-line);
  border-radius:16px;padding:24px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}

.lgcard h1{font-family:var(--fu);font-size:18px;line-height:27px;font-weight:600;color:var(--lg-tx)}
/* Berlaku di kartu modal juga. Tanpa .psmod di selektor ini, subjudul di
   Switch brand jatuh ke bawaan — 14px putih — dan terbaca lebih besar serta
   lebih jauh dari judulnya daripada di Pick a Brand. */
.lgcard .lgsub,.psmod .lgsub{font-size:16px;line-height:24px;font-weight:400;color:var(--lg-tx2);margin:5px 0 24px}

/* Field group: label, field, helper — gap 6 between them, 16 between groups.

   Aturan inputnya menyebut .lgpage DAN .psmod. Dua kelas, bukan satu yang
   lebih longgar, karena aturan global di atas berkas ini adalah
   input[type=text] — kekhususan yang sama — dan sebuah selektor satu-kelas
   akan kalah seri. Inilah sebabnya modal Profile Settings sempat memakai
   field setinggi 38 berwarna dashboard. */
/* Jaraknya diberikan ANTAR field, bukan di bawah tiap field. Dengan
   margin-bottom, field terakhir menyumbang 16 di atas margin-top tombol yang
   24 — terukur 40 di layar, sementara Popup menetapkan 24 antara body dan
   footer. */
.lgf{margin-bottom:0}
.lgf+.lgf{margin-top:16px}
.lgf label{display:block;font-family:var(--fu);font-size:14px;line-height:21px;font-weight:500;
  color:var(--lg-tx2);margin-bottom:6px;letter-spacing:0;text-transform:none}
.lgpage .lgf input,.psmod .lgf input{width:100%;height:48px;padding:12px 16px;border:1px solid transparent;border-radius:8px;
  background:var(--lg-field);color:var(--lg-tx);font-family:var(--fu);font-size:16px;line-height:24px;
  transition:border-color .12s,box-shadow .12s}
.lgpage .lgf input::placeholder,.psmod .lgf input::placeholder{color:var(--lg-tx3)}
.lgpage .lgf input:focus,.psmod .lgf input:focus{outline:0;border-color:var(--lg-focus)}
.lgpage .lgf input:disabled,.psmod .lgf input:disabled{background:var(--lg-off);color:var(--lg-tx4)}
/* The helper line is part of the Input component, so it is in the markup even
   when empty — and collapses to nothing until something is put in it. Error
   colours the helper and the field edge together, which is how the component
   defines that state. */
.lghelp{font-size:12px;line-height:18px;color:var(--lg-tx3);margin-top:6px}
.lghelp:empty{display:none}
.lgf.bad .lghelp{color:var(--lg-error)}
.lgpage .lgf.bad input,.psmod .lgf.bad input{border-color:var(--lg-error)}

.lgpw{position:relative}
.lgpage .lgpw input,.psmod .lgpw input{padding-right:48px}
.lgeye{position:absolute;right:8px;top:24px;transform:translateY(-50%);
  width:32px;height:32px;display:flex;align-items:center;justify-content:center;
  border:0;background:transparent;border-radius:8px;color:var(--lg-tx3);cursor:pointer;padding:0}
.lgeye svg{width:20px;height:20px}
.lgeye:hover{color:var(--lg-tx)}

/* Button / Type=Primary, Size=Medium: 48 tall, radius 8, 20 of side padding,
   8 between icon and label, label 14 at 600. */
/* Button / Type=Primary, Size=Medium. The ring is Glass 14, and it is there
   because the contrast audit put it there: "revised — primary button ring ·
   lifts the edge to 3.84:1". The approved artboard predates that revision and
   still draws this button without one, so this is the palette overruling the
   frame — on an accessibility fix the palette itself marks RESOLVED. */
.lgbtn{width:100%;height:48px;margin-top:24px;border:1px solid var(--lg-glass14);border-radius:8px;padding:0 20px;
  background:var(--lg-blue);color:#fff;font-family:var(--fu);font-size:14px;line-height:21px;font-weight:600;
  cursor:pointer;transition:background .12s,border-color .12s}
.lgbtn:hover{background:var(--lg-blue-h);border-color:var(--lg-glass16)}
.lgbtn:active{background:var(--lg-blue-p)}
.lgbtn:disabled{background:var(--lg-off);color:var(--lg-tx4);border-color:transparent;cursor:default}

.lgor{display:flex;align-items:center;gap:23px;margin:12px 0}
.lgor::before,.lgor::after{content:'';flex:1;height:2px;background:rgba(102,102,102,.25)}
.lgor span{font-family:var(--fu);font-size:12px;line-height:18px;color:#A0A0A0}

/* Type=Secondary carries no fill and no stroke in the component — the edge
   that reads as a border is the same inner highlight the card has. */
/* Button / Type=Secondary — the glass button, and the whole of its appearance
   is one named effect style: Glass/Strong (12:3868). The node carries
   fills:[] and strokes:[] — no background and no border, which is confirmed by
   the designer's own dev-mode readout listing only radius, padding, gap and
   these two shadows.

   So the gradient fill and the Glass 14 ring that were here are both gone.
   They were a reasonable guess from the palette and they were wrong: Glass 14
   is the PRIMARY button's ring, and this button has no ring at all.

     inner  0 1px 0 #FFFFFF17 inset   — #17 is 23/255, Glass 09
     drop   0 20px 56px -28px #000000CC — #CC is 204/255, Depth s2

   Both match what the designer sent byte for byte.

   Two things do not survive the crossing, and neither is a mistake to fix:

   Figma's GLASS is the third effect in the style and it does the refraction
   rim — the bright edge that makes the button read as a pane rather than as
   nothing. CSS has no equivalent, and inventing one would put a border on a
   component the file says has none. backdrop-filter carries the frost half of
   it, which is all CSS can honestly do.

   The drop shadow has showShadowBehindNode:true, so in Figma it is visible
   THROUGH the transparent button. A CSS outer box-shadow is clipped to outside
   the border box and never shows through. Here that costs nothing — the shadow
   is black at 80% with -28 of spread on a 48 tall button, so it is a thin
   smudge, and the ground behind it is #121418. */
/* ---------- tepi kaca, dipakai ulang ----------

   Figma menggambarnya lewat efek GLASS, dan REST API memulangkan efek itu
   sebagai {"type":"GLASS"} tanpa satu pun parameter — jadi bentuknya tidak
   bisa dibaca dari file dan datang dari pengamatan desainer atas hasil
   render: putih di kiri, kanan dan bawah, padam di sudut kanan-atas dan
   kiri-bawah.

   Dua sudut berseberangan yang padam dengan sisi-sisi yang terang berarti
   sumbu gradiennya membentang di antara kedua sudut itu, dengan puncak terang
   di tengah — bukan gradien dua-stop biasa. `to top right` membentang dari
   kiri-bawah ke kanan-atas; transparan di kedua ujung dan Glass 14 di tengah
   menaruh padamnya tepat di dua sudut itu, dan menyisakan terang di keempat
   sisi karena setiap titik sisi memproyeksi ke bagian tengah sumbu.

   Cincinnya pseudo-element bertopeng, bukan `border`: border tidak bisa
   bergradien, dan `border-image` mengabaikan `border-radius`. `inset:0` dengan
   `border-radius:inherit` membuatnya mengikuti radius apa pun milik
   pemakainya, jadi kelas ini bisa ditempel ke komponen lain apa adanya.

   Tepi atas sengaja tidak diandalkan ke sini — ia sudah punya garisnya sendiri
   dari `inset 0 1px 0 #FFFFFF17`, yang memang tertulis di Figma. */
.lgedge{position:relative}
.lgedge::before{content:'';position:absolute;inset:0;pointer-events:none;
  border-radius:inherit;padding:1px;
  background:linear-gradient(to top right,transparent 0%,var(--lg-glass14) 50%,transparent 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude}

.lgbtn2{width:100%;height:48px;display:flex;align-items:center;justify-content:center;gap:8px;
  border:0;border-radius:8px;padding:0 20px;background:transparent;color:var(--lg-tx);
  font-family:var(--fu);font-size:14px;line-height:21px;font-weight:600;cursor:pointer;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);
  transition:background .12s}
/* Glass 16 is "hover lift" in the palette, and a lift is the only thing that
   can happen here without adding a border the component does not have. */
.lgbtn2:hover{background:var(--lg-glass16)}
.lgbtn2 svg{width:24px;height:24px;flex-shrink:0}

.lgforgot{display:block;margin:24px auto 0;border:0;background:none;padding:0;
  color:var(--lg-link);font-family:var(--fu);font-size:14px;line-height:21px;font-weight:500;cursor:pointer}
.lgforgot:hover{text-decoration:underline}

/* The server answers one message for the whole form rather than one per field,
   so this stays a single box above the fields. Error styling follows the Input
   component's own: the error hue on the text, the field edge to match. */
.lgbad{background:rgba(255,107,107,.10);border:1px solid rgba(255,107,107,.34);color:var(--lg-error);
  border-radius:8px;padding:12px 16px;font-size:14px;line-height:21px;margin-bottom:16px}

/* ---------- Profile Settings ----------

   Kartu Popup yang sama dengan layar login, di dalam .mod milik dashboard.
   Semua aturan di bawah berlingkup .psmod supaya modal lain di aplikasi ini
   tidak ikut berubah — revamp ini hanya untuk satu layar.

   Kolom kiri-kanan di atas, dua baris status di tengah, tiga tombol bertumpuk
   di bawah, persis seperti gambarnya. */
/* .mod menjadi pembungkus bening, dan .psmod sendiri yang jadi kartunya.
   Sebabnya token: --lg-* dideklarasikan DI .psmod, jadi .mod sebagai induknya
   tidak bisa membacanya — menata .mod dengan var(--lg-panel) menghasilkan
   latar kosong dan garis warna dashboard. */
.ov:has(.psmod) .mod{background:transparent;border:0;border-radius:0;padding:0;
  width:700px;max-width:100%;overflow:visible}
.psmod{display:block;background:var(--lg-panel);border:1px solid var(--lg-line);
  border-radius:16px;padding:24px;max-height:88vh;overflow-y:auto;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}

.ps-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:24px}
.psmod h2{font-family:var(--fu);font-size:18px;line-height:27px;font-weight:600;color:var(--lg-tx);margin:0}
.ps-x{width:32px;height:32px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  border:0;background:transparent;border-radius:8px;color:var(--lg-tx2);cursor:pointer;padding:0}
.ps-x svg{width:20px;height:20px;stroke:currentColor;stroke-width:1.8;fill:none}
.ps-x:hover{color:var(--lg-tx);background:var(--lg-glass06)}

/* Dua kolom: kolom kiri memuai, tile foto tetap 268 selebar desainnya. */
.ps-top{display:grid;grid-template-columns:1fr 268px;gap:24px;align-items:start}
.ps-fields .lgf+.lgf{margin-top:16px}

.ps-tile{position:relative;display:block;width:268px;height:268px;border-radius:8px;overflow:hidden;
  background:var(--lg-field);cursor:pointer}
.ps-img{width:100%;height:100%;object-fit:cover;display:block}
.ps-ini{display:flex;align-items:center;justify-content:center;
  font-family:var(--fu);font-size:64px;font-weight:700}
/* Label yang membuka pemilih berkas; inputnya sendiri disembunyikan tapi tetap
   ada di DOM, karena saveMyProfile() membacanya lewat id. */
.ps-file{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.ps-cam{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  border-radius:50%;background:rgba(11,12,16,.55);color:#fff;
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.ps-cam svg{width:22px;height:22px;stroke:currentColor;stroke-width:1.7;fill:none}
.ps-tile:hover .ps-cam{background:rgba(11,12,16,.75)}

.ps-rows{margin-top:24px;display:flex;flex-direction:column;gap:16px}
.ps-row{display:flex;align-items:center;justify-content:space-between;gap:16px}
.ps-rt{font-family:var(--fu);font-size:18px;line-height:27px;font-weight:600;color:var(--lg-tx)}
.ps-rs{font-size:14px;line-height:21px;color:var(--lg-tx2);margin-top:2px}
.ps-rs b{color:var(--lg-tx);font-weight:600}
/* Tombol sekundernya melebar mengikuti isinya, bukan selebar kartu. */
.ps-act{width:auto;flex-shrink:0;min-width:140px}

.ps-foot{margin-top:24px;display:flex;flex-direction:column;gap:12px}
.ps-foot .lgbtn{margin-top:0}
/* Cancel adalah tombol hantu merah, satu-satunya merah di kartu ini. */
.ps-cancel{border:0;background:none;padding:8px 0;color:var(--lg-error);
  font-family:var(--fu);font-size:14px;line-height:21px;font-weight:500;cursor:pointer}
.ps-cancel:hover{text-decoration:underline}

/* Tombol saklar di blok status. Ia tetap sebuah <button> — handler-nya tidak
   berubah — tapi keadaannya terbaca dari gayanya, bukan hanya dari labelnya.
   aria-pressed ikut dipasang supaya pembaca layar tahu ini saklar.

   Nilainya dibaca dari node `btn / Select Assignee` (160:2768) di file Figma,
   yang isinya persis sama dengan panel dev-mode yang dikirim desainer:

     isian   linear gradient putih 6% -> putih 20%, handle (0,.5) -> (1,.5),
             jadi mendatar dari kiri ke kanan — sumbu yang sama dengan baris
             brand di layar Pick a Brand
     garis   1px putih 12%, All sides, Inner alignment
     bayang  Glass/Strong, sudah melekat di .lgbtn2

   Putih 20% TIDAK ada di keluarga Glass (02 04 05 06 07 09 11 14 16 26), jadi
   ia nilai lepas dari node ini, bukan token. Ditandai di sini supaya tidak
   dikira ada tone yang terlewat.

   Cincin .lgedge dimatikan saat aktif. Cincin itu aproksimasi saya untuk efek
   GLASS yang tidak bisa dibaca API; di state ini ada border sungguhan di
   datanya, dan yang nyata mengalahkan yang diperkirakan. Memasang keduanya
   berarti dua tepi bertumpuk. */
.ps-act.on{
  background:linear-gradient(to right,rgba(255,255,255,.06),rgba(255,255,255,.20));
  border:1px solid rgba(255,255,255,.12)}
.ps-act.on::before{display:none}
.ps-act.on:hover{background:linear-gradient(to right,rgba(255,255,255,.09),rgba(255,255,255,.26))}

/* Dialog konfirmasi memakai kartu yang sama. Footernya dua tombol sejajar —
   Secondary kaca di kiri, Primary di kanan — seperti varian "2 Button" di
   komponen Pop Up, masing-masing selebar separuh. */
.ps-say{font-size:16px;line-height:24px;color:var(--lg-tx2);margin-top:5px}
.ps-warn{margin-top:16px;border-radius:8px;padding:10px 12px;
  font-size:13px;line-height:20px;
  background:rgba(240,180,41,.10);border:1px solid rgba(240,180,41,.34);color:#F0B429}
.ps-warn svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none;
  vertical-align:-3px;margin-right:4px}
.ps-foot3{margin-top:24px;display:flex;gap:16px}
.ps-foot3>*{flex:1;margin-top:0}
/* Satu-satunya tombol merah di sistem ini: tindakan yang menghapus atau
   menolak. Error #FF6B6B, dengan ring Glass 14 seperti Primary biru. */
.lgbtn.danger{background:var(--lg-error);color:#0B0C10}
.lgbtn.danger:hover{background:#FF8787}
.lgbtn.danger:active{background:#E85555}

@media (max-width:560px){
  .ps-foot3{flex-direction:column-reverse;gap:12px}
}

/* Kartu Reset Password: kartu yang sama, lebih sempit, dan footernya sebaris
   rata kanan alih-alih bertumpuk — itu satu-satunya bedanya dari Profile
   Settings. */
.ov:has(.psmod-sm) .mod{width:540px}
.psmod-sm .lgf+.lgf{margin-top:16px}
.ps-foot2{margin-top:24px;display:flex;align-items:center;justify-content:flex-end;gap:16px}
.ps-save{width:auto;min-width:180px;margin-top:0}
/* changeMyPassword() menulis pesannya sebagai <div class="ftbox">, dan .ftbox
   itu milik dashboard. Handler-nya tidak disentuh — kotaknya saja yang ditata
   ulang di dalam kartu ini, mengikuti state Error komponen Input. */
.psmod .ftbox{background:rgba(255,107,107,.10);border:1px solid rgba(255,107,107,.34);
  color:var(--lg-error);border-radius:8px;padding:10px 12px;
  font-family:var(--fu);font-size:13px;line-height:20px;margin-top:8px}

@media (max-width:560px){
  .ps-foot2{flex-direction:column-reverse;align-items:stretch;gap:12px}
  .ps-save{width:100%}
}

@media (max-width:720px){
  .ps-top{grid-template-columns:1fr}
  .ps-photo{order:-1}
  .ps-tile{width:100%;height:auto;aspect-ratio:1}
  .ps-row{flex-direction:column;align-items:stretch;gap:10px}
  .ps-act{width:100%}
}
@media (hover:none){
  .ps-x:hover{color:var(--lg-tx2);background:transparent}
  .ps-cancel:hover{text-decoration:none}
  .ps-tile:hover .ps-cam{background:rgba(11,12,16,.55)}
}

/* ---------- Choose Brand ----------

   The screen after signing in for anybody in more than one brand. Same Popup
   card as the sign in, same canvas, no photograph — so the panel is the whole
   page rather than its right half.

   Two ambient glows here where the sign in has one, both blurred 115 in Figma:

     ambient / cobalt dim   747x747, centred 99.7% / 97%
                            linear #9949E5 at 16% into #1958DC at stop .52
     ambient / violet       860x820, centred 42.4% / 75.2%
                            radial #7349F6 at 20% / 7% / 0%

   The violet is a radial in the file and carries over exactly. The cobalt is a
   linear gradient inside an ellipse, blurred past recognition, so it is drawn
   as a radial that runs through both of its stops — the shape is gone either
   way, only the hues survive. */
.lgpage.solo{display:block}
.lgpage.solo .lgpanel{width:100%;height:100%;padding:40px 24px;
  background:
    radial-gradient(430px 410px at 42.4% 75.2%,rgba(115,73,246,.20),rgba(115,73,246,.07) 50%,transparent 100%),
    radial-gradient(374px 374px at 99.7% 97%,rgba(25,88,220,.34),rgba(153,73,229,.16) 55%,transparent 72%),
    var(--lg-canvas)}

.bplist{display:flex;flex-direction:column;gap:16px;margin-top:24px}

/* btn / Select Assignee — 66 tall, radius 8, 14 of padding, 17 between the
   tile and the name. The fill is a gradient the palette already names: Glass
   06 into Glass 02, running left to right (handles 0,.5 to 1,.5). The border
   is a flat white at 12%, inside, so it eats into the radius rather than
   growing the row. */
.bprow{display:flex;align-items:center;gap:17px;width:100%;height:66px;padding:14px;
  border:1px solid rgba(255,255,255,.12);border-radius:8px;
  background:linear-gradient(to right,var(--lg-glass06),var(--lg-glass02));
  color:var(--lg-tx);font-family:var(--fu);cursor:pointer;text-align:left;
  transition:background .12s,border-color .12s}
/* No hover is drawn in the file, and a row that can be clicked needs one.
   Glass 16 is the palette's own "hover lift", so the lift is the tokened
   answer rather than an invented tint. */
.bprow:hover{background:var(--lg-glass16);border-color:var(--lg-glass16)}
.bpname{font-size:18px;line-height:27px;font-weight:500;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Subjudul menempel di bawah judulnya, bukan terpisah sejauh margin kepala.
   .ps-head menyisakan 24px untuk kartu yang langsung masuk isi; ketika ada
   subjudul, jarak itu miliknya.

   margin-top-nya juga dinolkan. Kepala itu setinggi tombol X — 32px — sementara
   judulnya 27px, jadi align-items:flex-start menyisakan 5px kosong di bawah
   judul di dalam kepala. Lima itu sudah jarak yang dimaui, jadi lima milik
   .lgsub akan menumpuk jadi sepuluh. Di Pick a Brand tidak ada tombol X, dan
   di situlah selisihnya berasal. */
.psmod .ps-head:has(+ .lgsub){margin-bottom:0}
.psmod .ps-head + .lgsub{margin-top:0}

/* Switch brand memakai baris yang sama, tapi barisnya membawa dua baris teks:
   nama dan peran. Peran ada di sini dan tidak ada di Pick a Brand karena di
   sini orang memilih di antara brand yang semuanya sudah miliknya, dan
   perannya bisa berbeda di masing-masing. Tingginya jadi ikut isi, dengan 66
   sebagai lantai supaya sebaris dan dua baris tidak terlihat berbeda kelas. */
.bprow{height:auto;min-height:66px}
.bprow-tx{display:flex;flex-direction:column;gap:2px;min-width:0;text-align:left}
.bprole{font-size:14px;line-height:21px;color:var(--lg-tx2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Brand yang sedang dipakai. Gradien dan garis yang sama dengan saklar aktif
   di Profile Settings, jadi "sedang aktif" terlihat sama di seluruh aplikasi. */
.bprow.on{background:linear-gradient(to right,rgba(255,255,255,.06),rgba(255,255,255,.20));
  border-color:rgba(255,255,255,.12)}
.bprow.on:hover{background:linear-gradient(to right,rgba(255,255,255,.09),rgba(255,255,255,.26))}
.bpname i{font-style:normal;font-weight:400;color:var(--lg-tx2)}
/* Cancel di sini sendirian, jadi ia tidak melebar seperti pasangan tombol. */
.bs-cancel{width:auto;min-width:140px}

@media (max-width:560px){
  .lgpage.solo .lgpanel{padding:24px 16px}
  .bplist{gap:12px}
  .bprow{height:60px;gap:14px;padding:11px}
  .bpname{font-size:16px}
}
@media (hover:none){
  .bprow:hover{background:linear-gradient(to right,var(--lg-glass06),var(--lg-glass02));
    border-color:rgba(255,255,255,.12)}
}

/* ---------- narrow ----------
   There is no mobile artboard for this screen, so the rule chosen is: the
   photo stops being half the page and becomes a band above the form, keeping
   the caption panel it carries. Hiding it would be less work and would throw
   away the only thing on the screen that says what this product is. The band
   is tall enough that the panel is a caption on a photograph rather than a lid
   over one — measured against the rendered panel, not guessed. */
@media (max-width:1000px){
  .lgpage{flex-direction:column;overflow-y:auto}
  .lghero{flex:0 0 auto;height:320px}
  .lghero-tx{padding:24px;border-radius:28px 28px 0 0}
  .lghero-tx::before{border-radius:29.5px 29.5px 0 0}
  .lgbr{display:none}
  .lghero-tx h2{font-size:28px;line-height:34px}
  .lghero-tx p{font-size:16px;line-height:24px;margin-top:12px}
  .lgpanel{flex:1 0 auto;overflow:visible;padding:36px 22px 48px;align-items:flex-start}
  .lgmark{margin-bottom:32px}
}
@media (max-width:560px){
  .lghero{height:280px}
  .lghero-tx{padding:20px;border-radius:24px 24px 0 0}
  .lghero-tx::before{border-radius:25.5px 25.5px 0 0}
  .lghero-tx h2{font-size:22px;line-height:28px}
  .lghero-tx p{font-size:14px;line-height:21px;margin-top:10px}
  .lgpanel{padding:30px 16px 40px}
  .lgmark{margin-bottom:28px}
  .lgmark-img{max-width:290px}
  .lgmark-fb{font-size:27px}
  .lgmark-b{font-size:16px;gap:10px}
  .lgmark-b img{height:21px}
  .lgcard{padding:20px;border-radius:14px}
}
/* A finger has no hover, and a button that keeps its hover paint after a tap
   reads as still-pressed. Same reasoning as the other hover:none blocks. */
@media (hover:none){
  .lgbtn:hover{background:var(--lg-blue)}
  .lgbtn2:hover{background:transparent}
  .lgeye:hover{color:var(--lg-tx3)}
  .lgforgot:hover{text-decoration:none}
}

/* The markup index.html ships with, shown for the moment before app.js has
   run. Same palette as the screen it hands over to, so nothing flashes.

   .loginwrap and .loginbox further up are deliberately left in place: the
   brand picker in app.js still draws itself with them. */
.lgboot{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  background:#0B0C10}
.lgboot-n{font-family:var(--fnm);font-size:30px;font-weight:700;font-style:italic;
  letter-spacing:-.025em;color:#FFFFFF}
.lgboot-t{font-size:14px;color:#98A1B5}

/* ==========================================================================
   FE — Sidebar
   ==========================================================================

   Nilai dari komponen `sidebar` di Figma (65:4824, varian type=Filled):

     rel          268 lebar, latar #0B0C10
     baris        244x40, radius 12, padding 8/12, gap 12
     induk        14px w500 #98A1B5 — yang aktif w600 #FFFFFF
     anak         13.5px w400 #98A1B5 — yang aktif w600 #8FB3FF
     badge        10px w600 #7FA6FF
     nama brand   13px w500 #EAEDF5

   Ikonnya Phosphor: viewBox 256 dan path terisi, bukan stroke 24 seperti set
   I milik dashboard. Karena itu .phi punya aturan ukurannya sendiri dan tidak
   boleh ikut aturan `svg` global di puncak berkas ini. */

/* Panel berbingkai dengan radius 14, seperti di Figma — bukan kolom yang
   menempel ke tepi jendela. Garisnya #1B1F26, warna yang sama dengan pemisah
   di dalamnya. */
/* Melayang di atas latar itu, bukan menutupinya. Isian #0B0C10 pada 72% supaya
   cahaya ambient masih terbaca menembusnya, dengan blur di belakangnya, dan
   bayangan Depth s1 — yang di palet namanya memang "panel drop — 0 16 40 -24 ·
   sidebar, workspace". */
.sb{width:268px;background:rgba(11,12,16,.72);border:0;border-radius:14px;position:relative;
  -webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);
  box-shadow:0 16px 40px -24px rgba(0,0,0,.5);
  display:flex;flex-direction:column;padding:0;overflow:visible;margin:0}
/* Tepi kaca, resep yang sama dengan .lgedge: cincin gradien bertopeng, terang
   di keempat sisi dan padam di sudut kanan-atas serta kiri-bawah. Digambar di
   ::before dan bukan sebagai border karena border tidak bisa bergradien.

   position:absolute penting di sini — .sb sebuah flex container, dan sebuah
   ::before yang tidak absolut akan jadi flex item dan mendorong isinya. */
.sb::before,.brandsw::before{content:'';position:absolute;inset:0;pointer-events:none;z-index:1;
  border-radius:inherit;padding:1px;
  background:linear-gradient(to top right,transparent 0%,var(--lg-glass14) 50%,transparent 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude}
/* brand 268x66, padding 17/18. */
.sbhead{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:17px 18px;border-bottom:1px solid var(--lg-glass07)}
.sbmark{height:26px;width:auto;display:block;flex-shrink:0}
.sbmark-fb{font-family:var(--fnm);font-size:22px;font-weight:700;font-style:italic;color:#fff}
/* icon button 32x32, radius 7.11, isian #212429. */
.sbfold{width:32px;height:32px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:7px;background:#212429;color:var(--lg-tx2);cursor:pointer;padding:0}
.sbfold:hover{background:#2E333D;color:var(--lg-tx)}
.sbfold .phi{width:16px;height:16px}
/* workspace 268x72 dengan padding 12; kotaknya sendiri 244x48 radius 8. */
.sbtop{padding:12px;border-bottom:1px solid var(--lg-glass07)}
.brandsw{display:flex;align-items:center;gap:10px;height:48px;padding:12px;position:relative;
  border:0;border-radius:8px;background:transparent;cursor:pointer;
  transition:background .12s}
.brandsw:hover{background:var(--lg-glass04)}
.brandsw-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--fu);font-size:13px;font-weight:500;line-height:16px;color:#EAEDF5}
.brandsw-chev{width:16px;height:16px;flex:none;color:var(--lg-tx2)}
/* Varian tanpa brand hanyalah judul, bukan pemilih — ia tidak punya onclick.
   Jadi cincinnya dicabut: sebuah kotak bergaris yang tidak bisa ditekan
   menjanjikan kontrol yang tidak ada. */
.brandsw-none{cursor:default;padding:0 4px;height:auto}
.brandsw-none::before{display:none}
.brandsw-none:hover{background:transparent}
.brandsw-none .brandsw-name{font-size:14px;color:var(--lg-tx)}
.sbscroll{flex:1;overflow-y:auto;padding:8px 12px 16px;min-height:0}

/* Phosphor: 20px, terisi, mewarisi warna barisnya. */
.sb .phi{width:20px;height:20px;flex:none;fill:currentColor;stroke:none}
.nichev{width:16px;height:16px;margin-left:auto;opacity:.7}

.sb .ni{display:flex;align-items:center;gap:12px;height:40px;padding:8px 12px;
  border-radius:12px;cursor:pointer;color:var(--lg-tx2);
  font-family:var(--fu);font-size:14px;font-weight:500;line-height:17px;
  margin-bottom:2px;user-select:none;transition:background .12s,color .12s}
.nilabel{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sb .ni:hover{background:var(--lg-glass06);color:var(--lg-tx)}
/* Induk yang terbuka, dan induk yang memuat halaman sekarang: putih dan tebal,
   tapi tanpa latar — latarnya milik anak yang aktif, bukan induknya. */
.nigroup.open,.nigroup.has{color:var(--lg-tx);font-weight:600}

/* Anak-anak, dengan garis tegak yang menyambungkannya ke induk. Garisnya di
   ::before wadahnya, bukan di tiap baris, supaya ia satu garis utuh dan bukan
   tumpukan potongan yang bisa renggang. */
.nikids{position:relative;padding-left:22px;margin:2px 0 6px}
/* Garis penyambung anak menu: 2px, dan warnanya gradien — putih 26% di atas
   meluruh ke putih 6% di bawah. Itu resep "Sidebar rail" yang sudah terdaftar
   di keluarga Gradients design system, bukan warna yang dipilih di sini.
   Sebelumnya 1px rata #242A35, yang hampir tidak terlihat di atas panel. */
.nikids::before{content:'';position:absolute;left:10px;top:4px;bottom:4px;width:2px;
  border-radius:1px;
  background:linear-gradient(180deg,rgba(255,255,255,.26) 0%,rgba(255,255,255,.06) 100%)}
.sb .ni.nichild{height:38px;font-size:13.5px;font-weight:400;line-height:16px;gap:10px;
  padding:8px 10px;border-radius:10px}
.sb .ni.nichild .phi{width:18px;height:18px}

/* Aktif. Latar biru redup, label Cobalt 200 tebal, dan palang aksen di tepi
   kiri — di dalam padding wadah anak supaya ia sejajar dengan garis penyambung
   dan bukan menabraknya. */
/* Node `leaf pill` (210x40, r8) di Figma, apa adanya:

     isian    linear gradient, handle (0,.5) -> (1,.5) — mendatar kiri ke kanan
              #0D5BFF pada 50% meluruh ke #62708C pada 0%
     efek     GLASS, dan INNER_SHADOW putih 9% offset y1 dengan radius 4

   Radius 4 pada sorot dalamnya penting: di tombol nilainya 0, yang menghasilkan
   garis setebal satu piksel di tepi atas. Di sini ia kabur — cahaya yang jatuh
   ke dalam, bukan garis.

   Tepinya dari GLASS, yang API tidak jelaskan, jadi dipinjam resep cincin yang
   sama dengan .lgedge. Digambar di ::after karena ::before di sini sudah
   dipakai palang aksen milik anak. */
.sb .ni.on{background:linear-gradient(to right,rgba(13,91,255,.50),rgba(98,112,140,0));
  color:#8FB3FF;font-weight:600;position:relative;
  box-shadow:inset 0 1px 4px rgba(255,255,255,.09)}
.sb .ni.on::after{content:'';position:absolute;inset:0;pointer-events:none;
  border-radius:inherit;padding:1px;
  background:linear-gradient(to top right,transparent 0%,var(--lg-glass14) 50%,transparent 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude}
.sb .ni.on .phi{color:#8FB3FF}
.nikids .ni.on::before{content:'';position:absolute;left:-12px;top:6px;bottom:6px;width:2px;
  border-radius:2px;background:var(--lg-blue)}
/* Palang biru hanya milik anak. Induk yang aktif cukup ditandai latar dan
   tebal hurufnya — palang di induk membuat dua garis tegak bersaing dengan
   garis penyambung di sebelahnya. */

/* Badge. Angka Cobalt 200 di atas pelat biru redup, rata kanan. */
.sb .nb{margin-left:auto;flex:none;min-width:22px;height:18px;padding:0 6px;
  display:inline-flex;align-items:center;justify-content:center;border-radius:6px;
  background:rgba(20,84,219,.30);color:#7FA6FF;
  font-family:var(--fu);font-size:10px;font-weight:600;line-height:12px}
.sb .nb.new{background:rgba(255,107,107,.22);color:#FF9B9B}

/* ---------- rel yang diciutkan ----------
   Hanya ikon induk. Menekan induk berikon anak membuka flyout; induk tanpa
   anak langsung pindah halaman — aturan itu ada di navbar(), bukan di sini. */
body.navmini .sb{width:88px}
body.navmini .sbhead{flex-direction:column;gap:10px;padding:18px 8px 14px}
body.navmini .sbmark{height:24px;max-width:40px;object-fit:cover;object-position:left}
body.navmini .sbtop{padding:0 8px 14px}
body.navmini .brandsw{justify-content:center;padding:8px}
body.navmini .brandsw-txt{display:none}
body.navmini .sbscroll{padding:0 8px 16px}
body.navmini .sb .ni{justify-content:center;padding:8px;gap:0}
body.navmini .nilabel,body.navmini .nichev,body.navmini .sb .nb{display:none}
body.navmini .sb .ni.on::before{display:none}
body.navmini .sb .ni.on{background:var(--lg-glass11)}

.navfly{position:fixed;z-index:120;min-width:226px;padding:8px;
  background:var(--lg-panel);border:1px solid var(--lg-line);border-radius:12px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}
.nifly{display:flex;align-items:center;gap:10px;height:38px;padding:8px 10px;border-radius:10px;
  cursor:pointer;color:var(--lg-tx2);font-family:var(--fu);font-size:13.5px;font-weight:400}
.nifly .phi{width:18px;height:18px;flex:none;fill:currentColor}
.nifly:hover{background:var(--lg-glass06);color:var(--lg-tx)}
.nifly.on{background:linear-gradient(to right,rgba(13,91,255,.50),rgba(98,112,140,0));
  color:#8FB3FF;font-weight:600;box-shadow:inset 0 1px 4px rgba(255,255,255,.09)}
.nifly .nb{margin-left:auto;flex:none;min-width:22px;height:18px;padding:0 6px;
  display:inline-flex;align-items:center;justify-content:center;border-radius:6px;
  background:rgba(20,84,219,.30);color:#7FA6FF;font-size:10px;font-weight:600}

@media (hover:none){
  .sb .ni:hover{background:transparent;color:var(--lg-tx2)}
  .sbfold:hover{background:var(--lg-glass06);color:var(--lg-tx2)}
}

/* Sidebar lama mewarnai keadaan aktif dengan lime: border-left 3px, ikon, dan
   badge. Aturan baru menang untuk latar dan warna teks karena lebih khusus,
   tapi tidak pernah menyebut ketiganya — jadi yang tersisa adalah palang hijau
   di sebelah kiri baris aktif. Dicabut di sini, dilingkupi ke .sb saja supaya
   tidak ada tempat lain yang ikut berubah. */
.sb .ni{border-left:0}
.sb .ni.on svg,.sb .ni.on .phi{color:#8FB3FF}
.sb .ni.on .nb,.sb .ni.on .nb.q{background:rgba(20,84,219,.30);color:#7FA6FF}
.sb .ni.on .nb.new{background:rgba(255,107,107,.22);color:#FF9B9B}

/* children di Figma: padding 6/0/2/22 — anak menjorok 22 dari tepi dalam. */
.nikids{padding-left:22px;margin:0 0 2px;padding-top:6px;padding-bottom:2px}
.nikids::before{left:10px;top:8px;bottom:6px}

/* Saat terciut, logo yang dipakai m-logo.svg — 43x31, tanda M saja. */
body.navmini .sbmark{height:28px;max-width:none;object-fit:contain;object-position:center}
body.navmini .sbhead{border-bottom:1px solid var(--lg-glass07)}
body.navmini .sbtop{padding:12px 8px}
body.navmini .brandsw{height:40px;padding:6px;justify-content:center}
body.navmini .brandsw-name,body.navmini .brandsw-chev{display:none}

/* ==========================================================================
   FE — Header dan My Feedback
   ==========================================================================

   Dari frame `My Feedback - Unread + Read` (89:12338) dan `topbar` (89:12451). */

/* Tinggi topbar ditentukan isinya, bukan dipatok. Aturan lama di baris 73
   memasang height:56px dan aturan ini tidak pernah mencabutnya — sementara
   isinya 17 padding + 48 blok judul = 65. Selisih 9 itu tidak memotong apa pun,
   ia hanya membuat subjudul menonjol keluar kotaknya, sehingga jarak 24 ke
   baris filter terbaca 15. Itu jarak yang dikeluhkan.

   65 kebetulan juga tinggi topbar di Figma (89:12451), dan bukan kebetulan:
   angkanya memang 17 + 48 yang sama. min-height memeganginya supaya header
   tidak berubah tinggi di halaman yang tidak punya subjudul. */
.tb{display:flex;align-items:flex-start;gap:12px;padding:17px 0 0;background:transparent;
  border:0;height:auto;min-height:65px}
.tbtitle{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.tbtitle h1{font-family:var(--fu);font-size:22px;line-height:26px;font-weight:700;color:var(--lg-tx);margin:0}
.tbtitle p{font-size:12px;line-height:18px;color:var(--lg-tx2);margin:0}
.tbctl{display:flex;align-items:center;gap:12px;flex:none}

/* icon button 44x44, radius 12, isian #212429. */
.tbicon{position:relative;width:44px;height:44px;flex:none;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:12px;background:var(--lg-field);color:var(--lg-tx2);cursor:pointer;padding:0}
.tbicon .phi{width:20px;height:20px;fill:currentColor}
.tbicon:hover{background:#2E333D;color:var(--lg-tx)}
/* Badge memakai biru sistem, sama dengan badge di sidebar. Merah yang ada di
   sini sebelumnya nilai karangan saya; di palet merah berarti error, bukan
   "ada yang baru". Garis setebal 2px sewarna tombolnya memisahkannya dari
   ikon di baliknya. */
.tbdot{position:absolute;top:4px;right:4px;min-width:18px;height:18px;padding:0 5px;
  display:flex;align-items:center;justify-content:center;border-radius:9px;
  background:var(--lg-blue);color:#fff;border:2px solid var(--lg-field);
  font-family:var(--fu);font-size:10px;font-weight:600;line-height:12px}
.tbicon:hover .tbdot{border-color:#2E333D}

/* account 227x44, padding 8, gap 8. */
/* account 227x44 di Figma. Tanpa lebar minimum ia menyusut mengikuti nama —
   "Kavan" menghasilkan 120 dan gear-nya berimpit dengan teks. Lebarnya
   ditahan, dan .tbwho yang memuai supaya gear selalu terdorong ke tepi. */
.tbacct{display:flex;align-items:center;gap:8px;height:44px;padding:8px;flex:none;
  min-width:227px;
  border-radius:12px;background:var(--lg-field);cursor:pointer;transition:background .12s}
.tbacct:hover{background:#2E333D}
.tbav{width:32px;height:32px;flex:none;border-radius:16px;object-fit:cover;
  border:1px solid rgba(255,255,255,.10);display:flex;align-items:center;justify-content:center;
  font-family:var(--fu);font-size:12px;font-weight:600;line-height:1}
.tbwho{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}
.tbname{font-size:12px;line-height:14px;font-weight:500;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tbrole{font-size:10px;line-height:15px;font-weight:400;color:var(--lg-tx3)}
.tbgear{width:20px;height:20px;flex:none;color:var(--lg-tx2)}

/* ---------- My Feedback ---------- */
/* filter bar: segmented 166x38 r10 bergaris putih 12%, padding 4, gap 4. */
.fbbar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
.seg{display:inline-flex;align-items:center;gap:4px;padding:4px;border-radius:10px;
  border:1px solid rgba(255,255,255,.12)}
.segb{height:30px;padding:7px 16px;border:0;border-radius:8px;background:transparent;
  color:var(--lg-tx2);font-family:var(--fu);font-size:13px;font-weight:500;line-height:16px;cursor:pointer}
.segb.on{background:var(--lg-blue);color:#fff;border:1px solid rgba(255,255,255,.14)}
.segb:not(.on):hover{color:var(--lg-tx)}
.fbclear{height:32px;padding:8px 12px;border:0;border-radius:8px;background:transparent;
  color:#8FB3FF;font-family:var(--fu);font-size:13px;font-weight:500;cursor:pointer}
.fbclear:hover{background:var(--lg-glass06)}

.fbfeed{display:flex;flex-direction:column;gap:12px}
/* Kartu 90 tinggi, radius 12, padding 16/18, gap 16. Dua keadaan, dan keduanya
   punya node sendiri di Figma (89:12490 · feed) — bukan satu kartu yang diredupkan:

     feedback / unread   #0D142A  garis putih 12%
     feedback / read     #181B1F  garis putih 6%

   Sebelum ini yang sudah dibaca memakai --lg-panel #121418 dengan garis 12%,
   jadi terlalu gelap dan garisnya terlalu terang sekaligus. */
.fbcard{display:flex;align-items:center;gap:16px;min-height:90px;padding:16px 18px;
  border-radius:12px;background:#181B1F;border:1px solid rgba(255,255,255,.06);
  cursor:pointer;transition:background .12s}
.fbcard.new{background:#0D142A;border-color:rgba(255,255,255,.12)}
.fbcard:hover{background:#1E1F21}
.fbcard.new:hover{background:#111A35}
/* Titik belum-dibaca: elips 8, #1454DB, dengan drop shadow 0/0/8 #1454DB 50% —
   glow, bukan bayangan, karena offsetnya nol.

   Yang sudah dibaca kehilangan titiknya sepenuhnya. Figma menggambar titik abu
   #2E333D di kartu read, tapi Joe memutuskan ia hilang: titik yang masih
   terlihat tetap terbaca sebagai penanda, hanya penanda yang lebih pelan, dan
   itu justru membuat "sudah saya kerjakan" lebih sulit dibaca sekilas.

   Transparan, bukan display:none — ruang 8 dan gap 16 tetap dipegang, jadi kode
   dan judul di kartu yang sudah dibaca masih segaris dengan yang belum. Tanpa
   itu setiap baris terbaca bergeser 24px setiap kali statusnya berubah. */
.fbdot{width:8px;height:8px;flex:none;border-radius:50%;background:#1454DB;
  box-shadow:0 0 8px rgba(20,84,219,.5)}
.fbdot.off{background:transparent;box-shadow:none}
.fbcopy{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
/* Kode video hanya berwarna, tidak berlatar. Ia memakai kelas pl-* untuk
   mengambil warna tahapnya, dan kelas itu membawa latar serta padding karena
   aslinya untuk pil — keduanya dicabut di sini, jadi yang tersisa warnanya.

   Warnanya sendiri bukan warna pil lama. Design system memakai satu aturan:
   teks memakai tone 200 keluarganya, latar memakai tone 900. Itu yang terbaca
   di kartu Figma — hijau #97E1C9, kuning #F9DA94, biru #8FB2FF — sementara
   palet lama menjawab #4ADE80 dan #FFC46B, dua tingkat terlalu pekat.

   Merah dan netral tidak muncul di kartu mana pun, tapi tidak perlu ditebak:
   component set Chips (20:28743) punya keduanya. System/Error menjawab #FFB0B0,
   dan Neutral menjawab #98A1B5 — bukan #616A80 seperti dugaan pertama, yang
   akan bertabrakan dengan abu kartu yang sudah dibaca. */
.fbcode{font-family:var(--fu);font-size:13px;font-weight:600;line-height:16px;
  background:none;padding:0;border-radius:0;display:block}
.fbcard.new .fbcode.pl-s{color:#97E1C9}   /* Success 200 */
.fbcard.new .fbcode.pl-a{color:#F9DA94}   /* Warning 200 */
.fbcard.new .fbcode.pl-p,
.fbcard.new .fbcode.pl-e{color:#8FB2FF}   /* Cobalt 200  */
.fbcard.new .fbcode.pl-d{color:#FFB0B0}   /* Error 200   */
.fbcard.new .fbcode.pl-n{color:#98A1B5}   /* Content 500 */
/* Yang sudah dibaca abu rata, apa pun tahapnya — di Figma kartu read dengan pil
   Production hijau tetap membawa kode #616A80. Bukan opacity: sebelum ini
   .55 dipasang di atas warna pekat lama dan hasilnya coklat kehijauan. */
.fbcard:not(.new) .fbcode{color:#616A80}
.fbtitle{font-size:15px;font-weight:500;line-height:18px;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fbnote{font-size:13px;font-weight:400;line-height:16px;color:var(--lg-tx2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Nama penulis catatan: lima huruf pertama node teks itu dioverride di Figma —
   Inter Semi Bold 600, #E6E9F0. Bukan tone palet mana pun, jadi ditulis apa
   adanya; putih penuh terlalu keras di sebelah body #98A1B5. */
.fbnote b{color:#E6E9F0;font-weight:600}
.fbmeta{display:flex;align-items:center;gap:12px;flex:none}
.fbago{font-size:12px;line-height:18px;color:var(--lg-tx3);white-space:nowrap}
/* Badge tahap: .pill, bukan .chip. .chip adalah chip filter dashboard — ia
   punya border, latar --sf dan cursor sendiri, dan memakainya di sini membuat
   semua tahap terlihat sama abu. r4 dan padding 4/8 dari Figma. */
.fbmeta .pill{border-radius:4px;padding:4px 8px;font-family:var(--fu);font-size:10px;
  font-weight:500;line-height:15px;white-space:nowrap}
/* Ini bukan warna karangan: ia component set Chips (20:28743), ukuran Small —
   23 tinggi, r4, pad 4/8, label Inter 10/500 lh15, persis. Tiga tipe, dan
   tipenya yang menentukan apakah status ikut berwarna:

     System   status membawa warnanya  Success/Warning/Error/Info
     Brand    selalu cobalt            #222B47 / #8FB2FF, apa pun statusnya
     Neutral  selalu abu               #212429 / #98A1B5, apa pun statusnya

   Kartu Figma memakai ketiganya: Production → System/Success, Assets →
   System/Warning, Voice Over → Brand/Info. Jadi yang biru memang Brand, bukan
   System/Info — System/Info justru #14314D / #A2D1F4, biru yang lebih kalem.

   Dipagari ke .fbmeta supaya .pill di 37 tempat lain tidak ikut berubah. */
.fbmeta .pill.pl-s{background:#0C3B2C;color:#97E1C9}   /* System / Success */
.fbmeta .pill.pl-a{background:#4C360A;color:#F9DA94}   /* System / Warning */
.fbmeta .pill.pl-d{background:#5C1319;color:#FFB0B0}   /* System / Error   */
.fbmeta .pill.pl-p,
.fbmeta .pill.pl-e{background:#222B47;color:#8FB2FF}   /* Brand            */
.fbmeta .pill.pl-n{background:#212429;color:#98A1B5}   /* Neutral          */

/* Keadaan kosong mengisi sisa layar, bukan berhenti di 60vh lalu mengambang
   dengan ruang gelap di bawahnya. Tepi bawahnya berhenti sejajar dengan tepi
   bawah sidebar — keduanya lalu dibatasi padding 16 milik #app, jadi yang
   terbaca satu bidang, bukan kartu yang kependekan.

   Dipagari dengan :has() supaya hanya halaman yang sedang kosong yang berubah;
   .pg dipakai 32 halaman lain dan tidak satu pun ikut tersentuh. Kalau :has()
   tidak didukung, aturannya tidak berlaku sama sekali dan yang tersisa adalah
   perilaku lama yang benar — kartu 60vh, bukan kartu rusak.

   min-height 60vh tetap dipegang sebagai lantai: di jendela pendek kartunya
   tidak ikut menyusut, .pg yang menggulir. */
.fbempty{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:24px;padding:80px 24px;text-align:center;
  border:1px solid rgba(255,255,255,.12);border-radius:12px;min-height:60vh}
.pg:has(.fbempty){display:flex;flex-direction:column;padding-bottom:0}
.pg:has(.fbempty) .fbempty{flex:1}
.fbmascot{width:190px;max-width:60%;height:auto;display:block}
.fbempty p{font-size:16px;line-height:24px;color:var(--lg-tx);margin:0}

@media (max-width:860px){
  .tb{flex-wrap:wrap}
  .tbctl{width:100%;justify-content:flex-end}
  .tbwho{display:none}
  .tbacct{padding:6px}
  .fbcard{flex-wrap:wrap}
  .fbmeta{width:100%;justify-content:space-between;padding-left:24px}
}
@media (hover:none){
  .tbicon:hover,.tbacct:hover{background:var(--lg-field)}
  .fbcard:hover{background:#181B1F}
  .fbcard.new:hover{background:#0D142A}
}

/* Latar seluruh aplikasi, sama dengan layar login dan Pick a Brand: dua elips
   ambient dari Figma, digambar sebagai radial karena bentuknya sudah hilang di
   balik LAYER_BLUR 115 dan hanya warnanya yang tersisa.

     ambient / violet      radial #7349F6 20% / 7% / 0%, pusat 42.4% / 75.2%
     ambient / cobalt dim  #9949E5 16% ke #1958DC, pusat 99.7% / 97%

   Di #app, bukan di .ma. Ditaruh di area kerja saja, sidebar jadi blok rata
   yang memotong gradiennya dan jahitannya terlihat — latar itu milik seluruh
   layar, dan sidebar melayang di atasnya. */
#app{background:
  radial-gradient(900px 760px at 46% 104%,rgba(115,73,246,.16),rgba(115,73,246,.05) 50%,transparent 100%),
  radial-gradient(520px 520px at 100% 97%,rgba(25,88,220,.20),rgba(153,73,229,.10) 55%,transparent 72%),
  var(--lg-canvas)}
.ma{background:transparent}

/* ===========================================================================
   FE — Overview

   Semua angka di blok ini dibaca dari Figma 89:11044 dan 94:16887, bukan
   dipilih dengan mata. Lebarnya yang paling mudah dilewatkan:

     row / summary   696 + 16 + 412 = 1124
     row / queues    340 + 16 + 340 + 16 + 412 = 1124

   1124 itu lebar 'main' di artboard 1440. Karena itu keduanya ditulis sebagai
   pecahan, bukan piksel — supaya perbandingannya bertahan di lebar lain.
   =========================================================================== */
/* Baris antrean berhenti di tepi bawah layar, dan isinya yang menggulir.

   Yang membuatnya mungkin: halaman Overview berhenti jadi kolom yang menggulir
   utuh dan jadi kolom flex yang tingginya persis jendela. Baris ringkasan
   memakai tingginya sendiri; baris antrean mengambil sisanya; daftar di dalam
   tiap kolom yang overflow.

   Hanya berlaku selama ketiga kartunya masih satu baris. Di bawah 1180 grid
   melipat jadi dua baris, dan flex:1 membagi sisa tinggi ke DUA baris — kartu
   yang seharusnya 459 jadi 168, dan halamannya tetap menggulir. Di lebar itu
   yang benar adalah perilaku biasa: kartu setinggi isinya, halaman yang
   menggulir.

   Dipagari :has(.ovrow-q) supaya hanya halaman ini yang berubah — .pg dipakai
   32 halaman lain. min-height 360 adalah jalan mundur kedua: di jendela yang
   terlalu pendek, .pg kembali menggulir, bukan memampatkan kolomnya sampai
   tidak terbaca. */
@media (min-width:1181px){
  /* Padding bawah nol, bukan 16. Jarak ke tepi jendela sudah diberikan #app —
     padding 16 yang sama yang memberi sidebar jaraknya. Menambahkan 16 lagi di
     sini membuat kartunya berhenti 16px lebih tinggi daripada sidebar di
     sebelahnya, dan dua tepi bawah yang meleset 16px terbaca sebagai kartu yang
     kurang panjang, bukan sebagai jarak. */
  .pg:has(.ovrow-q){display:flex;flex-direction:column;padding-bottom:0}
  .pg:has(.ovrow-q) .ovrow-top{flex:none}
  .pg:has(.ovrow-q) .ovrow-q{flex:1;min-height:360px;margin-bottom:0}
  .pg:has(.ovrow-q) .ovcol,
  .pg:has(.ovrow-q) .ovrow-q>.ovcard{max-height:100%;overflow:hidden}
  /* Yang kosong ikut memenuhi tingginya, bukan mengambang di atas: pesannya
     duduk di tengah area yang tersisa. */
  .pg:has(.ovrow-q) .ovitems:has(.ovempty),
  .pg:has(.ovrow-q) .ovboard .ovempty{flex:1;display:flex;align-items:center;justify-content:center}
  .pg:has(.ovrow-q) .ovitems{overflow-y:auto;min-height:0}
  .pg:has(.ovrow-q) .ovboard .ovtable{overflow-y:auto;min-height:0}
}

.ovrow{display:grid;gap:16px;margin-bottom:16px}
.ovrow-top{grid-template-columns:696fr 412fr}
/* stretch, bukan start. Dengan align-items:start tiap kartu setinggi isinya,
   dan Leaderboard — yang isinya paling sedikit, apalagi waktu masih
   "Counting…" — berhenti di seperempat tinggi sementara dua kolom di
   sebelahnya turun sampai bawah. Yang terbaca bukan tiga kolom, tapi dua
   kolom dan sebuah kartu yang tertinggal. */
.ovrow-q{grid-template-columns:340fr 340fr 412fr;align-items:stretch}
.ovrow-q>.ovcard,.ovrow-q>.ovcol{min-width:0}

/* Kartu dan kolom sengaja berbeda ground. Kartu #121418 dengan garis 6% adalah
   permukaan panel; kolom antrean #101216 dengan garis 7% satu tingkat lebih
   gelap, jadi kartu tugas di dalamnya (#181B1F) naik ke atas, bukan tenggelam. */
.ovcard{background:var(--lg-panel);border:1px solid rgba(255,255,255,.06);
  border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:24px}
.ovhl{padding:24px}
.ovcol{background:#101216;border:1px solid rgba(255,255,255,.07);
  border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:16px}
.ovhead{display:flex;align-items:center;justify-content:space-between;gap:12px}
/* Di kepala kolom angkanya menempel judulnya — frame 'Frame 2147223726' di
   artboard bertata letak baris dengan gap 8 dan tanpa perata ke ujung. Didorong
   ke tepi kanan, angka itu berhenti terbaca sebagai "berapa yang ini" dan mulai
   terbaca sebagai kontrol tersendiri. Kartu lain tetap space-between: di sana
   yang di kanan memang kontrol. */
.ovcol .ovhead{justify-content:flex-start;gap:8px}
.ovhead h3{margin:0;font-size:16px;line-height:19px;font-weight:600;color:var(--lg-tx)}

/* Hitungan di kepala kolom: 24 persegi, r4, Cobalt 800 dengan angka Cobalt 300.
   Bukan merah — di palet ini merah berarti error, dan antrean bukan kesalahan. */
.ovflag{min-width:24px;height:24px;padding:0 5px;border-radius:4px;background:#082259;
  color:#7FA6FF;font-family:var(--fu);font-size:12px;font-weight:600;line-height:24px;
  text-align:center;flex:none}

/* ---- Highlights ---- */
.ovhlbody{display:flex;flex-direction:column;gap:16px}
.ovtiles{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.ovtile{display:flex;align-items:center;gap:16px;padding:16px;border-radius:12px;
  background:#181B1F;border:1px solid rgba(255,255,255,.07);min-width:0}
.ovtile.wide{padding:12px 16px}
.ovic{width:52px;height:52px;flex:none;border-radius:10px;display:flex;align-items:center;
  justify-content:center;background:rgba(20,84,219,.18);border:1px solid rgba(20,84,219,.35)}
.ovic svg{width:24px;height:24px;fill:#8FB3FF}
.ovtx{display:flex;flex-direction:column;gap:8px;min-width:0}
.ovlab{font-size:12.5px;line-height:15px;color:var(--lg-tx2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ovval{font-family:var(--fu);font-size:24px;line-height:29px;font-weight:700;color:var(--lg-tx);
  font-variant-numeric:tabular-nums}

/* ---- periode ---- */
/* Tombolnya memakai tepi kaca yang sama dengan tombol secondary: inner shadow
   putih 9% di atas, lalu Depth s2. Bukan fill — nodenya memang tanpa fill. */
/* Tombol kaca memakai resep yang sudah ada, bukan resep kedua. .lgedge adalah
   cincin gradien bertopeng yang dibangun untuk tombol secondary di layar login
   — terang di kiri, kanan dan bawah, hilang di dua pojok — dan Joe sudah
   menyebut waktu itu bahwa tepi yang sama dipakai komponen lain. Ini salah
   satunya: node Button di Period Picker membawa efek GLASS yang persis sama.

   Jadi yang ditambahkan di sini hanya sorot atas Glass 09 dan Depth s2; cincin
   tepinya datang dari .lgedge di markup. */
.ovperiod{display:inline-flex;align-items:center;gap:4px;height:34px;padding:8px 16px;
  border:0;border-radius:8px;background:transparent;cursor:pointer;
  color:var(--lg-tx2);font-family:var(--fu);font-size:12px;font-weight:500;line-height:18px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}
.ovperiod:hover{color:var(--lg-tx);background:var(--lg-glass16)}
.ovpc{width:16px;height:16px;fill:#868EA3;flex:none}

.ovpw{position:fixed;z-index:120;width:637px;max-width:calc(100vw - 24px);
  display:flex;background:#181B1F;border:1px solid rgba(255,255,255,.08);
  border-radius:12px;box-shadow:0 12px 32px rgba(0,0,0,.45);overflow:hidden}
.ovptags{width:113px;flex:none;padding:12px 24px;display:flex;flex-direction:column;gap:16px;
  border-right:1px solid rgba(255,255,255,.08)}
/* Rata tengah, bukan rata kiri. Node teksnya memang textAlignHorizontal CENTER
   di keempat label dan di Custom, dan lebarnya dipatok 65 — itu sebabnya
   "Last 7 days" dan "Today" terbaca sebagai satu kolom, bukan sebagai daftar. */
.ovptag{border:0;background:none;padding:0;text-align:center;cursor:pointer;
  font-size:12px;line-height:18px;color:var(--lg-tx2);white-space:nowrap}
.ovptag:hover{color:var(--lg-tx)}
.ovptag.on{color:#7FA6FF;font-weight:600}
.ovpmain{flex:1;min-width:0;display:flex;flex-direction:column}
.ovpcals{display:grid;grid-template-columns:1fr 1fr}
.ovpcal{padding:16px 12px;display:flex;flex-direction:column;gap:16px}
.ovpcal+.ovpcal{border-left:1px solid rgba(255,255,255,.08)}
.ovpmo{display:flex;align-items:center;justify-content:space-between;gap:8px;
  font-size:12px;line-height:18px;color:var(--lg-tx2)}
/* Panah bulan. Figma memberinya dua inner shadow setebal 0,6px dan 0,4px —
   sebuah cincin, digambar dengan cara yang tidak bisa dipakai ulang. Di sini ia
   ditulis sebagai yang memang dimaksudkannya: garis melingkar Glass 14, tone
   yang sama dengan cincin tombol primary.

   Yang mati benar-benar mati: disabled menghentikan klik dan fokus keyboard
   sekaligus, dan warnanya turun ke tone disabled supaya terlihat kenapa. */
.ovpnav{width:24px;height:24px;flex:none;border:1px solid var(--lg-glass14);border-radius:50%;
  background:#181B1F;cursor:pointer;display:flex;align-items:center;justify-content:center;
  box-shadow:inset 0 1px 0 var(--lg-glass09);transition:background .12s,border-color .12s}
.ovpnav svg{width:12px;height:12px;fill:#fff}
.ovpnav:hover:not(:disabled){background:var(--lg-field);border-color:var(--lg-glass16)}
.ovpnav:disabled{cursor:default;border-color:var(--lg-glass07);box-shadow:none}
.ovpnav:disabled svg{fill:var(--lg-tx4)}
.ovpnav-sp{width:24px;height:24px;flex:none}
.ovpdow,.ovpgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
.ovpdow span{font-family:var(--fu);font-size:10px;line-height:15px;font-weight:600;
  color:#616A80;text-align:center}
.ovpd{height:34px;border:0;border-radius:8px;background:transparent;cursor:pointer;
  font-size:12px;line-height:18px;color:#D2D8E4;font-variant-numeric:tabular-nums}
.ovpd:hover{background:var(--lg-field)}
.ovpd.off{visibility:hidden;pointer-events:none}
.ovpd.mid{background:rgba(8,34,89,.5)}
.ovpd.on{background:#082259;color:#fff}
/* Baris tombol rata kanan — primaryAxisAlignItems MAX di frame Button row.

   Padding mentahnya 9.68/12.91/12.91/0 dengan gap 6.46, dan angka-angka itu
   tidak dipakai apa adanya: tangga spacing design system berisi 2, 4, 8, 12,
   16, 20, 24 — tidak ada 13, tidak ada 10, tidak ada 6. Pecahan sekian koma
   itu artefak frame yang diskalakan, bukan keputusan desainer. Dibulatkan ke
   langkah terdekat yang benar-benar ada: 12 atas, 16 sisanya, gap 8.

   Yang 0 di kiri juga diberi 16. Tombolnya rata kanan, jadi padding kiri tidak
   pernah terlihat — tapi nol di satu sisi adalah jenis nilai yang diam-diam
   muncul lagi waktu tata letaknya berubah. */
.ovpfoot{display:flex;justify-content:flex-end;align-items:center;gap:8px;
  padding:12px 16px 16px;border-top:1px solid rgba(255,255,255,.08)}

/* ---- Our Project Movement ---- */
.ovbars{display:flex;flex-direction:column;gap:18px}
.ovbar{display:grid;grid-template-columns:104px 1fr 34px;align-items:center;gap:12px;cursor:pointer}
.ovbl{font-size:12px;line-height:15px;color:var(--lg-tx2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ovtrack{height:8px;border-radius:4px;background:rgba(255,255,255,.07);overflow:hidden}
/* Gradien Progress dari design system — #0F41A8 ke #4D87FF, horizontal. Ia
   sudah terdaftar di keluarga Gradients dengan catatan "scrubber fill", jadi
   bilah ini dan scrubber pemutar memakai resep yang sama, bukan dua biru yang
   kebetulan mirip.

   Node di artboard menulisnya sebagai #1454DB rata, dan itu yang terpasang
   sebelumnya. Yang benar gradiennya. */
.ovtrack i{display:block;height:8px;border-radius:4px;
  background:linear-gradient(90deg,#0F41A8 0%,#4D87FF 100%);
  box-shadow:0 0 8px rgba(20,84,219,.45)}
.ovbn{font-size:12px;line-height:15px;font-weight:500;color:#E6E9F0;text-align:right;
  font-variant-numeric:tabular-nums}
.ovbar:hover .ovbl{color:var(--lg-tx)}

/* ---- kartu tugas ---- */
.ovitems{display:flex;flex-direction:column;gap:12px;min-width:0}
.ovtask{background:#181B1F;border:1px solid rgba(255,255,255,.07);border-radius:12px;
  padding:14px;display:flex;flex-direction:column;gap:12px;cursor:pointer;
  transition:background .12s}
.ovtask:hover{background:#1E1F21}
.ovthead{display:flex;align-items:center;gap:12px;min-width:0}
.ovcode{font-family:var(--fu);font-size:13px;line-height:16px;font-weight:600;color:#616A80}
.ovchip{padding:4px 8px;border-radius:6px;font-family:var(--fu);font-size:11px;
  line-height:13px;font-weight:500;white-space:nowrap;margin-left:auto}
/* Chip di kartu tugas berwarna menurut BAGIAN PIPELINE, bukan menurut gerbang.
   Warnanya dibaca dari node Task Type (94:25930), bukan disusun dari aturan:

     Ideation     #14314D / #A2D1F4     Info 900 / Info 200
     Voice Over   #2A2440 / #C4B5FD     violet yang tidak ada di keluarga mana pun
     Assets       #5C1319 / #FFB0B0     Error 900 / Error 200
     Production   #0C3B2C / #97E1C9     Success 900 / Success 200

   Voice Over itu perlu dicatat: #2A2440 dan #C4B5FD tidak muncul di keluarga
   Violet — Ambient (900 adalah #301350, 200 adalah #D0ABF5). Ia dipakai apa
   adanya karena itu yang ada di node, bukan disamakan dengan tone terdekat.

   Yang belum terjawab: Quality Control dan Publish. Task Type hanya menamai
   empat, dan keduanya untuk sekarang ikut Production — hijau. PERLU DIPERIKSA. */
.ovchip.tt-idea{background:#14314D;color:#A2D1F4}
.ovchip.tt-voice{background:#2A2440;color:#C4B5FD}
.ovchip.tt-asset{background:#5C1319;color:#FFB0B0}
.ovchip.tt-prod{background:#0C3B2C;color:#97E1C9}
.ovchip.tt-n{background:#212429;color:#98A1B5}
.ovtbody{display:flex;flex-direction:column;gap:4px;min-width:0}
.ovttl{font-size:14px;line-height:17px;font-weight:500;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ovtsub{font-size:12px;line-height:15px;color:var(--lg-tx3);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ovdates{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0}
.ovdate{display:inline-flex;align-items:center;gap:10px;padding:8px;border-radius:4px;
  background:var(--lg-field);font-family:var(--fu);font-size:11px;line-height:13px;
  font-weight:500;color:#6F7789;white-space:nowrap}
.ovdi{width:12px;height:12px;fill:#6F7789;flex:none}
.ovfaces{display:flex;flex:none}
.ovface{width:24px;height:24px;border-radius:50%;border:.75px solid #fff;flex:none;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--fu);font-size:9px;font-weight:600;color:#fff;overflow:hidden}
.ovface+.ovface{margin-left:-8px}
.ovact{display:flex;align-items:center;gap:10px}
.ovbtn{height:30px;padding:7px 16px;border:0;border-radius:8px;cursor:pointer;
  background:transparent;color:var(--lg-tx);font-family:var(--fu);font-size:13px;
  font-weight:500;line-height:16px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}
.ovbtn:hover{background:var(--lg-glass16)}
/* Primary tidak berlapis kaca: nodenya fill #1454DB dengan cincin Glass 14
   rata, dan .lgedge di atasnya akan menghapus dua pojoknya. */
.ovbtn-p{background:var(--lg-blue);color:#fff;
  box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.ovbtn-p:hover{background:var(--lg-blue-h)}
.ovbtn-p::before{display:none}
/* Glass, untuk Select Assignee di laci, di kartu gerbang, dan di Idea Pool.

   Cincinnya PENUH — inset 0 0 0 1px Glass 14, cincin yang sama yang dipakai
   .ovbtn-p — bukan sorotan atas milik .ovbtn biasa. Itu yang membedakannya di
   gambar yang Joe kirim: tombolnya bergaris keliling, bukan bergaris di atas.

   Bukan primer, dan itu maksudnya. Menugaskan bukan keputusan gerbang; ia
   langkah sesudah keputusannya diambil, dan tombol biru kedua di baris yang
   sama akan bersaing dengan Accept untuk mata orang. */
.ovbtn-g{background:var(--lg-glass04);
  box-shadow:inset 0 0 0 1px var(--lg-glass14),inset 0 1px 0 var(--lg-glass09),var(--lg-s2);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.ovbtn-g:hover{background:var(--lg-glass16)}
.ovbtn-g::before{display:none}
/* Di kartu gerbang ia tombol KETIGA, jadi ia didorong ke ujung kanan — sama
   seperti Feedback di kartu Audio QC dulu. Accept dan Reject tetap berpasangan
   di kiri, tempat mata mencarinya. */
.iqasg{margin-left:auto}
/* Tombol di baris footer picker setinggi 34, bukan 30 — ukuran Button row. */
.ovpfoot .ovbtn{height:34px;padding:8px 16px;font-size:12px;line-height:18px}
/* Reject telanjang, tanpa plat. Di artboard ia teks merah saja — beban
   visualnya sengaja lebih ringan daripada Accept. */
.ovbtn-r{background:none;box-shadow:none;color:var(--lg-error);padding:7px 8px}
.ovbtn-r:hover{color:#FF8F8F}
/* Select Assignee selebar isinya, bukan selebar kolomnya — 128x30 di artboard,
   dengan label 100. Ia juga tidak memakai cincin bertopeng seperti tombol
   footer picker: nodenya membawa isian gradien Glass fill (#FFFFFF 6% ke 2%,
   vertikal — resep yang terdaftar di keluarga Gradients) dengan garis rata
   putih 12%. Dua perlakuan kaca yang berbeda, dan ini yang kedua.

   Paddingnya 7/14, bukan 8/16. Dengan 8/16 tingginya jadi 34 dan tidak lagi
   sebaris dengan Accept di kolom sebelah, yang 30. */
.ovassign{flex:none;width:auto;padding:7px 14px;height:30px;
  background:linear-gradient(180deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,.02) 100%);
  border:1px solid rgba(255,255,255,.12);color:#C8CEDA;box-shadow:none;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ovassign:hover{background:linear-gradient(180deg,var(--lg-glass16) 0%,var(--lg-glass06) 100%);
  color:var(--lg-tx)}
.ovassign::before{display:none}

/* Menu pilih assignee — bentuk Selection Menu dari design system, dipersempit
   ke satu daftar. Ia melayang di body, jadi ia tidak bisa terpotong oleh kolom
   yang menggulir di belakangnya. */
.ovamenu{position:fixed;z-index:130;width:280px;max-width:calc(100vw - 24px);
  display:flex;flex-direction:column;max-height:360px;
  background:#181B1F;border:1px solid rgba(255,255,255,.08);border-radius:12px;
  box-shadow:0 12px 32px rgba(0,0,0,.45);overflow:hidden}
.ovamhead{padding:12px 16px 8px;font-family:var(--fu);font-size:10px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--lg-tx3)}
.ovamlist{overflow-y:auto;min-height:0;padding:0 8px 8px;display:flex;flex-direction:column;gap:2px}
.ovamitem{display:flex;align-items:center;gap:10px;width:100%;height:40px;padding:0 8px;
  border:0;border-radius:8px;background:transparent;cursor:pointer;text-align:left;
  color:var(--lg-tx);font-family:var(--fu);font-size:13px}
.ovamitem:hover{background:var(--lg-glass06)}
.ovamitem.on{background:rgba(20,84,219,.18);box-shadow:inset 0 0 0 1px rgba(20,84,219,.35)}
.ovamav{width:24px;height:24px;flex:none;border-radius:50%;display:flex;align-items:center;
  justify-content:center;font-family:var(--fu);font-size:9px;font-weight:600;color:#fff;overflow:hidden}
.ovamn{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ovamempty{padding:14px 8px 18px;font-size:12.5px;color:var(--lg-tx3);text-align:center}
/* Menyerahkan video keluar dari pool bukan tindakan sepele, jadi menunya
   mengatakan apa yang akan terjadi — bukan hanya menampilkan daftar nama. */
.ovamfoot{border-top:1px solid rgba(255,255,255,.08);padding:10px 16px;
  font-size:11.5px;line-height:16px;color:var(--lg-tx3)}
.ovamaway{margin-left:auto;flex:none;font-family:var(--fu);font-size:10px;
  padding:2px 6px;border-radius:4px;background:#212429;color:#98A1B5}
.ovempty{padding:28px 12px;text-align:center;font-size:13px;color:var(--lg-tx3)}

/* ---- Leaderboard ---- */
.ovboard{gap:16px}
.ovtabs{display:flex;gap:8px;flex-wrap:wrap}
.ovtab{height:34px;padding:8px 16px;border:0;border-radius:8px;background:transparent;
  cursor:pointer;color:var(--lg-tx2);font-family:var(--fu);font-size:12px;font-weight:500;
  line-height:18px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}
.ovtab:hover{color:var(--lg-tx);background:var(--lg-glass16)}
.ovtab.on{background:var(--lg-blue);color:#fff;box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.ovtab.on::before{display:none}
.ovtable{border-radius:10px;overflow:hidden}
/* Kolom ketiga selebar isinya, bukan dipatok 84. Artboardnya memberi 84 pada
   kartu selebar 412; di lebar lain "Finished Task" pecah jadi dua baris dan
   barisnya ikut meninggi. auto + nowrap membuat kolomnya mengambil tepat yang
   dibutuhkan, dan yang mengalah adalah kolom nama — yang memang sudah
   memotong dirinya dengan ellipsis. */
/* Padding tegaknya 13, bukan 9 seperti di artboard. Di sana tabel ini duduk di
   kartu setinggi 603 dengan sepuluh baris; di sini kartunya ikut tinggi jendela
   dan sepuluh baris yang sama meninggalkan ruang kosong di bawahnya. 13
   memakainya untuk bernapas, bukan membiarkannya menganggur.

   Kepala tabel tetap 10: ia pita, bukan baris, dan di artboard pun lebih rapat
   daripada barisnya. */
.ovtr{display:grid;grid-template-columns:24px minmax(0,1fr) auto;gap:12px;padding:13px 12px;
  align-items:center;cursor:pointer}
.ovtr+.ovtr{border-top:1px solid rgba(255,255,255,.05)}
.ovtr span:first-child{font-size:12px;line-height:15px;color:#6F7789;font-variant-numeric:tabular-nums}
.ovtr span:nth-child(2){font-size:13px;line-height:16px;color:#C8CEDA;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ovtr span:last-child{font-size:13px;line-height:16px;font-weight:500;color:var(--lg-tx2);
  text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.ovth{background:#181B1F;padding:10px 12px;cursor:default;border-top:0}
.ovth span{font-family:var(--fu);font-size:11px;line-height:13px;font-weight:500;
  color:var(--lg-tx2)!important}
.ovtr:not(.ovth):hover{background:var(--lg-glass04)}

@media (max-width:1180px){
  .ovrow-top{grid-template-columns:1fr}
  .ovrow-q{grid-template-columns:1fr 1fr}
  .ovrow-q>.ovboard{grid-column:1/-1}
}
@media (max-width:860px){
  .ovrow-q{grid-template-columns:1fr}
  .ovtiles{grid-template-columns:1fr}
  .ovpw{flex-direction:column;width:auto}
  .ovptags{width:auto;flex-direction:row;flex-wrap:wrap;gap:12px;border-right:0;
    border-bottom:1px solid rgba(255,255,255,.08)}
  .ovpcals{grid-template-columns:1fr}
  .ovpcal+.ovpcal{border-left:0;border-top:1px solid rgba(255,255,255,.08)}
}

/* ===========================================================================
   FE — Notification panel dan pemilih brand

   Keduanya jendela melayang, dan keduanya memakai kerangka yang sama karena di
   Figma memang sama: #181B1F, garis putih 8%, bayangan 0/12/32 hitam 45%.
   Yang berbeda hanya radiusnya — panel notifikasi r10 (94:17869), dropdown
   brand r12 (94:17839).
   =========================================================================== */
/* Pembungkus lonceng jadi patokan panel. Aturan lama di baris 617 memasang
   top:56px dan right:28px — koordinat topbar lama — dan aturan ini tidak
   pernah mencabutnya, jadi panelnya menumpuk di atas header dan tepi kanannya
   meleset 283px dari loncengnya. Keempat sisinya sekarang ditulis eksplisit. */
.tbbell{position:relative;display:flex;align-items:center}
.notifpanel{position:absolute;z-index:120;width:440px;max-width:calc(100vw - 24px);
  top:calc(100% + 12px);right:0;left:auto;bottom:auto;
  max-height:min(486px,calc(100vh - 96px));display:flex;flex-direction:column;
  background:#181B1F;border:1px solid rgba(255,255,255,.08);border-radius:10px;
  box-shadow:0 12px 32px rgba(0,0,0,.45);overflow:hidden}
.nphead{display:flex;align-items:center;gap:12px;padding:12px 16px 8px;flex:none}
.nphead h3{margin:0;flex:1;font-size:16px;line-height:24px;font-weight:500;color:var(--lg-tx)}
.npall{border:0;background:none;padding:0;cursor:pointer;
  color:var(--lg-link);font-family:var(--fu);font-size:12px;font-weight:500}
.npall:hover{text-decoration:underline}
.npx{width:24px;height:24px;flex:none;border:0;background:none;padding:0;cursor:pointer;
  color:var(--lg-tx);display:flex;align-items:center;justify-content:center}
.npx svg{width:16px;height:16px;fill:currentColor;stroke:none}
.npx:hover{color:var(--lg-tx2)}
.nptabs{display:flex;gap:12px;padding:8px 16px 12px;flex:none}
.nptab{height:34px;padding:8px 16px;border:0;border-radius:8px;background:transparent;
  cursor:pointer;color:var(--lg-tx2);font-family:var(--fu);font-size:12px;font-weight:500;
  line-height:18px;box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}
.nptab:hover{color:var(--lg-tx);background:var(--lg-glass16)}
.nptab.on{background:var(--lg-blue);color:#fff;box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.nptab.on::before{display:none}
.nptabn{font-variant-numeric:tabular-nums;opacity:.85}
.npnudge{display:flex;align-items:center;gap:12px;margin:0 16px 12px;padding:12px 14px;
  border-radius:10px;background:var(--lg-glass04);border:1px solid var(--lg-glass07);
  font-size:12.5px;line-height:17px;color:var(--lg-tx2);flex:none}
.npnudge .ovbtn-p{flex:none}

.npList{overflow-y:auto;min-height:0}
/* Baris 64 tinggi, padding 12/16, gap 12. Yang belum dibaca berlatar cobalt
   10% — satu-satunya hal yang membedakannya, dan itu cukup karena ia berdiri
   di atas #181B1F yang rata. */
.npItem{position:relative;display:flex;align-items:center;gap:12px;min-height:64px;
  padding:12px 16px;cursor:default}
.npItem.unread{background:rgba(20,84,219,.10)}
.npItem:hover{background:var(--lg-glass04)}
.npItem.unread:hover{background:rgba(20,84,219,.16)}
.npic{width:32px;height:32px;flex:none;border-radius:50%;background:var(--lg-glass04);
  display:flex;align-items:center;justify-content:center}
.npic svg{width:16px;height:16px;fill:#8FB3FF}
.npItem.isread .npic svg{fill:var(--lg-tx3)}
.npBody{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;cursor:pointer}
.npMsg{font-size:13.5px;line-height:19px;color:var(--lg-tx);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.npItem.isread .npMsg{color:var(--lg-tx2)}
.npTime{font-size:11.5px;line-height:16px;color:var(--lg-tx3)}
/* Muncul saat hover, dan hanya pada baris yang sudah dibaca. Disembunyikan
   dengan visibility, bukan display, supaya lebarnya sudah dipesan — tanpa itu
   teks di sebelahnya bergeser tiap kali tetikus lewat. */
.npacts{flex:none;display:flex;gap:4px;padding:4px;border-radius:8px;
  background:#1E1F21;border:1px solid var(--lg-glass07);
  visibility:hidden;opacity:0;transition:opacity .12s}
.npItem.isread:hover .npacts,.npItem.isread:focus-within .npacts{visibility:visible;opacity:1}
.npact{width:26px;height:26px;border:0;border-radius:6px;background:transparent;cursor:pointer;
  display:flex;align-items:center;justify-content:center;color:var(--lg-tx2)}
.npact svg{width:15px;height:15px;fill:currentColor}
.npact:hover{background:var(--lg-glass06);color:var(--lg-tx)}
.npEmpty{padding:40px 16px 48px;text-align:center;font-size:13px;color:var(--lg-tx3)}

/* ---- pemilih brand ---- */
.bsw{position:fixed;z-index:130;width:253px;max-width:calc(100vw - 24px);
  padding:10px;display:flex;flex-direction:column;gap:16px;
  background:#181B1F;border:1px solid rgba(255,255,255,.08);border-radius:12px;
  box-shadow:0 12px 32px rgba(0,0,0,.45)}
.bswlist{display:flex;flex-direction:column}
.bswrow{display:flex;align-items:center;gap:10px;width:100%;min-height:48px;
  padding:8px 12px;border:0;border-radius:12px;background:transparent;cursor:pointer;
  text-align:left;color:var(--lg-tx);font-family:var(--fu);font-size:15px;line-height:23px}
.bswrow:hover{background:var(--lg-glass06)}
/* Yang sedang dipakai: latar #19264D dengan centang #1454DB. Bukan tulisan
   "(current)" — satu warna dan satu tanda mengatakannya tanpa menambah kata. */
.bswrow.on{background:#19264D}
.bswname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bswtick{width:16px;height:16px;flex:none;fill:var(--lg-blue)}
.bswload{padding:14px 12px;font-size:13px;color:var(--lg-tx3)}
.bswadd{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:40px;
  border:0;border-radius:8px;background:var(--lg-blue);color:#fff;cursor:pointer;
  font-family:var(--fu);font-size:16px;font-weight:600;line-height:24px;
  box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.bswadd:hover{background:var(--lg-blue-h)}

/* Kotak brand waktu belum ada brand sama sekali: tombol, bukan nama produk
   yang menyamar jadi nama brand. */
.brandsw-add{width:100%;justify-content:center;gap:8px;cursor:pointer;
  color:var(--lg-tx2);font-family:var(--fu);font-size:13.5px}
.brandsw-add:hover{color:var(--lg-tx)}
.brandsw-add svg{width:16px;height:16px;fill:currentColor}

/* Booked, On The Way — blok yang dibawa dari main saat merge. Ia memakai
   kerangka kartu yang sama dengan kolom antrean supaya tidak terbaca sebagai
   sisipan dari halaman lain, tapi ia satu baris penuh: ia bukan antrean yang
   menunggu keputusan, dan bukan pula yang menunggu orang. Tempat tetapnya di
   tata letak baru masih perlu diputuskan bersama desainer. */
.ovbooked{margin-bottom:16px}
.ovbnote{margin:-8px 0 0;font-size:12.5px;line-height:18px;color:var(--lg-tx3);max-width:70ch}
.ovbooked .ovitems{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:12px}

/* ===========================================================================
   FE — Idea Quality Control   (artboard 94:24445)

   Kartu 364 lebar dengan gap 16: 364*3 + 16*2 = 1124, lebar `main`. Ditulis
   sebagai pecahan grid, bukan piksel, supaya perbandingannya bertahan.
   =========================================================================== */
.iqbar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:16px;flex-wrap:wrap}
/* Search dan dropdown memakai isian gradien Glass fill — #FFFFFF 6% ke 2% —
   dengan garis rata putih 12%, perlakuan kaca yang sama dengan tombol Select
   Assignee, bukan cincin bertopeng milik tombol footer. */
.iqsearch{display:flex;align-items:center;gap:10px;flex:1 1 460px;max-width:460px;height:44px;
  padding:0 14px;border-radius:10px;border:1px solid rgba(255,255,255,.12);
  background:linear-gradient(180deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,.02) 100%)}
.iqsi{width:16px;height:16px;flex:none;fill:#6F7789}
.iqsearch input{flex:1;min-width:0;border:0;background:none;outline:0;color:var(--lg-tx);
  font-family:var(--fu);font-size:13.5px;line-height:16px}
.iqsearch input::placeholder{color:#6F7789}
.iqright{display:flex;align-items:center;gap:12px;flex:none}
/* width:auto, dan itu wajib ditulis.

   Aturan global di baris 228 berkas ini memberi SETIAP select width:100%, dan
   di dalam .iqright yang flex:none itu melingkar: lebar select dihitung dari
   wadahnya, wadahnya dari isinya. Hasilnya select 267px — hampir dua kali
   artboard — dan tombol Add Idea terdorong KELUAR wadahnya, ke 1436 pada
   halaman yang berakhir di 1424. Itu gulir horizontalnya.

   Jebakan yang sama sudah tercatat di baris 1073: ".rvsel{width:auto} beats
   the global select{width:100%}". Ini kejadian keduanya.

   Dibatasi dua arah: 140 dari artboard sebagai lantai, 200 sebagai langit-langit
   supaya satu nama panjang tidak menarik seluruh baris.

   Garis fokusnya juga ditulis di sini. Aturan global memberi border-color
   --lime, hijau dari palet lama, dan hijau itu tidak ada di design system ini
   sama sekali. */
.iqby{height:44px;width:auto;min-width:140px;max-width:200px;flex:none;
  padding:0 12px 0 14px;border-radius:10px;cursor:pointer;
  border:1px solid rgba(255,255,255,.12);color:#C8CEDA;font-family:var(--fu);font-size:13px;
  background:linear-gradient(180deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,.02) 100%)}
.iqby:focus{outline:0;border-color:var(--lg-focus)}
/* Dan wadahnya tidak boleh ikut menyusut di bawah isinya — itu yang membiarkan
   tombolnya menggantung di luar tepi. */
.iqright{flex:0 0 auto;min-width:0}
/* nowrap dan flex:none. Tanpa keduanya tombol ini boleh menyusut di dalam
   .iqright, dan "Add Idea" pecah jadi dua baris di dalam kotak setinggi 44 —
   yang terlihat seperti tombol rusak, bukan tombol sempit. Artboard memberinya
   116x44 dengan label satu baris. */
.iqadd{display:inline-flex;align-items:center;gap:8px;height:44px;padding:0 20px 0 18px;
  border:0;border-radius:8px;cursor:pointer;background:var(--lg-blue);color:#fff;
  font-family:var(--fu);font-size:13px;font-weight:500;line-height:16px;
  white-space:nowrap;flex:none;
  box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.iqadd:hover{background:var(--lg-blue-h)}
.iqpi{width:14px;height:14px;flex:none;fill:#fff}

/* stretch, bukan start: artboard menggambar tiap barisnya sama tinggi. Dengan
   start, kartu yang tautannya sedikit jadi lebih pendek dan barisan tombol
   Accept-nya naik-turun — mata mengikuti tombol, bukan kartunya. */
.iqgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;
  align-items:stretch}
/* Kartunya TANPA isian — nodenya fills:[] — jadi yang menggambarnya hanya garis
   putih 7%. Ia duduk langsung di atas latar ambient halaman. */
.iqcard{border:1px solid rgba(255,255,255,.07);border-radius:12px;padding:16px;
  display:flex;flex-direction:column;gap:16px;cursor:pointer;transition:background .12s}
.iqcard:hover{background:var(--lg-glass04)}
.iqhead{display:flex;align-items:center;gap:12px;min-width:0}
.iqhead .ovchip{margin-left:auto}
.iqbody{display:flex;flex-direction:column;gap:12px;min-width:0}
.iqtx{display:flex;flex-direction:column;gap:4px;min-width:0}
.iqttl{font-size:16px;line-height:19px;font-weight:500;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.iqdesc{font-size:12px;line-height:15px;color:var(--lg-tx3);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* Setinggi isi TERBANYAK yang mungkin, bukan setinggi isinya sendiri.

   Kartu ini punya dua blok yang panjangnya berubah-ubah: deskripsi (satu atau
   dua baris) dan daftar tautan (nol, satu, atau dua ditambah baris "+N links").
   Grid-nya sudah align-items:stretch, jadi kartu dalam SATU baris selalu sama
   tinggi — tapi barisnya sendiri naik-turun, dan halaman yang barisnya
   bergerigi itulah yang terlihat.

   Jadi keduanya memesan ruang sebanyak yang bisa dipakai:

     deskripsi   2 baris x 15               = 30
     tautan      39 + 8 + 39 + 12 + 17      = 115

   115 itu batas atas yang sebenarnya, bukan tebakan: iqCard() memotong di
   links.slice(0,2), jadi tidak ada kartu yang bisa menggambar tautan ketiga.
   Ditulis min-height dan bukan height supaya kalau suatu hari ada yang lebih
   tinggi dari dugaan ini, kartunya memanjang alih-alih memotong isinya —
   barisnya jadi tidak rata, dan itu jauh lebih baik daripada url yang terpotong
   separuh tanpa ada yang tahu.

   Harganya disebut supaya tidak jadi kejutan: kartu tanpa tautan dan tanpa
   deskripsi kini menyisakan ruang kosong sebesar dua tautan. Itu memang yang
   diminta — tinggi yang sama berapa pun jumlah tautannya. */
.iqqc .iqdesc{min-height:30px}
.iqsrc{display:flex;flex-direction:column;gap:12px;min-width:0;min-height:115px}
.iqlinks{display:flex;flex-direction:column;gap:8px;min-width:0}
/* Satu tautan per baris, 39 tinggi, padding 12, dan urlnya dipotong dengan
   ellipsis: sebuah url yang membungkus akan meninggikan kartu sampai grid
   tiga kolomnya bergerigi. */
.iqlink{display:flex;align-items:center;gap:10px;min-width:0;height:39px;padding:0 12px;
  border-radius:8px;border:1px solid rgba(255,255,255,.12);text-decoration:none;
  background:linear-gradient(180deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,.02) 100%)}
.iqlink:hover{background:linear-gradient(180deg,var(--lg-glass16) 0%,var(--lg-glass06) 100%)}
/* Hanya di dalam laci. Di halaman Idea QC kartunya duduk di atas latar halaman
   yang rata, jadi blur di sana membebani tiap tautan tanpa menghasilkan apa pun
   yang terlihat. */
.idw .iqlink{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.iqlink span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:12px;line-height:15px;color:#8FB2FF}
.iqgi{width:13px;height:13px;flex:none;fill:#8FB2FF}
.iqmore{font-size:14px;line-height:17px;font-weight:500;color:#8FB2FF}
.iqnolink{font-size:12px;line-height:15px;color:var(--lg-tx4)}
/* Tanggal dan avatar menempel ke dasar, bukan menggantung di bawah tautan.

   margin-top:auto ada di BARIS TANGGAL, bukan di pemisahnya. Ditaruh di
   pemisah, ruang kosongnya jatuh di antara tanggal dan tombol — jadi kartu
   dengan satu tautan menampilkan tanggalnya di tengah badan kartu, jauh dari
   apa pun. Ditaruh di sini, ruang kosong itu naik ke atas tanggal, dan blok
   tanggal-pemisah-tombol berdiri rapat di dasar setiap kartu. */
.iqcard .ovdates{margin-top:auto}
.iqdiv{height:1px;background:rgba(255,255,255,.07)}
.iqempty{padding:56px 16px;text-align:center;font-size:13px;color:var(--lg-tx3);
  border:1px solid rgba(255,255,255,.07);border-radius:12px}

/* Kotak Source Link di dialog Add Idea. Satu tautan per baris, jadi ia
   textarea, bukan input — dan tingginya dipatok supaya dialognya tidak tumbuh
   setiap kali orang menempelkan tautan keenam. */
.psmod .iqta{width:100%;min-height:132px;resize:vertical;padding:12px 16px;
  border:1px solid transparent;border-radius:8px;background:var(--lg-field);
  color:var(--lg-tx);font-family:var(--fu);font-size:16px;line-height:24px}
.psmod .iqta::placeholder{color:var(--lg-tx3)}
.psmod .iqta:focus{outline:0;border-color:var(--lg-focus)}

@media (max-width:860px){
  .iqbar{flex-direction:column;align-items:stretch}
  .iqsearch{max-width:none}
  .iqright{justify-content:space-between}
}

/* ===========================================================================
   FE — laci Idea QC   (artboard 94:24445, gambar 2 dan 3)

   Laci melayang di atas daftarnya, bukan halaman yang menggantikannya. Itu yang
   membuat panah naik-turun di kepalanya masuk akal: sepuluh ide ditinjau
   berturut-turut tanpa daftar di belakangnya pernah hilang.
   =========================================================================== */
.idw{position:fixed;inset:0;z-index:140;display:flex;align-items:center;justify-content:center;
  padding:24px;background:rgba(0,0,0,.55);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
/* Glass/Strong, dibaca dari panel Figma yang Joe kirim:

     isian     #000000 24%
     efek      Glass/Strong
     bayangan  0/1/0/0  #FFFFFF 9%  inset   — sorot tipis di tepi atas
               0/20/56/-28 #000000 80%      — Depth s2

   Sebelumnya ia #121418 rata, yang membuatnya kotak gelap biasa, bukan kaca.
   Bedanya bukan selera: daftar di belakangnya harus tetap terlihat samar, itu
   yang memberi tahu orang bahwa ia MELAYANG DI ATAS daftar dan belum
   meninggalkannya.

   TINGGINYA DIPATOK, bukan mengikuti isi. Sebelumnya max-height:100% dengan
   tinggi dari konten: menambahkan satu komentar membuat seluruh laci melompat
   tingginya di bawah orang yang sedang mengetik. Yang menggulir sekarang kolom
   di dalamnya, bukan lacinya. */
.idwbox{width:min(1240px,100%);height:min(900px,100%);display:flex;flex-direction:column;
  background:rgba(0,0,0,.24);
  -webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px);
  border:1px solid rgba(255,255,255,.08);border-radius:12px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),0 20px 56px -28px rgba(0,0,0,.8);
  overflow:hidden}

.idwtop{display:flex;align-items:center;gap:10px;padding:12px 16px;flex:none;
  border-bottom:1px solid var(--lg-glass07)}
.idwnav{width:28px;height:28px;flex:none;border:0;border-radius:8px;background:transparent;
  cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--lg-tx2)}
.idwnav svg{width:14px;height:14px;fill:currentColor}
.idwnav:hover:not(:disabled){background:var(--lg-glass06);color:var(--lg-tx)}
.idwnav:disabled{color:var(--lg-tx4);cursor:default}
.idwcrumb{font-size:13.5px;color:var(--lg-tx);min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.idwcrumb i{color:var(--lg-tx3);font-style:normal;margin:0 4px}
.idwmade{margin-left:auto;flex:none;font-size:12px;color:var(--lg-tx3);white-space:nowrap}
.idwshare{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 14px;flex:none;
  border:0;border-radius:8px;cursor:pointer;background:transparent;color:var(--lg-tx);
  font-family:var(--fu);font-size:12.5px;font-weight:500;
  box-shadow:inset 0 1px 0 var(--lg-glass09),var(--lg-s2)}
.idwshare:hover{background:var(--lg-glass16)}
.idwsi{width:14px;height:14px;fill:currentColor}
/* Flag Script tidak punya slot di artboard. Ditaruh di sini, bukan sebagai
   tombol keempat di sebelah Accept: menandai skrip bermasalah bukan tindakan
   sederajat dengan menerima atau menolaknya. PERLU DIPERIKSA ke desainer. */
.idwmenu,.idwx{width:32px;height:32px;flex:none;border:0;border-radius:8px;background:transparent;
  cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--lg-tx2)}
.idwmenu svg,.idwx svg{width:16px;height:16px;fill:currentColor}
.idwmenu:hover,.idwx:hover{background:var(--lg-glass06);color:var(--lg-tx)}

.idwbody{flex:1;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 440px}
/* min-height:0 di KEDUA kolom, dan itu bukan kehati-hatian berlebihan.

   Item grid berbawaan min-height:auto, yang membulat ke min-content — artinya
   kolom menolak menyusut di bawah isinya. Kolom kiri lolos tanpa sadar karena
   overflow-y:auto sudah membatalkan perilaku itu; kolom kanan tidak punya
   overflow sendiri, jadi ia tumbuh sampai setinggi seluruh percakapan.

   Terukur sebelum diperbaiki: 21 pesan membuat kolom kanan setinggi 3118 di
   dalam laci setinggi 852, kotak percakapannya tidak pernah menggulir sama
   sekali, dan komposernya terdorong keluar layar — orang tidak bisa membalas
   justru di percakapan yang paling panjang. */
/* contain:paint dan overscroll-behavior, keduanya untuk gulir.

   Kolom ini menggulir DI DALAM panel yang membawa backdrop-filter 24px. Tiap
   frame gulir mengubah isi panel itu, dan peramban ikut menghitung ulang
   kaburnya — itu yang membuat gulir terasa patah-patah, dan baru terasa sejak
   form sunting membuat kolomnya cukup panjang untuk digulir (1104 di dalam
   613). contain:paint mengatakan tidak ada yang digambar keluar dari kotak ini,
   jadi isinya tidak lagi membatalkan daerah kabur di luarnya.

   overscroll-behavior:contain menghentikan gulir menjalar ke halaman di
   belakang laci waktu sampai ujung — hal yang berbeda, tapi ikut terasa
   sebagai gulir yang tidak enak. */
.idwleft{min-width:0;min-height:0;overflow-y:auto;padding:20px 24px 28px;display:flex;flex-direction:column;gap:20px;
  contain:paint;overscroll-behavior:contain}
/* Kolom kanan sedikit lebih gelap supaya percakapan terbaca sebagai wilayahnya
   sendiri — tapi tetap tembus, karena keduanya satu lembar kaca.

   Blurnya sendiri, bukan hanya mewarisi blur laci: ia bidang terpisah di atas
   bidang yang sudah buram, dan itu yang memberi tepi kirinya kedalaman alih-alih
   sekadar garis. Radiusnya tidak bisa dibaca dari API — efek GLASS dijawab
   {"type":"GLASS"} tanpa satu pun parameter — jadi angkanya dipilih menurun
   dari laci: 24 untuk lacinya, 12 di sini, 8 untuk kartu di dalamnya. */
/* Tanpa backdrop-filter sendiri, sejak 18 September.

   Kolom ini duduk DI DALAM .idwbox, yang sudah mengaburkan latarnya 24px. Blur
   12px di sini mengaburkan permukaan yang sudah kabur — hampir tidak ada
   bedanya di layar, dan satu lintasan blur penuh lagi di setiap frame. Yang
   benar-benar membuat kolom ini lebih gelap adalah tintnya, dan tint itu
   tinggal. */
.idwright{min-width:0;min-height:0;border-left:1px solid var(--lg-glass07);display:flex;flex-direction:column;
  background:rgba(0,0,0,.22)}

.idwhead{display:flex;align-items:flex-start;gap:16px}
.idwhead h2{flex:1;min-width:0;margin:0;font-size:22px;line-height:28px;font-weight:600;color:var(--lg-tx)}
.idwact{display:flex;align-items:center;gap:10px;flex:none}

/* DUA kolom, dipatok. Node metanya di Figma bertata letak GRID 940x92 dengan
   lima baris setinggi 24 — 92 itu tiga baris ditambah dua gap, jadi tiga baris
   dua kolom.

   Sebelumnya auto-fit minmax(220px): di kolom kiri yang lebar ia memuat tiga
   sel sebaris, sehingga Stage, Status dan Assignee berjejer lalu Stalled
   sendirian di bawah — susunan yang tidak pernah digambar, dan yang berubah
   sendiri setiap kali lebar lacinya berubah. */
.idwmeta{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}
.idwcell{display:flex;align-items:center;gap:12px;min-width:0}
.idwk{width:88px;flex:none;font-size:12.5px;color:var(--lg-tx3)}
.idwv{min-width:0;font-size:13px;color:var(--lg-tx)}
.idwstat{padding:4px 8px;border-radius:6px;background:var(--lg-field);color:var(--lg-tx2);
  font-family:var(--fu);font-size:11px;font-weight:500}
.idwwho{display:inline-flex;align-items:center;gap:8px}
.idwwho i{color:var(--lg-tx3);font-style:normal}
/* Warna teks biasa. Kuning itu karangan saya — node aslinya memakai warna teks
   yang sama dengan nilai lain di grid ini, dan "sudah dua hari" bukan
   peringatan, hanya fakta. Yang memutuskan itu genting adalah orangnya. */
.idwstall{color:var(--lg-tx)}

.idwsec{display:flex;flex-direction:column;gap:10px;min-width:0}
.idwsh{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--lg-tx2)}
.idwshi{width:15px;height:15px;flex:none;fill:var(--lg-tx3)}
.idwtools{margin-left:auto;display:flex;align-items:center;gap:12px}
.idwlen{font-size:11.5px;color:var(--lg-tx3);white-space:nowrap}
/* Baris Edit: miliknya sendiri, di atas field pertama, rata kanan.

   Tidak menempel pada bagian mana pun — laci ini melayani empat tahap dan
   bagian pertamanya berbeda-beda, jadi tombol yang menempel pada field akan
   berpindah tempat setiap kali isinya berubah. Di sini letaknya satu titik yang
   sama di setiap laci. */
/* margin-bottom -20, yang persis membatalkan gap .idwleft di atasnya.

   Kolom kiri adalah flex column dengan gap 20, jadi baris ini akan berdiri 20px
   di atas field pertama tanpa ada yang memintanya — dan Joe memintanya nol.
   Ditulis sebagai kebalikan gapnya, bukan sebagai angka yang kebetulan cocok:
   kalau gap .idwleft berubah, yang ini harus ikut, dan sekarang alasannya
   tertulis di sebelahnya. */
.idwedbar{display:flex;justify-content:flex-end;margin-bottom:-20px}
/* Tab Activity / Script di kepala kolom kanan laci audio. Hanya digambar di
   tahap audio, karena hanya di situ ada dua hal untuk ditabkan. */
.idwtabs{display:flex;gap:8px;padding:16px 20px 0;flex:none}
/* Tab yang tidak aktif memakai kaca yang sama dengan Select Assignee —
   .ovbtn-g, cincin PENUH Glass 14 keliling, bukan sorotan atas saja. Sebelum
   ini ia transparan tanpa plat sama sekali, jadi satu-satunya tab yang
   terlihat sebagai tombol adalah yang sedang menyala, dan yang satunya
   terbaca seperti label yang kebetulan bisa diklik.

   Pasangannya sudah benar sejak awal dan tidak berubah: .on memakai fill biru
   dengan cincin Glass 14 yang sama, persis seperti .ovbtn-p di sebelah
   .ovbtn-g. Jadi dua tab ini sekarang bicara dengan kosakata yang sama dengan
   sepasang tombol di laci, bukan kosakata ketiga yang hanya ada di sini. */
/* .vchip ikut resep ini sejak 19 September, bukan menyalinnya: deretan V1..V4 di
   baris Version adalah deretan tab, dan artboard menggambarnya dengan plat kaca
   dan pasangan biru yang sama dengan tab di rail. Satu perubahan pada kaca ini
   mengubah keduanya, yang memang yang diinginkan. */
.idwtb,.vchip{height:32px;padding:0 14px;border:0;border-radius:8px;cursor:pointer;
  background:var(--lg-glass04);color:var(--lg-tx2);font-family:var(--fu);
  font-size:13px;font-weight:500;line-height:32px;
  box-shadow:inset 0 0 0 1px var(--lg-glass14),inset 0 1px 0 var(--lg-glass09),var(--lg-s2);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.idwtb:hover,.vchip:hover{background:var(--lg-glass16);color:var(--lg-tx)}
/* Blur dimatikan waktu menyala, bukan kelalaian: fill birunya legap, jadi
   tidak ada apa pun di belakangnya yang bisa terlihat, dan backdrop-filter
   yang tidak menggambar apa-apa tetap membuat lapisan komposit sendiri. */
.idwtb.on,.vchip.on{background:var(--lg-blue);color:#fff;
  box-shadow:inset 0 0 0 1px var(--lg-glass14);
  -webkit-backdrop-filter:none;backdrop-filter:none}
/* Take memilih di antara ekspor versi YANG SAMA, tepat di bawah baris yang
   memilih antar versi. Bentuknya sengaja sama — memang kontrol sejenis — tapi
   isiannya tembus pandang, bukan biru legap, supaya yang menyala di baris Take
   tidak terbaca sebagai pergantian versi sekilas pandang.

   HARUS di bawah .vchip.on: kekhususan keduanya sama (0,2,0), jadi hanya urutan
   yang memutuskan. Dipindahkan ke atasnya, isian birunya menang dan pembeda di
   atas hilang tanpa ada yang error. */
.ochip.on{background:var(--blBg);color:var(--bl);
  box-shadow:inset 0 0 0 1px rgba(125,184,255,.4)}
/* Isi tab Script memakai bagian yang sama dengan kolom kiri tahap lain, jadi
   ia butuh jarak dan gulirnya sendiri di dalam kolom kanan. */
.idwtabp{flex:1;min-height:0;overflow-y:auto;padding:20px;
  display:flex;flex-direction:column;gap:20px;
  contain:paint;overscroll-behavior:contain}
.idwtool{border:0;background:transparent;padding:0;cursor:pointer;color:var(--lg-link);
  font-family:var(--fu);font-size:12px;font-weight:500}
.idwtool:hover{text-decoration:underline}
.idwsb{font-size:14px;line-height:21px;color:var(--lg-tx)}
/* Kotak ketik sunting ide, DI DALAM laci.

   Bukan .iqta: aturan itu ditulis `.psmod .iqta` dan hanya berlaku di dalam
   dialog, jadi textarea yang sama di laci akan tergambar tanpa gaya sama
   sekali. Nilainya mengikuti badan bagiannya sendiri — 14/21, bukan 16/24
   milik dialog — supaya teks yang sedang disunting berukuran sama dengan teks
   yang barusan dibacanya di tempat itu juga. */
.idwta{width:100%;min-height:96px;resize:vertical;display:block;padding:10px 12px;
  border:1px solid var(--lg-glass07);border-radius:8px;background:var(--lg-field);
  color:var(--lg-tx);font-family:var(--fu);font-size:14px;line-height:21px}
.idwta::placeholder{color:var(--lg-tx3)}
.idwta:focus{outline:0;border-color:var(--lg-focus)}
/* Kotak skrip lebih tinggi: ia satu-satunya bagian yang isinya paragraf, dan
   96 memaksa penulis menggulir di dalam kotak sejak kalimat keempat. */
.idwtab{min-height:220px}
/* Tagar dan tag: input satu baris, bukan textarea yang dipendekkan. Nilainya
   sama dengan .idwta selain tingginya, jadi keduanya tergambar sebagai satu
   keluarga. */
.idwtx{width:100%;display:block;height:44px;padding:0 12px;
  border:1px solid var(--lg-glass07);border-radius:8px;background:var(--lg-field);
  color:var(--lg-tx);font-family:var(--fu);font-size:14px;line-height:21px}
.idwtx::placeholder{color:var(--lg-tx3)}
.idwtx:focus{outline:0;border-color:var(--lg-focus)}
/* Kaki Cancel/Save-nya rata kanan, di bawah kotak tautan. */
.idwedf{display:flex;justify-content:flex-end;gap:10px;margin-top:12px}
/* "No description yet." — bagiannya tetap digambar supaya tombol Edit-nya ada.
   Lihat catatan di iqDrawerHtml(). */
.idwnone{color:var(--lg-tx4)}
/* Judul yang sudah dikunci: pernyataan, bukan pilihan. Di ready_to_post
   keputusannya sudah ditulis ke server dan radio yang masih bisa diklik di
   sana mengundang seseorang mengubah judul video yang sedang diunggah. */
.idwtlock{display:flex;align-items:center;gap:10px;font-size:14px;line-height:21px;color:var(--lg-tx)}
.idwtlock svg{width:16px;height:16px;flex:none;fill:#4ADE80}
/* Status "Accepted" — hijau, satu-satunya keadaan di grid ini yang mengabarkan
   sesuatu yang sudah selesai dan bukan sesuatu yang sedang ditunggu. */
.idwstat.idwok{background:rgba(34,197,94,.16);color:#4ADE80}
/* Penanda "edited" di ujung badan pesan. Kecil dan redup: ia fakta tentang
   kalimatnya, bukan bagian dari kalimatnya. */
.idwedit{margin-left:6px;font-size:11px;line-height:16px;color:var(--lg-tx4);font-style:italic}
/* Kotak sunting pesan memakai .idwta yang sama dengan sunting ide, hanya lebih
   rendah — sebuah catatan bukan deskripsi ide. */
.idwmb .idwta{min-height:64px;margin-bottom:0}
.idwmb .idwedf{margin-top:8px}
.idwopts{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:6px}
/* ---------- Title Options di Ready to Post ----------

   Daftar yang sama, tapi di tahap ini memilih salah satunya ADALAH
   pekerjaannya — di tahap lain .idwopts cuma bacaan bernomor. Jadi bentuknya
   radio, dan titiknya digambar sendiri dengan div: sebuah <input type=radio>
   asli membawa gaya bawaan tiap platform yang tidak bisa disamakan, dan pola
   ini sudah dipakai di tempat lain di berkas ini (kotak centang 18x18 r4 yang
   terisi #1454DB).

   Barisnya <button>, bukan <label>: tidak ada input di baliknya untuk ditunjuk
   — yang tersimpan ditulis pickTitle() ke server. Kecuali yang terakhir, yang
   memang punya kotak ketik dan karena itu memang <label>. */
.idwrads{display:flex;flex-direction:column;gap:2px}
.idwrad{display:flex;align-items:center;gap:12px;width:100%;min-height:44px;padding:8px 10px;
  border:0;border-radius:8px;background:transparent;cursor:pointer;text-align:left;
  color:var(--lg-tx);font-family:var(--fu);font-size:14px;line-height:21px}
.idwrad:hover{background:var(--lg-glass04)}
.idwradi{width:16px;height:16px;flex:none;border-radius:50%;
  box-shadow:inset 0 0 0 1.5px var(--lg-tx4)}
.idwrad.on .idwradi{box-shadow:inset 0 0 0 1.5px var(--lg-blue);
  background:radial-gradient(circle,var(--lg-blue) 0 4.5px,transparent 4.5px)}
/* Baris kustom: kotaknya yang jadi jawabannya, jadi ia mengisi sisa barisnya.
   Tingginya diturunkan dari 44 .idwtx supaya barisnya tidak lebih tinggi
   daripada tiga di atasnya. */
.idwradc{cursor:text}
.idwradc .idwtx{height:36px;flex:1;min-width:0}
.idwscript{margin:0;white-space:pre-wrap;font-family:var(--fu);font-size:14px;line-height:23px;
  color:var(--lg-tx)}
/* Dua kolom di laci, satu kolom di kartu.

   auto-fit dengan minmax, bukan `1fr 1fr` tetap: laci ini menyempit di layar
   kecil, dan dua kolom 240 yang dipaksa bertahan akan memotong url-nya di
   tengah. Dengan auto-fit kolom keduanya turun sendiri begitu ruangnya habis,
   dan di layar lebar ia tetap dua seperti yang digambar. */
.idwlinks{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:8px}
/* Tautan yang tidak bisa dibuka — lihat iqLinkHtml(). Digambar sebagai teks
   redup tanpa warna tautan dan tanpa penunjuk tangan, supaya tidak ada yang
   menekannya dan menunggu sesuatu terjadi. */
.iqlinkoff{cursor:default}
.iqlinkoff span{color:var(--lg-tx3)}
.iqlinkoff .iqgi{fill:var(--lg-tx4)}
.iqlinkoff:hover{background:linear-gradient(180deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,.02) 100%)}
.idwpaste{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;height:48px;
  margin-top:8px;border:1px dashed rgba(255,255,255,.16);border-radius:8px;background:transparent;
  cursor:pointer;color:var(--lg-tx2);font-family:var(--fu);font-size:13px}
.idwpaste:hover{border-color:var(--lg-glass26,rgba(255,255,255,.26));color:var(--lg-tx)}
.idwpi{width:14px;height:14px;fill:currentColor}

/* ---- kartu berkas aset, di atas pemutar suara ----

   Kartu kaca dengan rusuk, bukan kotak polos: ia berdiri di kolom yang sama
   dengan bagian teks lain di laci, dan tanpa plat ia terbaca sebagai bagian
   keempat alih-alih sebagai sebuah BERKAS. Bingkai videonya memakai .aqcfr
   yang sudah ada di kartu Assets QC — rasio dan object-fit-nya sudah benar di
   sana dan menulisnya lagi berarti dua angka yang akan berbeda nanti. */
/* ---- Final QC: halaman tinjau ----  artboard 19 September

   Satu kartu gelap yang mengisi badan halaman, dengan kepala sendiri di
   atasnya dan dua kolom di bawahnya. Kolomnya grid dan bukan flex: lebar rail
   dipatok 420 seperti artboard, dan sisanya milik pemutarnya — dengan flex,
   video 9:16 akan menarik kolom kiri selebar apa pun yang ia mau. */
/* TINGGI PENUH SAMPAI BAWAH, dan TANPA angka ajaib.

   Joe: "buat high nya sampai ke paling bawah penuh." flex:1 saja tidak cukup —
   #pg adalah display:block, jadi tidak ada induk yang memberi tinggi untuk
   dibagi. Jalan yang sudah dipakai halaman review lama adalah
   calc(100vh - 112px): angka yang diukur sekali lalu salah setiap kali kepala
   halamannya berubah. Halaman ini punya kepala DAN subjudul di atasnya, jadi
   angkanya akan berbeda lagi.

   :has() memberi induknya tinggi hanya waktu ia memang memuat kartu ini, jadi
   tidak ada halaman lain yang ikut berubah bentuk, dan tidak ada angka yang
   perlu dijaga. Didukung Chrome 105+ dan Safari 15.4+, yang keduanya lebih tua
   dari mesin mana pun di tim ini.

   min-height tetap ada sebagai dasar: di mesin tanpa :has() kartunya tetap
   setinggi 520 alih-alih runtuh ke tinggi isinya. */
/* Sampai ke dasar layar, bukan sampai ke dasar padding halaman. .pg membawa
   padding bawah 48px untuk halaman yang MENGGULIR — ruang supaya baris terakhir
   sebuah daftar tidak menempel di tepi. Halaman ini tidak menggulir: ia satu
   kartu setinggi jendela, dan 48px di bawahnya adalah pita gelap tanpa isi.

   Dilepas hanya untuk halaman yang memuat kartu ini. Bingkai 16px milik #app
   sengaja TIDAK disentuh — itu inset jendela yang dipakai sidebar juga, dan
   mencabutnya di sini membuat kartunya turun lebih rendah daripada panel di
   sebelahnya. */
.pg:has(> .fqrv){display:flex;flex-direction:column;min-height:0;padding-bottom:0}
/* Bahan yang sama dengan laci — Glass/Strong dari panel Figma, resep .idwbox:
   isian hitam 24%, blur 24, garis putih 8%, sorot tipis di tepi atas, Depth s2.
   Joe, 19 September: "ikuti style laci, menggunakan glass dan border khusus
   untuk efek glass."

   Supaya kacanya terlihat, isian legap di dalamnya harus pergi: .fqrvl dan
   .fqrvr sekarang tembus pandang, dan begitu juga chrome pemutarnya. Yang
   tersisa legap cuma panggung videonya, yang memang harus hitam. */
.fqrv{display:flex;flex-direction:column;flex:1;min-height:520px;overflow:hidden;
  background:rgba(0,0,0,.24);
  -webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px);
  border:1px solid rgba(255,255,255,.08);border-radius:12px;
  box-shadow:inset 0 1px 0 var(--lg-glass09),0 20px 56px -28px rgba(0,0,0,.8)}
.fqrv .rvtop,.fqrv .rvscrub,.fqrv .rvctrl,.fqrv .rvtl{background:transparent}
.fqrv .rvtop{border-bottom-color:var(--lg-glass07)}
.fqrv .rvscrub{border-top-color:var(--lg-glass07)}
/* Fullscreen DI ujung kanan baris Version, di tempat pil "In Review" tadinya
   berdiri. Joe, 19 September: "hilangkan tags in review kuning, tombol full
   screen diletakan di tempat tags tersebut."

   Platnya #212429 — isian field dari design system, yang sama dipakai tombol
   ikon 32x32 di header. Sebagai .pbtn ia tadinya transparan sampai disentuh,
   jadi satu-satunya tombol di baris ini tidak terlihat sebagai tombol.

   Ia tetap di margin-left:auto: tidak ada lagi yang mengunci kanan, jadi ini
   yang membuatnya tetap di sana kalau suatu hari ada kontrol lain di baris ini. */
.rvtop .rvfull{margin-left:auto;background:#212429;border-radius:7px;color:var(--lg-tx2)}
.rvtop .rvfull:hover{background:#2A2E35;color:var(--lg-tx)}
.fqrvh{display:flex;align-items:center;gap:11px;flex-wrap:wrap;padding:14px 16px;
  border-bottom:1px solid var(--lg-glass07);flex:none}
/* Share, Send Back dan Approve memakai kosakata baru, DI SINI SAJA.

   Kelasnya tidak diganti di dalam gateBtns(): fungsi itu juga menggambar baris
   keputusan di halaman review lama dan di gerbang skrip, yang masih memakai
   kosakata lama dan belum digambar ulang. Menyetelnya lewat wadahnya berarti
   aturan tombolnya tetap satu tempat — tidak ada cabang "kalau di Final QC" di
   dalam JS, dan halaman lama tidak ikut berubah diam-diam.

   Send Back kehilangan warna bahayanya di sini, dan itu artboard yang memilih:
   mengembalikan satu cut untuk diperbaiki adalah yang dikerjakan pemegang
   gerbang sepanjang hari, bukan tindakan merusak. Yang ditandai justru Approve,
   yang mendorongnya maju. */
.fqrvh .btn{height:32px;padding:0 14px;border:0;border-radius:8px;
  background:var(--lg-glass04);color:var(--lg-tx2);font-family:var(--fu);
  font-size:13px;font-weight:500;
  box-shadow:inset 0 0 0 1px var(--lg-glass14),inset 0 1px 0 var(--lg-glass09),var(--lg-s2);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.fqrvh .btn:hover:not(:disabled){background:var(--lg-glass16);color:var(--lg-tx);border:0}
.fqrvh .btn svg{width:14px;height:14px}
.fqrvh .btn-s{background:var(--lg-blue);color:#fff;
  box-shadow:inset 0 0 0 1px var(--lg-glass14);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.fqrvh .btn-s:hover:not(:disabled){background:var(--lg-blue-h);color:#fff}
/* DUA tombol keputusan tanpa ikon, teksnya 16 — Joe, 19 September. Share tetap
   membawa ikon kirimnya, karena artboard menggambarnya begitu dan karena ia
   bukan keputusan: ia menyalin tautan.

   Ikonnya disembunyikan lewat CSS, bukan dicabut dari gateBtns(). Fungsi itu
   menggambar baris yang sama di halaman review lama dan di gerbang skrip, yang
   masih memakai kosakata lama — dan `svg` yang tidak digambar tidak menyisakan
   apa pun, termasuk gap flexnya, karena display:none melepas item flexnya.

   TINGGI KETIGANYA DISAMAKAN di 36. Teks 16 tidak muat di plat 32, dan Share
   yang tetap 13 akan berdiri lebih pendek daripada tetangganya kalau hanya
   yang dua ini yang tumbuh. Yang berbeda ukurannya hanya hurufnya. */
/* KETIGANYA 16px sekarang, jadi ini satu aturan dan bukan dua. Share menyusul
   pada 19 September; sebelumnya ia tertinggal di 13 dan berdiri lebih ringan
   daripada dua tetangganya. */
.fqrvh .btn{height:36px;font-size:16px;padding:0 18px}
.fqrvh .btn-s svg,.fqrvh .btn-d svg{display:none}
/* Ikon Share MENGIKUTI warna teksnya. Ia Phosphor — path yang diisi, bukan
   digaris — dan .btn svg di stylesheet dasar memaksakan fill:none dengan
   stroke:currentColor, yang mengubahnya jadi garis luar berwarna lain.
   Kekhususannya (0,1,1) mengalahkan .idwsi{fill:currentColor} (0,1,0), jadi
   atribut fill di <svg> tidak pernah menang. Dikembalikan di sini. */
.fqrvh .btn svg{width:16px;height:16px;fill:currentColor;stroke:none}
/* Panah atas/bawah di kiri remah: plat #212429 yang sama dengan fullscreen dan
   pemilih grid. Sebagai .idwnav ia transparan sampai disentuh — benar di dalam
   laci yang penuh tombol, tapi di baris ini ia satu-satunya kontrol di kiri dan
   tidak terbaca sebagai tombol sama sekali. Disetel lewat wadahnya, jadi .idwnav
   di laci dan di halaman review lama tidak ikut berubah. */
.fqrvh .idwnav{background:#212429;border-radius:7px;width:32px;height:32px}
.fqrvh .idwnav:hover:not(:disabled){background:#2A2E35;color:var(--lg-tx)}
.fqrvh .idwnav:disabled{background:var(--lg-glass04);color:var(--lg-tx4)}
.fqrvb{display:grid;grid-template-columns:1fr 420px;min-height:0;flex:1}
.fqrvl{min-width:0;display:flex;flex-direction:column;overflow:hidden}
/* Panggungnya yang mengambil sisa tinggi, jadi videonya tumbuh sampai ke
   bawah alih-alih berhenti di tinggi isinya dan menyisakan lubang hitam. */
.fqrvl .rvstage{flex:1;min-height:0}
.fqrvl .rvw{border:0;border-radius:0}
.fqrvr{min-width:0;display:flex;flex-direction:column;min-height:0;
  border-left:1px solid var(--lg-glass07)}
/* TIGA pil, bukan tujuh tab huruf besar yang membungkus dua baris — itu
   keluhan yang membuat halaman ini ditulis. Bentuknya sama dengan tab laci
   audio, jadi kosakatanya satu. */
.fqtabs{padding:14px 16px 0}
@media (max-width:1100px){
  /* Di layar sempit railnya turun ke bawah pemutarnya alih-alih meremas
     keduanya. Video 9:16 di kolom 300px tidak bisa dinilai siapa pun. */
  .fqrvb{grid-template-columns:1fr;grid-template-rows:auto auto}
  .fqrvr{border-left:0;border-top:1px solid var(--lg-glass07)}
}
/* Kartu aset DI DALAM rail bertumpuk: thumbnailnya di bawah fieldnya, bukan di
   sampingnya. Itu yang digambar artboard di tab Assets, dan itu juga satu-
   satunya bentuk yang muat — rail 420 dikurangi padding, dikurangi thumbnail
   196, menyisakan 139px untuk kolom field, dan "00:00:00 – 00:01:00" pecah
   jadi tiga baris di lebar itu. Terukur.

   Media query di atas tidak bisa menangkap ini: ia membaca lebar JENDELA, dan
   di jendela 1440 rail ini tetap 420. Yang membedakan bukan layarnya melainkan
   wadahnya, jadi yang dipakai adalah induknya. */
/* .idwtabp ikut sejak 19 September: tab Assets di laci Ready to Post berdiri
   di kolom kanan laci, dan kolom itu 440 dikurangi padding 20+20 — 400, lebih
   sempit lagi daripada rail Final QC. Tanpa baris ini kartunya tergambar
   menyamping di situ dan "Time Range" pecah jadi tiga baris di sebelah
   thumbnail 196px. Joe: "styling card ini seharusnya bukan yang ini, tetapi
   yang gambarnya full, seperti card yang ada di laci final qc."

   .idwleft SENGAJA tidak ikut. Di laci produksi dan laci aset kartu yang sama
   berdiri di kolom kiri yang lebar, dan di sana bentuk menyamping memang yang
   benar — thumbnail 196 di samping fieldnya, bukan gambar selebar kolom. */
.fqrvr .idwasb,.drpane .idwasb,.idwtabp .idwasb{flex-direction:column}
.fqrvr .idwasth,.drpane .idwasth,.idwtabp .idwasth{width:100%}

/* Isi tab Script dan Assets di rail pemutar. Railnya yang menggulir, bukan
   halamannya: pemutar di sebelahnya harus tetap terlihat selama seseorang
   membaca skripnya — itu seluruh gunanya tab ini ada di sini dan bukan di
   layar lain. Jarak dan gulirnya mengikuti .idwtabp di laci, karena isinya
   memang komponen yang sama. */
.drpane{flex:1;min-height:0;overflow-y:auto;padding:18px;
  display:flex;flex-direction:column;gap:20px;
  contain:paint;overscroll-behavior:contain}

/* ---- kartu aset, komponen 19 September ----

   Ditulis ulang dari nol mengikuti artboard: BARIS DATA, bukan pemutar. Yang
   sebelumnya di sini menggambar bingkai video selebar kolom dengan transport
   di bawahnya, dan itu bentuk yang salah dua kali sebelum Joe menyebutnya.

   Bagian aset adalah daftar, jadi kartunya perlu jarak antar-kartu. Satu kartu
   hari ini — lihat iqAssetList() — dan kolomnya sudah benar untuk yang kedua
   tanpa satu baris pun berubah di sini. */
.idwascol{display:flex;flex-direction:column;gap:10px}
.idwas{border:1px solid var(--lg-glass09);border-radius:12px;background:var(--lg-glass04);
  overflow:hidden}
.idwash{display:flex;align-items:center;gap:6px;padding:14px 14px 10px}
.idwasn{flex:1;min-width:0;font-family:var(--fu);font-size:15px;font-weight:600;color:var(--lg-tx)}
.idwasi{flex:none;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;
  border:0;border-radius:7px;background:transparent;cursor:pointer;color:var(--lg-tx3)}
.idwasi:hover{background:var(--lg-glass09);color:var(--lg-tx)}
.idwasi svg{width:15px;height:15px;fill:currentColor}
/* Field di kiri, thumbnail di kanan. Thumbnailnya lebar tetap supaya kolom
   fieldnya yang menyerah lebih dulu waktu lacinya sempit — teks yang memendek
   masih terbaca, gambar yang memendek tidak. */
.idwasb{display:flex;align-items:flex-start;gap:14px;padding:0 14px 14px}
.idwasfs{flex:1;min-width:0;display:flex;flex-direction:column;gap:12px}
/* Label dan nilainya berdampingan seperti di artboard, dan labelnya selebar
   tetap supaya kedua nilainya berbaris rata. */
.idwasr{display:flex;align-items:flex-start;gap:14px;min-width:0}
.idwask{flex:none;width:86px;font-family:var(--fu);font-size:13px;font-weight:500;color:var(--lg-tx2)}
.idwasv{flex:1;min-width:0;font-family:var(--fu);font-size:13px;line-height:20px;color:var(--lg-tx)}
.idwastc{font-family:var(--fm);font-size:12.5px;color:var(--lg-tx2)}
/* Skrip dipotong di tiga baris. Kartu ini menjawab "aset mana ini", bukan
   "apa isi skripnya" — tab Script di kolom kanan yang itu, utuh. */
.idwastx{display:-webkit-box;-webkit-line-clamp:3;line-clamp:3;-webkit-box-orient:vertical;
  overflow:hidden;color:var(--lg-tx2)}
.idwastx i{color:var(--lg-tx4)}
.idwasth{position:relative;flex:none;width:196px;aspect-ratio:16/9;border-radius:8px;overflow:hidden;
  background:#000;cursor:pointer}
.idwasth video{width:100%;height:100%;object-fit:cover;display:block}
/* Durasinya di pojok gambar, seperti di artboard. Kosong sampai metadata tiba,
   dan sebuah pil kosong tidak boleh terlihat — :empty yang mengurusnya. */
.idwasd{position:absolute;top:8px;right:8px;padding:3px 7px;border-radius:5px;
  background:rgba(0,0,0,.66);font-family:var(--fm);font-size:10.5px;color:#fff}
.idwasd:empty{display:none}
@media (max-width:900px){
  /* Di laci sempit thumbnailnya turun ke bawah fieldnya alih-alih meremas
     keduanya jadi dua kolom yang sama-sama tidak terbaca. */
  .idwasb{flex-direction:column}
  .idwasth{width:100%}
}

/* ---- baris versi cut, di bagian Final Attachment ----

   Satu baris per versi, bukan kartu: artboard menggambarnya sebagai baris
   ("Version 1: video.mp4") dan sebuah cut memang punya banyak versi di repo
   ini, jadi bentuk baris yang bisa berulang adalah yang jujur. */
/* Baris Assignee membawa sampai empat orang sekarang, jadi ia membungkus
   alih-alih memaksa selnya melebar. gap kolom lebih besar dari gap baris: dua
   nama di baris yang sama harus terbaca terpisah, dua baris tidak. */
.idwhands{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;min-width:0}
.idwvers{display:flex;flex-direction:column;gap:8px}
.idwver{display:flex;align-items:center;gap:10px;padding:10px 12px;
  border:1px solid var(--lg-glass09);border-radius:10px;background:var(--lg-glass04)}
.idwvi{width:16px;height:16px;fill:var(--lg-tx3);flex-shrink:0}
.idwvern{font-family:var(--fu);font-size:13px;font-weight:600;color:var(--lg-tx);flex:none}
/* Nama berkas boleh panjang dan tidak boleh mendorong tombol unduhnya keluar. */
.idwvert{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--fm);font-size:12px;color:var(--lg-tx3)}
/* Versi yang barisnya ada tapi berkasnya tidak — payload lama, atau versi yang
   dicatat sebelum unggahannya selesai. Dikatakan, bukan digambar seperti yang
   lain lalu mengecewakan waktu ditekan. */
.idwverno .idwvert{color:var(--lg-tx4);font-style:italic}

/* ---- memilih aset dari rak ----

   Dialognya berisi daftar yang bisa panjang — rak SFX punya tiga ratus lebih —
   jadi yang menggulir adalah DAFTARNYA, bukan dialognya. Kepala, bar cari dan
   kaki tetap di tempatnya; tanpa itu tombol Confirm tersapu ke bawah layar dan
   kotak carinya ikut hilang begitu orang menggulir. */
.aslib{display:flex;flex-direction:column;max-height:min(78vh,640px)}
.aslibbar{display:flex;gap:10px;margin-bottom:14px;flex:none}
.aslibbar .iqsearch{flex:1;min-width:0}
.asliblist{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;gap:8px}
.aslibr{display:flex;align-items:center;gap:12px;width:100%;padding:10px 12px;
  border:1px solid transparent;border-radius:10px;background:var(--lg-glass04);
  cursor:pointer;text-align:left;color:var(--lg-tx)}
.aslibr:hover{background:var(--lg-glass09)}
/* Terpilih memakai biru yang sama dengan tombol utama, dengan cincin Glass 14
   — kosakata yang sama dengan .ovbtn-p di laci sebelahnya. */
.aslibr.on{background:var(--lg-blue-s,rgba(20,84,219,.22));border-color:var(--lg-glass14)}
.aslibth{flex:none;width:56px;height:40px;border-radius:7px;overflow:hidden;background:var(--lg-field);
  display:flex;align-items:center;justify-content:center;color:var(--lg-tx4)}
.aslibth img,.aslibth video{width:100%;height:100%;object-fit:cover;display:block}
.aslibth svg{width:18px;height:18px;fill:currentColor}
.aslibtx{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
/* Nama berkas boleh sepanjang apa pun dan tidak boleh mendorong centangnya
   keluar baris. */
.aslibn{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--fu);font-size:14px;font-weight:500;color:var(--lg-tx)}
.aslibby{font-family:var(--fu);font-size:12px;color:var(--lg-tx3)}
.aslibck{flex:none;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;border:1px solid var(--lg-glass14);color:var(--lg-tx3)}
.aslibr.on .aslibck{background:var(--lg-blue);border-color:transparent;color:#fff}
.aslibck svg{width:13px;height:13px;fill:currentColor}
.aslibfoot{flex:none;padding-top:14px}
/* Kalimat yang mengatakan bahwa Confirm belum menyimpan apa pun. Di kaki
   dialognya dan bukan hanya di toast: orang yang sudah mencari di antara tiga
   ratus berkas dan mencentang empat sudah terlanjur membuang pekerjaan. */
.aslibnote{font-family:var(--fu);font-size:12px;line-height:18px;color:var(--lg-tx3);
  margin-bottom:4px}
.aslib .lgbtn{margin-top:8px}

/* ---- Activity dan percakapan ---- */
.idwfeed{display:flex;flex-direction:column;min-height:0;flex:1}
.idwfh{display:flex;align-items:center;gap:12px;padding:16px 18px 10px;flex:none}
.idwfh h3{margin:0;flex:1;font-size:15px;font-weight:600;color:var(--lg-tx)}
.idwhide{display:inline-flex;align-items:center;gap:6px;border:0;background:none;padding:0;
  cursor:pointer;color:var(--lg-tx2);font-family:var(--fu);font-size:12.5px}
.idwhide:hover{color:var(--lg-tx)}
.idwhi{width:12px;height:12px;fill:currentColor}
.idwlog{margin:0;padding:0 18px 12px;list-style:none;display:flex;flex-direction:column;gap:7px;
  max-height:150px;overflow-y:auto;flex:none}
.idwlog li{display:flex;gap:12px;align-items:baseline;font-size:12.5px;line-height:18px}
.idwlog li span{flex:1;min-width:0;color:var(--lg-tx2)}
.idwlog li i{flex:none;font-style:normal;font-size:11px;color:var(--lg-tx3);white-space:nowrap}
.idwnone span{color:var(--lg-tx3)}
.idwtalk{flex:1;min-height:0;overflow-y:auto;padding:4px 18px 12px;
  display:flex;flex-direction:column;gap:10px}
.idwnone2{padding:28px 4px;text-align:center;font-size:12.5px;line-height:19px;color:var(--lg-tx3)}
/* Kartu pesan BUKAN kaca. Panel Figma menyebutnya apa adanya:

     isian   Surface/Panel  #121418
     garis   1px, sisi bawah saja, #212429, inner

   Yang terpasang gradien putih 6%-2% dengan bingkai penuh dan blur 8 — kaca,
   di tempat yang seharusnya permukaan padat. Bedanya terlihat waktu banyak
   kartu bertumpuk: kaca di atas kaca membuat kolomnya berkabut, sementara
   #121418 memberi tiap pesan tepi yang jelas.

   Blur dicabut, bukan sekadar tertutup isian padat: backdrop-filter tetap
   dihitung browser di balik isian buram, dan di kolom yang bisa memuat puluhan
   pesan itu biaya per simpul untuk sesuatu yang tidak terlihat.

   Garisnya di sisi bawah baris kaki, bukan mengelilingi kartu — itu yang
   terlihat di gambar sebagai pemisah antara isi pesan dan barisan labelnya,
   dan itu pula satu-satunya sisi yang disebut panelnya. */
.idwmsg{border:0;border-radius:10px;background:#121418;padding:0;
  display:flex;flex-direction:column}
/* Yang sudah diselesaikan TIDAK diredupkan.

   opacity:.66 di seluruh kartu menurunkan isian #121418-nya juga, jadi kartu
   feedback yang selesai jadi abu pucat sementara kartu comment di sebelahnya
   padat — dua warna kotak untuk satu jenis kotak. Yang menandainya sudah ada
   dan cukup: lingkaran centang biru di kepalanya.

   Baris ini sengaja dibiarkan kosong daripada dihapus, supaya jelas bahwa
   tidak-meredupkan itu keputusan, bukan kelupaan. */
.idwmh{display:flex;align-items:center;gap:9px;min-width:0;padding:12px 14px 0}
.idwmn{flex:1;min-width:0;display:flex;flex-direction:column;font-size:13px;color:var(--lg-tx)}
.idwmn i{font-style:normal;font-size:11px;color:var(--lg-tx3)}
.idwmt{flex:none;font-size:11px;color:var(--lg-tx3)}
/* Lingkaran centang di kepala pesan, bukan tautan teks di kakinya — itu yang
   digambar desain, dan ia dua keadaan dari SATU kontrol: garis abu waktu belum
   selesai, biru penuh waktu sudah. Tautan "Mark resolved" di kaki membuat
   keduanya terlihat seperti dua hal berbeda. */
.idwtick{flex:none;width:24px;height:24px;border:0;border-radius:50%;background:transparent;
  display:flex;align-items:center;justify-content:center;color:var(--lg-tx4);padding:0}
.idwtick svg{width:18px;height:18px;fill:currentColor}
button.idwtick{cursor:pointer}
button.idwtick:hover{color:var(--lg-tx2);background:var(--lg-glass06)}
.idwtick.on{color:var(--lg-blue-h)}
.idwmb{font-size:13.5px;line-height:20px;color:var(--lg-tx2);word-break:break-word;
  padding:8px 14px 12px}
.idwmf{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 14px;
  border-top:1px solid #212429}
.idwtag{padding:3px 8px;border-radius:5px;font-family:var(--fu);font-size:10.5px;font-weight:500}
/* Satu warna untuk ketiganya, dan itu yang digambar desain: di artboard chip
   Comment dan chip Feedback adalah pil abu yang sama persis.

   Biru dan kuning yang sempat saya pasang adalah karangan saya — niatnya baik,
   membedakan yang bisa diselesaikan dari yang tidak, tapi akibatnya kolomnya
   jadi dua warna mencolok yang saling bersaing dengan chip tahap di kolom kiri.
   Bedanya tetap terbaca tanpa warna: labelnya sendiri, dan lingkaran centang
   yang hanya dipunyai Feedback. */
.idwtag.cm,.idwtag.fb,.idwtag.mk{background:var(--lg-field);color:var(--lg-tx2)}

/* --- Menu titik tiga, balasan, dan kotak balas ---------------------------

   Menunya digambar DI DALAM kartunya, bukan dilampirkan ke body.

   Kolom percakapan ini bergulir sendiri (.idwtalk overflow-y), jadi panel yang
   dipasang ke body dengan koordinat tetap akan tertinggal di tempatnya begitu
   orangnya menggulir satu baris — menu yang mengambang di atas kartu yang
   salah. Di dalam kartu ia ikut bergulir karena ia memang bagian dari kartunya.

   Harganya satu: .idwtalk memotong apa pun yang melewati tepinya. Menu ini
   dibuka KE BAWAH dan tetap aman, karena tombolnya ada di KEPALA kartu, bukan
   di kakinya: ruang di bawah kepala selalu setinggi badan pesan ditambah baris
   chip, yang lebih tinggi daripada satu baris menu. Diukur di kartu terpendek
   yang ada — kartu Comment satu baris, tinggi 122px, menu 41px, sisa 36px di
   bawah menu. Menu yang tumbuh jadi dua baris masih muat; yang tumbuh jadi
   empat tidak, dan saat itu tombolnya yang harus pindah, bukan arahnya. */
.idwmenuw{flex:none;position:relative}
.idwdots{width:24px;height:24px;border:0;border-radius:50%;background:transparent;cursor:pointer;
  display:flex;align-items:center;justify-content:center;color:var(--lg-tx4);padding:0}
.idwdots svg{width:18px;height:18px;fill:currentColor}
.idwdots:hover{color:var(--lg-tx2);background:var(--lg-glass06)}
.idwpop{position:absolute;top:calc(100% + 6px);right:0;z-index:5;min-width:148px;
  border:1px solid var(--lg-glass09);border-radius:10px;background:var(--lg-panel);
  box-shadow:0 12px 28px rgba(0,0,0,.45);padding:5px;display:flex;flex-direction:column}
.idwpopi{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:7px;
  background:none;cursor:pointer;text-align:left;font-family:var(--fu);font-size:12.5px;
  color:var(--lg-tx2)}
.idwpopi:hover{background:var(--lg-glass06)}
.idwpopi.danger{color:var(--lg-error)}
.idwpopi.danger:hover{background:rgba(255,107,107,.12)}
.idwpi2{width:15px;height:15px;flex:none;fill:currentColor}

/* Reply didorong ke kanan kaki kartu dengan margin-left:auto, yang juga yang
   menempatkan chip dan penghitung balasan di kiri tanpa satu pun pembungkus
   tambahan. Kalau suatu saat ada kontrol kedua di kanan, auto-nya pindah ke
   yang pertama dari mereka — bukan ditambah di dua tempat. */
.idwri{width:14px;height:14px;flex:none;fill:currentColor}
.idwrtog{display:flex;align-items:center;gap:5px;border:0;background:none;cursor:pointer;padding:3px 4px;
  border-radius:6px;font-family:var(--fu);font-size:11.5px;color:var(--lg-tx3)}
.idwrtog:hover,.idwrtog.on{color:var(--lg-tx2)}
.idwrly{margin-left:auto;border:0;background:none;cursor:pointer;padding:3px 6px;border-radius:6px;
  font-family:var(--fu);font-size:12px;font-weight:500;color:var(--lg-blue-h)}
.idwrly:hover{background:var(--lg-glass06)}

/* Balasan dibedakan dengan rel di kirinya, bukan dengan latar kedua.

   Kartu induknya sudah #121418 di atas kanvas gelap; kotak kedua yang lebih
   terang di dalamnya membuat kartu bertumpuk terlihat seperti tiga lapis kaca,
   yang justru yang dicabut dari kartu pesan sejak awal. Satu garis 1px dan
   sedikit inden sudah cukup mengatakan "ini di bawah yang itu". */
.idwreps{display:flex;flex-direction:column;gap:2px;margin:0 14px 10px;
  padding-left:11px;border-left:1px solid #262a31}
.idwrep{padding:7px 0 3px}
.idwreph{display:flex;align-items:center;gap:8px;min-width:0}
.idwreph .ovface{width:20px;height:20px;font-size:9px}
.idwrepb{font-size:12.5px;line-height:19px;color:var(--lg-tx2);word-break:break-word;padding:5px 0 0 28px}

.idwrbox{margin:0 14px 12px;border:1px solid var(--lg-glass07);border-radius:10px;
  background:var(--lg-glass02);padding:9px 10px;display:flex;flex-direction:column;gap:8px;
  transition:border-color .12s}
.idwrbox:focus-within{border-color:var(--lg-focus)}
.idwrbox textarea{width:100%;resize:vertical;min-height:46px;border:0;background:none;padding:0;
  color:var(--lg-tx);font-family:var(--fu);font-size:13px;line-height:19px}
.idwrbox textarea:focus{outline:0}
.idwrbox textarea::placeholder{color:var(--lg-tx3)}
.idwrf{display:flex;align-items:center;justify-content:flex-end;gap:8px}
.idwrcx{border:0;background:none;cursor:pointer;padding:5px 8px;border-radius:7px;
  font-family:var(--fu);font-size:12px;color:var(--lg-tx3)}
.idwrcx:hover{color:var(--lg-tx2);background:var(--lg-glass06)}
.idwrgo{display:flex;align-items:center;gap:6px;border:0;cursor:pointer;padding:6px 12px;border-radius:8px;
  background:var(--lg-blue);color:#fff;font-family:var(--fu);font-size:12px;font-weight:500}
.idwrgo:hover{background:var(--lg-blue-h)}


/* Satu bingkai membulat yang memuat area ketik DAN kontrolnya, seperti
   artboard. Textarea di dalamnya tanpa bingkai sendiri: dua bingkai bersarang
   membuat kotak ini terbaca sebagai dua benda yang kebetulan bertetangga.

   Cincin fokusnya pindah ke bingkai luar lewat :focus-within, supaya yang
   menyala waktu orang mengetik adalah kotak yang ia lihat, bukan kotak dalam
   yang tidak digambar sama sekali. */
.idwcomp{flex:none;border-top:1px solid var(--lg-glass07);padding:14px 18px 16px}
.idwcbox{border:1px solid var(--lg-glass07);border-radius:12px;padding:10px 10px 10px 14px;
  background:linear-gradient(180deg,rgba(255,255,255,.04) 0%,rgba(255,255,255,.02) 100%);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  display:flex;flex-direction:column;gap:8px;transition:border-color .12s}
.idwcbox:focus-within{border-color:var(--lg-focus)}
.idwcomp textarea{width:100%;resize:vertical;min-height:76px;padding:2px 4px 0 0;border:0;
  background:none;color:var(--lg-tx);font-family:var(--fu);font-size:13.5px;line-height:20px}
.idwcomp textarea:focus{outline:0}
.idwcomp textarea::placeholder{color:var(--lg-tx3)}
.idwatt{align-items:center;gap:8px;font-size:12px;color:var(--lg-tx2);padding:0 4px}
.idwatt svg{width:14px;height:14px;flex:none}
.idwcf{display:flex;align-items:center;gap:8px}
/* Pil kecil, bukan select selebar baris. Dan width:auto ditulis lagi di sini
   karena aturan global select{width:100%} berlaku di mana pun. */
.idwkind{height:32px;width:auto;min-width:0;flex:none;padding:0 8px 0 10px;border-radius:8px;
  cursor:pointer;border:1px solid var(--lg-glass07);background:var(--lg-field);color:var(--lg-tx2);
  font-family:var(--fu);font-size:12.5px}
.idwkind:focus{outline:0;border-color:var(--lg-focus)}
.idwclip{width:32px;height:32px;flex:none;border:0;border-radius:8px;cursor:pointer;
  background:var(--lg-field);color:var(--lg-tx2);
  display:flex;align-items:center;justify-content:center}
.idwclip svg{width:15px;height:15px;fill:currentColor}
.idwclip:hover{background:var(--lg-glass06);color:var(--lg-tx)}
.idwsend{margin-left:auto;width:36px;height:32px;flex:none;border:0;border-radius:8px;cursor:pointer;
  background:var(--lg-blue);color:#fff;display:flex;align-items:center;justify-content:center;
  box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.idwsend svg{width:15px;height:15px;fill:#fff}
.idwsend:hover{background:var(--lg-blue-h)}

body.idwopen{overflow:hidden}
@media (max-width:1080px){
  .idwbody{grid-template-columns:1fr;grid-template-rows:auto minmax(320px,1fr)}
  .idwright{border-left:0;border-top:1px solid var(--lg-glass07)}
}

/* ===========================================================================
   FE — Script Quality Control

   Tidak ada artboard gelap untuk layar ini. Yang ada cuma versi terang di
   halaman "Current Mastershort": 1:15018 (daftar) dan 1:15338 (dengan laci).
   Warnanya #FFFFFF / #1E1E1E / #EAF7FF dan tidak satu pun dipakai di sini.

   Kerangkanya menumpang .iqcard dan .iqgrid — kartu gerbang yang SUDAH ada di
   design system gelap (94:24445). Yang ditulis di bawah hanya yang memang
   berbeda: baris penulis, sudut kanan kepala kartu, dan barisan tombol
   gerbang yang datang dari acted() dengan kelas .btn lama.
   =========================================================================== */

/* Pencariannya 460 seperti di Idea QC, bukan selebar sisa baris.

   Artboard TERANG memberinya 1271 dari 1417 dan baris itulah yang ditulis di
   sini semula. Gambar yang dikirim Joe 2026-09-17 memberi bentuk lain — kotak
   sekitar sepertiga baris di kiri, dropdown di kanan, sisanya kosong — dan itu
   proporsi .iqsearch apa adanya. Jadi yang benar di sini adalah TIDAK menulis
   apa pun: satu baris override dihapus, dan halaman ini memakai ukuran yang
   sama dengan gerbang saudaranya. */

/* Umur, flag dan chip tahap berdiri bersama di kanan kepala kartu. .iqhead
   memberi margin-left:auto pada .ovchip; di sini yang dodorong adalah
   pembungkusnya, supaya ketiganya bergerak sebagai satu blok. */
.sqcard .iqhead .ovchip{margin-left:0}
.sqhr{margin-left:auto;display:flex;align-items:center;gap:8px;flex:none}

/* Umurnya sebagai kata, bukan sebagai warna.

   .age menaikkan warna pada 12 jam (amber) dan 24 jam (merah) — itu yang
   membuat "1d" tergambar merah di tangkapan layar. Desain menggambarnya abu
   dan berbunyi penuh, "5h ago", jadi kelas ini tidak membawa tingkatan itu.

   Yang ikut hilang bersamanya perlu disebut daripada ditemukan nanti: skrip
   yang menunggu tiga hari kini terlihat sama dengan yang menunggu satu jam.
   Angkanya masih tertulis di kartunya, hanya tidak lagi berteriak. */
.sqage{font-family:var(--fm);font-size:12px;color:var(--lg-tx2);white-space:nowrap}

/* "Writer: ABC" — 12/500 di artboard, di sini dengan Content/500 supaya ia
   sejajar dengan label sekunder lain di kartu dan tidak bersaing dengan judul. */
.sqwho{font-size:12px;line-height:15px;font-weight:500;color:var(--lg-tx2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Barisan tombol gerbang. Pemisahnya menggantikan .iqdiv milik kartu Idea QC —
   .qacts sudah membawa garis atasnya sendiri, jadi menambah .iqdiv akan
   menggambar dua garis dengan 16px kosong di antaranya. */
.sqcard .qacts{margin-top:0;padding-top:16px;gap:8px;
  border-top:1px solid var(--lg-glass07)}
.sqcard .lanebadge{margin:0}
/* Kelas .btn lama memakai palet lama: --lime hijau limau untuk primer, --gr
   hijau untuk Approve, --rd merah untuk Reject. Tidak satu pun ada di design
   system ini. Yang diganti RUPANYA saja — tombolnya, handlernya dan syarat
   tampilnya tetap milik gateBtns(). */
.sqcard .qacts .btn{height:30px;padding:0 16px;border:0;border-radius:8px;
  background:var(--lg-glass06);color:var(--lg-tx2);
  font-family:var(--fu);font-size:12px;line-height:18px;font-weight:500;
  box-shadow:inset 0 0 0 1px var(--lg-glass09)}
.sqcard .qacts .btn:hover{background:var(--lg-glass16);color:var(--lg-tx);border:0}
.sqcard .qacts .btn svg{width:14px;height:14px}
/* Approve dan tombol primer lain: isian cobalt padat dengan cincin putih 14%,
   perlakuan yang sama dengan .ovbtn-p di Overview dan Idea QC. */
.sqcard .qacts .btn-s,.sqcard .qacts .btn-p{background:var(--lg-blue);color:#fff;
  box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.sqcard .qacts .btn-s:hover,.sqcard .qacts .btn-p:hover{background:var(--lg-blue-h);color:#fff}
/* Reject dan Drop Script: teks merah tanpa plat, persis seperti Reject di
   artboard. Keduanya destruktif dan keduanya digambar begitu. */
.sqcard .qacts .btn-d{background:none;box-shadow:none;color:var(--lg-error);padding:0 8px}
.sqcard .qacts .btn-d:hover{background:none;color:#FF8F8F}

/* Peringatan yang menumpang kartu ini datang dari layar lama dengan margin
   layar lama. Di dalam kartu flex bergap 16, margin itu menumpuk. */
.sqcard .ftbox{margin:0}
.sqcard .lanebadge+.qacts{margin-top:0}

/* ===========================================================================
   FE — laci Script QC   (artboard 1:15647, 1:15892, 1:16143, 1:16390)

   Lacinya sama dengan laci Idea QC — kolom kirinya memang sudah menggambar
   Title Options, Description, Scripts, Hashtags, Tags dan Source Link sejak
   awal, dan kolom kanannya Activity yang sama. Yang ditulis di bawah hanya
   barisan tombol gerbangnya, yang di sini datang dari gateBtns() dengan kelas
   .btn lama dan palet lama.
   =========================================================================== */

/* Dua tombol duduk di samping judul, seperti yang digambar. Lebih dari dua
   turun ke barisnya sendiri — .idwwide dipasang iqDrawerHtml() waktu gerbangnya
   menawarkan lebih dari dua jawaban. Dipaksa tetap sebaris, judul 22px-nya
   pecah dua baris untuk memberi ruang, dan itu yang terukur sebelum aturan ini
   ada: judul 281 lebar dengan tombol membungkus dua baris di sebelahnya. */
.idwact.idwgate{flex-wrap:wrap;justify-content:flex-end;gap:8px}
.idwhead.idwwide{flex-wrap:wrap}
.idwhead.idwwide h2{flex:1 1 100%}
.idwhead.idwwide .idwact{width:100%;justify-content:flex-end}
.idwact.idwgate .btn{height:30px;padding:0 16px;border:0;border-radius:8px;
  background:var(--lg-glass06);color:var(--lg-tx2);
  font-family:var(--fu);font-size:12px;line-height:18px;font-weight:500;
  box-shadow:inset 0 0 0 1px var(--lg-glass09)}
.idwact.idwgate .btn:hover{background:var(--lg-glass16);color:var(--lg-tx);border:0}
.idwact.idwgate .btn svg{width:14px;height:14px}
.idwact.idwgate .btn-s,.idwact.idwgate .btn-p{background:var(--lg-blue);color:#fff;
  box-shadow:inset 0 0 0 1px var(--lg-glass14)}
.idwact.idwgate .btn-s:hover,.idwact.idwgate .btn-p:hover{background:var(--lg-blue-h);color:#fff}
.idwact.idwgate .btn-d{background:none;box-shadow:none;color:var(--lg-error);padding:0 8px}
.idwact.idwgate .btn-d:hover{background:none;color:#FF8F8F}
/* laneNote() dan .qacts ikut keluar dari gateBtns() lewat acted(), tapi laci
   memanggil gateBtns() langsung — jadi yang sampai ke sini hanya tombolnya.
   Baris ini menjaga kalau suatu hari jalurnya berubah. */
.idwact.idwgate .qacts{border:0;padding:0;margin:0;gap:8px}

/* ===========================================================================
   FE — Assets Quality Control   (desain 2026-09-17)

   Kerangkanya menumpang kartu gerbang yang sudah ada — .iqcard, .iqgrid,
   .aqbox, .aqplay, .ovdates, .ovact — karena kartu ini memang kartu Audio QC
   dengan video di tempat teks skrip. Yang ditulis di bawah cuma tiga hal yang
   memang belum ada: sakelar jenis, bingkai videonya, dan pembatalan tinggi
   tetap milik .aqbox.
   =========================================================================== */

/* Sakelar AI / Video / Image. TIDAK DIPAKAI SEJAK 18 SEPTEMBER, dan dibiarkan
   di sini dengan sengaja: Joe meminta sakelarnya pindah ke header di sebelah
   ikon notifikasi, jadi ini aturan yang menunggu markup barunya, bukan sisa
   yang terlupa. Kalau enam bulan lagi headernya belum dipasang, buang saja —
   git menyimpannya.

   Perlakuan kaca yang sama dengan .iqsearch dan .iqby yang dulu berdiri di
   sebelahnya — isian gradien putih 6%→2%, garis putih 12% — supaya ketiga
   kontrol di baris itu terbaca sebagai satu keluarga. Tingginya 44 mengikuti
   keduanya; padding 4 di luar membuat plat terpilihnya duduk di dalam, bukan
   menempel tepi. Di header tingginya hampir pasti bukan 44 lagi. */
.aqckind{display:flex;align-items:center;gap:4px;height:44px;padding:4px;flex:none;
  border-radius:10px;border:1px solid rgba(255,255,255,.12);
  background:linear-gradient(180deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,.02) 100%)}
.aqckind button{height:34px;padding:0 16px;border:0;border-radius:7px;cursor:pointer;
  background:none;color:#98A1B5;font-family:var(--fu);font-size:13px;font-weight:500}
.aqckind button:hover{color:var(--lg-tx)}
.aqckind button.on{background:var(--lg-blue);color:#fff}
.aqckind button.on:hover{background:var(--lg-blue-h);color:#fff}

/* .aqbox dipatok 170 tinggi supaya sembilan kotak skrip berbaris rata. Di sini
   isinya bingkai video dengan rasio tetap, jadi patokan itu harus dilepas —
   dibiarkan, videonya terpotong di tengah dan barisan pemutarnya hilang di
   bawah overflow:hidden milik .aqbox.

   `.aqbox.aqcbox`, bukan `.aqcbox`. Blok ini duduk DI ATAS blok Audio QC di
   berkas ini, dan dua pemilih satu-kelas dengan bobot sama diputuskan oleh
   urutan — jadi .aqbox{height:170px} di bawah sana menang dan bingkainya
   terjepit jadi 79px. Terukur begitu sebelum baris ini diperbaiki. Dua kelas
   menang lewat bobot, bukan lewat tempat, jadi memindahkan blok ini nanti
   tidak menghidupkan kembali bugnya. */
.aqbox.aqcbox{height:auto}

/* 16:9, dan rasio yang menentukan tingginya, bukan angka px. Render HeyGen
   datang dalam lebih dari satu ukuran; tinggi tetap akan memberi salah satunya
   pita hitam di atas-bawah dan memotong yang lain. object-fit:contain menjaga
   yang di dalamnya utuh apa pun bentuknya. */
.aqcfr{position:relative;aspect-ratio:16/9;background:#000;overflow:hidden}
.aqcfr video{display:block;width:100%;height:100%;object-fit:contain;cursor:pointer;background:#000}

/* Accept yang dimatikan tetap terbaca sebagai tombol yang sedang tidak boleh
   ditekan, bukan sebagai tombol biasa yang kebetulan tidak bereaksi. Render
   basi adalah satu-satunya keadaan yang memakai ini, dan kalimat merah di
   atasnya yang menjelaskan sebabnya. */
.aqccard .ovbtn[disabled]{opacity:.45;cursor:not-allowed}
.aqccard .ovbtn-p[disabled]:hover{background:var(--lg-blue)}

/* ===========================================================================
   FE — Final Quality Control   (desain 2026-09-17)

   Kartunya kembaran kartu Assets QC dan memakai kelasnya apa adanya. Yang
   ditulis di bawah cuma dua: rantai gerbang di atas daftar, dan bingkai yang
   lebih tegak karena isinya shorts, bukan render avatar lanskap.
   =========================================================================== */

/* Rantai QC1 -> QC2 -> QC3 dengan pemegang tiap gerbang. Membungkus di layar
   sempit, dan panahnya ikut membungkus bersamanya — itu sebabnya ia elemen di
   dalam flex, bukan ::after yang menempel di kanan tiap langkah. */
.fqchain{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin:0 0 16px}
.fqstep{display:flex;flex-direction:column;gap:2px;min-width:0}
.fqstep b{font-family:var(--fu);font-size:12px;font-weight:600;color:var(--lg-tx2)}
.fqstep i{font-style:normal;font-size:11px;line-height:15px;color:var(--lg-tx4)}
/* Gerbang yang memang dipegang orang yang sedang melihat. Tanpa ini rantainya
   cuma tulisan; dengan ini ia menjawab "yang mana punya saya" sekali lihat. */
.fqstep.on b{color:var(--lg-tx)}
.fqstep.on i{color:var(--lg-blue)}
.fqarr{width:12px;height:12px;flex:none;fill:var(--lg-tx4)}

/* 7:5, bukan 16:9 milik Assets QC. Yang diputar di sini shorts — 9:16 tegak —
   dan bingkai selebar 16:9 memberinya dua pita hitam selebar setengah kartu.
   Terukur dari desain: kotak 336 dengan area video 237, yaitu 1,42. */
.fqfr{aspect-ratio:7/5}

/* Gerbang yang memegang video tanpa berkas yang bisa diputar — jalur langsung,
   yang potongannya ditinjau di tautannya. Tingginya mengikuti .aqnone milik
   Audio QC supaya kartu tanpa pemutar tidak jadi separuh tinggi tetangganya. */
.fqnone{min-height:96px}

/* ===========================================================================
   FE — Audio Quality Control   (artboard 103:9537, 153:4335, 153:6513)

   Gerbang pertama yang punya artboard gelapnya sendiri. Kartunya `task /
   AUG100` 364x377 radius 12 padding 14 gap 12 — ukuran kartu Idea QC, jadi
   .iqcard dan .iqgrid dipakai apa adanya dan yang ditulis di bawah hanya kotak
   Script berisi pemutarnya.
   =========================================================================== */

/* Kalimat yang memberi tahu ke mana tiap tombol mengirim pekerjaan orang. */
.aqnote{margin:-4px 0 14px;max-width:80ch}

/* 336x170, isian #181B1F, radius 4. Tingginya dipatok supaya sembilan kartu
   berbaris rata: teks yang panjang menggulir di dalamnya, tidak menumbuhkan
   kartunya. Itu juga yang digambar artboard — ada batang gulir 3px di
   sebelah kanan teksnya. */
.aqbox{background:#181B1F;border-radius:4px;display:flex;flex-direction:column;
  height:170px;min-height:0;overflow:hidden}
.aqbh{flex:none;height:39px;display:flex;align-items:center;padding:0 16px;
  font-size:10px;line-height:15px;font-weight:600;color:var(--lg-tx);
  letter-spacing:.06em;text-transform:uppercase}
/* 12px, bukan 10px seperti artboard. Skrip di gerbang ini DIBACA sambil
   menyimak suaranya — itu seluruh pekerjaannya — dan layar lama menggambarnya
   13px. 10 adalah ukuran pratinjau. 12 adalah ukuran badan terkecil yang
   benar-benar dipakai design system ini (.iqdesc). */
.aqtext{flex:1;min-height:0;overflow-y:auto;padding:0 8px 0 16px;
  font-size:12px;line-height:17px;color:#A4A4A4;
  scrollbar-width:thin;scrollbar-color:#34363A transparent}
.aqtext::-webkit-scrollbar{width:3px}
.aqtext::-webkit-scrollbar-thumb{background:#34363A;border-radius:100px}
.aqtext::-webkit-scrollbar-track{background:transparent}

/* Baris pemutar, 52 tinggi, padding 8/16. */
.aqplay{flex:none;height:52px;display:flex;align-items:center;gap:10px;padding:0 16px}
.aqb{width:25px;height:25px;flex:none;display:inline-flex;align-items:center;justify-content:center;
  border:0;border-radius:5px;cursor:pointer;background:var(--lg-blue);padding:0}
.aqb:hover{background:var(--lg-blue-h)}
.aqbi{width:11px;height:11px;fill:#fff;display:block}
/* Play digeser sedikit ke kanan: segitiga yang dipusatkan secara geometris
   selalu terbaca terlalu ke kiri, dan pause tidak boleh ikut bergeser. */
.aqb:not(.on) .aqbi{margin-left:1px}
.aqt{flex:none;font-family:var(--fm);font-size:11px;line-height:16px;color:var(--lg-tx2);
  white-space:nowrap;font-variant-numeric:tabular-nums}
/* Scrubber 92x6 di artboard, di sini melar mengisi sisa baris supaya kartu
   tetap benar waktu kolomnya lebih lebar dari 364. */
.aqbar{flex:1;min-width:24px;height:6px;border-radius:12px;cursor:pointer;
  background:var(--lg-glass07)}
.aqfill{display:block;height:100%;width:0;border-radius:12px;background:var(--lg-blue)}
.aqic{width:28px;height:28px;flex:none;position:relative;display:inline-flex;
  align-items:center;justify-content:center;border:0;border-radius:8px;padding:0;
  cursor:pointer;background:none;color:var(--lg-tx3)}
.aqic:hover{background:var(--lg-glass06);color:var(--lg-tx)}
.aqici{width:15px;height:15px;fill:currentColor;display:block}
/* Bisu digambar dengan garis miring dari CSS, bukan ikon kedua: satu jalur
   SVG yang salah tulis menggambar tidak ada apa-apa, dan itu sudah terjadi
   sekali di berkas ini. */
.aqic.off{color:var(--lg-tx4)}
.aqic.off::after{content:'';position:absolute;left:5px;right:5px;top:50%;height:1.5px;
  background:currentColor;transform:rotate(-45deg);border-radius:2px}
.aqnone{flex:none;height:52px;display:flex;align-items:center;gap:8px;padding:0 16px;
  font-size:12px;line-height:16px;color:var(--lg-tx4)}
.aqni{width:14px;height:14px;flex:none;fill:currentColor}

/* Tanggal menempel ke dasar seperti di kartu Idea QC; kotak Script di atasnya
   sudah patok tingginya, jadi yang tersisa jatuh di sini. */
.aqcard .ovdates{margin-top:auto}

/* ===========================================================================
   FE — Assets Library   (artboard 103:12644, 103:13077)

   Kartunya `task / AUG100` 364x402, bentuk yang sama dengan kartu Audio QC —
   jadi .iqcard, .iqgrid, .aqbox dan seluruh baris pemutarnya dipakai ulang.
   Yang ditulis di sini cuma kepala kartunya, pratinjaunya, dan dua kontrol di
   atas grid.
   =========================================================================== */

/* Kontrol tersegmen, 210x38 radius 10 padding 4 gap 4; tiap segmen 30 tinggi
   radius 8 padding 7/16, yang aktif berisi #1454DB. */
.lbseg{display:inline-flex;align-items:center;gap:4px;padding:4px;margin-bottom:16px;
  border-radius:10px;background:var(--lg-glass04);
  box-shadow:inset 0 0 0 1px var(--lg-glass07)}
.lbsegb{height:30px;padding:0 16px;border:0;border-radius:8px;cursor:pointer;background:none;
  font-family:var(--fu);font-size:13px;line-height:16px;font-weight:500;color:var(--lg-tx2)}
.lbsegb:hover{color:var(--lg-tx)}
.lbsegb.on{background:var(--lg-blue);color:#fff}
.lbsegb.on:hover{background:var(--lg-blue-h);color:#fff}

/* Tiga aturan dilepas di sini 18 September, bersama tombol yang memakainya:
   .lbtools (pembungkus barisan kedua), `.iqright .lbtool` (tinggi 44 untuk Add
   Many At Once dan Build Waveforms yang dulu berdiri di barisan kanan) dan
   .lbtool.danger (Delete this folder). Tidak ada lagi yang menggambarnya.

   Berbeda dengan .aqckind yang dibiarkan berdiri beberapa ratus baris di atas:
   sakelar itu PINDAH ke header dan aturannya menunggu markup barunya. Yang tiga
   ini tidak pindah ke mana pun.

   .lbtool sendiri tetap, dan bukan demi Assets Library: papan, Bug Reports dan
   Hiring memakainya untuk tombol Try again dan Refresh mereka. */
.lbtool{display:inline-flex;align-items:center;gap:7px;height:36px;padding:0 14px;
  border:0;border-radius:8px;cursor:pointer;background:var(--lg-glass06);color:var(--lg-tx2);
  font-family:var(--fu);font-size:12px;line-height:18px;font-weight:500;white-space:nowrap;
  box-shadow:inset 0 0 0 1px var(--lg-glass09)}
.lbtool:hover{background:var(--lg-glass16);color:var(--lg-tx)}
.lbtool svg{width:14px;height:14px;stroke:currentColor;stroke-width:1.8;fill:none}
/* Sebesar .iqadd di sebelahnya — 44 tinggi, padding 20, teks 13. Tiga tombol
   berjajar dengan yang satu 8px lebih pendek daripada dua lainnya terbaca
   seperti tombol yang belum selesai, bukan seperti hierarki. Joe, 19 September:
   "ikuti ukuran tombol add candidate."

   Modifier, bukan .lbtool yang diubah: kelas yang sama dipakai tombol Try
   again dan Refresh di papan dan Bug Reports, yang tidak berdampingan dengan
   tombol 44 mana pun. */
.lbtool-lg{height:44px;padding:0 20px;font-size:13px;line-height:16px}
.lbnote{margin:0 0 16px;max-width:80ch}
/* Nama folder bisa panjang; select global dibatasi 200 di .iqby, dan di sini
   ia perlu sedikit lebih dari itu untuk "bass pulsating (3)". */
.lbcat{max-width:260px}

/* Kepala kartu: nama dan pemiliknya di kiri, chip folder di kanan. */
.lbhead{display:flex;align-items:flex-start;gap:8px;min-width:0}
.lbwho{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.lbn{font-size:14px;line-height:17px;font-weight:500;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lbby{font-size:12px;line-height:15px;color:var(--lg-tx3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lbfold{flex:none;max-width:45%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.lbfold:hover{color:var(--lg-tx)}

/* Kotak medianya lebih tinggi dari kotak Script di Audio QC — 271 lawan 170 —
   karena pratinjaunya 178 dan bukan tujuh baris teks. */
.lbbox{height:271px}
/* Baris format berkas membawa Move dan Delete di kanannya sekarang, jadi ia
   flex — bukan satu blok teks. Tingginya tetap 39 dari artboard. */
/* padding-right 16, sama dengan baris pemutar di bawahnya. Tanpa itu tepi
   kanan tombol Delete berhenti di 636 sementara tombol unduh tepat di bawahnya
   berhenti di 628 — dua tombol yang seharusnya satu kolom, meleset 8px. */
.lbfile{font-family:var(--fm);letter-spacing:0;text-transform:none;
  display:flex;align-items:center;gap:12px;padding-right:16px}
.lbfile > span:first-child{flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lbacts{flex:none;display:flex;align-items:center;gap:2px}
/* Sebentuk DAN seukuran dengan tombol unduh di baris pemutar tepat di
   bawahnya, supaya ketiganya berdiri di satu kolom. */
.lbacts .aqic{width:28px;height:28px}
.lbacts .aqici{width:15px;height:15px}
.lbdel:hover{color:var(--lg-error);background:var(--lg-glass06)}
/* 178 tinggi, isian hitam: itu yang digambar artboard, dan hitam adalah latar
   yang benar untuk sebuah bingkai video apa pun warnanya. */
.lbprev{flex:1;min-height:0;background:#000;display:flex;align-items:center;justify-content:center;
  overflow:hidden}
.lbimg,.lbvid{max-width:100%;max-height:100%;width:100%;height:100%;object-fit:contain;display:block}
.lbvid{cursor:pointer}
/* Gelombangnya duduk di slot scrubber, bukan di blok pratinjau: kartu musik
   dan SFX tidak punya gambar untuk ditunjukkan, dan kotak hitam setinggi 178
   untuk satu gelombang menghabiskan separuh kartu. Ia melar mengisi sisa baris
   kendali, persis seperti .aqbar yang digantikannya. */
.lbwave{flex:1;min-width:24px;height:28px;cursor:pointer}
.lbwave svg{width:100%;height:100%;display:block}
/* Kotak tanpa pratinjau: tingginya dari isinya, bukan dipatok 271. */
.lbflat{height:auto}
/* Satu baris setinggi 52 di slot pemutar, sebentuk dengan .aqnone di kartu
   Audio QC — bukan kotak hitam setinggi 271. */
.lbno{display:flex;align-items:center;gap:8px;height:52px;padding:0 16px;
  font-size:12px;line-height:16px;color:var(--lg-tx4)}
.lbnoi{width:14px;height:14px;flex:none;fill:currentColor}
/* Kartu yang sedang berbunyi diberi cincin, bukan isian: isian akan bertengkar
   dengan latar hitam pratinjaunya. */
.lbcard.on{box-shadow:inset 0 0 0 1px var(--lg-blue)}
.lbcard .ovdates{margin-top:auto}


/* --- dialog rak: Add / Move / New folder (artboard 103:13510) --- */
/* Select di dalam kartu .psmod dibuat sama dengan inputnya. Tanpa ini ia
   mewarisi aturan global di baris 228 berkas ini dan muncul sebagai kontrol
   dari palet lama di tengah kartu yang baru — jebakan yang sama, ketiga
   kalinya. */
.psmod .lgf select{width:100%;height:48px;padding:12px 16px;border:1px solid transparent;
  border-radius:8px;background:var(--lg-field);color:var(--lg-tx);
  font-family:var(--fu);font-size:16px;line-height:24px;cursor:pointer}
.psmod .lgf select:focus{outline:0;border-color:var(--lg-focus)}
.psmod .lgf input:disabled{cursor:default}
.psmod .lghelp{margin-top:8px;font-size:12px;line-height:18px;color:var(--lg-tx3)}

/* Berkas yang dipilih, digambar sebagai baris: nama, ukuran, dan tong sampah
   untuk membatalkannya. <input type="file"> aslinya disembunyikan tapi TETAP
   di dalam dokumen — ia yang memegang FileList-nya, dan saveSfx() membacanya
   lewat id yang sama seperti dulu. */
.lbfhide{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.lbfpick{width:100%;height:48px;display:flex;align-items:center;justify-content:center;gap:10px;
  border:1px dashed var(--lg-glass16);border-radius:8px;cursor:pointer;background:var(--lg-field);
  color:var(--lg-tx2);font-family:var(--fu);font-size:14px;font-weight:500}
.lbfpick:hover{color:var(--lg-tx);border-color:var(--lg-focus)}
.lbfpick svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none}
.lbfrow{display:flex;align-items:center;gap:12px;padding:12px 16px;border-radius:8px;
  background:var(--lg-field)}
.lbfmeta{flex:1;min-width:0}
.lbfn{font-size:14px;line-height:21px;font-weight:500;color:var(--lg-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lbfsz{font-size:12px;line-height:18px;color:var(--lg-tx3)}
.lbfx{width:32px;height:32px;flex:none;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:8px;cursor:pointer;background:none;color:var(--lg-tx3)}
.lbfx:hover{color:var(--lg-error);background:var(--lg-glass06)}
.lbfx svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none}

/* --- dialog Add Many At Once, di dalam kartu .psmod --- */
/* Kelas .bk* lama memakai palet lama: --tx3 abu-abu lama, --gr hijau dan --rd
   merah untuk status. Dibatasi ke dalam kartu ini supaya tidak ada tempat lain
   yang ikut berubah, dan diberi warna design system. */
.psmod .bkhead{display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin:0 0 8px;font-family:var(--fu);font-size:12px;line-height:18px;font-weight:500;
  letter-spacing:0;text-transform:none;color:var(--lg-tx2)}
.psmod .bklist{max-height:240px;overflow-y:auto;margin:0;
  display:flex;flex-direction:column;gap:8px;
  scrollbar-width:thin;scrollbar-color:#34363A transparent}
.psmod .bklist::-webkit-scrollbar{width:3px}
.psmod .bklist::-webkit-scrollbar-thumb{background:#34363A;border-radius:100px}
.psmod .bkrow{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:8px;
  background:var(--lg-field)}
/* Kotak namanya transparan di dalam barisnya: dua isian bertumpuk — satu di
   baris, satu di kotak — membuat baris itu terbaca seperti field di dalam
   field. Garisnya baru muncul waktu difokus, waktu ia memang sedang disunting. */
.psmod .bkrow input{flex:1;min-width:0;height:32px;padding:0 10px;border:1px solid transparent;
  border-radius:6px;background:transparent;color:var(--lg-tx);
  font-family:var(--fu);font-size:14px;line-height:21px}
.psmod .bkrow input:focus{outline:0;background:var(--lg-ink2,#181B1F);border-color:var(--lg-focus)}
.psmod .bksz{font-family:var(--fm);font-size:12px;color:var(--lg-tx3);white-space:nowrap}
.psmod .bkst{font-family:var(--fm);font-size:12px;color:var(--lg-tx4);width:56px;
  text-align:right;white-space:nowrap}
.psmod .bkst.ok{color:#97E1C9}
.psmod .bkst.bad{color:var(--lg-error)}
/* Tong sampah di kepala daftar membuang SEMUA berkasnya — FileList tidak bisa
   disunting satu per satu, dan tombol per baris akan menjanjikan sesuatu yang
   tidak bisa ditepati. */
.psmod .bkhead .lbfx{width:28px;height:28px;flex:none}

/* --- input berkas yang tersembunyi ---

   Cincin fokusnya sendiri sudah benar di mana-mana sejak baris 12 memakai
   --lg-focus; yang tersisa di sini cuma satu kasus bentuk.

   Input berkasnya disembunyikan tapi tetap bisa dijangkau Tab — dan sebuah
   kotak 1x1 dengan garis luar hijau adalah titik yang muncul entah dari mana.
   Fokusnya dipinjamkan ke tombol pemilih yang memang mewakilinya. */
.psmod .lbfhide:focus-visible{outline:0}
.psmod .lbfhide:focus-visible+.lbfpick,
.psmod .lbfhide:focus-visible+div>.lbfpick{outline:2px solid var(--lg-focus);outline-offset:2px}

/* ===========================================================================
   FE — Hiring   (artboard 109:17967, dan palet status 109:17717)

   Tabel di dalam kartu, bukan tabel telanjang di atas halaman. Kelas .tbl
   lama tetap dipakai — ia yang membawa .tblcard (kartu per baris di bawah
   700px) dan .tbl-act (kolom keempat hilang antara 700 dan 900), dua perilaku
   yang tidak boleh hilang — dan semua yang di bawah ini menimpanya di dalam
   .hstbl saja.
   =========================================================================== */

.hscard{background:#121418;border-radius:12px;padding:20px 16px;
  box-shadow:inset 0 0 0 1px var(--lg-glass07)}
.hstbl{overflow-x:auto}
.hstbl .tbl{font-size:14px}
/* Kepala 41 tinggi dengan isian #181B1F, dan sudut membulat di ujung-ujungnya
   karena ia baris pertama di dalam kotak beradius 10. */
.hstbl .tbl thead th{background:#181B1F;border:0;padding:12px;
  font-family:var(--fu);font-size:14px;font-weight:500;line-height:17px;
  letter-spacing:0;text-transform:none;color:var(--lg-tx2);white-space:nowrap}
.hstbl .tbl thead th:first-child{border-radius:10px 0 0 0}
.hstbl .tbl thead th:last-child{border-radius:0 10px 0 0}
/* Tiap judul kolom adalah tombol urut. Artboard menggambar corong penyaring di
   lima kolom dan panah di dua; tujuh panel penyaring di layar yang sudah punya
   pencarian dan chip status adalah tiga cara menyaring hal yang sama, jadi
   ketujuhnya jadi satu hal: urutkan. */
.hsth{cursor:pointer;user-select:none}
.hsth > span{vertical-align:middle}
.hsth:hover{color:var(--lg-tx)}
.hsi{width:11px;height:11px;margin-left:8px;fill:var(--lg-tx4);vertical-align:middle}
.hsth:hover .hsi{fill:var(--lg-tx2)}
.hsth.on{color:var(--lg-tx)}
.hsth.on .hsi{fill:var(--lg-blue-h)}
.hsnum{width:36px;color:var(--lg-tx3);font-family:var(--fm);font-size:13px}
/* 11, bukan 9 seperti yang tertulis di frame artboard. Barisnya digambar
   setinggi 52 dengan isi 29 (nama 17 di atas telepon 12), dan 52 dikurangi 29
   dibagi dua adalah 11,5 — angka padding di frame itu tidak menjelaskan
   tingginya sendiri. Tingginya yang diikuti, karena itu yang terlihat. */
.hstbl .tbl td{padding:11px 12px;border-bottom:1px solid var(--lg-glass07);
  font-family:var(--fu);font-size:14px;font-weight:500;line-height:17px;color:var(--lg-tx2)}
.hstbl .tbl tbody tr:last-child td{border-bottom:0}
.hstbl .lbrow:hover{background:var(--lg-glass04)!important}
.hstbl .tbl .num{font-family:var(--fu);font-size:14px;font-weight:500;color:var(--lg-tx2)}
/* Nama 14/400 #C8CEDA dengan nomor teleponnya 10/400 di bawahnya — satu-satunya
   sel di baris ini yang membawa dua baris. */
.hsn{font-weight:400;font-size:14px;line-height:17px;color:#C8CEDA}
.hsc{font-family:var(--fm);font-size:10px;line-height:12px;color:#C8CEDA;opacity:.7;
  white-space:nowrap}
.hsdash{color:var(--lg-tx4)}
.hsempty{padding:40px 12px;text-align:center;font-size:13px;color:var(--lg-tx3)}
/* Kolom riwayat. "15d ago" dijaga satu baris dan diredupkan: ia penanda, bukan
   isi barisnya — yang dibaca orang kalimat di kolom ketiga.

   Ditulis dengan .hstbl .tbl di depan, bukan dengan !important: aturan sel di
   atas berspesifisitas (0,2,1) dan satu kelas telanjang akan kalah darinya.
   Menang lewat spesifisitas yang benar meninggalkan aturan yang masih bisa
   ditimpa nanti; !important tidak. */
.hstbl .tbl .hswhen{white-space:nowrap;width:1%;color:var(--lg-tx4);
  font-family:var(--fm);font-size:12px}
.hstbl .tbl .hswhat{color:var(--lg-tx);font-weight:400}

/* Chip status. Tujuh warna dari artboard 109:17717 "Status Type", dipasangkan
   lewat kelas yang dikirim server (lib/hiring.js) supaya warnanya diputuskan
   di satu tempat saja. */
/* Ukurannya dari lembar "Status Type" yang Joe kirim 19 September: chipnya
   jauh lebih besar daripada 10px/padding 4 yang terpasang sebelumnya —
   tulisannya seukuran tabelnya sendiri, bukan huruf kaki.

   WARNA TULISANNYA DINAIKKAN SATURASINYA, dan itu perbedaan yang sebenarnya
   di lembar itu. Yang lama nyaris putih bernoda warna (#B5FDEC, #FDB5F1,
   #97E1C9 semuanya di atas 90% terang), jadi tujuh status terbaca sebagai
   tujuh chip gelap dengan tulisan putih — warnanya ada di latarnya saja, dan
   latar segelap itu tidak bisa dibedakan sekilas. Sekarang warnanya di
   tulisannya, tempat mata membacanya.

   Dibaca DENGAN MATA dari lembarnya, bukan dari nilai yang diberikan. Kalau
   ada hex pastinya di Figma, tujuh baris ini yang diganti dan tidak ada
   tempat lain. */
.hschip{display:inline-flex;align-items:center;padding:5px 10px;border-radius:6px;
  font-family:var(--fu);font-size:12px;line-height:18px;font-weight:500;white-space:nowrap}
.hs-training {background:#0F2F4F;color:#6BA8E0}
.hs-assigned {background:#10382E;color:#3FD0A0}
.hs-sample   {background:#3D1535;color:#E07AC8}
.hs-review   {background:#26235A;color:#8B90E8}
.hs-rejected {background:#4D0F14;color:#E06B6B}
.hs-probation{background:#3A3A0D;color:#D4D060}
.hs-hired    {background:#0B3B25;color:#3FC98A}

/* Barisan penyaring statusnya dilepas 19 September, dan .hsflt/.hsf ikut
   pergi — tidak ada satu pun yang menggambarnya lagi. Halaman Hiring lama di
   Old Menu punya barisan chipnya sendiri dengan kelas .filt/.chip yang lama,
   jadi ia tidak ikut kehilangan apa pun. */

/* Lima tile, bukan empat. Di bawah 1180 keduanya jadi dua kolom — lima kolom
   pada lebar itu memberi tiap tile 190px, dan "Posts Scheduled" sendirian
   sudah 108px sebelum angkanya. */
/* Dua kelas di selektornya, bukan satu. Aturan ini berdiri SEBELUM .bdtiles
   di berkas ini, dan keduanya berkekhususan sama — jadi yang empat kolom
   menang dan lima tile tergambar dalam empat kolom. Terlihat waktu diukur di
   browser, bukan dari membaca CSS-nya. */
.bdtiles.bdtiles5{grid-template-columns:repeat(5,minmax(0,1fr))}
@media (max-width:1180px){.bdtiles,.bdtiles.bdtiles5{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* "2 active now" di sebelah nama. Kecil dan redup: ia fakta TENTANG orangnya,
   bukan bagian dari namanya. */
.lbnote2{display:block;font-style:normal;font-size:11.5px;line-height:17px;color:var(--lg-tx3)}

/* ---------- Weekly Report ----------  artboard 19 September

   Tidak ada keluarga baru di sini: .bdtiles, .bdcard, .bdhead, .bdtabs dan
   .hstbl semuanya milik papan Leaderboard, dan artboard menggambar kartu yang
   sama persis di kedua layar. Yang di bawah ini cuma yang memang tidak ada di
   sana — delta, grafik, dan pasangan dua kartu. */
.wrd{margin-left:10px;font-size:12px;line-height:18px;font-weight:500;color:#3FC98A;white-space:nowrap}
.wrdn{color:var(--lg-error)}
.wrsub{display:block;margin-top:2px;font-size:11.5px;line-height:17px;color:var(--lg-tx3)}
/* Dua kartu berdampingan, satu kolom begitu tidak muat. 1100 bukan angka
   selera: tabel Dropped Information punya tiga kolom teks dan yang tengahnya
   kalimat bebas, dan di bawah itu keduanya mulai memotong judul. */
.wrpair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px;margin-top:24px}
.wrpair .bdcard{margin-top:0}
@media (max-width:1100px){.wrpair{grid-template-columns:1fr}}
.wrnone{padding:28px 4px;text-align:center;font-size:13px;line-height:20px;color:var(--lg-tx3)}
.wrlegend{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-bottom:14px}
.wrlg{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;color:var(--lg-tx2)}
.wrlg i{width:8px;height:8px;border-radius:50%;flex:none}
/* width:100% dengan viewBox tetap dan preserveAspectRatio bawaan: SVG-nya
   berskala utuh, jadi garisnya tidak menebal ke satu arah waktu kartunya
   melebar. height:auto supaya rasionya yang menentukan tingginya. */
.wrsvg{display:block;width:100%;height:auto;overflow:visible}
.wrline{fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.wrgrid{stroke:var(--lg-glass07);stroke-width:1}
.wraxis{font-family:var(--fu);font-size:11px;fill:var(--lg-tx3)}
.wrdrop{color:var(--lg-error)}

/* ---------- Manage Role ----------  artboard 19 September

   Tabelnya .hstbl yang sama dengan Hiring, dan itu bukan kemalasan: kedua
   layar menggambar tabel dengan kepala, nomor baris, dan kaki halaman yang
   sama persis di artboardnya. Dua gaya tabel di aplikasi yang sama terbaca
   seperti salah satunya belum selesai. */
.mrbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.mrbar .lbseg{margin-bottom:0}
.mrname{display:flex;align-items:center;gap:10px;min-width:0}
.mrname .ovface{position:static;margin:0}
.mrname .hsn{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Angka bawaan, digambar redup. Sel kosong di kolom bernama Claim Limit
   terbaca sebagai batas yang hilang; angka redup terbaca sebagai batas yang
   belum disetel, yang memang keadaannya. */
.mrdef{color:var(--lg-tx4)}
/* Kolomnya boleh melar dan membungkus: daftar akses sebuah peran admin
   enam belas nama panjang, dan memaksanya satu baris akan mendorong pensil di
   ujungnya keluar layar. */
.mracc{color:var(--lg-tx2);font-size:12.5px;line-height:19px;min-width:240px}
.mrpen{width:48px;text-align:right}
.mrpen .idwasi{margin-left:auto}
.mrtgt{display:flex;align-items:center;gap:10px}
.mrtgt input{flex:1;min-width:0}
.mrtgt span{font-family:var(--fm);font-size:12px;color:var(--lg-tx3);flex:none}
/* Kaki dialognya membawa DUA kelompok: yang merusak di kiri, yang menyimpan di
   kanan. Reset Password dan Remove ditaruh sejauh mungkin dari Save, karena
   keduanya tidak bisa dibatalkan dan Save bisa. */
.mrfoot{margin-top:24px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.mrdel{color:var(--lg-error)}
.mrdel:hover{background:rgba(255,107,107,.12);color:#FF8F8F}

/* Kaki tabel, artboard 190:1725. */
.hspag{display:flex;align-items:center;gap:10px;margin-top:16px;flex-wrap:wrap}
.hspl{font-family:var(--fu);font-size:11px;line-height:13px;color:var(--lg-tx3)}
.hsper{height:30px;width:auto;min-width:61px;padding:0 8px;border-radius:8px;cursor:pointer;
  border:0;box-shadow:inset 0 0 0 1px var(--lg-glass09);background:var(--lg-glass04);
  color:var(--lg-tx2);font-family:var(--fu);font-size:11px;font-weight:500}
.hsper:focus{outline:0;box-shadow:inset 0 0 0 1px var(--lg-focus)}
/* Kalimat "11–20 of 34" mengambil sisa baris, jadi angka halaman terdorong ke
   kanan seperti di artboard — dan ia menjawab yang tidak dijawab nomor
   halaman: berapa dari berapa yang sedang dilihat. */
.hspmid{flex:1;text-align:center;font-family:var(--fm);font-size:11px;color:var(--lg-tx4)}
.hspb{height:22px;padding:0 6px;border:0;border-radius:6px;background:none;cursor:pointer;
  font-family:var(--fu);font-size:11px;font-weight:500;color:var(--lg-tx2)}
.hspb:hover:not(:disabled){color:var(--lg-tx);background:var(--lg-glass06)}
.hspb:disabled{color:var(--lg-tx4);cursor:not-allowed}
.hspn{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:21px;
  padding:0 10px;border-radius:6px;background:var(--lg-blue);color:#fff;
  font-family:var(--fu);font-size:11px;font-weight:500}

@media (max-width:700px){
  .hscard{padding:14px 12px}
  .hsnum{display:none}
  .hspmid{flex:1 0 100%;text-align:left;order:9}
}

/* --- dialog Hiring: Manage Positions dan Add Candidate --- */

/* Kartu sedang, untuk dialog dua kolom. .psmod-sm 540 terlalu sempit untuk
   sepuluh isian berpasangan. */
.ov:has(.psmod-md) .mod{width:640px}
.psmod-md .lgf+.lgf{margin-top:0}
/* Dua kolom, dan isian yang panjang mengambil lebarnya sendiri. */
.lgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
@media (max-width:640px){.lgrid{grid-template-columns:1fr}}

/* Baris posisi: isian dengan silang di kanannya. */
.hprow{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.hprow input{flex:1;min-width:0;height:48px;padding:12px 16px;border:1px solid transparent;
  border-radius:8px;background:var(--lg-field);color:var(--lg-tx);
  font-family:var(--fu);font-size:16px;line-height:24px}
.hprow input::placeholder{color:var(--lg-tx3)}
.hprow input:focus{outline:0;border-color:var(--lg-focus)}
.hpx{width:40px;height:40px;flex:none;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:8px;background:none;cursor:pointer;color:var(--lg-tx3)}
.hpx:hover{color:var(--lg-error);background:var(--lg-glass06)}
.hpx svg{width:16px;height:16px;stroke:currentColor;stroke-width:2;fill:none}
.hplink{border:0;background:none;padding:0;cursor:pointer;
  font-family:var(--fu);font-size:14px;font-weight:500;color:var(--lg-blue-h)}
.hplink:hover{color:#8FB2FF;text-decoration:underline}

/* --- pemilih satu nilai dengan pencarian (110:19817) ---
   Panel 320 #121418 radius 16, pencarian 37 #212429 radius 8, baris 45 radius
   12, yang terpilih #19264D. Tanpa kotak centang: ini pemilih SATU nilai, dan
   kotak centang menjanjikan bahwa dua bisa dinyalakan sekaligus. */
.selw{position:relative}
.selb{width:100%;height:48px;display:flex;align-items:center;gap:10px;
  padding:0 14px 0 16px;border:1px solid transparent;border-radius:8px;cursor:pointer;
  background:var(--lg-field);color:var(--lg-tx);
  font-family:var(--fu);font-size:16px;line-height:24px;text-align:left}
.selw.open .selb{border-color:var(--lg-focus)}
.sell{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.seli{width:14px;height:14px;flex:none;fill:var(--lg-tx3);transition:transform .12s}
.selw.open .seli{transform:rotate(180deg)}
/* fixed, bukan absolute: .psmod membawa overflow:auto dan wadah gulir memotong
   keturunan absolutnya. Koordinatnya dihitung selPlace(). */
.selp{display:none;position:fixed;z-index:200;min-width:260px;
  background:#121418;border-radius:16px;overflow:hidden;
  box-shadow:inset 0 0 0 1px var(--lg-glass09),0 20px 56px -28px rgba(0,0,0,.8)}
.selw.open .selp{display:block}
/* Arah balik dan koordinatnya dipasang selPlace() sebagai gaya inline. */
/* Kotak pencarian panel ini adalah <label> yang berisi <input>, dan keduanya
   berdiri DI DALAM .lgf — sebuah field berlabel di dialog. Dua aturan di sana
   mengalahkannya kalau ditulis dengan satu kelas saja:

     .lgf label            display:block   (0,2,0)  lawan .selqb   (0,1,0)
     .psmod .lgf input     height:48 pad   (0,2,1)  lawan .selqb input (0,1,1)

   Akibatnya terukur: .selqb menghitung display:block sehingga ikon dan kotak
   ketiknya bertumpuk, dan inputnya jadi 48 tinggi dengan isian #212429 dan
   sudut membulat sendiri di dalam kotak setinggi 37 — sebuah field di dalam
   field, meluber menutupi baris pertama daftarnya.

   Jadi keduanya ditulis dengan .selw di depan, dan tiap properti yang dipasang
   kedua aturan itu dikembalikan satu per satu. Mewarisi diam-diam dari aturan
   yang tidak ditujukan untuk komponen ini adalah cara kerusakan seperti ini
   datang kembali. */
.selq{padding:12px 12px 0}
.selw .selqb{display:flex;align-items:center;gap:6px;height:37px;margin:0;padding:0 12px;
  border-radius:8px;background:var(--lg-field);
  font-family:var(--fu);font-size:14px;font-weight:400;letter-spacing:0;text-transform:none}
.selqi{width:16px;height:16px;flex:none;fill:var(--lg-tx2)}
.selw .selqb input{flex:1;min-width:0;width:auto;height:auto;margin:0;padding:0;
  border:0;border-radius:0;background:none;outline:0;box-shadow:none;color:var(--lg-tx);
  font-family:var(--fu);font-size:14px;line-height:21px;font-weight:400}
.selw .selqb input:focus{border:0;box-shadow:none;outline:0}
.selw .selqb input::placeholder{color:var(--lg-tx3)}
.sell2{padding:12px;display:flex;flex-direction:column;gap:4px;max-height:264px;overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:#34363A transparent}
.sell2::-webkit-scrollbar{width:3px}
.sell2::-webkit-scrollbar-thumb{background:#34363A;border-radius:100px}
.seli2{display:flex;align-items:center;gap:8px;min-height:45px;padding:12px;border:0;border-radius:12px;
  cursor:pointer;background:none;text-align:left;
  font-family:var(--fu);font-size:14px;line-height:21px;font-weight:500;color:var(--lg-tx1,#EAEDF5)}
.seli2 > span{flex:1;min-width:0}
.seli2:hover{background:var(--lg-glass06)}
.seli2.on{background:#19264D;color:#fff}
.selt{width:14px;height:14px;flex:none;fill:#fff}
.selnone{padding:16px 12px;font-size:13px;color:var(--lg-tx3)}

/* --- catatan kandidat: judul bagian, kotak centang, dan kaki tiga tombol --- */

/* Judul bagian. .docsl lama memakai palet lama; ini versinya di design system,
   dengan garis tipis di bawahnya supaya empat bagian terbaca sebagai empat
   dan bukan sebagai enam belas isian berurut. */
.pssec{margin:24px 0 16px;padding-bottom:10px;border-bottom:1px solid var(--lg-glass07);
  font-family:var(--fu);font-size:13px;line-height:20px;font-weight:600;color:var(--lg-tx)}
.pssec:first-of-type{margin-top:0}
.pssub{margin-top:4px;font-size:13px;line-height:20px;color:var(--lg-tx3)}
.ps-head h2+.pssub{margin-top:4px}

/* Satu isian yang memakai kedua kolom. */
.lgf.wide{grid-column:1/-1}

/* Tombol "Move to X" di bawah Status. Bukan .btn-s hijau dari palet lama:
   memindahkan seseorang satu tahap adalah hal yang paling sering dilakukan di
   dialog ini, jadi ia tombol kaca tenang yang duduk rapat di bawah isiannya. */
.hsnext{display:inline-flex;align-items:center;gap:7px;margin-top:10px;height:36px;padding:0 14px;
  border:0;border-radius:8px;cursor:pointer;background:var(--lg-glass06);color:var(--lg-tx2);
  font-family:var(--fu);font-size:12.5px;font-weight:500;
  box-shadow:inset 0 0 0 1px var(--lg-glass09)}
.hsnext:hover{background:var(--lg-glass16);color:var(--lg-tx)}
.hsnext svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none}

/* Kotak centang. Bawaan browser digambar dengan aksen sistem operasi dan tidak
   mengikuti apa pun di sini, jadi yang asli disembunyikan dan kotaknya
   digambar sendiri — 18x18 radius 4, terisi #1454DB waktu dicentang, persis
   ukuran dan warna Check di komponen Selection Menu (110:19875). */
.pscheck{display:flex;align-items:center;gap:10px;min-height:48px;cursor:pointer;
  font-family:var(--fu);font-size:14px;line-height:21px;font-weight:400;color:var(--lg-tx2);
  letter-spacing:0;text-transform:none;margin:0}
.pscheck input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.pscheck span{position:relative;padding-left:28px}
.pscheck span::before{content:'';position:absolute;left:0;top:50%;margin-top:-9px;
  width:18px;height:18px;border-radius:4px;background:var(--lg-field);
  box-shadow:inset 0 0 0 1px var(--lg-glass16)}
.pscheck span::after{content:'';position:absolute;left:5px;top:50%;margin-top:-4px;
  width:8px;height:4px;border-left:2px solid #fff;border-bottom:2px solid #fff;
  transform:rotate(-45deg);opacity:0}
.pscheck input:checked+span::before{background:var(--lg-blue);box-shadow:none}
.pscheck input:checked+span::after{opacity:1}
.pscheck input:focus-visible+span::before{box-shadow:0 0 0 2px var(--lg-focus)}
.pscheck:hover span::before{box-shadow:inset 0 0 0 1px var(--lg-glass26,rgba(255,255,255,.26))}

/* Kaki dengan Delete di kiri dan Save di kanan. */
.psfoot3{flex-wrap:wrap}
.psfoot3 .lgbtn2,.psfoot3 .lgbtn{width:auto;min-width:120px;flex:none}
.psgap{flex:1;min-width:0}
.psdel{color:var(--lg-error);box-shadow:inset 0 0 0 1px rgba(255,107,107,.3)}
.psdel:hover{background:rgba(255,107,107,.12);color:#FF8F8F}
.psdel svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none}
@media (max-width:640px){
  .psfoot3 .lgbtn2,.psfoot3 .lgbtn{flex:1 1 100%}
  .psgap{display:none}
}

/* ===========================================================================
   FE — Leaderboard   (artboard 109:16562)

   Kartu dan tabelnya menumpang .hscard / .hstbl dari layar Hiring — dua layar
   yang menggambar tabel harus menggambarnya sama. Yang ditulis di sini cuma
   baris jendela, tile, tab, dan tiga sel yang punya warnanya sendiri.
   =========================================================================== */

.bdbar{align-items:center}
/* Jendelanya MEMBACA dan bukan memilih, jadi ia bukan tombol: papan ini sengaja
   selalu minggu ini, dan kotak yang terlihat bisa ditekan tapi tidak bisa
   adalah kontrol mati. */
/* Sejak 2026-09-15 jendelanya bisa dipilih, bukan cuma dibaca — jadi ia
   pemilih, dengan panel dan pencarian yang sama dengan layar Hiring. */
.bdweekw{width:296px;max-width:100%}
.bdrefresh{margin-left:auto;height:32px;padding:0 12px;border:0;border-radius:8px;background:none;
  cursor:pointer;font-family:var(--fu);font-size:13px;font-weight:500;color:#8FB3FF}
.bdrefresh:hover:not(:disabled){background:var(--lg-glass06)}
.bdrefresh:disabled{color:var(--lg-tx4);cursor:not-allowed}

/* Lima tile di artboard, empat di sini. Yang kelima — Posts Scheduled — tidak
   punya datanya di mana pun: Metricool yang menjadwalkan, dan kita tidak
   menyimpan jadwalnya. Kotak kosong akan terbaca sebagai angka yang hilang. */
.bdtiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-bottom:24px}
.bdtile{display:flex;align-items:center;gap:16px;min-height:84px;padding:16px;border-radius:12px;
  background:var(--lg-glass04);box-shadow:inset 0 0 0 1px var(--lg-glass07)}
.bdti{width:52px;height:52px;flex:none;display:flex;align-items:center;justify-content:center;
  border-radius:10px;background:rgba(20,84,219,.18)}
.bdtii{width:24px;height:24px;fill:#8FB3FF}
.bdtx{display:flex;flex-direction:column;gap:8px;min-width:0}
.bdtl{font-size:12.5px;line-height:15px;color:var(--lg-tx2)}
.bdtv{font-family:var(--fd,var(--fu));font-size:24px;line-height:29px;font-weight:700;color:var(--lg-tx)}
@media (max-width:1100px){.bdtiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.bdtiles{grid-template-columns:1fr}}

.bdcard+.bdcard{margin-top:24px}
.bdhead{display:flex;align-items:center;gap:8px;margin-bottom:20px}
.bdhead h3{flex:1;min-width:0;margin:0;font-size:16px;line-height:19px;font-weight:600;color:var(--lg-tx)}
.bddl{width:28px;height:28px;flex:none;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:8px;background:none;cursor:pointer;color:var(--lg-tx2)}
.bddl:hover{background:var(--lg-glass06);color:var(--lg-tx)}
.bddli{width:16px;height:16px;fill:currentColor}

.bdtabs{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:20px}
.bdtab{height:34px;padding:0 16px;border:0;border-radius:8px;cursor:pointer;
  background:var(--lg-glass04);color:var(--lg-tx2);
  font-family:var(--fu);font-size:12px;font-weight:500;white-space:nowrap;
  box-shadow:inset 0 0 0 1px var(--lg-glass07)}
.bdtab:hover{background:var(--lg-glass09);color:var(--lg-tx)}
.bdtab.on{background:var(--lg-blue);color:#fff;box-shadow:none}
/* Tautan ke Views Contest, bukan tab keempat: ia berpindah halaman, dan
   menggambarnya sama dengan tiga yang lain akan menjanjikan ia menukar isi
   kartu ini. */
.bdgo{color:#8FB3FF;background:none;box-shadow:none}
.bdgo:hover{background:var(--lg-glass06);color:#8FB3FF}

.bdtbl .tbl thead th{white-space:nowrap}
.bdrank{white-space:nowrap;color:var(--lg-tx2)}
.bdtbl .medal{font-size:13px}
/* Juara bertahan juara di halaman mana pun ia tergambar — barisnya diberi
   cincin, bukan isian, supaya ia tidak bertengkar dengan hover. */
.bdtop td{box-shadow:inset 0 1px 0 rgba(143,178,255,.22),inset 0 -1px 0 rgba(143,178,255,.22)}
.bdtop td:first-child{box-shadow:inset 1px 1px 0 rgba(143,178,255,.22),inset 0 -1px 0 rgba(143,178,255,.22)}
.bdtop td:last-child{box-shadow:inset -1px 1px 0 rgba(143,178,255,.22),inset 0 -1px 0 rgba(143,178,255,.22)}
.bdwho{display:flex;align-items:center;gap:10px;min-width:0}
.bdnote{font-size:11px;color:var(--lg-tx4)}
.bdtbl .bdviews{color:#8FB3FF}
.bdtbl .bdscore{color:var(--lg-tx);font-weight:700}
.bdtbl .bddim{color:var(--lg-tx3)}

/* ===========================================================================
   FE — Bug Reports   (artboard 126:4108, popup 126:4334)

   Artboard menulis "My Feedback" di kepala layar ini — salah tempel dari layar
   sebelahnya. Halaman ini Bug Reports, dan PAGE_TITLES memang sudah benar.
   =========================================================================== */

.bgbar{align-items:center;gap:16px}
.bgbar .lbseg{margin-bottom:0}
.bgbar .iqright{margin-left:auto}

.bgcard{background:#121418;border-radius:12px;padding:20px;margin-bottom:16px;
  box-shadow:inset 0 0 0 1px var(--lg-glass07)}
.bghead{display:flex;align-items:flex-start;gap:12px}
.bghead .av{width:32px;height:32px;flex:none;font-size:12px}
.bgwho{flex:1;min-width:0}
.bgt{font-size:15px;line-height:20px;font-weight:600;color:var(--lg-tx)}
.bgm{margin-top:3px;font-size:12px;line-height:18px;color:#8FB3FF}
.bgright{flex:none;display:flex;align-items:center;gap:12px}
.bgup{font-size:12px;line-height:18px;color:var(--lg-tx3);white-space:nowrap}
.bgchip{display:inline-flex;align-items:center;padding:4px 8px;border-radius:4px;
  font-family:var(--fu);font-size:10px;line-height:15px;font-weight:500;white-space:nowrap}
.bg-open    {background:#4C360A;color:#F9DA94}
.bg-resolved{background:#0C3B2C;color:#97E1C9}
.bg-rejected{background:#5C1319;color:#FFB0B0}

.bgbody{margin-top:14px;font-size:14px;line-height:21px;color:var(--lg-tx);white-space:pre-wrap}
/* Screenshot ditunjukkan, bukan ditautkan. Screenshot yang harus dibuka di tab
   baru untuk dilihat sudah setengah jalan menuju tidak terlampir. */
.bgshots{margin-top:14px;display:flex;flex-direction:column;gap:10px}
.bgshots img{max-width:100%;max-height:360px;border-radius:8px;display:block;
  background:#000;box-shadow:inset 0 0 0 1px var(--lg-glass07)}
.bgfiles{margin-top:12px;display:flex;flex-wrap:wrap;gap:8px}
.bgfile{display:inline-flex;align-items:center;gap:8px;height:34px;padding:0 12px;border-radius:8px;
  background:var(--lg-glass06);color:var(--lg-tx2);text-decoration:none;font-size:12.5px}
.bgfile:hover{background:var(--lg-glass16);color:var(--lg-tx)}
.bgfi{width:14px;height:14px;flex:none;fill:currentColor}
.bgnote{margin-top:12px}

.bgacts{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-top:16px;padding-top:16px;border-top:1px solid var(--lg-glass07)}
/* Delete tidak digambar artboard; ia ada di layar lama dan Joe memintanya
   tetap ada. Ikon, di ujung kanan, jauh dari Resolved — tombol berlabel
   berdampingan berjarak sama adalah cara laporan terhapus oleh tangan yang
   sedang menyelesaikannya. */
.bgdel{margin-left:auto;width:34px;padding:0;justify-content:center;color:var(--lg-tx3)}
.bgdel:hover{background:rgba(255,107,107,.12);color:var(--lg-error)}
.bgdi{width:15px;height:15px;fill:currentColor}

/* Zona jatuh-dan-tempel di dalam kartu .psmod.

   .lgf .bgdrop, bukan .bgdrop telanjang: ia sebuah <label> di dalam sebuah
   field berlabel, dan `.lgf label{display:block;margin-bottom:6px}` menang
   spesifisitas atas satu kelas. Jebakan yang sama sudah tercatat di .selqb —
   ini kejadian keduanya, dan keduanya berakhir dengan kontrol yang tergambar
   rata kiri di dalam kotak yang seharusnya memusatkannya. */
.bgzone{display:flex;flex-direction:column;gap:10px}
.lgf .bgdrop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  margin:0;padding:20px 16px;min-height:104px;cursor:pointer;
  border:1px dashed var(--lg-glass16);border-radius:8px;background:var(--lg-field);
  font-family:var(--fu);font-size:14px;font-weight:500;color:var(--lg-tx2);
  letter-spacing:0;text-transform:none;text-align:center}
.lgf .bgdrop:hover{border-color:var(--lg-focus);color:var(--lg-tx)}
.bgdrop svg{width:20px;height:20px;stroke:currentColor;stroke-width:1.8;fill:none}
.bgdt{font-size:14px;line-height:21px;font-weight:500}
.bgdh{font-size:12px;line-height:18px;font-weight:400;color:var(--lg-tx3);max-width:36ch}
/* Kelasnya `dragover`, bukan `over` — itu yang dipasang penangan drop global
   di public/app.js, dan menebak namanya berarti zona yang tidak pernah
   menyala waktu sesuatu diseret di atasnya. */
.bgzone.dragover{outline:2px dashed var(--lg-focus);outline-offset:4px;border-radius:8px}
.bgatts{display:flex;flex-direction:column;gap:8px}
/* Halaman lapor untuk yang bukan admin: kartu yang sama, tanpa hamparan. */
.bgpage{max-width:640px}

/* ===========================================================================
   FE — Idea Pool   (artboard 94:20621 / 94:21848 / 94:22463)

   Dua kolom berdampingan, gap 16, masing-masing 364 lebar. Kolomnya bukan
   kartu berisi: fills-nya ada di file Figma tapi visible:false, jadi yang
   menggambarnya hanya garis putih 7% dan bayangan — sama seperti .iqcard.

   Kartunya SATU-SATUNYA di revamp ini yang punya isian sendiri (#181B1F),
   karena ia duduk di atas kolom dan bukan di atas latar halaman. Tanpa isian
   itu kartu dan kolom jadi dua persegi panjang bergaris tanpa beda kedalaman.

   .ovcode, .ovchip, .ovdates, .ovface dan .ovbtn dipakai apa adanya — nilainya
   sudah sama persis dengan yang digambar artboard ini, dan menyalinnya jadi
   keluarga kelas kedua adalah cara termudah membuat dua kartu yang mirip
   berbeda pelan-pelan.
   =========================================================================== */
/* Baris halaman ini. Kolom dan kartunya memakai .ovcol / .ovtask milik
   Overview apa adanya — lihat catatan di ipCol() — jadi yang ditulis di sini
   hanya barisnya, dua penimpa yang memang berbeda, dan aturan tinggi penuh. */
.ipcols{display:flex;align-items:stretch;gap:16px;flex-wrap:wrap}
/* 364 dan berhenti di situ, tidak melebar.

   Artboard menggambar dua kolom 364 di dalam baris 1116 dan membiarkan sisa
   kanannya kosong — bukan kelalaian: kartu setinggi 198 yang direntangkan jadi
   550 lebar berubah bentuk, dan judul satu barisnya mulai berenang di ruang
   kosong. Jadi flex-grow 0, dan yang melar adalah halamannya.

   flex-shrink tetap 1 dengan max-width 364: di layar yang lebih sempit dari
   dua kolom, kolom kedua turun ke bawah (flex-wrap) alih-alih memeras
   kartunya sampai bergerigi. */
.ipcols>.ovcol{flex:0 1 364px;max-width:364px;min-width:0}
/* Dua yang berbeda dari Overview, dan hanya dua. Artboard Idea Pool menggambar
   judul kartu 16/19; Overview menggambarnya 14/17, dan itu bukan kekeliruan di
   salah satunya — kartu Overview duduk di kolom yang lebih sempit. */
.ipcols .ovttl{font-size:16px;line-height:19px}
/* Dua baris dipesan penuh, aturan yang sama yang meratakan kartu Idea QC. Di
   dua kolom berdampingan ia lebih penting lagi: kartu yang tingginya
   berbeda-beda membuat dua daftar terbaca sebagai dua hal yang tak
   berhubungan. */
.ipcols .ovtsub{min-height:30px}
/* Kartu di sini tidak membuka apa pun waktu ditekan — .ovtask membawa
   cursor:pointer karena kartu Overview memang menuju halaman videonya. Sampai
   halaman ini punya tujuan klik yang diputuskan, penunjuknya tidak boleh
   menjanjikan satu. */
/* Kartu di kedua kolam membuka laci sekarang, jadi penunjuk tangan dan angkat
   hover-nya memang pada tempatnya. Dua baris yang dulu di sini mematikan
   keduanya, ditulis waktu kartu Idea Pool belum menuju ke mana pun — dan
   tertinggal sesudah ia menuju. */

/* Kotaknya sampai ke dasar layar, dan yang menggulir ada DI DALAMNYA.

   Pola yang sama persis dengan Overview — lihat blok @media di atas .ovrow —
   dan disalin polanya, bukan disalin perilakunya: halaman jadi kolom flex,
   toolbar memakai tingginya sendiri, baris kolom mengambil sisanya, dan daftar
   di dalam tiap kolom yang overflow.

   Ambangnya 1060 dan bukan 1181 seperti Overview, karena yang ditunggu memang
   berbeda: halaman ini butuh dua kolom 364 plus gap 16 = 744, ditambah sidebar
   268 dan padding 48 — jadi di bawah 1060 kolom keduanya turun ke baris kedua,
   dan flex:1 akan membagi sisa tinggi ke DUA baris. Di lebar itu yang benar
   adalah perilaku biasa: kolom setinggi isinya, halaman yang menggulir.

   min-height 360 adalah jalan mundur kedua, sama seperti di Overview: di
   jendela yang terlalu pendek .pg kembali menggulir alih-alih memampatkan
   kolomnya sampai tidak terbaca. */
@media (min-width:1060px){
  /* padding-bottom nol: jarak ke tepi jendela sudah diberikan #app, dan
     menambah 16 lagi di sini membuat kolomnya berhenti 16px lebih tinggi
     daripada sidebar di sebelahnya — dua tepi bawah yang meleset terbaca
     sebagai kotak yang kurang panjang, bukan sebagai jarak. */
  .pg:has(.ipcols){display:flex;flex-direction:column;padding-bottom:0}
  .pg:has(.ipcols) .iqbar{flex:none}
  .pg:has(.ipcols) .ipcols{flex:1;min-height:360px;margin-bottom:0}
  .pg:has(.ipcols)>.ipcols>.ovcol{max-height:100%;overflow:hidden}
  .pg:has(.ipcols) .ovitems{overflow-y:auto;min-height:0}
  /* Kolom kosong ikut memenuhi tingginya dan pesannya duduk di tengah, bukan
     mengambang di puncak kotak setinggi layar. */
  .pg:has(.ipcols) .ovitems:has(.ovempty){flex:1;display:flex;align-items:center;justify-content:center}
}

/* Ringkasan ide di kepala dialog Claim / Assign To. */
.ipdsum{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--lg-glass07)}
.ipdsl{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.ipdcode{font-size:13px;line-height:16px;font-weight:600;color:var(--lg-link)}
.ipdttl{font-size:16px;line-height:24px;font-weight:500;color:var(--lg-tx)}
.ipdnote{font-size:12px;line-height:18px;color:var(--lg-tx3);margin-bottom:16px}
.ipreq{color:var(--lg-error);margin-left:2px}
/* Kotak "belum ada rutenya". Amber, bukan merah: tidak ada yang rusak — yang
   ada adalah sesuatu yang belum dibangun, dan dua warna itu mengatakan hal
   yang berbeda kepada orang yang membacanya. */
.ipdwarn{display:flex;align-items:flex-start;gap:8px;margin-top:4px;padding:10px 12px;
  border-radius:8px;border:1px solid rgba(245,158,11,.28);background:rgba(245,158,11,.08);
  font-size:12px;line-height:18px;color:#F0B429}
.ipdwarn svg{width:14px;height:14px;flex:none;margin-top:2px;fill:currentColor}

/* ---- Script Pool ----
   Komponennya milik Idea Pool dan Overview; yang berbeda hanya dua baris di
   kaki kartu yang digambar dua mock Joe. */
/* .spupd, .spfoot dan .spasg dihapus 17 September: baris "Last Updated" dan
   baris "Assign to X" keduanya dilepas, dan nama yang dibawa baris kedua itu
   sekarang dibawa avatarnya di kanan tanggal — bentuk yang sama dengan kartu
   Idea Pool, supaya dua kolom berdampingan tidak berbicara dua bahasa. */
