/* Tema TERANG untuk seluruh kerangka aplikasi.
 *
 * Sampai Milestone 44 halaman ini gelap seluruhnya, termasuk kanvasnya. ETABS terang, dan
 * perencana yang membandingkan dua program bolak-balik sepanjang hari membaca dua tingkat
 * kecerahan yang berlawanan — melelahkan, dan tangkapan layarnya tidak bisa ditaruh
 * berdampingan di satu laporan.
 *
 * Latar KANVAS-nya sendiri tetap bisa dipilih terang atau gelap: sebagian orang membaca
 * geometri lebih baik di atas gelap, dan itu pilihan yang sah. Yang tidak lagi bisa
 * dipilih hanya kerangkanya — menyediakan dua tema penuh berarti dua palet yang keduanya
 * harus tetap terbaca di setiap panel, dan yang kedua akan membusuk tanpa ada yang tahu.
 */
:root {
  --bg: #f4f6f8;
  --panel: #ffffff;
  --line: #ccd4dc;
  --text: #14181c;
  --muted: #5c6873;
  --accent: #1a6fb5;
  --warn: #b26a00;
  --error: #c62828;
  /* Isian kendali dan sorotannya. Dulu ditulis langsung di dua belas tempat, dan
     dua belas tempat itu tetap gelap ketika temanya berganti. */
  --isian: #eef1f4;
  --isian-terpilih: #d8e6f3;
  --isian-redup: #e7ebef;
  color-scheme: light;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 "Segoe UI", system-ui, sans-serif;
}

header {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 0.35rem 1rem;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}

.merek {
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

.merek b {
  color: var(--accent);
}

h2 {
  margin: 0 0 0.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

main {
  flex: 1;
  display: flex;
  min-height: 0;
}

aside {
  flex-shrink: 0;
  overflow-y: auto;
  padding: 1rem;
  background: var(--panel);
}

#sidebar {
  width: 20rem;
  border-right: 1px solid var(--line);
}

#properti {
  width: 15rem;
  border-left: 1px solid var(--line);
  transition: width 0.18s ease;
}

/* Panel properti yang dilipat menyusut ke pita tegak, bukan lenyap.

   Sasaran kliknya karena itu tetap di tempat yang sama di kedua keadaan, dan tidak ada
   tombol mengambang yang menimpa gambar. */
body.properti-tertutup #properti {
  width: 1.9rem;
  padding-left: 0;
  padding-right: 0;
  overflow: hidden;
}

body.properti-tertutup #properti-isi {
  display: none;
}

.properti-alih {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  width: 100%;
  margin: 0 0 0.5rem;
  padding: 0.15rem 0;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}

.properti-alih:hover {
  color: var(--text);
}

.properti-panah {
  transition: transform 0.18s ease;
}

body.properti-tertutup .properti-alih {
  /* Diputar, bukan diganti tombol kedua: dua tombol untuk satu perintah berarti dua
     tempat yang bisa menyimpang, dan yang tersembunyi tidak akan ketahuan rusak. */
  writing-mode: vertical-rl;
  justify-content: flex-start;
  height: 100%;
  margin: 0;
  padding: 0.5rem 0;
}

body.properti-tertutup .properti-panah {
  transform: rotate(180deg);
}

/* PANEL SINGLE MEMBER IKUT BERGESER, dan ini kaitan yang paling mudah terlewat.

   `.mengambang` berada DI LUAR `#viewport`, sehingga `right`-nya mengukur dari tepi
   layar — angkanya dipatok mengikuti lebar panel properti. Melipat panel tanpa
   menyesuaikannya meninggalkan celah selebar 15rem yang terbaca sebagai kerusakan tata
   letak, bukan sebagai panel yang tertutup. */
body.properti-tertutup .mengambang {
  right: 2.9rem;
}

section + section {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

#viewport {
  flex: 1;
  min-width: 0;
  position: relative;
}

canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Kursor menuntun, bukan kalimat — seperti ETABS. Disetel `Editor._pasangKursor()`;
   baris status tidak lagi menulis "klik titik berikutnya …" (keputusan 2026-09-11). */
canvas[data-kursor="gambar"] {
  cursor: crosshair;
}

canvas[data-kursor="zoom"] {
  cursor: zoom-in;
}

button,
select,
input[type="file"] {
  width: 100%;
  margin-top: 0.5rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--isian);
  color: inherit;
  font: inherit;
}

button {
  cursor: pointer;
}

button:hover:not(:disabled) {
  border-color: var(--accent);
}

/*
  Tombol nonaktif harus terbaca sebagai TERKUNCI, bukan sebagai tidak ada.

  Sebelumnya hanya `opacity: 0.45`. Di panel gelap, tombol gelap pada opasitas itu nyaris
  lenyap — dan pengguna melaporkannya persis begitu: "pilihan gambarnya tidak muncul".
  Alat yang terkunci karena belum ada model karena itu tampak seperti bug, bukan seperti
  langkah yang belum dilakukan.
*/
button:disabled {
  opacity: 0.7;
  color: var(--muted);
  background: var(--isian-redup);
  border-style: dashed;
  cursor: not-allowed;
}

.petunjuk {
  position: absolute;
  left: 50%;
  top: 1.25rem;
  transform: translateX(-50%);
  z-index: 2;
  max-width: min(42rem, calc(100% - 2rem));
  padding: 0.7rem 1rem;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: rgb(255 255 255 / 94%);
  color: var(--text);
  font-size: 0.86rem;
  line-height: 1.45;
  text-align: center;
  pointer-events: none;
}

.petunjuk strong {
  color: var(--accent);
}

/*
  Status lintas tab, di bar atas.

  KOSONG BERARTI TIDAK TERLIHAT: tanpa `:empty` ia menyisakan ruang kosong yang menggeser
  tata letak bar setiap kali pesannya datang dan pergi.

  Tidak berkotak dan tidak merah. Bentuk sebelumnya — kotak merah melayang di sudut
  viewport — menegur untuk hal yang berhasil dan menutupi model yang sedang dilihat.
  Kegagalan tetap terbaca dari tab mana pun karena bar ini di luar seluruh tab, dan
  `console.error()` tetap menjadi jaring terakhirnya.
*/
.status-global {
  max-width: 28rem;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.78rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.status-global:empty {
  display: none;
}


/*
  Angka beban di atas kanvas.

  Warnanya mengikuti glif bebannya, bukan `--accent`: angka dan panah yang dirujuknya harus
  terbaca sebagai satu hal. Dibedakan dari `.label-nilai` (hasil analisis) karena keduanya
  boleh tampil bersamaan — memeriksa diagram momen sambil melihat beban yang
  menghasilkannya justru pekerjaan yang lazim, dan dua angka sewarna di sana tidak bisa
  dibedakan mana beban mana hasil.
*/
.label-beban {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 0.05rem 0.25rem;
  border-radius: 3px;
  background: rgb(255 255 255 / 88%);
  color: var(--error);
  font-size: 0.64rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}

/*
  Tombol kembali ke tampilan awal, melayang di sudut kiri atas kanvas.

  Di atas kanvas, bukan di sidebar: inilah tombol yang dicari saat layar sudah penuh glif
  dan pengguna kehilangan pijakan — dan saat itu mata sedang di gambar, bukan di panel.

  `width: auto` menimpa aturan `button` global yang membentangkannya selebar induknya.
*/
/*
  DI SUDUT KIRI BAWAH, bukan kiri atas.

  Sudut kiri atas milik tab *Tampak*, dan dua kendali melayang di titik yang sama membuat
  yang di bawah mustahil ditekan — dilaporkan pengguna sebagai "3D view tertimpa tampak".
  Sudut kiri bawah kosong sejak kotak status merah dicabut.
*/
.tampak-awal {
  position: absolute;
  left: 1rem;
  bottom: 1rem;
  z-index: 3;
  width: auto;
  margin-top: 0;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: rgb(255 255 255 / 94%);
  color: var(--muted);
  font-size: 0.76rem;
}

.tampak-awal:hover {
  border-color: var(--accent);
  color: var(--text);
}

/*
  Penunjuk nilai kontur, mengikuti kursor.

  `pointer-events: none` WAJIB: kotak yang mengikuti kursor dan menerima klik akan
  menangkap pointer-nya sendiri, sehingga `pointermove` berikutnya melaporkan kotak itu
  alih-alih pelat di bawahnya — angkanya membeku dan kursor berhenti bisa memilih apa pun.

  Diletakkan dengan offset ke kanan-bawah kursor supaya tidak menutupi titik yang sedang
  ditanya.
*/
.penunjuk-kontur {
  position: absolute;
  z-index: 4;
  padding: 0.28rem 0.55rem;
  border: 1px solid var(--accent);
  border-radius: 5px;
  background: rgb(255 255 255 / 94%);
  color: var(--text);
  font-size: 0.74rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}

.penunjuk-kontur strong {
  color: var(--accent);
  margin-right: 0.3rem;
}

input[type="range"] {
  width: 100%;
  margin-top: 0.5rem;
}

label {
  display: block;
  margin-top: 0.75rem;
  color: var(--muted);
  font-size: 0.8rem;
}

.muted {
  color: var(--muted);
  font-size: 0.8rem;
}

/* Peringatan yang harus terbaca, bukan sekadar tersedia.
   Dipakai untuk batas yang membuat angka di sekitarnya belum boleh dipakai sebagai
   angka desain — teks abu-abu `.muted` terlalu mudah dilewati mata untuk itu. */
.peringatan {
  color: var(--warn);
  border-left: 3px solid var(--warn);
  padding: 0.4rem 0.6rem;
  margin: 0.6rem 0;
  background: rgb(178 106 0 / 10%);
  line-height: 1.45;
}

fieldset {
  margin: 0.75rem 0 0;
  padding: 0.5rem 0.7rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 4px;
}

legend {
  padding: 0 0.35rem;
  color: var(--muted);
  font-size: 0.8rem;
}

dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem 0.75rem;
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
}

dt {
  color: var(--muted);
}

dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

#temuan {
  margin: 0.75rem 0 0;
  padding-left: 1.1rem;
  font-size: 0.8rem;
}

#temuan li.error {
  color: var(--error);
}

#temuan li.warning {
  color: var(--warn);
}

.tabel-bungkus {
  max-height: 16rem;
  overflow: auto;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

th,
td {
  padding: 0.2rem 0.35rem;
  border-bottom: 1px solid var(--line);
  text-align: right;
}

th:first-child,
td:first-child {
  text-align: left;
}

th {
  position: sticky;
  top: 0;
  background: var(--panel);
  color: var(--muted);
  font-weight: 500;
}

input[type="number"],
input[type="checkbox"] {
  margin-top: 0.5rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--isian);
  color: inherit;
  font: inherit;
}

input[type="number"] {
  width: 100%;
}

input[type="checkbox"] {
  width: auto;
  margin: 0 0.4rem 0 0;
  vertical-align: middle;
}

label.sebaris {
  display: flex;
  align-items: center;
  color: var(--text);
  font-size: 0.85rem;
}

.kecil {
  font-size: 0.72rem;
  line-height: 1.4;
}

.legenda {
  margin-top: 0.75rem;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.legenda-kepala {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}

.legenda-kepala strong {
  color: var(--text);
}

.legenda-kepala > span {
  color: var(--muted);
  font-size: 11px;
  white-space: nowrap;
}

.legenda-skala {
  display: grid;
  row-gap: 10px;
}

.legenda-tik {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  position: relative;
  color: var(--text);
  font-size: 10px;
}

.legenda-tik > span {
  position: relative;
  line-height: 1;
  white-space: nowrap;
}

.legenda-tik > span::before {
  content: "";
  position: absolute;
  left: 0;
  top: -7px;
  height: 4px;
  border-left: 1px solid var(--muted);
}

.legenda-tik > span:nth-child(2) {
  text-align: center;
}

.legenda-tik > span:nth-child(2)::before {
  left: 50%;
}

.legenda-tik > span:last-child {
  text-align: right;
}

.legenda-tik > span:last-child::before {
  left: auto;
  right: 0;
}

#legenda-bar {
  height: 14px;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 10%);
  border-radius: 2px;
}

.baris-tiga {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

/* Dua medan sebaris — cacah bagian `n x m` di dialog Divide Shells. */
.baris-dua {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.4rem;
}

.baris-tiga label,
.baris-dua label {
  margin-top: 0.5rem;
  font-size: 0.72rem;
}

.baris-tiga input,
.baris-tiga select,
.baris-dua input,
.baris-dua select {
  width: 100%;
  margin-top: 0.2rem;
}

/* Empat medan sebaris — koordinat garis cermin `X1 Y1 X2 Y2`. */
.baris-empat {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.4rem;
}

.baris-empat label {
  margin-top: 0.5rem;
  font-size: 0.72rem;
}

.baris-empat input {
  width: 100%;
  margin-top: 0.2rem;
}

/* Tab di dalam dialog dimulai dari kiri; `.tab` bawaan didorong ke kanan karena ia
   dirancang untuk bilah tab aplikasi di header. */
#rep-tab {
  margin: 0.6rem 0 0.2rem;
}

.alat {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.35rem;
}

.alat button {
  margin-top: 0.5rem;
  padding: 0.4rem 0.2rem;
  font-size: 0.78rem;
}

/* Seluruh papan alat terkunci: rangka putus-putus menamai keadaannya sekali, alih-alih
   membiarkan lima tombol redup terbaca sebagai lima tombol yang hilang. */
.alat.terkunci {
  outline: 1px dashed var(--line);
  outline-offset: 3px;
  border-radius: 4px;
}

.alat button.aktif {
  border-color: var(--accent);
  background: var(--isian-terpilih);
  color: var(--accent);
}

.galat {
  color: var(--error);
  font-weight: 600;
}


/* ===========================================================================
   Kerangka aplikasi (Milestone 13)
   =========================================================================== */

.menu {
  display: flex;
  gap: 0.15rem;
}

.menu-slot {
  position: relative;
}

.menu > .menu-slot > button,
.tab button {
  width: auto;
  margin: 0;
  padding: 0.3rem 0.7rem;
  border: 1px solid transparent;
  background: transparent;
  font-size: 0.82rem;
  white-space: nowrap;
}

.menu > .menu-slot > button.terbuka {
  border-color: var(--line);
  background: var(--isian);
}

.menu-isi {
  position: absolute;
  top: calc(100% + 0.25rem);
  left: 0;
  z-index: 10;
  min-width: 14rem;
  padding: 0.35rem;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--panel);
  box-shadow: 0 6px 20px rgb(0 0 0 / 14%);
}

.menu-isi button,
.menu-berkas {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0.4rem 0.55rem;
  border: none;
  border-radius: 3px;
  background: transparent;
  color: var(--text);
  text-align: left;
  font-size: 0.82rem;
  cursor: pointer;
}

.menu-isi button:hover:not(:disabled),
.menu-berkas:hover {
  background: var(--isian-terpilih);
}

.menu-berkas input[type="file"] {
  display: none;
}

/* Tab alur kerja: bentuk yang tidak akan berubah lagi. Perancangan penampang SENGAJA
   bukan tab di sini — ia aplikasi tersendiri di rencana produk. */
.tab {
  display: flex;
  gap: 0.15rem;
  margin-left: auto;
}

.tab button.aktif {
  border-color: var(--accent);
  background: var(--isian-terpilih);
  color: var(--accent);
}

#health {
  white-space: nowrap;
}

/* Kisi tombol ala ETABS/SkyCiv: kategori dengan tombol yang membuka dialog. Bentuk ini
   yang sanggup menampung puluhan jenis entitas tanpa sidebar jadi panjang tak terbatas. */
.kisi {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.35rem;
}

/* Kendali berlabel panjang — nama gaya pada End releases, bukan singkatan DOF. */
.kisi.satu-lajur {
  grid-template-columns: 1fr;
}

.kisi button {
  margin-top: 0.35rem;
  padding: 0.45rem 0.3rem;
  font-size: 0.78rem;
}

/* Terkunci DAN dinyatakan. Menyembunyikan fitur yang sudah direncanakan membuat pengguna
   mengira ia tidak pernah direncanakan. */
/* Dialog berada DI ATAS setiap lapisan lain di halaman.
 *
 * `bukaDialog()` sengaja memakai `show()` dan bukan `showModal()`, supaya kanvas tetap
 * hidup di belakangnya — seluruh alur beban Milestone 15 adalah "pilih di kanvas, isi
 * dialog, terapkan", dan dialog modal mematikannya.
 *
 * Harganya: dialog non-modal TIDAK masuk top layer, jadi ia bersaing z-index seperti
 * elemen biasa. Tanpa nilai di bawah ini, panel petunjuk (`z-index: 2`), kotak seleksi,
 * dan lapisan label tergambar MENIMPA isi dialog — dua tulisan bertumpuk yang keduanya
 * terbaca separuh.
 *
 * Angkanya sengaja jauh di atas seluruh lapisan lain, dan ada test statis yang
 * memastikannya tetap yang tertinggi saat lapisan baru ditambahkan. */
dialog {
  z-index: 100;
  max-width: min(30rem, calc(100vw - 2rem));
  max-height: calc(100vh - 6rem);
  padding: 1rem 1.1rem;
  border: 1px solid var(--accent);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
  overflow-y: auto;
  box-shadow: 0 10px 40px rgb(0 0 0 / 18%);
}

dialog h3 {
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  color: var(--accent);
}

/* Judul dialog adalah pegangan geser — satu-satunya bagiannya yang tidak punya pekerjaan
   lain. Menyeret dari mana saja akan menangkap gerakan di dalam kotak teks dan daftar
   pilihan, sehingga menyorot angka untuk menyalinnya justru memindahkan dialognya. */
dialog h3.pegangan {
  cursor: move;
  user-select: none;
}

dialog::backdrop {
  background: transparent;
}

/* Properties of Object — panel gambar.

   Letak bawaannya menempel di sisi KANAN layar, di atas panel properti: pilihan pengguna
   2026-08-20. Ia tetap `<dialog>` supaya ikut bisa digeser seperti yang lain, dan supaya
   tidak ada jalur kedua yang harus dipelihara hanya untuk satu panel. */
dialog.panel-gambar {
  right: 1rem;
  left: auto;
  top: 5rem;
  margin: 0;
  max-width: 19rem;
}

/* Tabel data menampung sampai sembilan kolom; lebar dialog baku memaksanya menggulung
   mendatar sejak kolom keempat. */
dialog.lebar {
  max-width: min(64rem, calc(100vw - 2rem));
}

.tabel-bungkus.lebar {
  max-height: 26rem;
}

.tabel-bungkus input[type="number"],
.tabel-bungkus select {
  width: 100%;
  min-width: 5rem;
  margin: 0;
  padding: 0.15rem 0.25rem;
  font-size: 0.78rem;
}

.tabel-bungkus input[type="checkbox"] {
  margin: 0;
}

.tabel-bungkus button {
  width: auto;
  margin: 0;
  padding: 0.1rem 0.4rem;
  font-size: 0.72rem;
}

button.tutup {
  margin-top: 1rem;
}

/* Bar tampak melayang di atas kanvas: pemilih tampak dan sakelar grid ada di tempat
   yang dilihat saat menggambar, bukan di ujung sidebar.

   SEJAK 2026-08-20 IA MENYEMBUNYIKAN DIRI. Barisan sakelar yang selalu tampil menutupi
   justru bagian gambar yang sedang dikerjakan. Yang tinggal sebuah tab kecil; kursor
   yang mendekat menurunkan barnya, kursor yang pergi menaikkannya kembali.

   BUNGKUSNYA TIDAK MENERIMA KLIK, dan itu bukan kerapian melainkan syarat.
   Ia membentang selebar viewport supaya `max-width` bar tetap punya acuan; bila ia ikut
   menerima klik, seluruh jalur atas kanvas berhenti bisa digambari. */
.bar-tampak-bungkus {
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  top: 0.75rem;
  z-index: 3;
  pointer-events: none;
}

.bar-tampak-tab {
  pointer-events: auto;
  width: auto;
  margin: 0;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: rgb(255 255 255 / 96%);
  color: var(--muted);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.bar-tampak-tab:hover,
.bar-tampak-bungkus.terkunci .bar-tampak-tab {
  color: var(--text);
  border-color: var(--accent);
}

/* Meluncur TURUN dari bawah tab, bukan naik keluar viewport.

   Arah itu dipilih supaya tidak ada yang perlu dipotong: bar yang meluncur ke atas akan
   tergambar menimpa bilah menu selama transisinya, dan menutupnya menuntut
   `overflow: hidden` pada `#viewport` yang juga memotong hal lain. */
.bar-tampak {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 0.3rem;
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-0.4rem);
  transition:
    transform 0.16s ease,
    opacity 0.16s ease,
    visibility 0.16s;
  display: flex;
  /* MENURUN, bukan menyamping.
     Tujuh kendali berjajar mendatar membuat barnya selebar layar dan menutupi model di
     belakangnya; kotak centang yang berjajar juga tidak punya tepi kiri yang sama
     sehingga sulit dibaca sebagai satu daftar. */
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.35rem 0.65rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: rgb(255 255 255 / 96%);
}

/* CELAH ANTARA TAB DAN BAR HARUS DIJEMBATANI.

   Barnya duduk `0.3rem` di bawah tab supaya keduanya terbaca sebagai dua benda. Celah
   itu bukan milik siapa-siapa: kursor yang menyeberanginya meninggalkan tab DAN bar
   sekaligus, `:hover` pada bungkusnya lepas, dan barnya menutup tepat saat pengguna
   bergerak untuk memakainya.

   Pseudo-elemen ini memperpanjang daerah singgung bar ke atas menutupi celahnya. Ia
   tidak terlihat dan tidak menggeser apa pun; ia hanya membuat perjalanan kursor dari
   tab ke bar tidak pernah menyentuh ruang kosong. */
.bar-tampak::before {
  content: "";
  position: absolute;
  top: -0.3rem;
  left: 0;
  right: 0;
  height: 0.3rem;
}

/* `:focus-within` BUKAN pelengkap aksesibilitas di sini — ia yang membuat `<select>`
   bisa dipakai sama sekali. Daftar pilihan yang terbuka menggambar dirinya DI LUAR bar,
   sehingga kursor yang bergerak ke sana meninggalkan `:hover` dan menutup bar beserta
   daftarnya. Selama `<select>` memegang fokus, barnya tetap terbuka. */
.bar-tampak-bungkus:hover > .bar-tampak,
.bar-tampak-bungkus:focus-within > .bar-tampak,
.bar-tampak-bungkus.terkunci > .bar-tampak {
  pointer-events: auto;
  visibility: visible;
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .bar-tampak,
  #properti {
    transition: none;
  }
}

.bar-tampak label {
  margin: 0;
  white-space: nowrap;
}

.bar-tampak select {
  width: auto;
  min-width: 12rem;
  margin: 0;
  padding: 0.25rem 0.45rem;
  font-size: 0.8rem;
}

#properti-isi dl {
  margin-top: 0.5rem;
}

.bentuk {
  padding-top: 0.25rem;
}

/* Judul kategori kedua di dalam satu bagian butuh jarak dari kisi tombol di atasnya. */
h2.berjarak {
  margin-top: 1.25rem;
}

/* ===========================================================================
   Mode tampilan & seleksi terlihat (Milestone 14)
   =========================================================================== */

/* Kotak seleksi saat diseret. Murni tampilan: entitas mana yang terpilih tetap
   diputuskan `editor._pilihJendela()`, dan kotak ini tidak boleh menangkap klik. */
/* Kotak seleksi. Warnanya menyatakan ATURAN yang berlaku, bukan sekadar arah seretan:
   biru memilih entitas yang terlingkupi PENUH, hijau memilih yang cukup TERSENTUH.
   Konvensi yang sama dengan AutoCAD, dan alasannya sama: dua aturan yang berbeda wajib
   bisa dibedakan sebelum tombolnya dilepas, bukan sesudah hasilnya terlihat. */
.kotak-seret {
  position: absolute;
  z-index: 4;
  border: 1px dashed var(--accent);
  background: rgb(26 111 181 / 12%);
  pointer-events: none;
}

.kotak-seret.menyilang {
  border-color: #2e7d32;
  background: rgb(46 125 50 / 12%);
}

.lapisan-label {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
}

.label-penampang {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  background: rgb(255 255 255 / 88%);
  color: var(--accent);
  font-size: 0.68rem;
  font-weight: 600;
  white-space: nowrap;
}

/*
  Angka di atas diagram gaya dalam.

  Warnanya mengikuti garis tepi diagram, bukan `--accent`: angka dan pita yang
  dirujuknya harus terbaca sebagai satu hal. Latar pekatnya wajib — angka tipis di atas
  bidang diagram tembus pandang tidak terbaca pada separuh sudut pandang.

  `tabular-nums` supaya deretan angka di sepanjang batang tidak bergoyang lebarnya saat
  pandangan diputar.
*/
.label-nilai {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 0.05rem 0.25rem;
  border-radius: 3px;
  background: rgb(255 255 255 / 88%);
  color: #2e7d32;
  font-size: 0.64rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}

/*
  Reactions ▸ Plot Type = Tabulated (Milestone 77d): kotak bergaris di sebelah tiap tumpuan,
  satu baris per komponen ("Fz = 109.7317"), seperti ETABS. Label joint di atasnya, biru.
*/
.label-tabel {
  position: absolute;
  transform: translate(8px, -50%);
  padding: 0.1rem 0.35rem;
  border: 1px solid #555;
  background: rgb(255 255 255 / 92%);
  color: #222;
  font-size: 0.62rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
  white-space: nowrap;
  pointer-events: none;
}

.label-tabel-joint {
  color: #2c3eb8;
  font-weight: 600;
}

.bar-tampak label.sebaris {
  gap: 0.25rem;
  white-space: nowrap;
}

/* Pemisah kelompok di bar Tampak, sejajar pemisah kelompok dialog Set View Options. */
.bar-tampak hr {
  align-self: stretch;
  margin: 0.1rem 0;
  border: 0;
  border-top: 1px solid var(--line);
}

/* Butir anak — *Invisible* di bawah *Joint Objects*, seperti ETABS. */
label.sebaris.menjorok {
  padding-left: 1.3rem;
}

/* Dialog Set View Options: dua kolom kelompok, seperti ETABS. */
dialog.lebar-sedang {
  max-width: min(38rem, calc(100vw - 2rem));
}

.kolom-dua {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.6rem;
  align-items: start;
}

.kolom-dua label.sebaris {
  margin-top: 0.2rem;
}

/* Available ⇄ Visible Grid Systems. */
.baris-pindah {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 0.5rem;
  align-items: center;
}

.baris-pindah select {
  width: 100%;
  margin-top: 0.2rem;
}

.kolom-panah {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.kolom-panah button {
  width: auto;
  padding: 0.2rem 0.6rem;
}

/* Butir berstatus menu VIEW — *Show Axes* — seperti ikon berbingkai di menu ETABS. */
.menu-view button[aria-pressed="true"] {
  border-color: var(--accent);
  color: var(--accent);
}

/* Teks *Object Assignments* — label dan nama penampang — kecil dan abu-abu seperti ETABS. */
.label-tugasan {
  position: absolute;
  transform: translate(-50%, -50%);
  color: var(--muted);
  font-size: 0.66rem;
  white-space: nowrap;
}

/* Daftar jenis hasil — Milestone 22b.

   Menggantikan pemilih tunggal dengan daftar bergaya SkyCiv: tiap baris membuka
   pilihannya sendiri di tempat, dan baris yang aktif ditandai. Tanpa penanda, daftar
   tidak memberi tahu apa yang sedang digambar — dan aturan "satu tampilan pada satu
   waktu" justru membuat itu penting: yang tidak tergambar tidak punya jejak di layar. */
.daftar-hasil {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.daftar-hasil > button {
  text-align: left;
  padding: 0.45rem 0.6rem;
  font-size: 0.82rem;
  border-left: 3px solid transparent;
}

/* NAMA VARIABELNYA HARUS BENAR-BENAR ADA, dan sampai Milestone 48 dua-duanya tidak.

   Aturan ini menulis `var(--aksen, #4fb3ff)` dan `var(--teks, #e8eef4)` — nama Indonesia,
   sementara palet proyek ini mendefinisikan `--accent` dan `--text`. Keduanya karena itu
   jatuh diam-diam ke nilai cadangannya, dan nilai cadangan itu warna tema GELAP yang lama.

   Akibatnya nama baris hasil LENYAP justru saat barisnya aktif: latar `rgba(26,111,181,
   0.12)` di atas panel putih terbaca `rgb(228,238,246)`, sementara teks `#e8eef4` adalah
   `rgb(232,238,244)`. Rasio kontrasnya 1,0 — bukan pudar, melainkan tidak ada bedanya.

   Kenapa ia lolos sekian lama: fallback `var()` TIDAK PERNAH BERGALAT. Salah ketik nama
   variabel memberi halaman yang tetap dimuat dan tetap tergambar, hanya salah warna.

   Nilai cadangannya sengaja dibuang. Cadangan yang tidak akan pernah terpakai hanya
   menyimpan warna tema mati supaya bisa dipakai lagi oleh salah ketik berikutnya. */
.daftar-hasil > button.aktif {
  border-left-color: var(--accent);
  background: rgba(26, 111, 181, 0.12);
  color: var(--text);
}

/* Opsi baris menjorok ke dalam, supaya kepemilikannya terbaca tanpa garis pembatas. */
.opsi-hasil {
  padding: 0.4rem 0 0.6rem 0.75rem;
  border-left: 1px solid var(--line);
  margin-left: 0.4rem;
}

/* Kendali Mode diagram dipindahkan MASUK ke panel baris yang sedang terbuka, sehingga ada
   `.opsi-hasil` di dalam `.opsi-hasil`. Tanpa aturan ini ia menjorok dan bergaris tepi dua
   kali, dan terbaca sebagai anak dari anaknya alih-alih bagian dari kotak yang sama. */
.opsi-hasil .opsi-hasil {
  padding: 0.2rem 0 0;
  border-left: 0;
  margin-left: 0;
}

/* Kelompok sidebar yang bisa dilipat — Milestone 22b.

   Native `<details>`: tanpa satu baris JavaScript, dan karena itu tanpa satu pun
   keadaan baru yang bisa menyimpang dari layarnya. Aksesibilitas keyboard datang
   gratis dari peramban. */
.kelompok > summary {
  cursor: pointer;
  margin: 0 0 0.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  list-style: none;
}

.kelompok > summary::-webkit-details-marker {
  display: none;
}

.kelompok > summary::before {
  content: "▸ ";
  display: inline-block;
  transition: transform 0.12s ease;
}

.kelompok[open] > summary::before {
  content: "▾ ";
}

/* Panel Single Member — Milestone 22c.

   Mengambang di atas viewport, bukan di sidebar: grafiknya dibaca berdampingan dengan
   elemen yang sedang dipilih, dan sidebar yang sudah penuh akan memaksanya menjadi
   terlalu sempit untuk dibaca. */
.mengambang {
  position: absolute;
  right: 16rem;
  bottom: 1rem;
  width: 24rem;
  padding: 0.75rem 0.9rem;
  background: var(--panel, #ffffff);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgb(0 0 0 / 14%);
  z-index: 30;
}

.mengambang header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.mengambang h3 {
  margin: 0 0 0.4rem;
}

.tutup-kecil {
  padding: 0 0.35rem;
  font-size: 1rem;
  line-height: 1;
  background: none;
  border: none;
  color: var(--muted);
}

/* Grafik SVG. Tingginya tetap supaya kurva tidak berubah bentuk saat panel diisi
   catatan yang panjangnya berbeda-beda. */
.grafik {
  display: block;
  width: 100%;
  height: 11rem;
  margin: 0.5rem 0;
  background: rgb(20 24 28 / 3%);
  border-radius: 3px;
}

.grafik-bantu {
  stroke: #ccd4dc;
  stroke-width: 0.5;
}

.grafik-nol {
  stroke: #5c6873;
  stroke-width: 1;
}

.grafik-label {
  fill: var(--muted);
  font: 10px sans-serif;
  text-anchor: middle;
}

/* Bar kemajuan analisis. Lebar penuh panel supaya persennya terbaca sebagai posisi,
   bukan sebagai hiasan kecil di pojok. */
#jalan-bar {
  width: 100%;
  height: 6px;
  margin: 0.35rem 0 0.2rem;
  border: none;
  border-radius: 3px;
  background: var(--line);
  accent-color: var(--accent);
}

#jalan-bar::-webkit-progress-bar {
  background: var(--line);
  border-radius: 3px;
}

#jalan-bar::-webkit-progress-value {
  background: var(--accent);
  border-radius: 3px;
}

/* Pohon tabel bergaya *Choose Tables for Display* — Milestone 41 */
.pohon-tabel {
  max-height: 18rem;
  overflow-y: auto;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-size: 0.82rem;
}

.pohon-tabel details {
  margin: 0.15rem 0;
}

.pohon-tabel summary {
  cursor: pointer;
  padding: 0.1rem 0;
  color: var(--text);
}

.pohon-tabel .bagian > summary {
  font-weight: 600;
  letter-spacing: 0.03em;
}

.pohon-tabel .daun {
  display: block;
  width: 100%;
  margin: 0.05rem 0;
  padding: 0.15rem 0.4rem;
  border: 0;
  border-radius: 3px;
  background: transparent;
  color: var(--text);
  font-size: 0.82rem;
  text-align: left;
  cursor: pointer;
}

.pohon-tabel .daun:hover:not([aria-disabled="true"]) {
  background: var(--line);
}

.pohon-tabel .daun[aria-current="true"] {
  background: var(--accent);
  color: var(--bg);
}

/* Daun kosong TETAP terlihat, hanya redup: yang tidak ada wajib bisa dibaca namanya,
   karena itulah satu-satunya cara pengguna tahu apa yang belum dibangun. */
.pohon-tabel .daun[aria-disabled="true"] {
  color: var(--muted);
  cursor: help;
}

.pohon-tabel .daun.tambahan::after {
  content: " ·";
  color: var(--muted);
}

/* Kepala tabel hasil yang bisa disortir. */
#hasil-tabel th[data-kolom] {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

#hasil-tabel th[data-kolom]:hover {
  text-decoration: underline;
}

/* --------------------------------------------------------------------------
   Panel keluaran tabel hasil — Milestone 51

   Mengikuti panel kanan *Choose Tables for Display* ETABS: kotak berjudul, tombol
   pembuka pemilih jamak, dan ringkasan `N of M Selected` di bawahnya.
   -------------------------------------------------------------------------- */

.kotak-keluaran {
  margin: 0.5rem 0;
  padding: 0.5rem 0.7rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 4px;
}

.kotak-keluaran > legend {
  padding: 0 0.3rem;
  color: var(--muted);
  font-size: 0.78rem;
}

.kotak-keluaran > button {
  display: block;
  width: 100%;
  margin: 0.15rem 0;
}

/* Ringkasan `N of M Selected` menempel pada tombolnya, bukan mengambang di antara
   dua kotak — jarak bawaan `<p>` membuatnya terbaca sebagai milik kotak berikutnya. */
.kotak-keluaran > p {
  margin: 0.1rem 0 0.45rem;
}

.kotak-keluaran > p:last-child {
  margin-bottom: 0;
}

.daftar-centang {
  max-height: 16rem;
  overflow-y: auto;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-size: 0.82rem;
}

/* Satu baris per entri. Label kombinasi panjang (`1,2D + 1,0Ex + 0,3Ey [+Ex, −Ey]`),
   dan yang terpotong menghapus justru penanda tanda yang membedakan dua entri. */
.daftar-centang label.sebaris {
  display: flex;
  gap: 0.4rem;
  align-items: baseline;
  padding: 0.12rem 0;
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   Kotak Report — Milestone 52
   -------------------------------------------------------------------------- */

/* Daftar tabel laporan lebih tinggi daripada daftar centang biasa: ia memuat tiga puluh
   lima entri berkelompok, dan gulungan 16rem membuat kelompok kedua tidak pernah
   terlihat tanpa digulir lebih dulu. */
#lap-daftar-tabel {
  max-height: 22rem;
}

#lap-daftar-tabel fieldset {
  margin: 0 0 0.5rem;
}

#lap-daftar-tabel fieldset:last-child {
  margin-bottom: 0;
}

.daftar-gambar {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

/* Pratinjau kecil di kiri, keterangan yang bisa disunting di tengah, tombol buang di
   kanan. Pratinjaunya dibatasi tinggi, bukan lebar: tangkapan kanvas berorientasi
   mendatar maupun tegak harus sama tingginya supaya barisnya tidak melompat. */
.baris-gambar {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  padding: 0.4rem;
  border: 1px solid var(--line);
  border-radius: 4px;
}

.baris-gambar img {
  height: 3.5rem;
  width: auto;
  border: 1px solid var(--line);
  border-radius: 2px;
}

.baris-gambar .tumbuh {
  flex: 1;
  min-width: 0;
}

.baris-gambar input[type="text"] {
  width: 100%;
}
.displacement-probes {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 8;
}
.displacement-probe {
  position: absolute;
  box-sizing: border-box;
  width: 192px;
  padding: 9px 11px;
  border: 1px solid #a8b7c4;
  border-left: 3px solid #1c7580;
  border-radius: 4px;
  background: rgba(255, 255, 248, 0.97);
  box-shadow: 0 3px 12px #182f4824;
  color: #203347;
  font-size: 11px;
  line-height: 1.45;
}
.displacement-probe.is-pinned { border-left-color: #bd782b; }
.displacement-probe-leaders { position: absolute; inset: 0; stroke: #a77541; stroke-width: 1; }
.displacement-probe strong, .displacement-probe small { display: block; }
.displacement-probe small { color: #586777; overflow-wrap: anywhere; margin: 2px 0 7px; }
.displacement-probe dl { display: grid; grid-template-columns: 24px 1fr; gap: 2px 8px; margin: 0; }
.displacement-probe dt, .displacement-probe dd { margin: 0; }
.displacement-probe dd { text-align: right; font-variant-numeric: tabular-nums; }
.displacement-probe-mark {
  position: absolute;
  width: 8px;
  height: 8px;
  transform: translate(-50%, -50%);
  border: 1px solid #be462e;
  background: #fff9;
  border-radius: 50%;
}
.frame-probe {
  position: absolute;
  z-index: 8;
  max-width: min(280px, calc(100% - 8px));
  box-sizing: border-box;
  padding: 8px 11px;
  border: 1px solid #a8b7c4;
  border-left: 3px solid #1c7580;
  border-radius: 4px;
  background: rgba(255, 255, 248, 0.97);
  box-shadow: 0 3px 12px #182f4824;
  color: #203347;
  font-size: 11px;
  line-height: 1.5;
  pointer-events: none;
  overflow-wrap: anywhere;
}
.frame-probe strong, .frame-probe small { display: block; }
.frame-probe small { color: #586777; }
.frame-probe-value { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
.frame-probe-mark {
  position: absolute;
  z-index: 7;
  width: 6px;
  height: 6px;
  transform: translate(-50%, -50%);
  border: 1px solid #be462e;
  background: white;
  pointer-events: none;
}

/* ===========================================================================
   Submenu bilah menu (Define/Assign, Milestone 75) dan dialog Joint Restraints
   =========================================================================== */

.menu-isi hr {
  margin: 0.25rem 0.2rem;
  border: 0;
  border-top: 1px solid var(--line);
}

.menu-induk {
  position: relative;
}

.menu-induk-tombol::after {
  content: "\25B8";
  float: right;
  margin-left: 1.5rem;
  opacity: 0.7;
}

.menu-anak {
  display: none;
  position: absolute;
  top: -0.35rem;
  left: 100%;
  z-index: 11;
  min-width: 14rem;
  padding: 0.35rem;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--panel);
  box-shadow: 0 6px 20px rgb(0 0 0 / 14%);
}

.menu-induk:hover > .menu-anak,
.menu-induk:focus-within > .menu-anak,
.menu-induk.terbuka > .menu-anak {
  display: block;
}

.tumpuan-kisi {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.35rem 1.5rem;
}

.tumpuan-cepat {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
}

.tumpuan-ikon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0.2rem;
}

.tumpuan-ikon svg {
  width: 1.8rem;
  height: 1.8rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tumpuan-ikon circle {
  fill: currentColor;
}

/*
  Display ▸ Story Response Plots (Milestone 77e): grid properti di kiri, grafik di kanan,
  seperti jendela Story Response ETABS. Lebarnya ditetapkan di sini karena tak satu pun
  ukuran bersama dialog muat untuk grid dan grafik berdampingan.
*/
dialog.app-dialog#dlg-story-response {
  --dialog-width: 900px;
}

.story-response {
  display: grid;
  grid-template-columns: minmax(15rem, 18rem) minmax(0, 1fr);
  gap: 0.8rem;
  align-items: start;
}

#dlg-story-response .story-response-grid label {
  display: grid;
  grid-template-columns: 6.8rem minmax(0, 1fr);
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.3rem;
}

#dlg-story-response .story-response-grid label[hidden] {
  display: none;
}

/* Setinggi layar yang tersisa, bukan selebar kolom: judul sumbu dan kaki Max/Min harus
   terlihat tanpa menggulung. Sisa lebarnya ikut kelabu seperti latar grafik ETABS. */
.story-response-grafik svg {
  display: block;
  width: 100%;
  height: min(31rem, calc(100dvh - 16rem));
  background: #d3d3d3;
  border: 1px solid #a9a9a9;
}

/* Kaki "Max: (…);   Min: (…)" — tiga spasi ETABS tetap tiga spasi. */
.story-response-kaki {
  margin: 0.3rem 0 0;
  font-size: 0.78rem;
  white-space: pre;
}

#srp-titik:not(:empty) {
  margin-left: 1.5rem;
}

@media (max-width: 760px) {
  .story-response {
    grid-template-columns: minmax(0, 1fr);
  }
}
