/* ===== CSS Variables: single source of truth untuk margin gambar =====
     Sebelumnya angka "4px" ditulis manual di 2 tempat terpisah
     (".editor-content img" utk editor & ".scope-section img" utk
     preview/hasil akhir) -- persis pola yang sama dengan masalah lama
     SIZE_FULL_WIDTH/SIZE_SMALL_WIDTH (dua angka hardcode yang gampang
     lupa disinkronkan kalau salah satu diubah tanpa ubah yang lain).
     Sekarang cuma didefinisikan SEKALI di sini; kedua rule di bawah
     tinggal pakai var(--img-margin-block) lewat margin-block (shorthand
     utk margin-top + margin-bottom, arahnya otomatis ikut writing-mode
     tapi untuk LTR sama saja efeknya dgn margin-top/bottom manual).
     Kalau nanti mau ubah jaraknya (mis. 4px -> 6px), CUKUP ubah angka
     di baris ini saja -- otomatis konsisten di editor & preview. */
  :root {
    /* Satu sumber untuk tinggi minimum section, dipakai bareng di
       .cv-section (frame section di editor) DAN di wrapper .text-XX
       (lihat rule di bawah, dekat .scope-section) -- supaya kalau nanti
       angkanya diubah, cukup ganti di sini saja, otomatis konsisten
       di kedua tempat (persis pola yang sama dengan --img-margin-block
       di bawah). */
    --section-min-height: 50px;
    --img-margin-block: 4px;
    /* Horizontal HANYA dipakai di editor (".editor-content img"), untuk
       jarak napas gambar inline ke huruf kiri-kanannya. TIDAK dipakai
       di ".scope-section img" (preview) karena kiri-kanan di situ
       sengaja "auto" (buat centering) -- lihat catatan di rule
       ".scope-section img". Tetap didefinisikan sebagai variable (bukan
       angka langsung di rule) supaya kalau suatu saat butuh dipakai di
       tempat lain juga, sumbernya tetap satu. */
    --img-margin-inline-editor: 4px;
  }

  body {
    font-family: Arial, sans-serif;
    height: calc(100vh - 30px);
    display: flex;
    flex-direction: row;
    overflow: hidden;
  }

  ._top-header {
    padding: 0 10px;
    position: fixed;
    width: 100%;
    height: 30px;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    background-color: #ffffff;
    border-bottom: 1px solid #cccccc;
    z-index: 1000;
    box-sizing: border-box;
    top: 0;
    left: 0;
  }

  ._top-header ._user-main {
    display: flex;
    align-items: center;
  }

  ._top-header ._user-icon {
    margin-right: 8px;
    position: relative;
    width: 20px;
    height: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #ffffff;
    background-color: #03befc;
    font-size: 10px;
    font-weight: bold;
    border-radius: 50%;
  }

  ._top-header ._user-icon::after {
    content: "";
    position: absolute;
    bottom: -2px;
    right: -2px;
    width: 4px;
    height: 4px;
    background-color: #4caf50;
    border-radius: 50%;
    border: 2px solid #ffffff;
    z-index: 1;
  }

  ._top-header ._user-name {
    margin-right: 10px;
    padding-right: 10px;
    color: #808080;
    font-size: 14px;
    border-right: 1px solid #ccc;
  }

  ._top-header ._user-main i {
    font-size: 14px;
    color: #a6a6a6;
  }

  ._top-header ._user-main i:hover {
    color: #03befc;
    cursor: pointer;
  }

  ._top-header ._user-main i.active {
    color: #03befc;
  }

  /* Header Center Toggle: Preview (mata) & Magic Tools */
  .header-center-toggle {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    align-items: center;
    width: 60px;
    height: 22px;
    padding: 2px;
    background-color: #f0f0f2;
    border: 1px solid #e2e2e5;
    border-radius: 2px;
    box-sizing: border-box;
    z-index: 1001;
  }

  .header-center-toggle .header-toggle-slider {
    position: absolute;
    top: 2px;
    left: 29px;
    width: 27px;
    height: 16px;
    background-color: #ffffff;
    border-radius: 2px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
    transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    box-sizing: border-box;
    z-index: 0;
  }

  .header-center-toggle.magic-active .header-toggle-slider {
    left: 2px;
  }

  .header-toggle-btn {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 18px;
    border: none;
    background: transparent;
    color: #a6a6a6;
    font-size: 10px;
    line-height: 1;
    cursor: pointer;
    transition: color 0.2s ease, transform 0.15s ease;
  }

  .header-toggle-btn:active {
    transform: scale(0.88);
  }

  .header-toggle-btn:focus,
  .header-toggle-btn:focus-visible {
    outline: none;
  }

  .header-toggle-btn:hover {
    color: #03befc;
  }

  .header-toggle-btn.active {
    color: #03befc;
  }

  body.dark-mode .header-center-toggle {
    background-color: #2c2d31 !important;
    border-color: #555 !important;
  }

  body.dark-mode .header-center-toggle .header-toggle-slider {
    background-color: #46474c !important;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }

  body.dark-mode .header-toggle-btn {
    color: #888 !important;
  }

  body.dark-mode .header-toggle-btn:hover,
  body.dark-mode .header-toggle-btn.active {
    color: #03befc !important;
  }
  /* End Header Center Toggle */

  /* Language Toggle Styles */
  .language-toggle {
    display: flex;
    align-items: center;
    cursor: pointer;
    color: #a6a6a6;
    font-size: 14px;
    padding: 4px 8px;
    border-radius: 4px;
    transition: all 0.2s ease;
  }

  /* PERBAIKAN: Gunakan parent selector untuk hover */
  .language-toggle:hover,
  .language-toggle:hover i,
  .language-toggle:hover span {
    color: #03befc;
  }

  .language-toggle.active,
  .language-toggle.active i,
  .language-toggle.active span {
    color: #03befc;
  }

  .language-toggle i {
    margin-right: 5px;
    font-size: 14px;
    transition: color 0.2s ease;
  }

  .language-toggle span {
    font-size: 14px;
    font-weight: bold;
    transition: color 0.2s ease;
  }

  .language-container {
    position: absolute;
    top: 25px;
    left: 180px;
    width: 140px;
    color: #777;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    z-index: 1000;
    display: none;
    padding: 5px 0;
  }

  .language-container.show {
    display: block;
  }

  .language-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    cursor: pointer;
    transition: background-color 0.2s;
  }

  .language-option:hover {
    background-color: #f5f5f5;
  }

  .language-option img {
    width: 20px;
    height: 12px;
    border-radius: 2px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  }

  .language-option.active {
    background-color: #e6f0ff;
    color: #007bff;
  }

  .language-option i {
    margin-right: 8px;
    font-size: 14px;
  }

  .language-option span {
    font-size: 13px;
  }

  ._main-list {
    margin-top: 100px;
    padding: 20px;
    width: 385px;
    max-width: 385px;
    max-height: 100vh;
    display: flex;
    flex-direction: column;
    flex: 0 0 385px;
    order: 2;
    background-color: #f7f7f7;
    border: 1px solid #cccccc;
    border-top: none;
    border-bottom: none;
    overflow-y: auto;
    box-sizing: border-box;
  }

  ._main-list h3,
  .settings-header h4 {
    margin: 0 0 10px 0;
    font-size: 14px;
    font-weight: 600;
    color: #777777;
    display: flex;
    align-items: center;
    gap: 6px;
  }

  ._main-list h3 i,
  .settings-header h4 i {
    font-size: 13px;
    color: #777777;
  }

  ._sidebar-header {
    padding: 0 20px;
    width: 385px;
    max-width: 385px;
    height: 70px;
    position: absolute;
    top: 30px;
    right: 0;
    display: flex;
    align-items: center;
    background-color: #ffffff;
    border: 1px solid #cccccc;
    border-top: none;
    z-index: 10;
    box-sizing: border-box;
  }

  ._sidebar-header ._logo {
    display: flex;
    align-items: center;
    white-space: nowrap;
    color: #007bff;
    font-size: 18px;
    font-weight: bold;
  }

  ._sidebar-header ._logo i {
    margin-right: 10px;
    font-size: 20px;
  }

  ._save-button,
  ._publish-button,
  ._cancel-section-button,
  ._cancel-setting-button,
  ._save-setting-button {
    padding: 6px 12px;
    font-size: 14px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    min-width: 90px;
    min-height: 28px;
  }

  #_cancel-section-button,
  #_cancel-setting-button,
  #_save-setting-button {
    display: none;
  }

  ._save-button {
    position: absolute;
    right: 120px;
    color: #ffffff;
    background-color: #007bff;
  }

  ._save-button:hover {
    background-color: #0056b3;
  }

  ._publish-button {
    position: absolute;
    right: 20px;
    color: #ffffff;
    background-color: #03befc;
  }

  ._publish-button:hover {
    background-color: #0298d6;
  }

  /* ===== Indikator "ada perubahan belum disimpan" =====
     Dipasang/dilepas otomatis oleh refreshDirtyUI() (app.js) lewat class
     .has-unsaved-changes -- murni visual, tidak menonaktifkan tombol.
     Titik oranye kecil di pojok kanan-atas tombol Simpan/Publish
     (._save-button/._publish-button sudah position:absolute di atas). */
  ._save-button.has-unsaved-changes::after,
  ._publish-button.has-unsaved-changes::after {
    content: "";
    position: absolute;
    top: -3px;
    right: -3px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: #ff9800;
    border: 1.5px solid #ffffff;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);
  }

  ._save-button i,
  ._publish-button i {
    margin-right: 2px;
    font-size: 12px;
  }

  ._cancel-section-button,
  ._cancel-setting-button {
    position: absolute;
    right: 20px;
    color: #ffffff;
    background-color: #6c757d;
  }

  ._cancel-section-button:hover,
  ._cancel-setting-button:hover {
    background-color: #5a6268;
  }

  ._save-setting-button {
    position: absolute;
    right: 20px;
    color: #ffffff;
    background-color: #28a745;
  }

  ._save-setting-button:hover {
    background-color: #218838;
  }

  ._cancel-setting-button {
    right: 120px;
  }

  #_menu-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  ._sidebar-footer {
    padding: 0 8px;
    width: 385px;
    max-width: 385px;
    height: 30px;
    position: absolute;
    bottom: 0;
    right: 0;
    left: auto;
    display: flex;
    gap: 6px;
    align-items: center;
    justify-content: space-between;
    background-color: #ffffff;
    border: 1px solid #cccccc;
    z-index: 10;
    box-sizing: border-box;
  }

  ._sidebar-footer i {
    margin: 0 2px;
    padding: 3px;
    font-size: 18px;
    color: #555555;
    background-color: transparent;
    cursor: pointer;
    transition: all 0.2s ease;
    border: 1px solid rgba(0, 123, 255, 0.1);
    border-radius: 3px;
    line-height: 1;
  }

  /* ✅ Dibungkus @media (hover: hover) supaya style hover ini HANYA
     berlaku di device dengan mouse asli (PC/laptop). Di layar sentuh
     (HP/Tablet), :hover dipicu oleh tap dan bisa "nyangkut" (stuck) di
     icon yang terakhir disentuh sampai user tap di tempat lain -- ini
     yang bikin icon lama (mis. PC) kelihatan masih "aktif" (background
     biru dari :hover yang nyangkut) BERSAMAAN dengan icon baru yang
     benar-benar aktif (mis. HP/Tablet) setelah layar di-resize/rotasi.
     Membatasi hover ke device bermouse asli menghilangkan efek stuck
     ini sepenuhnya di HP/Tablet. */
  @media (hover: hover) and (pointer: fine) {
    ._sidebar-footer i:hover:not(.active) {
      color: #007bff;
      background-color: rgba(0, 123, 255, 0.05);
    }
  }

  ._sidebar-footer i.fa-magic {
    padding-right: 5px;
  }

  ._sidebar-footer i.preview-mode-icon.active {
    color: #007bff;
    background-color: rgba(0, 123, 255, 0.1);
    border-radius: 3px;
  }

  ._menu-item {
    padding: 6px 12px;
    display: flex;
    align-items: center;
    background-color: #ffffff;
    border: 1px solid #dddddd;
    border-radius: 2px;
    box-sizing: border-box;
    position: relative;
  }

  /* ===== Badge "section ini punya perubahan belum disimpan" =====
     Dipasang/dilepas otomatis oleh refreshDirtyUI() (app.js) lewat class
     .section-has-unsaved-changes pada tiap ._menu-item. Titik oranye
     kecil menempel di kiri judul section, murni indikator visual. */
  ._menu-item.section-has-unsaved-changes::before {
    content: "";
    position: absolute;
    left: 2px;
    top: 50%;
    transform: translateY(-50%);
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: #ff9800;
  }

  ._menu-item .hamburger {
    margin-right: 6px;
    padding: 4px;
    font-size: 18px;
    cursor: grab;
    touch-action: none;
    transition: transform 0.15s ease, color 0.15s ease;
  }

  ._menu-item .hamburger:hover {
    color: #0298d6;
    transform: scale(1.2);
  }

  ._menu-item i.section-icon {
    font-size: 14px;
    opacity: 0.3;
  }

  ._menu-item:hover i.section-icon,
  ._menu-item.dragging i.section-icon {
    color: grey;
    opacity: 1;
  }

  ._menu-item input {
    margin: 0 8px;
    background-color: transparent;
    border: none;
    outline: none;
    flex: 1;
  }

  ._menu-item .settings {
    margin-right: 6px;
    font-size: 12px;
    color: #555555;
    cursor: pointer;
    box-sizing: border-box;
    display: inline-block;
  }

  ._menu-item .settings:hover,
  ._menu-item ._grip-btn:hover {
    transform: scale(1.25);
  }

  ._menu-item.placeholder {
    background-color: #f7f7f7;
    border: none;
  }

  ._menu-item.dragging {
    width: 100%;
    max-width: none;
    user-select: none;
    cursor: grabbing;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    border-radius: 2px;
    transform: scale(1.03);
    /* transform: scale(1.03) translateY(-2px);*/
    transition: transform 0.05s ease, box-shadow 0.05s ease;
    z-index: 9;
  }

  ._menu-item.dragging input {
    color: grey;
  }

  ._menu-item.connected-scale {
    transform: scale(1.03);
    transition: transform 0.3s ease;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
    z-index: 1;
  }

  body.dark-mode ._menu-item.connected-scale {
    box-shadow: 0 2px 6px rgba(255, 255, 255, 0.15);
    background-color: #2a2a2e;
    border-color: #444;
  }

  ._section-sidebar {
    padding: 20px;
    width: 385px;
    max-width: 385px;
    height: calc(100vh - 130px);
    position: fixed;
    top: 100px;
    left: auto;
    right: 0;
    display: none;
    flex-direction: column;
    background-color: rgba(255, 255, 255, 0.97);
    border: 1px solid #cccccc;
    border-top: none;
    border-bottom: none;
    overflow-y: auto;
    z-index: 2;
    box-sizing: border-box;
  }

  ._section-sidebar-content {
    max-height: 100%;
  }

  ._section-sidebar-content h4 {
    margin-bottom: 12px;
    font-size: 16px;
  }

  ._section-options {
    margin: 0;
    padding: 0 0 20px 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    list-style: none;
  }

  ._section-options li {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 8px 10px;
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 2px;
    font-size: 14px;
    height: 32px;
    box-sizing: border-box;
    width: 100%;
    transition: background 0.3s ease, transform 0.2s ease;
  }

  ._section-options li:hover {
    color: #007bff;
    background: #f0f0f0;
    transform: translateX(2px);
  }

  ._section-options li i {
    min-width: 24px;
    text-align: center;
    font-size: 16px;
    margin-right: 10px;
    opacity: 0.7;
  }

  ._section-options li span {
    flex-grow: 1;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /*Color Icon*/
  ._menu-item i.fa-heading,
  ._section-sidebar i.fa-heading,
  #modal-section-options i.fa-heading {
    color: #3498db;
  }

  ._menu-item i.fa-align-left,
  ._section-sidebar i.fa-align-left,
  #modal-section-options i.fa-align-left {
    color: #2ecc71;
  }

  ._menu-item i.fa-image,
  ._section-sidebar i.fa-image,
  #modal-section-options i.fa-image {
    color: #e74c3c;
  }

  ._menu-item i.fa-list,
  ._section-sidebar i.fa-list,
  #modal-section-options i.fa-list {
    color: #f39c12;
  }

  ._menu-item i.fa-arrow-down,
  ._section-sidebar i.fa-arrow-down,
  #modal-section-options i.fa-arrow-down {
    color: #e67e22;
  }

  ._menu-item i.fa-images,
  ._section-sidebar i.fa-images,
  #modal-section-options i.fa-images {
    color: #1abc9c;
  }

  ._menu-item i.fa-map,
  ._section-sidebar i.fa-map,
  #modal-section-options i.fa-map {
    color: #16a085;
  }

  ._menu-item i.fa-square-poll-horizontal,
  ._section-sidebar i.fa-square-poll-horizontal,
  #modal-section-options i.fa-square-poll-horizontal {
    color: #f1c40f;
  }

  ._menu-item i.fa-panorama,
  ._section-sidebar i.fa-panorama,
  #modal-section-options i.fa-panorama {
    color: #34495e;
  }

  ._menu-item i.fa-shoe-prints,
  ._section-sidebar i.fa-shoe-prints,
  #modal-section-options i.fa-shoe-prints {
    color: #9b59b6;
  }

  ._grip-btn {
    background: transparent;
    border: none;
    color: #ff2525;
    font-weight: bold;
    cursor: pointer;
    display: inline-block;
  }

  #cv-preview {
    container-type: inline-size;
    container-name: preview-container;
  }

  .cv-preview {
    width: 100%;
    height: 100%;
    /* 🐞 FIX KONSISTENSI RESIZE HP/TABLET/PC:
       Sebelumnya padding horizontal (10px kiri+kanan) ditaruh di SINI,
       di elemen yang sama persis yang jadi query container
       (container-type: inline-size; container-name: preview-container
       di bawah). Akibatnya @container preview-container (max-width: ...)
       selalu membaca CONTENT-BOX elemen ini (border-box dikurangi
       padding), bukan lebar var(--sim-width) yang di-set JS
       (applyMobileDeviceSimulation()) -- jadi breakpoint 360/768/1280px
       yang seharusnya persis, selalu meleset 20px (paling kentara di
       mode HP karena 20px dari basis 360px jauh lebih besar porsinya
       dibanding dari basis 768px/1280px).
       FIX: padding horizontal dipindah ke .cv-section (lihat rule
       .cv-section di bawah) -- elemen ANAK, bukan elemen container
       query-nya sendiri -- sehingga tidak ikut mengurangi lebar yang
       dibaca @container, tapi hasil visualnya (lebar konten CV) tetap
       identik seperti sebelumnya (100% - 10px - 10px yang sama persis,
       cuma dihitung di level anak). Padding vertikal (20px atas-bawah)
       TETAP di sini karena container-type:inline-size cuma peduli
       lebar (inline axis), jadi padding vertikal tidak pernah
       memengaruhi bug ini. */
    padding: 20px 0;
    background: #ffffff;
    overflow-y: auto;
    /* [KEAMANAN] Boundary containment terhadap CSS section user.
       scopeCssToSection() hanya memaksa "position: relative" dan
       meng-clamp z-index -- itu cuma menutup jalur out-of-flow +
       tumpuk-di-atas. Elemen yang TETAP in-flow (position relative/
       static apa adanya) masih bisa melukis JAUH di luar kotak
       section-nya sendiri lewat ukuran besar (width:400vw dsb),
       margin negatif, atau transform:scale()/translate() -- tanpa
       perlu position:fixed/absolute maupun z-index tinggi sama
       sekali, karena default CSS tidak meng-clip paint pada boundary
       parent. overflow-x:hidden + contain:layout paint di sini
       menutup jalur itu di titik yang TIDAK BISA dijangkau CSS user
       manapun: scopeSelectorList() selalu men-scope selector user
       sebagai DESCENDANT dari .cv-section (mis. ".cv-section[..] sel"),
       jadi user tidak pernah bisa menulis selector yang naik ke
       ancestor .cv-preview ini. overflow-y sengaja tetap "auto" (bukan
       ikut di-hidden) supaya scroll vertikal builder tidak rusak;
       clipping horizontal + containment layout/paint sudah cukup
       menutup jalur "keluar section" secara visual. */
    overflow-x: hidden;
    contain: layout paint;
    container-type: inline-size;
    container-name: preview-container;
    resize: none;
    min-width: 100%;
    max-width: 100%;
    box-sizing: border-box;
  }

  .cv-section {
    font-family: Arial, sans-serif;
    transition: box-shadow 0.1s ease, opacity 0.1s ease;
    box-sizing: border-box;
    width: 100%;
    /* ✅ Padding horizontal 10px kiri+kanan dipindah ke sini (dari
       .cv-preview) sebagai bagian dari fix konsistensi resize
       HP/Tablet/PC -- lihat komentar lengkap di rule .cv-preview.
       Karena .cv-section adalah ANAK dari query container, padding di
       sini tidak pernah dibaca/mengurangi lebar @container
       preview-container, sehingga breakpoint 360/768/1280px selalu
       persis di ketiga mode. Lebar konten akhir tetap sama seperti
       sebelumnya (branding-section ikut kena juga karena classlist-nya
       "cv-section branding-section", tanpa override padding sendiri). */
    padding-left: 10px;
    padding-right: 10px;
    position: relative;
    /* [KEAMANAN] Boundary containment PER-SECTION. Fix sebelumnya
       (overflow-x:hidden + contain:layout paint di .cv-preview) cuma
       menutup jalur bocor ke LUAR seluruh kanvas builder (sidebar,
       header, dst) -- TIDAK menutup jalur bocor ANTAR-SECTION di
       dalam kanvas yang sama, karena semua .cv-section adalah sibling
       biasa tanpa clipping antara satu sama lain. Efek seperti
       "box-shadow: 0 0 1000px red" (atau outline besar, transform
       skala besar, dsb) murni efek PAINT -- tidak butuh
       position:fixed/absolute sama sekali -- sehingga lolos total dari
       sanitizer yang cuma menyaring "position"/"z-index". overflow:
       hidden + contain:paint di sini mengunci area paint tiap section
       ke kotaknya sendiri, termasuk box-shadow/outline/transform/
       elemen oversized apa pun dari isinya.
       AMAN ditaruh langsung di .cv-section (bukan cuma di ancestor)
       karena scopeSelectorList() sekarang tidak pernah meloloskan
       selector user yang cocok dengan .cv-section itu sendiri --
       selalu dipaksa jadi descendant lewat prefix scope (lihat fix
       idempotency-shortcut sebelumnya), jadi user tidak bisa
       membatalkan overflow/contain ini lewat CSS section-nya sendiri. */
    overflow: hidden;
    contain: paint;
    /* Lantai tinggi minimum -- supaya section tetap terlihat/kepilih
       di preview walau isinya (.section-wrapper) collapse ke 0 tinggi,
       mis. akibat user menulis "body { display: none !important; }"
       di section-css (lihat scopeCssToSection(): body diarahkan ke
       .section-wrapper, BUKAN ke .cv-section ini, jadi min-height di
       sini tidak pernah ikut hilang bersama kontennya). */
    min-height: var(--section-min-height);
  }

  /* [HIGHLIGHT RING] Ring border/box-shadow biasa (di .cv-section langsung)
     tidak bisa "pas" ke tepi kiri-kanan section karena .cv-section punya
     padding HORIZONTAL saja (10px kiri+kanan, lihat komentar di atas),
     sedangkan padding vertikal = 0. box-shadow/outline cuma punya satu
     angka inset yang berlaku rata 4 sisi, jadi tidak bisa mengikuti
     tepi kiri-kanan (yang harus mundur 10px) sekaligus tepi atas-bawah
     (yang harus tetap 0) dalam satu deklarasi.
     Solusi: pseudo-element ::before yang di-pin manual per-sisi
     (left/right:10px, top/bottom:0) -- elemen datanya TETAP di
     .cv-section yang sama (tidak dipindah), cuma "kanvas" gambar
     ring-nya yang dibuat presisi lewat posisi absolute ini. */
  .cv-section::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 10px;
    right: 10px;
    box-sizing: border-box;
    pointer-events: none;
    z-index: 100;
    border: 2px solid transparent;
    transition: border-color 0.05s ease, box-shadow 0.05s ease;
  }

  .cv-section.magic-hover::before {
    border-color: #03befc;
  }

  .cv-section.magic-active::before {
    border-color: #03befc;
  }

  .section-wrapper {
    position: relative;
  }

  .cv-section strong {
    font-size: 1.2em;
    font-weight: bold;
  }

  .cv-section.dragging,
  .cv-section.focused {
    z-index: 99;
    position: relative;
    transform-origin: center;
  }

  .cv-section.dragging::before,
  .cv-section.focused::before {
    border-color: #03befc;
  }

  .controls {
    margin-top: 10px;
  }

  .controls button {
    background: #fff;
    border: 1px solid #ddd;
    padding: 8px;
    font-size: 14px;
    cursor: pointer;
    width: 100%;
    box-sizing: border-box;
  }

  .controls button:hover {
    background: #eee;
  }

  .toast {
    font-family: "Segoe UI", sans-serif;
    position: fixed;
    top: 10px;
    left: -320px;
    background-color: #03befc;
    color: white;
    padding: 20px 24px 60px 24px;
    border-radius: 4px;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
    z-index: 100000;
    transition: left 0.5s ease-in-out;
    font-size: 18px;
    min-height: 100px;
    width: 250px;
    box-sizing: border-box;
    overflow: hidden;
  }

  /* Dipakai saat tombol "Lihat" disembunyikan (lihat renderToastNow()) --
     tanpa tombol, toast tidak perlu ruang kosong ekstra di bawah teks,
     jadi tinggi toast mengikuti panjang kalimat pesannya saja. */
  .toast.toast--compact {
    padding: 16px 24px;
    min-height: 0;
  }

  /* FIX teks toast vs tombol close (.close-toast, top:8px right:12px):
     #toast-message diberi padding-right tetap (bukan margin-top/geser ke
     bawah, dan bukan float yang cuma menyempitkan baris pertama) supaya:
     - posisi top teks TIDAK berubah dari semula, dan
     - semua baris (1, 2, 3, dst) tetap sama lebar / sejajar satu sama
       lain, karena padding-nya seragam untuk semua baris.
     word-break dibiarkan normal (default) supaya kata tidak dipotong di
     tengah -- pindah baris hanya di antar kata, seperti wrap teks pada
     umumnya. */
  #toast-message {
    display: block;
    padding-right: 20px;
  }

  .toast.success {
    background-color: #03befc;
  }

  .toast.danger {
    background-color: #dc3545;
  }

  .toast.warning {
    background-color: #ffc107;
  }

  .toast.show {
    left: 20px;
  }

  .close-toast {
    position: absolute;
    top: 8px;
    right: 12px;
    font-weight: bold;
    font-size: 20px;
    cursor: pointer;
  }

  .toast-view-button {
    position: absolute;
    bottom: 10px;
    left: 50%;
    transform: translateX(-50%);
    padding: 8px 40px;
    background-color: #ffffff;
    color: #4caf50;
    border: none;
    font-weight: bold;
    cursor: pointer;
    font-size: 16px;
    border-radius: 25px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    transition: background-color 0.3s, box-shadow 0.3s;
  }

  .toast-view-button:hover {
    background-color: #f1f1f1;
  }

  .settings-overlay {
    position: fixed;
    top: 100px;
    right: 0;
    left: auto;
    width: 385px;
    max-width: 385px;
    height: calc(100vh - 130px);
    background: white;
    border: 1px solid #cccccc;
    border-top: none;
    border-bottom: none;
    z-index: 10;
    padding: 15px;
    display: none;
    overflow-y: auto;
    box-sizing: border-box;
  }

  .settings-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .settings-group {
    margin-bottom: 20px;
    padding: 15px;
    background: #f9f9f9;
    border-radius: 8px;
  }

  .settings-group h5 {
    margin-top: 0;
    color: #555;
  }

  .settings-option {
    margin-bottom: 15px;
  }

  .settings-option label {
    display: block;
    margin-bottom: 5px;
    font-weight: 500;
    color: #333;
  }

  .settings-input {
    width: 100%;
    padding: 8px;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-bottom: 10px;
  }

  /* Perbaikan tata letak untuk confirm toast */
  .confirm-toast {
    position: fixed;
    background-color: #fa8072;
    right: 20px;
    color: white;
    padding: 15px 20px;
    border-radius: 4px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    z-index: 99999;
    transition: opacity 0.3s ease, transform 0.3s ease;
    width: 250px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    opacity: 0;
    pointer-events: none;
    box-sizing: border-box;
  }

  .confirm-toast.show {
    opacity: 1;
    pointer-events: auto;
  }

  .confirm-toast-message {
    font-size: 16px;
    margin-top: 10px;
    max-width: 200px;
  }

  .confirm-toast-button-container {
    display: flex;
    gap: 8px;
    justify-content: space-between;
    align-items: center;
    margin-top: 5px;
  }

  .confirm-toast-button {
    padding: 6px 12px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-weight: bold;
    flex: 1;
    min-width: 60px;
    text-align: center;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
  }

  .close-confirm-toast {
    position: absolute;
    top: 8px;
    right: 12px;
    font-weight: bold;
    font-size: 20px;
    cursor: pointer;
    color: #ccc;
    transition: color 0.2s ease;
    z-index: 1;
  }

  .close-confirm-toast:hover {
    color: #fff;
  }

  .confirm-toast-button.add {
    background-color: #fff;
    color: #28a745;
  }

  .confirm-toast-button.add:hover {
    background-color: #28a745;
    color: #fff;
  }

  .confirm-toast-button.duplicate {
    background-color: #fff;
    color: #007bff;
  }

  .confirm-toast-button.delete {
    background-color: #fff;
    color: #dc3545;
  }

  .confirm-toast-button.duplicate:hover {
    background-color: #007bff;
    color: #fff;
  }

  .confirm-toast-button.delete:hover {
    background-color: #dc3545;
    color: #fff;
  }

  @keyframes flashGlow {
    0% {
        box-shadow: inset 0 0 0px #03befc;
    }
    40% {
        box-shadow: inset 0 0 10px #03befc;
    }
    100% {
        box-shadow: inset 0 0 0px #03befc;
    }
}

  .floating-toggle {
    position: fixed;
    right: 0;
    top: auto;
    bottom: 20px;
    transform: none;
    width: 30px;
    height: 40px;
    background-color: #343a40;
    color: white;
    display: none;
    align-items: center;
    justify-content: center;
    border-radius: 5px 0 0 5px;
    cursor: pointer;
    z-index: 1000;
    box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.2);
  }

  .floating-toggle:hover {
    background-color: #495057;
  }

  .floating-toggle i {
    font-size: 16px;
  }

  .history-container {
    position: absolute;
    top: 25px;
    left: 140px;
    width: 215px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    z-index: 1000;
    display: none;
    padding: 10px;
  }

  .history-body {
    max-height: 200px;
    overflow-y: auto;
  }

  .history-container.show {
    display: block;
  }

  .history-item {
    margin: 0 5px;
    padding: 8px 14px;
    cursor: pointer;
    font-size: 13px;
    box-sizing: border-box;
  }

  .history-item:last-child {
    border-bottom: none;
  }

  .history-item:hover {
    border-radius: 2px;
    background-color: #f5f5f5;
  }

  .history-item span {
    font-weight: bold;
  }

  .history-item span.active {
    position: relative;
    color: #03befc;
  }

  .history-item span.active::before {
    content: "";
    position: absolute;
    left: -10px;
    top: 50%;
    transform: translateY(-50%);
    width: 0;
    height: 0;
    border-top: 6px solid transparent;
    border-bottom: 6px solid transparent;
    border-left: 7px solid #4caf50;
  }

  .history-header {
    font-weight: bold;
    margin-bottom: 8px;
    padding-bottom: 5px;
    border-bottom: 1px solid #ddd;
  }

  /* ✅ Search History -- input filter di dalam .history-container, di
     atas .history-body. Padding kiri-kanan disisakan ruang untuk icon
     kaca pembesar (kiri) & tombol clear (kanan, disematkan lewat JS
     saat input tidak kosong). border-radius 4px & border #ddd disamakan
     dengan .settings-input supaya konsisten dengan input lain di app. */
  .history-search-wrapper {
    position: relative;
    margin: 0 5px 8px;
  }

  .history-search-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: #999;
    font-size: 12px;
    pointer-events: none;
  }

  .history-search-input {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 26px 7px 28px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 13px;
    font-family: inherit;
    outline: none;
    transition: border-color 0.15s ease;
  }

  .history-search-input:focus {
    border-color: #03befc;
  }

  .history-search-input::placeholder {
    color: #aaa;
  }

  .history-search-clear {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: #999;
    font-size: 11px;
    cursor: pointer;
  }

  .history-search-clear:hover {
    color: #333;
  }

  .history-meta {
    font-size: 11px;
    font-weight: bold;
    color: #777;
    margin-top: 3px;
  }

  .history-created {
    margin-top: 3px;
    color: #a8c3a0;
    font-size: 10px;
  }

  .loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(255, 255, 255, 0.8);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 99999;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
  }

  .loading-overlay.active {
    opacity: 1;
    pointer-events: auto;
  }

  .loading-spinner {
    width: 50px;
    height: 50px;
    border: 5px solid #f3f3f3;
    border-top: 5px solid #03befc;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
  }

  .loading-logo {
    font-size: 24px;
    font-weight: bold;
    color: #03befc;
    margin-bottom: 20px;
    display: flex;
    align-items: center;
  }

  .loading-logo i {
    margin-right: 10px;
    font-size: 28px;
    animation: rotateCube 2s linear infinite;
  }

  @keyframes spin {
    0% {
      transform: rotate(0deg);
    }

    100% {
      transform: rotate(360deg);
    }
  }

  @keyframes rotateCube {
    0% {
      transform: rotate(0deg);
    }

    100% {
      transform: rotate(360deg);
    }
  }

  /*Scope Section*/
  .scope-section {
    position: relative;
    box-sizing: border-box;
    overflow-x: hidden;
    white-space: normal;
    overflow-wrap: break-word;
    word-break: break-word;
  }

  /* Clearfix: gambar yang di-wrap kiri/kanan (Wrap Text left/right) pakai
     float, dan TANPA ini tinggi .scope-section (alias .text-XX di
     output/preview) collapse -- tidak ikut menghitung tinggi elemen yang
     di-float, sehingga background-color/image section cuma nutupin
     sebagian & tidak sampai ke gambar. Wrap "none" (center) tidak
     terpengaruh karena gambarnya display:block biasa, bukan float. */
  .scope-section::after,
  .scope-section > div[class^="text-"]::after {
    content: "";
    display: table;
    clear: both;
  }

  /* [FIX] Cegah margin-collapse dari elemen pertama/terakhir di dalam
     wrapper .text-XX (mis. margin-top bawaan browser pada <p>, heading,
     ul/ol, blockquote, dst). Tanpa ini, margin tersebut "bocor tembus"
     ke luar kotak .text-XX (karena .text-XX tidak punya padding/border
     secara default), sehingga background-color yang di-set lewat tab
     Design (rule ".text-XX { background-color: ... }", lihat
     updateCssFromVisualEditor()) jadi terlihat cuma menutupi baris teks
     itu sendiri -- BUKAN seluruh section -- padahal secara CSS rule-nya
     sudah benar diarahkan ke wrapper, bukan ke teksnya.
     "display: flow-root" membuat .text-XX jadi Block Formatting Context
     baru, yang menahan margin anak-anaknya tetap di DALAM kotaknya
     sendiri (tanpa efek visual lain seperti overflow:hidden yang bisa
     memotong konten yang sengaja meluber, mis. shadow/negative margin).

     min-height: var(--section-min-height) DITAMBAHKAN di sini (bukan
     lewat flex/grid) supaya tinggi .text-XX otomatis ikut minimal sama
     dengan lantai tinggi minimum section (.cv-section, lihat variable
     yang sama di :root) -- caranya murni lewat efek berjenjang block
     layout biasa: .scope-section & .section-wrapper di atasnya sama-sama
     height:auto (menyesuaikan isi), jadi begitu .text-XX (anak
     satu-satunya) minimal setinggi ini, KEDUANYA otomatis ikut
     "mengembang" mengikuti tinggi anaknya -- TANPA perlu flex/grid sama
     sekali. Ini SENGAJA tidak dibuat lewat flex/percentage-height,
     karena:
       (a) konten di dalam .text-XX (<p>, dst) TETAP mengalir normal
           (posisi/ukurannya tidak ikut di-stretch/center) -- cuma kotak
           pembungkusnya yang jadi lebih tinggi, sisanya tetap kosong;
       (b) tidak menyentuh "display" milik .cv-section/.section-wrapper/
           .scope-section itu sendiri (yang dipakai banyak tipe section
           lain di file ini), jadi TIDAK ada efek samping ke section lain
           di luar section yang kontennya memang pendek;
       (c) percentage-height (mis. "height:100%") TIDAK dipakai karena
           .cv-section cuma punya min-height (bukan height eksplisit) --
           menurut spec CSS, percentage-height anak akan resolve ke
           "auto" (tidak berfungsi) kalau ancestor-nya cuma punya
           min-height tanpa height pasti, jadi pendekatan itu tidak akan
           konsisten di semua kasus. */
  .scope-section > div[class^="text-"] {
    display: flow-root;
    box-sizing: border-box;
    min-height: var(--section-min-height);
  }

  .scope-section img {
  max-width: 100%;
  height: auto;
  display: block;
  /* Atas-bawah pakai var(--img-margin-block) -- didefinisikan SEKALI di
     :root (lihat awal <style>), dipakai bareng ".editor-content img" di
     bawah supaya jarak napas gambar ke elemen di atas/bawahnya konsisten
     antara tampilan editing & preview/hasil akhir, TANPA angka hardcode
     ganda yang gampang lupa disinkronkan. Kiri-kanan SENGAJA tetap
     "auto" (bukan ikut variable ini) -- ini yang menengahkan gambar
     secara horizontal untuk mode default/tidak-wrap; kalau diganti
     fixed, gambar berhenti center (jadi nempel ke kiri, karena "auto"
     cuma berlaku kalau kedua sisi auto). Mode wrap kiri/kanan tetap
     tidak terpengaruh margin ini karena JS (applyWrap(), lihat sekitar
     baris 20330-20430) menulis marginLeft/marginRight 14px sendiri
     lewat inline style, yang tetap menang atas margin di sini. */
  margin: var(--img-margin-block) auto;
  padding: 0;
  object-fit: contain;
}

  /* Visual Editor */
  .visual-editor-container {
    margin-bottom: 20px;
    border: 1px solid #ddd;
    border-radius: 4px;
    overflow: hidden;
  }

  .visual-editor {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
  }

  .editor-container-tabs {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 2px;
    background: #f6f7f9;
    border-radius: 25px;
    padding: 3px;
    margin: 5px 10px 6px;
    min-height: 36px;
  }

  /* Divider terpisah dari pembungkus pill di atas -- sengaja dibuat
     sebagai garis lurus tersendiri (bukan ikut border-bottom pada
     .editor-container-tabs), supaya tidak ikut melengkung mengikuti
     border-radius pill dan tetap konsisten sebagai pemisah rata
     penuh seperti border-bottom versi sebelumnya, persis di atas
     .editor-container-panes. */
  .editor-container-tabs::after {
    content: "";
    position: absolute;
    left: -10px;
    right: -10px;
    bottom: -6px;
    height: 1px;
    background: #ddd;
    pointer-events: none;
  }

  .editor-tab {
    position: relative;
    z-index: 1;
    padding: 0px 16px;
    cursor: pointer;
    color: #7d8390;
    font-weight: 500;
    letter-spacing: 0.1px;
    transition: color 0.25s ease, transform 0.15s ease;
    user-select: none;
  }

  .editor-tab:hover:not(.active) {
    color: #333;
  }

  .editor-tab:active {
    transform: scale(0.96);
  }

  .editor-tab.active {
    /* Warna teks disamakan dengan pola tombol Warna Teks/Warna Latar
       Belakang Teks (.toolbar-color-btn) di editor toolbar: di default
       mode tombol itu ber-background cyan (#03befc) dengan icon PUTIH
       (lihat "body:not(.dark-mode) .toolbar-color-btn i" di bawah) --
       pill tab aktif sekarang ikut background cyan yang sama, jadi
       teksnya ikut disamakan jadi putih supaya tetap kontras & konsisten
       dengan pola tombol itu (bukan cuma warna background yang disamakan). */
    color: #ffffff;
    /* Dulu pakai font-weight:bold, tapi itu mengubah lebar teks
       (bold sedikit lebih lebar dari normal). Karena
       .editor-container-tabs pakai justify-content:center, perubahan
       lebar itu bikin SELURUH baris tab (termasuk tab lain yang tidak
       aktif) ikut bergeser posisi tiap ganti tab -- terlihat seperti
       "kedutan"/reflow. Diganti pakai trik fake-bold via text-shadow
       (menumpuk bayangan solid tipis di atas teks) -- visual tetap
       terlihat tebal, tapi font-weight & lebar teks TIDAK berubah,
       jadi baris tab tidak reflow lagi. Warna shadow disesuaikan jadi
       varian putih (dari sebelumnya hitam) supaya cocok dgn teks putih
       baru di atas background cyan. */
    text-shadow: 0 0 0.4px rgba(255, 255, 255, 0.6), 0 0 0.4px rgba(255, 255, 255, 0.6);
  }

  /* Indikator untuk tab Editor/Design/Settings -- berupa pill
     rounded (border-radius) yang bergeser mengisi latar tab aktif.
     Posisi (left/width) digeser lewat JS (moveEditorTabsIndicator())
     -- lihat app.js. Versi modern: diisi gradasi warna brand +
     bayangan lembut supaya terlihat "melayang" di atas track abu-abu,
     bukan cuma pill putih polos. */
  .editor-tabs-indicator {
    position: absolute;
    top: 6px;
    bottom: 6px;
    border-radius: 18px;
    /* Disamakan dengan warna background tombol Warna Teks/Warna Latar
       Belakang Teks (.toolbar-color-btn) di editor toolbar pada default
       mode -- lihat "body:not(.dark-mode) .toolbar-color-btn" di bawah
       (background-color: #03befc). Sebelumnya #ffffff (putih polos). */
    background: #03befc;
    transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
      background-color 0.25s ease;
    pointer-events: none;
    z-index: 0;
  }

  .editor-container-panes {
    display: flex;
  }

  .editor-pane {
    flex: 1;
    padding: 10px;
    overflow-y: auto;
    display: none;
  }

  .editor-pane.active {
    display: block;
  }

  /* Elemen pembungkus di belakang .editor-content: ukurannya dibuat
     persis sama & fleksibel mengikuti .editor-content (flex, width,
     max-width, margin, height). Background mengikuti tampilan referensi:
     putih (#ffffff) di mode default/light, dan #222327 di dark mode
     (lihat rule body.dark-mode .editor-content-backdrop di bawah) --
     BUKAN lagi mengikuti background-color/image section (.text-XX),
     yang memang sudah tidak ditampilkan di area editor ini.

     PENTING soal scrollbar: overflow/scroll SENGAJA ditaruh di sini
     (elemen luar), BUKAN di .editor-content (elemen dalam yang punya
     padding-top:20px). Kalau overflow:auto ditaruh di elemen yang
     sama dengan yang punya padding-top, sebagian browser menghitung
     area scrollbar mulai dari bawah padding itu -> scrollbar jadi
     "turun"/tidak menempel di batas atas. Dengan overflow di sini
     (elemen tanpa padding), scrollbar selalu menempel pas di batas
     atas-bawah backdrop, sementara padding-top tetap membuat isi
     konten punya jarak 20px dari atas seperti biasa. */
  .editor-content-backdrop {
    flex: 1;
    width: 100%;
    max-width: 360px;
    margin: 0 auto;
    height: 215px;
    box-sizing: border-box;
    background-color: #ffffff;
    overflow-y: auto;
    overflow-x: hidden;
    /* [Dihilangkan padding kanan-kiri atas permintaan] -- sebelumnya
       "padding: 10px" (rata 4 sisi). Ini satu-satunya sumber jarak
       kanan-kiri konten editor: #html-content .xa-editor sudah
       di-override padding:0 di rule lain, dan .editor-content sendiri
       juga tidak punya padding, jadi 10px kiri-kanan yang terlihat
       selama ini berasal dari sini. Atas-bawah tetap dipertahankan
       supaya konten tidak langsung mepet ke tepi backdrop secara
       vertikal. */
    padding: 10px 0;
    position: relative;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
  }

  /* Track scrollbar SENGAJA dibuat transparent (bukan warna solid),
     supaya tetap terlihat bagus baik di mode default (backdrop putih)
     maupun dark mode (backdrop #222327) tanpa perlu kotak track
     berwarna yang kontras aneh di salah satu mode. Thumb-nya sendiri
     tetap terlihat jelas di kedua mode (lihat warna abu gelap di sini,
     dan override lebih terang untuk dark mode di bawah). */
  .editor-content-backdrop {
    scrollbar-width: auto; /* Firefox: ukuran normal/default, TIDAK diubah */
    scrollbar-color: rgba(0, 0, 0, 0.35) transparent; /* Firefox: thumb track */
  }

  .editor-content-backdrop::-webkit-scrollbar {
    width: 16px; /* Chrome, Edge, Safari: ukuran normal/default, TIDAK diubah */
  }

  .editor-content-backdrop::-webkit-scrollbar-track {
    background: transparent;
  }

  .editor-content-backdrop::-webkit-scrollbar-thumb {
    background-color: rgba(0, 0, 0, 0.35);
    border-radius: 4px;
  }

  .editor-content-backdrop::-webkit-scrollbar-thumb:hover {
    background-color: rgba(0, 0, 0, 0.5);
  }

  .editor-content {
    flex: 1;
    background: transparent;
    outline: none;
    height: auto;
    min-height: 100%;
    container-type: inline-size;
    container-name: editor-container;
    width: 100%;
    max-width: 100%;
    margin: 0;
    overflow: visible;
    box-sizing: border-box;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    /* CATATAN: celah kosong di bawah konten TIDAK ditaruh sebagai
       padding-bottom di sini. #html-content juga punya class
       "scope-section" (ditempel runtime), dan "#html-content.scope-section"
       punya rule "padding-bottom: 0 !important" (statis DI FILE INI &
       versi identik yang disuntik ulang lewat JS di
       initEditorLensSuppressionStyle(), lihat MANAGED_DESIGN_PROPERTIES/
       buildEditorLensSuppressionCss() di app.js) -- sengaja dipaksa 0
       supaya padding-bottom KONFIGURASI SECTION sendiri (Design tab)
       tidak ikut tampil saat sedang diedit. Selector itu SAMA PERSIS
       ("#html-content.scope-section") & lebih spesifik dari ".editor-content"
       (ID+class vs class saja), jadi padding-bottom apa pun yang ditulis
       di sini akan SELALU kalah -- ditambah versi hasil injeksi JS-nya
       juga selalu ditaruh PALING TERAKHIR di <head> (di-append saat
       runtime), jadi menang cascade order juga. Solusi space bawah
       yang benar-benar berpengaruh: lihat rule ".editor-content::after"
       di bawah (properti "height", bukan "padding" -- tidak kena
       suppression di atas sama sekali). */
  }

  .editor-content:focus {
    outline: none;
  }

  /* Jaminan tampilan superscript/subscript (<sup>/<sub>) selalu benar
     (mengecil + naik/turun dari baseline), baik di editor maupun preview
     section (.cv-section) -- keduanya sama-sama memakai wrapper
     .scope-section. Rule eksplisit ini sengaja ditaruh di sini (bukan
     mengandalkan default browser) supaya tidak bisa ketimpa reset apapun
     yang mungkin ada di css/style.css, karena <link rel="stylesheet"
     href="css/style.css"> dimuat lebih dulu di <head> daripada blok
     <style> ini -- jadi rule di bawah ini otomatis menang di cascade
     tanpa perlu !important. */
  .scope-section sup {
    vertical-align: super;
    font-size: smaller;
    line-height: 0;
  }

  .scope-section sub {
    vertical-align: sub;
    font-size: smaller;
    line-height: 0;
  }

  /* Sama seperti sup/sub di atas: reset.css (Meyer) menolkan margin,
     padding, border, dan font-size SEMUA elemen termasuk blockquote --
     jadi tanpa rule eksplisit ini, <blockquote> hasil tombol toolbar
     akan terlihat identik dengan paragraf biasa meski tag-nya sudah
     benar. Style di bawah cukup umum (border kiri + indent + italic) --
     silakan disesuaikan kalau mau tampilan lain. */
  .scope-section blockquote {
    margin: 1em 0;
    padding: 0.25em 0 0.25em 1em;
    border-left: 4px solid #000000;
    font-style: italic;
    color: #555555;
  }

  /* PENTING: setiap baris blockquote dirender XaEditor/Quill sebagai
     elemen <blockquote> TERPISAH per baris (beda dari <li> yang berbagi
     satu <ol>/<ul>) -- jadi begitu Enter ditekan DI DALAM satu quote
     (lanjut ke baris berikutnya, masih quote yang sama secara isi/niat
     user), hasilnya adalah DUA elemen <blockquote> bersebelahan, bukan
     satu. Tanpa rule di bawah, margin 1em atas-bawah pada rule di atas
     berlaku ke MASING-MASING elemen -- total jadi ~2em jarak + border
     kiri terputus di antara keduanya, sehingga satu kutipan yang
     harusnya menyambung malah terlihat seperti dua kotak kutipan
     terpisah ("putus") hanya karena satu kali Enter.
     Rule ini menghilangkan jarak DI ANTARA dua <blockquote> yang
     langsung bersebelahan (baris hasil split Enter), sambil tetap
     mempertahankan margin normal di sisi LUAR kelompok (sebelum quote
     pertama & sesudah quote terakhir). Border kiri masing-masing baris
     tetap independen -- kalau warnanya ("quotecolor") berbeda per baris,
     itu akan tetap kelihatan sebagai perubahan warna di titik sambungan,
     bukan bug, itu memang tujuan fitur warna per-baris.
     :has() didukung browser modern (Chrome 105+/Safari 15.4+/Firefox
     121+); kalau perlu dukung browser lampau tanpa :has(), cukup pakai
     rule "blockquote + blockquote" saja (jarak SETELAH baris pertama
     hilang, meski jarak SEBELUM baris kedua dari baris pertama tidak
     ikut nol -- hasilnya gap jadi lebih kecil, tidak sepenuhnya nol). */
  .scope-section blockquote + blockquote {
    margin-top: 0;
    padding-top: 0;
  }
  .scope-section blockquote:has(+ blockquote) {
    margin-bottom: 0;
    padding-bottom: 0;
  }

  /* Sembunyikan tampilan background-color & background-image section
     (rule ".text-XX { background-color/background-image: ... }" yang
     di-generate updateCssFromVisualEditor()) KHUSUS di dalam area editor
     WYSIWYG (#html-content). Class ".text-XX" ini ditempel ke #html-content
     sendiri lewat syncEditorScopeClass() supaya style lain (font, warna
     teks, list, spacing, dst -- semuanya ada di selector anak seperti
     ".text-XX p", ".text-XX h1", dsb, BUKAN di root ".text-XX") tetap ikut
     live tampil di editor seperti sebelumnya. Root ".text-XX" itu sendiri
     dipakai untuk background-color/background-image DAN padding (lihat
     updateCssFromVisualEditor()), jadi keduanya di-override di sini:
     hanya menghilangkan background & padding section di area editing,
     sama sekali tidak menyentuh style lainnya. Data section.css TIDAK
     diubah -- tetap tersimpan & tetap tampil normal di preview
     (.cv-section) maupun hasil akhir/export.

     Padding-nya SENGAJA dibuat tidak berlaku di editor (walau ikut
     tersimpan & tetap dipakai di preview/output) karena area editing
     sudah punya jarak/padding sendiri lewat ".editor-content-backdrop"
     (lihat rule-nya di atas) -- padding section tidak perlu dan tidak
     seharusnya ikut menggeser tampilan konten SAAT SEDANG DIEDIT. */
  /* Rule ini sekarang juga DIGENERATE ulang dari registry
     MANAGED_DESIGN_PROPERTIES (lihat buildEditorLensSuppressionCss() &
     initEditorLensSuppressionStyle() di blok script utama) dan disuntik
     ke head dokumen sebagai style id="editor-lens-suppression-style". Rule
     statis di bawah ini SENGAJA dipertahankan sebagai fallback/defense-
     in-depth kalau script itu gagal jalan -- keduanya identik untuk
     properti yang ada sekarang. Kalau menambah properti terkelola baru
     di masa depan, cukup daftarkan di registry (JS) -- TIDAK perlu edit
     rule statis ini secara manual lagi. */
  #html-content.scope-section {
    background-color: transparent !important;
    background-image: none !important;
    padding: 0 !important;
  }

   #html-content .xa-editor {
    padding: 0;
  }

  /* Safety net: matikan pseudo-element placeholder bawaan library
     (dari xaeditor.core.css) supaya tidak dobel dengan rule custom di
     bawah. Tanpa opsi `placeholder` di init (lihat xaeditor-integration.js),
     data-placeholder harusnya tidak pernah ke-set, tapi ini jaga-jaga. */
  #html-content .xa-editor.xa-blank::before {
    content: none !important;
  }

  /* Placeholder "Type here.." saat editor kosong.
     XaEditor (fork Quill) menambahkan class "xa-blank" pada root editor
     ketika kosong (this.editor.isBlank()) — pada kondisi ini isinya cuma
     satu paragraf kosong (<p><br></p>).
     PENTING: pseudo-element ditaruh di *:first-child (paragraf itu
     sendiri), BUKAN di .xa-editor (container). Kalau ditaruh di
     container dengan position:absolute, teks placeholder tidak ikut
     padding/font-size milik <p> (lihat rule .text-${suffix} p yang
     di-generate dinamis: padding 4px 0, font-size ikut variabel per
     section), sehingga posisinya meleset dari titik kursor blink.
     Dengan ditaruh di *:first-child sebagai konten normal-flow (bukan
     absolute), placeholder otomatis mewarisi padding, font-size,
     font-family, line-height, dan posisi baris yang sama persis dengan
     tempat karakter pertama akan diketik -> pas nempel di kursor. */
  /* ===== Video embed (YouTube/Vimeo) =====
     Video selalu disisipkan sebagai Blot bawaan "video" XaEditor ->
     <iframe class="xa-video"> langsung.
     CATATAN: di dalam editor (#html-content.editor-content) iframe ini
     SELALU ditutup total oleh ".editor-video-tap-overlay" (thumbnail
     statis, lihat initEditorVideoHoverToolbar()) supaya video TIDAK
     PERNAH bisa diputar di editor -- hanya tampil sbg gambar diam tanpa
     tombol play. Video hanya bisa benar-benar diputar di section
     preview (.cv-section, lewat applyVideoFacade()) atau di halaman
     publik (preview.php). */
  iframe.xa-video {
    display: block;
    width: 100%;
    max-width: 100%;
    aspect-ratio: 16 / 9;
    /* FIX (permintaan: posisi video upload harus CENTER dengan ukuran
       SAMA seperti upload foto): sebelumnya "margin: 8px 0" (kiri-kanan
       0, BUKAN auto) -- makanya video selalu nempel KIRI di editor
       maupun preview, beda dengan foto (".editor-content img" &
       ".scope-section img") yang kiri-kanannya "auto" supaya center.
       "max-width: 640px" juga dihapus (diganti 100%) karena itu yang
       membuat video punya batas lebar sendiri, berbeda dari foto yang
       lebarnya murni ikut aturan width % di bawah (sama seperti
       ".editor-content img" / ".scope-section img"). Sekarang video
       dan foto sama-sama: lebar ikut container (dibatasi oleh rule
       width% di bawah, konsisten dgn img), dan center lewat
       margin-left/right:auto. Vertikal tetap pakai var(--img-margin-block)
       yang SAMA dengan foto, supaya jarak napas atas-bawah konsisten. */
    margin: var(--img-margin-block) auto;
    overflow: hidden;
    background: #000;
    border: 0;
  }

  /* ===== Facade video (thumbnail + tombol play) =====
     Dipasang oleh applyVideoFacade() menggantikan <iframe class="xa-video">
     SETIAP KALI dirender untuk canvas (.cv-section) & output publik
     (computeSectionOutputMarkup -> preview.php) -- BUKAN di dalam
     #html-content.editor-content (editor tetap iframe langsung seperti
     semula, supaya toolbar hover/wrap/resize video tidak terganggu).
     Tujuan: video YouTube tidak lagi memuat player penuh (iframe berat)
     di page-load pertama -- cukup gambar thumbnail ringan, iframe
     player baru dibuat SAAT diklik.
     Ukuran/posisi disamakan PERSIS dengan rule "iframe.xa-video" di
     atas supaya tidak ada "loncatan" layout saat facade berganti jadi
     iframe sungguhan setelah diklik. */
  .xa-video-facade {
    position: relative;
    display: block;
    width: 100%;
    max-width: 100%;
    aspect-ratio: 16 / 9;
    margin: var(--img-margin-block) auto;
    overflow: hidden;
    background: #000;
    cursor: pointer;
  }

  /* [FIX v5] SEBELUMNYA thumbnail dirender sebagai elemen <img> yang
     diperbesar (135% + position:absolute) lalu di-clip oleh parent
     ".xa-video-facade" (overflow:hidden + border-radius). Ternyata
     BERAPA PUN persentase zoom-nya, tetap muncul garis/noda hitam
     tipis persis di 4 sudut lengkung -- ini BUKAN soal persentase crop,
     tapi bug rendering browser yang dikenal luas: meng-clip elemen
     <img> (raster JPG) yang diperbesar dengan border-radius+overflow:
     hidden bisa membuat browser salah sampling piksel tepat di area
     lengkungan sudutnya.
     FIX: ganti mekanisme sepenuhnya dari <img> ke CSS background-image
     di dalam sebuah <div> (bukan <img>), zoom dilakukan lewat
     "transform: scale()" pada div ini. Pola background-image + transform
     di dalam parent overflow:hidden ini SUDAH LAZIM dipakai utk efek
     "zoom crop" thumbnail/kartu di banyak situs justru karena TIDAK
     kena bug rasterisasi sudut yang sama seperti elemen <img> --
     background CSS di-render lewat jalur berbeda oleh browser. */
  .xa-video-facade-thumb {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }

  /* Selector lama ".scope-section img.xa-video-facade-thumb" /
     ".cv-section img.xa-video-facade-thumb" sudah TIDAK relevan lagi
     -- thumbnail sekarang sebuah <div> (bukan <img>), jadi otomatis
     tidak lagi kena rule generik ".scope-section img" / ".cv-section img"
     sama sekali (selector-nya butuh tag "img", div tidak match). Rule
     override khusus & !important yang dulu dipakai utk menang lawan
     rule generik itu juga sudah tidak diperlukan lagi -- dihapus supaya
     tidak ada CSS mati/membingungkan yang tertinggal. */


  .xa-video-facade-play {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 68px;
    height: 48px;
    transform: translate(-50%, -50%);
    background: rgba(0, 0, 0, 0.7);
    border-radius: 14px;
    pointer-events: none;
  }

  .xa-video-facade-play::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 54%;
    transform: translate(-50%, -50%);
    border-style: solid;
    border-width: 11px 0 11px 19px;
    border-color: transparent transparent transparent #ffffff;
  }

  .xa-video-facade:hover .xa-video-facade-play {
    background: #ff0000;
  }


  /* ===== Overlay video di editor (thumbnail permanen, tanpa play) =====
     Dipasang & diposisikan lewat JS di initEditorVideoHoverToolbar()
     (fungsi overlayFor()/syncOverlays()) -- SELALU pointer-events:auto,
     di SEMUA mode (mouse maupun touch), dan TIDAK PERNAH "dibuka" --
     lihat komentar lengkap di overlayFor(). Absolute relatif ke
     .editor-content-backdrop (position:relative), posisi & ukurannya
     disamakan persis dengan iframe.xa-video di baliknya per video.
     Tujuannya SEKARANG dua: (1) tetap jembatan klik/tap ke toolbar
     hover/wrap/resize (iframe cross-origin tidak meneruskan event klik
     ke dokumen induk), DAN (2) menutup total iframe asli dengan gambar
     thumbnail statis (background-image, lihat applyOverlayThumbnail())
     supaya video HANYA tampil sebagai gambar diam tanpa tombol play --
     video hanya bisa benar-benar diputar di section preview. */
  .editor-video-tap-overlay {
    position: absolute;
    background-color: #000;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    cursor: pointer;
    z-index: 2;
    /* ✅ FIX (kedip biru saat di-tap di HP): overlay ini elemen tap-able
       (punya listener "click"), jadi kena efek highlight semi-transparan
       bawaan browser mobile yang sama seperti bug ".editor-toolbar button"
       dkk di atas -- lihat catatan lengkap di rule "-webkit-tap-highlight-color"
       untuk toolbar. Reset di sini supaya klik/tap di thumbnail video
       tidak lagi memunculkan kedip biru bawaan itu. */
    -webkit-tap-highlight-color: transparent;
    user-select: none;
  }

  #html-content .xa-editor.xa-blank > *:first-child {
    position: relative;
  }

  #html-content .xa-editor.xa-blank > *:first-child::before {
    /* Teks placeholder ikut bahasa aktif lewat custom property
       --placeholder-text (di-set oleh JS di changeLanguage()).
       Fallback "Type here.." dipakai kalau property belum di-set. */
    content: var(--placeholder-text, "Type here..");
    position: absolute;
    top: 0;
    left: 0;
    font-style: normal;
    color: #999;
    pointer-events: none;
  }

  /* PENTING: JANGAN taruh max-width/width/height di sini lagi.
     Sebelumnya rule ini set "max-width: min(60%, 600px)" + "width: auto"
     dengan selector yang SAMA spesifisitasnya dengan ".scope-section img"
     (keduanya single-class descendant selector), dan #html-content
     punya KEDUA class itu sekaligus ("editor-content" dari markup +
     "scope-section" ditempel runtime lewat syncEditorScopeClass()).
     Karena urutan CSS ini di bawah ".scope-section img", rule ini SELALU
     menang & memaksa max-width 60%/600px pada SEMUA gambar di editor --
     termasuk saat sudah di-toggle "full" (width:80% inline lewat
     applySize()/#eit-size-btn): browser tetap membatasi lebar akhirnya
     ke max-width sini, jadi mode kecil & mode penuh terlihat SAMA PERSIS
     (bug "Perbesar/Perkecil Gambar" tidak berefek). Lebar gambar
     sekarang murni dikontrol oleh ".scope-section img" (max-width: 80%
     default) + inline style width yang ditulis applySize() -- BUKAN
     rule ini, supaya togglenya benar-benar kelihatan & konsisten dengan
     apa yang disinkronkan ke section.html/preview (lihat
     PRESERVED_STYLE_PROPS di updateHtmlFromVisualEditor()). max-height
     (dulu 500px, jaga-jaga UX gambar sangat tinggi) SUDAH DIHAPUS --
     lihat catatan di rule .editor-content img di bawah, karena ternyata
     ikut memaksa width mengecil untuk gambar portrait/tinggi. */
  .editor-content img {
    /* max-height DIHAPUS (sebelumnya 500px): kalau ada, browser
       menghitung ulang width lewat algoritma replaced-element -- begitu
       height hasil hitung (dari width% x rasio asli gambar) melebihi
       max-height, width ikut DIPAKSA MENGECIL supaya height pas di batas
       itu. Efeknya, gambar portrait/tinggi jadi lebih kecil dari width
       100% yang diminta, padahal preview/output final (.scope-section img,
       tanpa max-height) tetap tampil 100% apa adanya. Sesuai permintaan
       "width 100%, height menyesuaikan", height sekarang murni auto
       mengikuti rasio asli gambar tanpa batas atas. */
    object-fit: contain;
    /* FIX: default browser utk <img> adalah vertical-align:baseline --
       bagian BAWAH gambar disejajarkan ke garis dasar huruf, bukan ke
       tengah baris. Efeknya, gambar yang disisipkan di tengah kalimat
       (inline embed, lihat insertEmbed() di xaeditor-integration.js)
       terlihat "turun"/tidak center terhadap teks di sampingnya, padahal
       secara struktur DOM gambar itu sudah menyatu persis di titik
       kursor saat insert. vertical-align:middle menyamakan tengah
       gambar dengan titik tengah baris teks -- jauh lebih natural utk
       gambar kecil yang disisipkan di tengah kalimat. */
    vertical-align: middle;
    /* FIX (sebelumnya CUMA disebut di komentar atas, tidak pernah
       benar-benar ditulis sebagai property -- jadi margin ini TIDAK
       PERNAH aktif walau komentarnya bilang begitu): tanpa margin,
       gambar inline yang disisipkan pas di tengah kalimat mepet
       LANGSUNG ke huruf sebelum/sesudahnya (nempel tanpa jarak),
       terlihat menyatu tidak rapi. Nilai dibaca dari var(--img-margin-block)
       & var(--img-margin-inline-editor) yang didefinisikan SEKALI di
       :root (awal <style>) -- bukan angka hardcode -- supaya kalau
       nanti perlu diubah, cukup ubah di satu tempat & otomatis
       konsisten dengan margin-block yang dipakai bareng ".scope-section
       img" (preview). Atas-bawah & kiri-kanan sengaja disamakan
       nilainya (4px) supaya jaraknya konsisten di semua sisi, termasuk
       saat gambar disisipkan di antara baris/paragraf (bukan cuma di
       tengah kalimat).
       CATATAN: TIDAK pakai !important & TIDAK berlaku untuk mode wrap
       kiri/kanan atau align tengah -- applyWrap()/applyAlign() (lihat
       bawah, sekitar baris 20330-20430) menulis margin lewat inline
       style (img.style.marginLeft/marginRight, TIDAK menyentuh
       marginTop/marginBottom), yang SELALU lebih tinggi specificity-nya
       daripada class selector di sini untuk margin-left/right, jadi
       otomatis override kedua sisi itu tanpa konflik. margin-top/
       bottom di sini TETAP AKTIF di semua mode (wrap/align/inline)
       karena tidak ada inline style lain yang menimpanya -- ini
       konsisten dengan prinsip: base margin didefinisikan di level
       paling umum (rule ini), konteks lebih spesifik (wrap/float/
       align-center) override sebagian lewat mekanisme yang sudah ada,
       tanpa perlu duplikasi rule terpisah utk tiap konteks. */
    margin-top: var(--img-margin-block);
    margin-bottom: var(--img-margin-block);
    margin-left: var(--img-margin-inline-editor);
    margin-right: var(--img-margin-inline-editor);
  }


  /* ===== Override VISUAL SAJA di area editing: width 80% (50% saat wrap) ===== */
  /* PENTING (supaya tidak menyesatkan): KEDUA angka di sini (80% & 50%
     pada rule wrap di bawah) HANYA berlaku SAAT SEDANG MENGEDIT di
     dalam #html-content -- sama sekali TIDAK mengubah data tersimpan
     (data-w-mobile/data-w-tablet/data-w-desktop, ditulis
     writeResponsiveImageCss() ke section.css). Preview & hasil akhir/
     export tetap tampil sesuai ukuran yang sudah diatur user lewat
     popup ukuran (default 80/50/50, atau custom) -- sama sekali
     independen dari angka 80%/50% di bawah ini. Tujuannya murni supaya
     SAAT SEDANG MENGEDIT, gambar selalu kelihatan besar & jelas di
     kolom editor (80% lebar kolom secara default), lepas dari berapa
     pun ukuran akhir yang tersimpan -- KECUALI saat gambar di-wrap
     kiri/kanan (data-wrap="left"/"right"), yang lebih kecil lagi (50%)
     supaya ruang untuk teks mengalir di samping gambar kelihatan
     jelas. Lihat rule "[data-wrap]" tepat di bawah rule ini untuk
     pengecualiannya.

     Specificity sengaja dibuat (1,1,1) -- ID "#html-content" + class
     ".editor-content" + tag "img" -- supaya SELALU menang atas rule
     "[data-img-id=\"...\"] { width: X% !important }" (specificity
     (0,1,0)) yang di-inject ke dalam #html-content itu sendiri oleh
     applyMobileMediaQueriesToEditor() (via container query
     @container editor-container). Tanpa specificity lebih tinggi ini,
     rule tsb bisa menang karena datang belakangan di DOM (child
     #html-content, sesudah <style> utama ini), walau sama-sama
     !important. */
  #html-content.editor-content img {
    width: 80% !important;
  }

  /* FIX (permintaan: ukuran video upload di editor SAMA seperti foto):
     video tidak punya mode "wrap kiri/kanan" seperti foto (tidak ada
     tombol Wrap Text untuk video), jadi cukup SATU rule 80% -- tidak
     perlu pengecualian 50% seperti rule img[data-wrap] di atas.
     Specificity (ID + class + tag) disamakan dengan rule img di atas
     supaya konsisten menang atas base rule "iframe.xa-video"
     (width:100%). */
  #html-content.editor-content iframe.xa-video {
    width: 80% !important;
  }

  /* Pengecualian dari rule di atas: gambar yang sedang di-wrap kiri/kanan
     (data-wrap="left"/"right", di-set oleh applyWrap() lewat tombol
     Wrap Text) HARUS lebih kecil dari mode normal (80%) supaya efek
     "teks mengalir di samping gambar" benar-benar kelihatan -- kalau
     tetap 80%, hampir tidak ada sisa ruang di kolom editor untuk teks
     mengalir. Angka 50% di sini HANYA VISUAL KHUSUS EDITOR (sama
     seperti rule 80% di atasnya) -- BUKAN nilai yang ditulis/disimpan
     ke data-w-mobile/data-w-tablet/data-w-desktop, jadi TIDAK
     memengaruhi lebar gambar di preview atau hasil akhir/export.
     Preview/export tetap murni mengikuti ukuran yang di-set user lewat
     popup ukuran (HP/Tablet/PC, lihat applyResponsiveImageSize()) --
     sama sekali independen dari state wrap kiri/kanan ini. Specificity
     selector ini (ID + class + attribute + tag) lebih tinggi dari rule
     ".editor-content img" polos di atas, jadi SELALU menang untuk
     gambar yang sedang wrap, terlepas urutan CSS. Tidak perlu JS
     tambahan: begitu applyWrap() menulis/menghapus attribute
     data-wrap, rule ini otomatis aktif/nonaktif mengikuti. */
  #html-content.editor-content img[data-wrap="left"],
  #html-content.editor-content img[data-wrap="right"] {
    width: 50% !important;
  }

  /* Posisi video (kiri/tengah/kanan) lewat toolbar hover -- SAMA PERSIS
     dengan pola posisi gambar di atas (img[data-wrap="left"/"right"]):
     video yang diposisikan kiri/kanan (float) dibuat lebih kecil (50%)
     supaya efek "teks mengalir di samping video" jelas kelihatan, sama
     seperti gambar. */
  #html-content.editor-content iframe.xa-video[data-wrap="left"],
  #html-content.editor-content iframe.xa-video[data-wrap="right"] {
    width: 50% !important;
  }

  /* FIX: gambar BARU (upload/paste/drag-drop) selalu nempel KIRI di
     editor walau di preview/hasil akhir tampil CENTER. Penyebabnya:
     ".editor-content img" (di atas) menulis margin-left/right FIXED
     (var(--img-margin-inline-editor), 4px) -- rule ini dibutuhkan utk
     kasus gambar inline di tengah kalimat supaya ada jarak ke huruf
     kiri-kanannya. Tapi karena ".scope-section img" (margin: auto,
     yang menengahkan gambar di preview) & ".editor-content img" sama-
     sama specificity (0,1,1) dan class "editor-content" ditulis
     BELAKANGAN di file ini, rule 4px-nya SELALU menang di editor --
     gambar block biasa (bukan cuma yang benar-benar inline) ikut
     kena, jadi selalu nempel kiri, padahal normalizeNewImage() tidak
     pernah menulis align "left" secara eksplisit lewat JS.
     Fix: override balik ke "auto" KHUSUS utk gambar yang TIDAK sedang
     di-wrap kiri/kanan (data-wrap="left"/"right" dikecualikan lewat
     :not(), supaya mode wrap yg butuh margin asimetris -- lihat
     applyWrap() -- tetap tidak terganggu). Specificity selector ini
     (ID + class + 2x :not() + tag) lebih tinggi dari ".editor-content
     img" polos, jadi selalu menang untuk kasus normal/block, dan
     editor jadi konsisten dengan preview (".scope-section img"). */
  #html-content.editor-content img:not([data-wrap="left"]):not([data-wrap="right"]) {
    margin-left: auto !important;
    margin-right: auto !important;
  }

  /* FIX: clearfix -- diperlukan supaya .editor-content (yang punya
     background) tidak collapse saat isinya ada gambar float (Wrap
     Text: kiri/kanan, lihat applyWrap() di bawah). Float dikeluarkan
     dari normal flow, jadi tanpa ini tinggi container tidak ikut
     menghitung gambar yang di-float, dan background terlihat terpotong
     padahal gambar masih "menggantung" keluar secara visual. */
  /* ::after ini awalnya cuma clearfix (content:""+display:table+clear:both).
     Sekarang JUGA dipakai untuk memberi celah kosong di BAWAH konten
     terakhir (foto, teks, dst) di editor -- lewat "height", BUKAN
     "padding". Kenapa harus lewat ::after dan bukan padding-bottom di
     ".editor-content"/"#html-content": #html-content juga punya class
     "scope-section" (ditempel runtime), dan rule
     "#html-content.scope-section { padding-bottom: 0 !important; }"
     (lihat rule-nya + versi ter-inject JS-nya di bawah, dekat komentar
     MANAGED_DESIGN_PROPERTIES) SELALU memaksa padding-bottom balik ke 0
     -- baik karena selector itu lebih spesifik (ID+class) maupun karena
     versi hasil injeksi JS-nya selalu ditaruh paling akhir di <head>.
     "height" pada ::after tidak tersentuh rule itu sama sekali (beda
     properti), jadi space di sini AMAN dari suppression tsb & tetap
     konsisten kalau section punya padding-bottom sendiri yang memang
     sengaja disembunyikan saat mode edit.

     CATATAN (klik di area ini): ::after adalah generated content, BUKAN
     node DOM sungguhan -- klik di sini TIDAK otomatis menaruh cursor di
     akhir konten (algoritma placement-cursor bawaan browser cuma bekerja
     kalau klik kena node yang benar-benar bisa diedit). Supaya klik di
     celah ini tetap menaruh cursor, ada handler khusus di app.js --
     lihat initEditorClickBelowContentFocus() (dipanggil sebarengan
     initEditorImageHoverToolbar()/initEditorVideoHoverToolbar()). */
  .editor-content::after {
    content: "";
    display: table;
    clear: both;
    height: 30px;
  }

  /* ===== Toolbar hover-gambar (Alignment + Hapus) =====
     Muncul di ATAS gambar saat gambar di-hover. Bentuknya
     pill putih rounded dengan panah kecil (::before/::after) yang
     menunjuk ke bawah, ke arah gambar -- gaya & warnanya disamakan
     dengan popup dropdown lain di situs (border #ddd/#444, box-shadow
     lembut, aksen biru #03befc). Posisi top/left dihitung & di-set
     lewat JS (initEditorImageHoverToolbar()) supaya selalu menempel
     tepat di atas gambar yang sedang aktif, termasuk saat scroll. */
  .editor-image-toolbar {
    /* Posisi dihitung & di-set lewat JS (positionToolbar() di
       initEditorImageHoverToolbar()), relatif ke .editor-content-backdrop
       (position:relative) -- toolbar tetap "absolute" supaya scroll-nya
       native/mulus mengikuti backdrop tanpa perlu JS reposisi per-frame.
       Supaya TIDAK kepotong overflow-x:hidden backdrop saat gambar dekat
       tepi kiri/kanan, posisi horizontalnya di-CLAMP di JS (box digeser
       secukupnya ke kiri/kanan supaya tetap utuh terlihat). Karena box
       jadi bisa tidak center persis di atas gambar lagi, PANAH
       (::after) posisinya TIDAK lagi hardcode left:50%, tapi pakai
       custom property --eit-arrow-left yang di-set JS supaya tetap
       menunjuk pas ke tengah gambar walau box-nya sudah digeser. */
    position: absolute;
    display: none;
    align-items: center;
    gap: 2px;
    padding: 3px;
    background: #ffffff;
    border-radius: 2px;
    /* Pakai filter: drop-shadow (bukan box-shadow) supaya bayangannya
       mengikuti bentuk asli toolbar TERMASUK segitiga panahnya. Kalau
       pakai box-shadow biasa, bayangan cuma nempel di sisi kotak persegi
       toolbar-nya saja, dan garis sisi kotak itu jadi kelihatan
       memotong pas di pangkal segitiga -- keliatan seperti segitiganya
       punya border/garis tepi sendiri, padahal itu cuma bayangan kotak. */
    filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.18));
    z-index: 40;
    box-sizing: border-box;
  }

  .editor-image-toolbar.visible {
    display: flex;
  }

  /* Panah kecil penunjuk -- dibuat dari KOTAK KECIL yang dirotasi 45°
     (bukan trik border-triangle seperti sebelumnya). Alasan ganti
     teknik: versi border-triangle lama tidak punya "border" beneran di
     2 sisi miringnya -- sisi itu cuma hasil bentuk segitiga dari 3
     border solid, jadi TIDAK BISA dikasih outline/border sendiri tanpa
     trik "2 segitiga ditumpuk" yang hasilnya kurang tegas (selisih
     lebar 1px jadi ~0.7px doang di sisi diagonal 45°, gampang
     ke-antialias hilang -- makanya versi lama nyaris tidak kelihatan
     border-nya di dark mode). Kotak dirotasi ini punya `border` asli
     di 2 sisinya (border-right + border-bottom), solid & konsisten di
     ukuran berapa pun.

     Kenapa border-right + border-bottom (bukan sisi lain): kotak
     dirotasi rotate(45deg) membuat sudut kanan-bawah (BR) jadi UJUNG
     BAWAH si diamond -- 2 sisi yg nyambung ke sudut BR itu persis sisi
     "right" & "bottom" si kotak sebelum dirotasi. Makanya 2 sisi itu
     yang dikasih border supaya muncul di ujung lancip yang mengarah ke
     bawah (2 sudut lancip yang dimaksud), sisi lain (top/left) sengaja
     TIDAK dikasih border karena itu bagian yang "ketutup" toolbar (ada
     di atas garis top:100%, nyaru sama background toolbar).

     Ukuran width/height 4.95px dipilih supaya kedalaman ujung panah
     (jarak dari tepi box ke ujung lancip) = 4.95 * cos(45°) = 3.5px --
     PERSIS sama seperti versi lama, jadi TOOLBAR_ARROW_HEIGHT = 3.5 di
     positionToolbar() (js/app.js, dipakai baik utk toolbar gambar
     maupun video) TETAP VALID tanpa perlu diubah. Kalau ukuran ini
     diubah, turunkan lagi nilai TOOLBAR_ARROW_HEIGHT-nya dengan rumus
     yang sama (width * cos(45°)). */
  .editor-image-toolbar::after {
    content: "";
    position: absolute;
    top: 100%;
    left: var(--eit-arrow-left, 50%);
    width: 4.95px;
    height: 4.95px;
    box-sizing: border-box;
    background: #ffffff;
    border-right: 1px solid transparent;
    border-bottom: 1px solid transparent;
    transform: translate(-50%, -50%) rotate(45deg);
  }

  /* Tiap tombol (Wrap Text / Alignment / Hapus) sekarang jadi "pill"
     sendiri dengan border tipis (bukan dipisah garis divider seperti
     sebelumnya), meniru referensi. */
  .eit-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    height: 26px;
    padding: 0 6px;
    background: #ffffff;
    border: none;
    outline: none;
    appearance: none;
    -webkit-appearance: none;
    border-radius: 2px;
    cursor: pointer;
    color: #444;
    font-size: 11px;
    box-sizing: border-box;
    transition: background 0.15s ease, color 0.15s ease;
  }

  .eit-btn:hover {
    background: rgba(3, 190, 252, 0.1);
  }

  /* Tombol icon-only (Hapus) -- persegi, tanpa caret/label */
  .eit-btn.eit-icon-only {
    width: 26px;
    padding: 0;
    justify-content: center;
  }

  .eit-group {
    position: relative;
  }

  .eit-group.open .eit-btn {
    background: rgba(3, 190, 252, 0.15);
    color: #03befc;
  }

  .eit-caret {
    font-size: 7px;
    opacity: 0.6;
  }

  .eit-delete-btn {
    color: #e03131;
  }

  .eit-delete-btn:hover {
    background: #fff0f0;
  }

  /* Dropdown pilihan (Wrap Text & Alignment) -- baris horizontal
     berisi kotak-kotak kecil (bukan list vertikal lagi), menempel
     tepat di bawah tombolnya sendiri (top:100%, left:0). */
  .eit-options {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    margin: 6px 0 0;
    padding: 3px;
    gap: 3px;
    list-style: none;
    background: #ffffff;
    border-radius: 2px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
    z-index: 41;
    box-sizing: border-box;
  }

  .eit-group.open .eit-options {
    display: flex;
  }

  .eit-options li {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 2px;
    color: #444;
    font-size: 13px;
    cursor: pointer;
    box-sizing: border-box;
    transition: background 0.15s ease, color 0.15s ease;
  }

  .eit-options li:hover {
    background: rgba(3, 190, 252, 0.1);
  }

  .eit-options li.active {
    background: rgba(3, 190, 252, 0.12);
    color: #03befc;
  }

  /* Ikon toolbar Wrap Text sekarang pakai file PNG Material Symbols
     yang disimpan lokal di assets/images/material/ (bukan inline SVG atau font
     lagi). Varian kanan pakai file yang sama dengan varian kiri kalau
     memang mirror, atau file terpisah kalau sudah tersedia. */
  /* Tombol tunggal ukuran gambar (pengganti dropdown Alignment lama):
     toggle antara mode kecil (default, lebar mengikuti gambar asli,
     dibatasi max-width 80% lewat .scope-section img) dan mode penuh
     (lebar dipaksa 80%, sama dengan batas max-width itu). */
  .eit-size-btn.active {
    background: rgba(3, 190, 252, 0.15);
    color: #03befc;
  }

  /* Sama gaya dengan .eit-size-btn.active -- nyala biru saat posisi
     gambar (wrap) SEDANG BUKAN default (tengah), ditoggle lewat
     setActiveWrap() setiap gambar diklik/opsi wrap diganti. */
  .eit-wrap-btn.active {
    background: rgba(3, 190, 252, 0.15);
  }

  .eit-icon {
    display: block;
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    object-fit: contain;
  }

  /* Popup kecil untuk isi Alt Text (SEO), dibuka dari tombol
     picture_in_picture_alt.png. Sengaja pakai <input> polos (bukan
     dropdown pilihan seperti .eit-options) karena isinya teks bebas.
     Disimpan ke attribute alt gambar saat Enter/blur/klik luar --
     lihat initEditorImageHoverToolbar() -> saveAltText(). */
  .eit-alt-popup {
    display: none;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin: 3px 0 0;
    padding: 3px;
    background: #ffffff;
    border-radius: 2px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
    z-index: 41;
    box-sizing: border-box;
  }

  /* Popup Ukuran Gambar Responsif (HP/Tablet/PC) -- struktur & posisi
     SENGAJA dibuat identik dengan .eit-alt-popup (position:absolute,
     center relatif ke toolbar penuh) supaya konsisten & bisa pakai
     fungsi positioning yang sama (lihat positionSizePopup() di
     initEditorImageHoverToolbar()). */
  .eit-size-popup {
    display: none;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin: 3px 0 0;
    padding: 3px;
    background: #ffffff;
    border-radius: 2px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
    z-index: 41;
    box-sizing: border-box;
  }

  .eit-size-popup.open {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  .eit-size-field {
    display: flex;
    align-items: center;
    gap: 4px;
    height: 26px;
    padding: 0 4px;
    background: #ffffff;
    border-radius: 2px;
    box-sizing: border-box;
    transition: background-color 0.15s ease;
  }

  /* Menandai field device (HP/Tablet/PC) yang sedang cocok dengan mode
     preview aktif -- disinkronkan dari setPreviewMode() lewat
     highlightSizeFieldForMode()/window.syncEitSizeInputsWithMode(),
     baik dipicu dari klik ikon footer, tab Section Padding, maupun
     klik/fokus ke field ini sendiri. Warna aksen (#03befc) sengaja
     dibuat konsisten dengan .padding-device-tab.active supaya ketiga
     kontrol (footer, tab padding, field ukuran gambar) terlihat "satu
     sistem" yang sama saat disinkronkan. */
  .eit-size-field.eit-size-field-active {
    background: rgba(3, 190, 252, 0.12);
  }

  .eit-size-field.eit-size-field-active label {
    color: #03befc;
  }

  .eit-size-field label {
    display: flex;
    align-items: center;
    color: #777;
    font-size: 12px;
  }

  .eit-size-input {
    /* Tinggi disamakan dengan .eit-options li (24px, border-radius 2px)
       di sebelahnya supaya terlihat satu set/konsisten. Lebar dibuat
       lebih besar dari 24px supaya angka 3 digit (mis. "100") tidak
       terpotong. */
    width: 34px;
    height: 24px;
    padding: 0 2px;
    background: #ffffff;
    border: 1px solid #ddd;
    border-radius: 2px;
    outline: none;
    color: #333;
    font-size: 12px;
    text-align: center;
    box-sizing: border-box;
    /* Sembunyikan spinner bawaan browser (nomor kecil naik/turun) supaya
       lebih ringkas di dalam popup yang sudah padat. */
    -moz-appearance: textfield;
  }

  .eit-size-input::-webkit-outer-spin-button,
  .eit-size-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }

  .eit-size-input:focus {
    border-color: #03befc;
  }

  .eit-size-pct {
    color: #999;
    font-size: 12px;
  }

  body.dark-mode .eit-size-popup {
    background: #3a3a40;
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  }

  body.dark-mode .eit-size-field label,
  body.dark-mode .eit-size-pct {
    color: #aaa;
  }

  body.dark-mode .eit-size-field {
    background: #3a3a40;
  }

  body.dark-mode .eit-size-field.eit-size-field-active {
    background: #4a4a52;
  }

  body.dark-mode .eit-size-field.eit-size-field-active label {
    color: #fff;
  }

  body.dark-mode .eit-size-input {
    background: #1f1f1f;
    border-color: #444;
    color: #eee;
  }

  .eit-alt-popup.open {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .eit-alt-input {
    width: 180px;
    height: 26px;
    padding: 0 8px;
    background: #ffffff;
    border: 1px solid #ddd;
    border-radius: 2px;
    outline: none;
    color: #333;
    font-size: 12px;
    box-sizing: border-box;
  }

  .eit-alt-input:focus {
    border-color: #03befc;
  }

  /* Dikunci read-only saat alt text sudah terisi (lihat setAltLocked()
     di initEditorImageHoverToolbar()) -- dibuat kelihatan "non-aktif"
     supaya user sadar harus klik tombol edit dulu sebelum bisa mengubah. */
  .eit-alt-input[readonly] {
    background: #f2f2f2;
    color: #777;
    cursor: default;
  }

  .eit-alt-input[readonly]:focus {
    border-color: #ddd;
  }

  /* Tombol edit di sebelah kanan input -- default tersembunyi, cuma
     muncul saat popup dalam state "is-locked" (input readonly). */
  .eit-alt-edit-btn {
    display: none;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex: 0 0 auto;
    padding: 0;
    background: #ffffff;
    border: 1px solid #ccc;
    border-radius: 2px;
    color: #555;
    font-size: 12px;
    cursor: pointer;
  }

  /* Disamakan dengan tampilan NYATA tombol pena Insert Link
     (#link-edit-btn) -- BUKAN nilai mentah di ".link-action-btn:hover"
     (background:#e6e6e6), karena di tombol itu ada rule global
     ".editor-toolbar button:hover" (baris ~3294) yang spesifisitasnya
     lebih tinggi (class+element+pseudo-class > class+pseudo-class) dan
     MENIMPANYA jadi biru. #eit-alt-edit-btn ada di luar .editor-toolbar
     (di #editor-image-toolbar, sibling-nya), jadi tidak otomatis kena
     rule global itu -- makanya di sini disalin manual nilai efektifnya
     supaya tampilannya konsisten dengan yang benar-benar terlihat di
     tombol pena Insert Link. */
  .eit-alt-edit-btn:hover {
    background: rgba(3, 190, 252, 0.25);
    border-color: rgba(3, 190, 252, 0.25);
  }

  .eit-alt-popup.is-locked .eit-alt-edit-btn {
    display: inline-flex;
  }

  /* Nyala biru saat gambar aktif sudah punya alt text -- indikator
     visual cepat supaya kelihatan gambar mana yang belum diisi
     (penting buat SEO), sama gayanya dengan .eit-size-btn.active. */
  .eit-alt-btn.has-alt {
    background: rgba(3, 190, 252, 0.15);
    color: #03befc;
  }

  .eit-icon.flip {
    transform: scaleX(-1);
  }

  body.dark-mode .editor-image-toolbar {
    /* Sebelumnya #2a2a2e -- kelihatan nyaris nyatu sama background
       editor di dark mode yg warnanya #222327 (kontrasnya terlalu
       tipis). Dinaikkan ke abu-abu lebih terang + tambah border tipis
       supaya toolbar-nya jelas kelihatan menonjol di atas kontennya. */
    background: #3a3a40;
    border: 1px solid rgba(255, 255, 255, 0.1);
  }

  /* [Border panah dark mode] Karena ::after sekarang dibuat dari kotak
     dirotasi (bukan trik border-triangle lama), border-nya tinggal
     diwarnai langsung lewat border-right-color & border-bottom-color
     (2 sisi yang sama persis dipakai versi light-mode di atas, cuma
     warnanya diganti dari transparan ke rgba(255,255,255,0.1) --
     senada border popup dark mode lain: body.dark-mode
     .editor-image-toolbar & .eit-alt-popup). Ini border ASLI (properti
     `border` beneran), jadi tampil solid & konsisten 1px di sepanjang
     2 sisi miringnya, tidak seperti trik "2 segitiga ditumpuk" yang
     sempat dicoba sebelumnya (hasilnya nyaris tak kelihatan karena
     selisih ukurannya kepotong sudut 45°). */
  body.dark-mode .editor-image-toolbar::after {
    background: #3a3a40;
    border-right-color: rgba(255, 255, 255, 0.1);
    border-bottom-color: rgba(255, 255, 255, 0.1);
  }

  body.dark-mode .eit-icon {
    /* PNG-nya sendiri warnanya gelap/hitam (bukan pakai currentColor
       kayak font/SVG), jadi supaya kelihatan di dark mode dipaksa
       putih lewat filter: brightness(0) bikin jadi hitam solid dulu,
       invert(1) baru balik jadi putih. Asumsinya file PNG-nya
       monokrom (satu warna) di atas background transparan. */
    filter: brightness(0) invert(1);
  }

  body.dark-mode .eit-btn {
    color: #ddd;
    background: #3a3a40;
  }

  body.dark-mode .eit-btn:hover {
    background: rgba(255, 255, 255, 0.1);
  }

  body.dark-mode .eit-group.open .eit-btn {
    background: rgba(255, 255, 255, 0.24);
    color: #ffffff;
  }

  body.dark-mode .eit-size-btn.active {
    background: rgba(255, 255, 255, 0.24);
    color: #ffffff;
  }

  body.dark-mode .eit-wrap-btn.active {
    background: rgba(255, 255, 255, 0.24);
  }

  body.dark-mode .eit-alt-popup {
    background: #3a3a40;
    border: 1px solid rgba(255, 255, 255, 0.1);
  }

  body.dark-mode .eit-alt-input {
    background: #2a2a2e;
    border-color: #555;
    color: #eee;
  }

  body.dark-mode .eit-alt-input[readonly] {
    background: #333338;
    border-color: #555;
    color: #999;
  }

  /* Disamakan dengan tampilan NYATA #link-edit-btn di dark mode --
     rule global "body.dark-mode .editor-toolbar button:hover" (baris
     ~5206, spesifisitas lebih tinggi) menimpa ".link-action-btn:hover"
     jadi rgba(255,255,255,0.08), bukan #333. Border tetap netral #444
     (tidak ikut berubah), sama seperti aslinya. */
  body.dark-mode .eit-alt-edit-btn {
    background: #2a2a2e;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .eit-alt-edit-btn:hover {
    background: rgba(255, 255, 255, 0.08);
  }

  body.dark-mode .eit-alt-btn.has-alt {
    background: rgba(255, 255, 255, 0.24);
    color: #ffffff;
  }

  body.dark-mode .eit-delete-btn {
    color: #ff6b6b;
  }

  body.dark-mode .eit-delete-btn:hover {
    background: rgba(255, 107, 107, 0.15);
  }

  body.dark-mode .eit-options {
    background: #3a3a40;
    border: 1px solid rgba(255, 255, 255, 0.1);
  }

  body.dark-mode .eit-options li {
    color: #ddd;
  }

  body.dark-mode .eit-options li:hover {
    background: rgba(255, 255, 255, 0.1);
  }

  body.dark-mode .eit-options li.active {
    background: rgba(255, 255, 255, 0.24);
    color: #ffffff;
  }

  .style-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 15px;
    padding: 10px;
    max-width: 100%;
    box-sizing: border-box;
    background: #f6f7f9;
    border-radius: 8px;
  }

  .style-control-group {
    display: flex;
    flex-direction: column;
    min-width: 120px;
  }

  .style-control-group label {
    font-size: 12px;
    margin-bottom: 5px;
    color: #555;
  }

  .color-picker {
    display: inline-flex;
    align-items: center;
    margin-right: 10px;
  }

  .color-picker input[type="color"] {
    width: 25px;
    height: 25px;
    padding: 0;
    border: 1px solid #ddd;
    margin-right: 5px;
  }

  @container preview-container (max-width: 768px) {
    .editor-toolbar {
      padding: 5px;
    }

    .editor-toolbar button {
      padding: 4px 8px;
      font-size: 11px;
    }

    .style-controls {
      gap: 5px;
    }

    .style-control-group {
      min-width: 100px;
    }
  }

  /*Html, Css, Js*/
  .settings-tabs {
    position: relative;
    display: flex;
    gap: 0;
    margin: 24px 0 0;
    width: 100%;
    border-bottom: 1px solid #e5e5e5;
    box-sizing: border-box;
  }

  .tab-button {
    display: flex;
    flex: 1 1 0;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 6px 2px 12px;
    background: transparent;
    border: none;
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    color: #9a9a9a;
    position: relative;
    transition: color 0.2s ease;
    user-select: none;
  }

  .tab-button i {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    font-size: 13px;
    opacity: 0.5;
    transition: opacity 0.2s ease;
  }

  .tab-button[data-tab="html"] i {
    color: #e34c26;
  }

  .tab-button[data-tab="css"] i {
    color: #2965f1;
  }

  .tab-button[data-tab="js"] i {
    color: #c9a227;
  }

  .tab-button:hover:not(.active) {
    color: #333;
  }

  .tab-button:hover:not(.active) i {
    opacity: 0.85;
  }

  .tab-button.active {
    color: #1a1a1a;
    font-weight: 600;
  }

  .tab-button.active i {
    opacity: 1;
  }

  /* Indikator underline tunggal yang berpindah (slide kiri/kanan) dari
     satu tab ke tab lain, BUKAN membesar dari tengah (scaleX) seperti
     sebelumnya. Posisi & lebarnya diatur lewat JS (lihat
     moveSettingsTabsIndicator()) berdasarkan bounding box tombol yang
     sedang aktif, lalu transisi "left"/"width" yang membuat efek
     bergesernya terlihat menyamping. */
  .settings-tabs-indicator {
    position: absolute;
    bottom: 0;
    height: 2px;
    border-radius: 2px;
    background: #e34c26;
    transition: left 0.25s ease, width 0.25s ease, background-color 0.25s ease;
    pointer-events: none;
  }

  .tab-content {
    display: none;
    padding: 15px 0;
  }

  .tab-content.active {
    display: block;
  }

  /* Header baris label + icon perbesar (expand) untuk tiap code editor
     (HTML Structure / Custom CSS / Custom JavaScript). Label tetap
     memakai style aslinya (.settings-option label), hanya margin-bottom
     dipindah ke wrapper ini supaya sejajar dengan icon-nya. */
  .settings-option-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 5px;
  }

  .settings-option-header label {
    margin-bottom: 0;
    padding-bottom: 10px;
  }

  .code-expand-icon {
    font-size: 13px;
    color: #777;
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 3px;
    transition: all 0.2s ease;
  }

  .code-expand-icon:hover {
    color: #007bff;
    background-color: rgba(0, 123, 255, 0.1);
  }

  body.dark-mode .code-expand-icon {
    color: #aaa;
  }

  body.dark-mode .code-expand-icon:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.08);
  }

  /* Wrapper posisi utk icon perbesar yg dipindah ke DALAM input
     (pojok kanan-atas code editor). Sengaja dibuat wrapper BARU
     (bukan position:relative langsung di div#section-html) supaya
     icon tetap SIBLING dari div editor, bukan child-nya -- karena
     openCodeEditorModal() me-reparent div#section-html/css/js
     (persis elemen itu) ke dalam modal saat diklik. Kalau icon jadi
     child dari div editor, dia bakal ikut ke-drag ke modal juga.
     Wrapper ini yg jadi anchor position:relative-nya. */
  .code-editor-wrap {
    position: relative;
  }

  /* NOTE PENTING: .xamirror (wrapper asli dari lib xamirror, lihat
     assets/library/xamirror/lib/xamirror.css) sudah punya
     "position: relative; overflow: hidden;" bawaan. Karena overflow
     hidden itu, icon TIDAK BOLEH ditaruh sebagai child di dalam
     .xamirror -- dia akan kepotong. Makanya icon di sini posisinya
     absolute relatif ke .code-editor-wrap (parent di LUAR .xamirror),
     lalu "mengambang" di atas editor pakai z-index. z-index tertinggi
     yang dipakai internal xamirror.css cuma 6 (punya scrollbar, yang
     sudah disembunyikan lewat display:none!important di atas), jadi
     20 di sini lebih dari cukup supaya icon selalu di atas. */
  .code-editor-wrap .code-expand-icon {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 20;
    background-color: rgba(255, 255, 255, 0.85);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  }

  .code-editor-wrap .code-expand-icon:hover {
    background-color: rgba(0, 123, 255, 0.15);
  }

  body.dark-mode .code-editor-wrap .code-expand-icon {
    background-color: rgba(40, 40, 40, 0.85);
  }

  body.dark-mode .code-editor-wrap .code-expand-icon:hover {
    background-color: rgba(255, 255, 255, 0.08);
  }

  /* Icon "Copy" di editor kecil (.code-editor-wrap) -- ditaruh di
     SEBELAH KIRI icon download & expand, pakai class .code-expand-icon
     yg sama supaya warna/hover/ukuran konsisten, cuma beda posisi
     horizontal. */
  .code-editor-wrap .code-copy-icon {
    right: 62px;
  }

  /* Icon "Download" di editor kecil -- ditaruh di SEBELAH KANAN icon
     copy (di antara icon copy & icon expand), pakai class
     .code-expand-icon yg sama juga. */
  .code-editor-wrap .code-download-icon {
    right: 34px;
  }

  /* Icon "Search" di editor kecil (.code-editor-wrap) -- ditaruh di
     SEBELAH KIRI icon copy, jarak 28px (62 -> 90) supaya konsisten
     dgn jarak antar icon copy/download/expand yg lain, PERSIS pola yg
     sama dipakai icon Search di modal Perbesar (lihat
     #code-editor-modal-search-icon: 28px di kiri
     #code-editor-modal-copy-icon). Klik icon ini memanggil
     openSectionSearch() yg meneruskan ke window.openXamirrorSearch(),
     fungsi yg sama persis dipakai Ctrl+F & icon search modal. */
  .code-editor-wrap .code-search-icon {
    right: 90px;
  }

  /* Icon Copy di dalam MODAL Perbesar Code Editor -- sengaja dibuat
     overlay-absolute PERSIS gaya yang sama dengan icon di
     .code-editor-wrap (nempel di pojok kanan-atas editor, bukan di
     header modal), supaya konsisten & benar-benar terasa "di dalam
     input". Anchor position:relative-nya adalah .modal-body milik
     modal ini (lihat rule .code-editor-modal-content .modal-body di
     bawah).
     PENTING: top TIDAK BOLEH dihitung dari padding-top modal-body saja
     (8px), karena baris tab HTML/CSS/JS (#code-editor-modal-tabs,
     order:-1) tampil PALING ATAS di dalam modal-body (flex column) --
     editor (div#section-html/css/js) baru mulai SETELAH baris tab itu.
     Diukur langsung: tabs setinggi ~33px + margin-bottom 10px, jadi
     editor mulai di 8 + 33 + 10 = 51px dari atas modal-body. Tambah
     6px (jarak yang sama seperti editor kecil) => top: 57px. (Sempat
     58px/64px di iterasi sebelumnya saat padding-top modal-body masih
     15px; sekarang padding-top sudah dikurangi jadi 8px supaya garis
     tab naik sedikit, jadi angka ini ikut disesuaikan turun.) Kalau ini
     tidak disesuaikan, icon nemplok di garis pembatas bawah tab, bukan
     mengambang di pojok kanan-atas KOTAK EDITOR seperti seharusnya.
     right DIGESER 16px lebih jauh ke kiri dari versi sebelumnya (26px
     -> 42px utk download, 54px -> 70px utk copy) supaya kedua icon
     tidak menutupi scrollbar vertikal khusus modal (.xamirror-vscrollbar,
     lihat rule-nya di bawah) yang nempel di tepi kanan kotak editor --
     16px dipilih karena sama dengan lebar scrollbar custom 16px yang
     sudah dipakai di tempat lain pada file ini (.editor-content-backdrop
     ::-webkit-scrollbar width:16px), jadi cukup memberi jarak aman. */
  #code-editor-modal-copy-icon {
    position: absolute;
    top: 57px;
    right: 70px;
    z-index: 20;
    background-color: rgba(255, 255, 255, 0.85);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  }

  #code-editor-modal-copy-icon:hover {
    background-color: rgba(0, 123, 255, 0.15);
  }

  body.dark-mode #code-editor-modal-copy-icon {
    background-color: rgba(40, 40, 40, 0.85);
  }

  body.dark-mode #code-editor-modal-copy-icon:hover {
    background-color: rgba(255, 255, 255, 0.08);
  }

  /* Icon Search di dalam modal Perbesar -- SEBELAH KIRI icon copy,
     gaya & anchor sama persis (lihat catatan di atas
     #code-editor-modal-copy-icon). Jarak antar icon dibuat 28px
     (70 -> 98) supaya konsisten dgn jarak antar icon copy/download/
     expand di editor kecil (.code-editor-wrap: 62/34/6, juga 28px). */
  #code-editor-modal-search-icon {
    position: absolute;
    top: 57px;
    right: 98px;
    z-index: 20;
    background-color: rgba(255, 255, 255, 0.85);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  }

  #code-editor-modal-search-icon:hover {
    background-color: rgba(0, 123, 255, 0.15);
  }

  body.dark-mode #code-editor-modal-search-icon {
    background-color: rgba(40, 40, 40, 0.85);
  }

  body.dark-mode #code-editor-modal-search-icon:hover {
    background-color: rgba(255, 255, 255, 0.08);
  }

  /* Icon Download di dalam modal Perbesar -- SEBELAH KANAN icon copy,
     gaya & anchor sama persis (lihat catatan di atas #code-editor-modal-copy-icon,
     termasuk alasan top:57px bukan lagi 21px/64px, dan right:42px yg
     sudah digeser 16px ke kiri dari 26px sebelumnya supaya tidak
     menutupi scrollbar vertikal .xamirror-vscrollbar). */
  #code-editor-modal-download-icon {
    position: absolute;
    top: 57px;
    right: 42px;
    z-index: 20;
    background-color: rgba(255, 255, 255, 0.85);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  }

  #code-editor-modal-download-icon:hover {
    background-color: rgba(0, 123, 255, 0.15);
  }

  body.dark-mode #code-editor-modal-download-icon {
    background-color: rgba(40, 40, 40, 0.85);
  }

  body.dark-mode #code-editor-modal-download-icon:hover {
    background-color: rgba(255, 255, 255, 0.08);
  }

  /* Saat editor sedang dipindah (reparent) ke dalam modal Perbesar,
     wrapper aslinya jadi kosong (cuma sisa icon mengambang tanpa
     editor di baliknya). Class ini ditambahkan oleh
     openCodeEditorModal()/closeCodeEditorModal() lewat JS supaya
     icon disembunyikan sementara selama modal terbuka. */
  .code-editor-wrap.editor-detached .code-expand-icon {
    display: none;
  }

  /* Modal Perbesar Code Editor -- pakai kerangka .modal-overlay/.modal-content
     yang sama persis dengan modal "Pilih Seksi" (#add-section-modal), hanya
     ukurannya dibuat lebih lebar & tinggi supaya nyaman dipakai mengedit
     kode, dan modal-body dibuat flex-column supaya editor mengisi penuh. */
  /* Modal Perbesar Code Editor -- pakai kerangka .modal-overlay/.modal-content
     yang sama persis dengan modal "Pilih Seksi" (#add-section-modal), hanya
     ukurannya dibuat lebih lebar & tinggi supaya nyaman dipakai mengedit
     kode, dan modal-body dibuat flex-column supaya editor mengisi penuh.
     Diperbesar jadi 85% dari tampilan layar (lebar & tinggi), max-width
     900px lama dilepas supaya benar-benar bisa mencapai 85vw di layar
     besar. */
  /* Modal Perbesar Code Editor -- jarak ke tepi layar diatur eksplisit
     per-sisi (bukan lagi persentase lebar/tinggi yang di-center flex):
     atas tetap sama seperti sebelumnya (dulu di-center via 85vh, jadi
     jarak atasnya = (100vh - 85vh) / 2 = 7.5vh, dipertahankan di sini),
     bawah dipepetkan jadi cuma 10px, kiri & kanan jadi 40px. Karena
     posisinya sekarang "position: fixed" dengan 4 sisi diisi sekaligus,
     .modal-overlay (flex center) otomatis tidak lagi berpengaruh ke
     modal ini -- lebar/tinggi mengikuti jarak ke tepi yang diisi. */

  .modal-content.code-editor-modal-content {
    position: fixed;
    top: 7.5vh;
    left: 40px;
    right: 40px;
    bottom: 10px;
    width: auto;
    height: auto;
    max-width: none;
    max-height: none;
    margin: 0;
  }

  /* Mode layar HP -- jarak kiri/kanan modal Perbesar Code Editor
     (section-html/css/js) dipepetkan jadi cuma 5px, supaya area
     editor lebih lega di layar sempit. */
  @media (max-width: 768px) {
    .modal-content.code-editor-modal-content {
      left: 10px;
      right: 10px;
    }
  }

  /* Header modal Perbesar Code Editor diperkecil jadi ~setengah tinggi
     aslinya, KHUSUS modal ini (scoped ke .code-editor-modal-content,
     tidak menyentuh .modal-header generik yang dipakai modal lain
     seperti "Pilih Seksi"). Tinggi header asli ~69px; sekarang ~40px
     lewat kombinasi padding vertikal dibagi dua (20px -> 8px) dan
     ukuran judul diperkecil, TAPI icon close (X) sengaja dibuat lebih
     besar lagi (font-size 20px, bukan ikut proporsi kecil seperti h4)
     supaya tetap mudah di-klik/terlihat walau header sudah dipepetkan.
     Karena .modal-body punya "flex: 1" (lihat rule .modal-body
     generik) dan .modal-content.code-editor-modal-content punya tinggi
     TETAP (top/bottom fixed ke tepi layar), modal-body OTOMATIS
     melebar mengisi sisa ruang yang dibebaskan header. */
  .code-editor-modal-content .modal-header {
    padding: 8px 20px;
  }

  .code-editor-modal-content .modal-header h4 {
    font-size: 13px;
  }

  .code-editor-modal-content .modal-header .close-modal {
    font-size: 20px;
  }

  /* padding-top modal-body dikurangi dari 15px -> 8px supaya garis
     pembatas & Tab HTML/CSS/JS (#code-editor-modal-tabs) naik sedikit,
     lebih dekat ke header -- lihat penyesuaian top:57px pada icon
     copy/download di bawah (offset ikut turun karena tab & editor
     sama-sama naik 7px). */
  .code-editor-modal-content .modal-body {
    /* position:relative -- anchor utk icon copy overlay
       (#code-editor-modal-copy-icon) yg diposisikan absolute di
       pojok kanan-atas, PERSIS gaya icon copy di .code-editor-wrap. */
    position: relative;
    padding: 8px 20px 20px;
    display: flex;
    flex-direction: column;
    /* PENTING (fix highlight search tidak menyeluruh saat discroll):
       .modal-body generik (dipakai modal LAIN spt "Pilih Seksi") sengaja
       diberi overflow-y:auto krn kontennya memang perlu scroll sendiri.
       TAPI di modal Perbesar Code Editor ini, scroll SEHARUSNYA cuma
       ditangani xamirror sendiri lewat scroller internalnya
       (.xamirror-scroll, lihat xamirror.css) -- itulah yang dipakai
       xamirror utk tahu baris mana yg sedang terlihat & perlu digambar
       ulang (termasuk highlight overlay pencarian).
       Kalau #code-editor-modal-body (ancestor) IKUT bisa discroll krn
       overflow-y:auto warisan dari .modal-body generik, dan tinggi
       kontennya (.code-editor punya min-height:70vh) sedikit melebihi
       ruang tersisa, maka scroll gesture user bisa "ditangkap" oleh
       #code-editor-modal-body alih-alih .xamirror-scroll -- akibatnya
       xamirror TIDAK PERNAH tahu user sedang scroll (scrollTop
       internalnya tidak berubah, event "scroll" bawaannya tidak
       terpicu), sehingga highlight/re-render baris yang baru "terlihat"
       tidak pernah terpicu, walau secara visual box editor ikut
       bergeser lewat scroll milik ancestor-nya.
       overflow: hidden di sini memastikan #code-editor-modal-body TIDAK
       BISA jadi scroll container sendiri -- satu-satunya yang boleh
       scroll di dalam modal ini adalah .xamirror-scroll milik xamirror.

       CATATAN (jangan dihapus): fix overflow:hidden ini BENAR dan
       TETAP DIPERLUKAN, tapi ini BUKAN satu-satunya penyebab highlight
       search sempat hilang saat discroll di modal Perbesar. Ada root
       cause KEDUA yg terpisah (opsi xamirror "maxHighlightLength"
       yg skip highlight utk baris >10.000 karakter) -- lihat catatan
       lengkap & fix-nya di initXamirrorEditor() / openXamirrorSearch()
       pada script bawah (cari "maxHighlightLength"). Kalau bug serupa
       muncul lagi, cek KEDUA kemungkinan ini, jangan cuma salah satu. */
    overflow: hidden;
  }

  /* PENTING: dulu .code-editor (div#section-html/css/js yang di-reparent)
     langsung jadi flex-item dari #code-editor-modal-body (flex:1 di sini).
     Sekarang div itu dipindah ke DALAM wrapper baru
     #code-editor-modal-editor-wrap (lihat rule-nya di bawah) yang
     mengambil alih flex:1 -- .code-editor tinggal mengisi 100% lebar &
     tinggi wrapper itu saja (min-height:70vh lama juga dipindah ke
     wrapper). Alasan perubahan ini: minimap di modal Perbesar
     (.code-editor-modal-minimap) butuh anchor position:relative yang
     PERSIS membungkus area editor saja (tanpa ikut tabs/bottom-bar),
     supaya tinggal top:0/bottom:0 tanpa perlu hitung offset piksel
     manual yang gampang meleset kalau tinggi tabs/bottom-bar berubah
     di kemudian hari. */
  .code-editor-modal-content .code-editor {
    width: 100%;
    height: 100%;
  }

  /* Wrapper baru, SATU-SATUNYA flex-item bertipe "isi editor" di dalam
     #code-editor-modal-body (order default 0, di antara tabs order:-1 &
     bottom-bar order:5) -- div#section-html/css/js di-appendChild() ke
     DALAM wrapper ini (bukan lagi langsung ke modal-body), lihat
     openCodeEditorModal()/switchCodeEditorModalTab() di script bawah.
     min-height:0 WAJIB supaya flex:1 di dalam flex-column bisa menyusut
     dgn benar. position:relative jadi anchor utk
     .code-editor-modal-minimap (absolute, top:0/bottom:0). */
  #code-editor-modal-editor-wrap {
    position: relative;
    flex: 1;
    min-height: 70vh;
  }

  /* ============================================================
     MINIMAP di dalam MODAL Perbesar -- prinsip kerja & rendering
     SAMA PERSIS dengan .code-editor-wrap .code-minimap (lihat catatan
     lengkap di rule itu), cuma bedanya: karena modal punya 3 editor
     (HTML/CSS/JS) yang gantian direparent ke #code-editor-modal-editor-wrap
     yang SAMA (bukan wrapper terpisah per section spt di sidebar), maka
     minimap-nya JUGA dibuat 3 set elemen statis TERPISAH (satu per
     section, tidak ikut direparent -- sama seperti tabs/status-bar/badge
     di modal ini), lalu HANYA salah satu yang ditampilkan (.active)
     sesuai tab yang sedang aktif -- lihat toggle class-nya di
     updateCodeEditorModalTabsUI(). Masing-masing tetap didengarkan lewat
     initCodeMinimap(xm, "modal-section-html"/"modal-section-css"/
     "modal-section-js") di initAllXamirrorEditors(), jadi tetap
     sinkron real-time walau sedang tersembunyi (tab lain yg aktif).

     right:16px (bukan 0 spt sidebar) supaya TIDAK menutupi scrollbar
     vertikal custom .xamirror-vscrollbar yang HANYA tampil di modal ini
     (lihat rule-nya di atas) -- 16px angka yang sama dipakai di tempat
     lain pada file ini utk lebar scrollbar custom. z-index 15, sama
     seperti sidebar, tetap di BAWAH icon copy/search/download modal
     (z-index 20) supaya icon selalu di atas saat bertumpuk di pojok
     kanan-atas. ============================================================ */
  .code-editor-modal-minimap {
    display: none;
    position: absolute;
    top: 0;
    right: 16px;
    bottom: 0;
    width: 126px;
    overflow: hidden;
    z-index: 15;
    background-color: transparent;
    cursor: pointer;
    user-select: none;
  }

  .code-editor-modal-minimap.active {
    display: block;
  }

  /* Di ukuran layar HP/tablet (breakpoint disamakan dengan
     isTabletOrMobileViewport() di script bawah: max-width 768px),
     minimap modal Perbesar disembunyikan total -- termasuk saat
     sedang .active -- karena ruang layar sempit lebih baik dipakai
     penuh utk kode, bukan berbagi dgn minimap kecil. padding-right
     .xamirror-lines (lihat rule-nya, disediakan utk menghindari
     tabrakan teks dgn minimap) ikut dikembalikan ke 0 di breakpoint
     ini krn minimapnya sendiri sudah tidak ada. */
  @media (max-width: 768px) {
    .code-editor-modal-minimap.active {
      display: none;
    }

    .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-lines {
      padding-right: 0;
    }
  }

  .code-editor-modal-minimap-content {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    margin: 0;
    padding: 2px 3px;
    box-sizing: border-box;
    font-family: "Courier New", monospace;
    font-size: 2px;
    line-height: 3px;
    white-space: pre;
    color: #5c6570;
    pointer-events: none;
    will-change: transform;
  }

  body.dark-mode .code-editor-modal-minimap-content {
    color: #b6bcc3;
  }

  .code-editor-modal-minimap-viewport {
    display: none !important;
    position: absolute;
    left: 0;
    right: 0;
    background-color: rgba(3, 190, 252, 0.16);
    border-top: 1px solid rgba(3, 190, 252, 0.65);
    border-bottom: 1px solid rgba(3, 190, 252, 0.65);
    box-sizing: border-box;
    pointer-events: none;
  }

  /* Jarak/padding di sisi kanan area baris kode KHUSUS di modal Perbesar,
     supaya teks kode (termasuk highlight syntax) berhenti SEBELUM area
     minimap (.code-editor-modal-minimap: right:16px + width:126px = 142px
     dari tepi kanan) -- tanpa ini, baris yang wrapping (lineWrapping aktif
     di modal ini) bisa melebar sampai ketiban/menabrak minimap. Nilai
     150px = 142px (lebar+jarak minimap) + sedikit jarak aman tambahan.
     Ditarget ke .xamirror-lines (bukan .xamirror-scroll) karena elemen
     inilah yang dipakai xamirror utk menghitung lebar wrap teks. */
  .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-lines {
    padding-right: 150px;
  }

  .code-editor {
    font-family: "Courier New", monospace;
    font-size: 13px;
    color: #333;
    background: #f8f9fa;
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 12px;
    width: 100%;
    min-height: 250px;
    tab-size: 2;
    overflow-x: auto;
    /* PENTING: overflow-y dikunci ke "hidden" secara eksplisit. Tanpa ini,
       browser otomatis memperlakukan overflow-y sebagai "auto" juga
       (krn overflow-x sudah bukan "visible") -- membuat elemen INI
       (pembungkus luar #section-html/css/js, tempat .xamirror di-mount
       sbg anak) berpotensi jadi scroll container KEDUA yg bersaing
       dengan .xamirror-scroll milik xamirror sendiri. Kalau gesture
       scroll user "ditangkap" elemen luar ini, xamirror tidak pernah
       tahu ada scroll (scrollTop internalnya tidak berubah, event
       "scroll" internalnya tidak terpicu) -- akibatnya baris baru yang
       kelihatan cuma bergeser visual lewat scroll ancestor, TANPA
       benar-benar di-render ulang oleh xamirror (termasuk overlay
       highlight pencarian). Satu-satunya yg boleh scroll vertikal di
       sini adalah .xamirror-scroll (lihat xamirror.css).
    */
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
    box-sizing: border-box;
    line-height: 1.5;
    white-space: pre;
    resize: none;
  }

  .code-editor::-webkit-scrollbar {
    display: none;
  }

  .code-editor:focus {
    outline: 1px solid #03befc;
  }

  /* === xamirror code editor (pengganti total textarea section-html/css/js) ===
     #section-html/css/js sekarang adalah <div> kosong yang langsung
     dijadikan host oleh xamirror (bukan textarea tersembunyi lagi).
     Class .code-editor bawaannya dinetralkan di sini supaya box lama
     (border/bg/padding) tidak dobel dengan box .xamirror di dalamnya.
     Warna/tema TIDAK diatur manual di sini lagi -- 100% datang dari
     theme asli bawaan xamirror (assets/library/xamirror/theme/): "default"
     untuk light mode, "monokai" untuk dark mode. Di sini hanya diatur
     layout/ukuran supaya konsisten dengan box .code-editor lama. */
  div#section-html.code-editor,
  div#section-css.code-editor,
  div#section-js.code-editor {
    padding: 0;
    border: none;
    border-radius: 0;
    background: none;
    min-height: 0;
    overflow: visible;
  }

  .code-editor-xamirror.xamirror {
    width: 100%;
    height: 320px;
    min-height: 320px;
    font-family: "Courier New", monospace;
    font-size: 13px;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-sizing: border-box;
  }

  .code-editor-xamirror.xamirror .xamirror-vscrollbar,
  .code-editor-xamirror.xamirror .xamirror-hscrollbar,
  .code-editor-xamirror.xamirror .xamirror-scrollbar-filler,
  .code-editor-xamirror.xamirror .xamirror-gutter-filler {
    display: none !important;
  }

  /* === Scrollbar vertikal KHUSUS di modal Perbesar Code Editor ===
     Permintaan: HANYA scrollbar vertikal yang tampil di modal Perbesar
     (horizontal TETAP disembunyikan), posisinya sejajar lurus dengan
     ujung kanan garis pembatas Tab HTML/CSS/JS (yaitu tepi kanan kotak
     editor itu sendiri, karena #code-editor-modal-tabs & .code-editor
     sama-sama stretch penuh selebar modal-body). Di-scope ke
     ".code-editor-modal-content" saja, TIDAK mengubah editor kecil di
     panel Setting.

     PENTING (dikoreksi setelah cek xamirror.css asli): JANGAN ubah
     overflow wrapper .xamirror ke "visible". .xamirror-scroll di dalam
     lib ini pakai trik "margin-right/-bottom: -50px" + "overflow:hidden"
     di wrapper .xamirror utk MENYEMBUNYIKAN scrollbar NATIVE browser
     dari elemen .xamirror-scroll (yang overflow:scroll asli) -- kalau
     wrapper diubah ke visible, trik itu bocor dan scrollbar native
     (vertikal MAUPUN horizontal) ikut nongol tanpa diminta. overflow:
     hidden wrapper WAJIB tetap seperti aslinya.

     .xamirror-vscrollbar sendiri adalah scrollbar PALSU/custom (elemen
     terpisah dari .xamirror-scroll) yang defaultnya sudah pas di
     "right: 0" relatif ke wrapper -- artinya nempel PAS di tepi kanan
     DALAM kotak editor, yang justru SAMA PERSIS dengan tepi kanan tab
     (karena keduanya stretch penuh lebar modal-body). Jadi cukup
     tampilkan elemen ini apa adanya (display saja yang diubah), TANPA
     menggeser "right" atau mengubah overflow wrapper.
     .xamirror-hscrollbar/-scrollbar-filler/-gutter-filler TETAP
     disembunyikan seperti aslinya (rule display:none!important di atas
     tetap berlaku, tidak di-override di sini). */
  .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar {
    display: block !important;
    right: 0;
  }

  /* Background scrollbar vertikal di modal Perbesar dibuat TRANSPARAN
     (bukan warna solid/putih bawaan .xamirror), pakai pola yang sama
     seperti scrollbar transparan lain di file ini (lihat
     .editor-content-backdrop::-webkit-scrollbar-track). .xamirror-vscrollbar
     sendiri memang sudah "overflow-y: scroll" (lihat xamirror.css asli),
     jadi bisa langsung ditarget dgn ::-webkit-scrollbar/scrollbar-color
     seperti scrollbar native lainnya -- track dibuat transparent supaya
     menyatu dgn warna editor di baliknya (light/dark), thumb tetap
     terlihat jelas (abu gelap di light mode). */
  .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar {
    background-color: transparent;
    scrollbar-color: rgba(0, 0, 0, 0.35) transparent; /* Firefox: thumb track */
  }

  .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar::-webkit-scrollbar-track {
    background: transparent;
  }

  .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar::-webkit-scrollbar-thumb {
    background-color: rgba(0, 0, 0, 0.35);
    border-radius: 4px;
  }

  .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar::-webkit-scrollbar-thumb:hover {
    background-color: rgba(0, 0, 0, 0.5);
  }

  /* Dark mode: track TETAP transparent, thumb diganti lebih terang
     supaya tetap kontras di atas background gelap (sama seperti pola
     body.dark-mode .editor-content-backdrop di file ini). */
  body.dark-mode .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar {
    scrollbar-color: #666 transparent; /* Firefox */
  }

  body.dark-mode .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar::-webkit-scrollbar-track {
    background: transparent;
  }

  body.dark-mode .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar::-webkit-scrollbar-thumb {
    background-color: #666;
  }

  body.dark-mode .code-editor-modal-content .code-editor-xamirror.xamirror .xamirror-vscrollbar::-webkit-scrollbar-thumb:hover {
    background-color: #888;
  }

  body.dark-mode .code-editor-xamirror.xamirror {
    border-color: #444;
  }

  /* Theme "monokai" bawaan xamirror sengaja meniadakan garis pembatas
     gutter (border-right: 0px di theme/monokai.css). Dikembalikan lagi
     di sini supaya tetap ada pembatas seperti theme "default". */
  .code-editor-xamirror.xm-s-monokai .xamirror-gutters {
    border-right: 1px solid #444;
  }

  /* === Indent Guides (addon custom: assets/library/xamirror/addon/indent/indent-guides.js) ===
     Elemen .xamirror-indent-guide di-append oleh addon tsb ke tiap
     <pre class="xamirror-line"> (yang sudah position: relative bawaan
     xamirror), jadi cukup diposisikan absolute penuh tinggi baris di
     sini. Warna dibedakan untuk theme "default" (light) & "monokai"
     (dark) supaya kontrasnya pas di kedua mode. */
  .code-editor-xamirror .xamirror-indent-guide {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 0;
    border-left: 1px solid rgba(0, 0, 0, 0.08);
    pointer-events: none;
    z-index: 1;
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-indent-guide {
    border-left-color: rgba(255, 255, 255, 0.09);
  }

  /* === Code Folding (addon bawaan: assets/library/xamirror/addon/fold/) ===
     Gutter kedua di sebelah nomor baris berisi panah lipat (▾/▸).
     Warna default addon (foldgutter.css) terlalu polos utk ikut tema
     situs, jadi disamakan di sini dgn warna teks gutter tiap theme:
     abu-abu utk "default" (light), abu-abu terang utk "monokai" (dark).
     Hover disorot pakai warna aksen biru (#03befc) yg sama dgn fokus
     .code-editor supaya konsisten. */
  /* === Code Folding (addon bawaan: assets/library/xamirror/addon/fold/) ===
     Gutter kedua di sebelah nomor baris berisi panah lipat (▾/▸).
     Panah ▾ (terbuka, belum dilipat) dibiarkan netral abu-abu spt
     nomor baris. Panah ▸ (SEDANG dilipat) SENGAJA selalu diwarnai aksen
     biru (#03befc, sama dgn warna fokus .code-editor) + background
     highlight tipis -- jadi user langsung kelihatan sekilas mata baris
     mana saja yg sedang dilipat, tanpa perlu hover dulu. */
  .code-editor-xamirror .xamirror-foldgutter-open {
    color: #999;
  }

  .code-editor-xamirror .xamirror-foldgutter-open:hover {
    color: #03befc;
  }

  .code-editor-xamirror .xamirror-foldgutter-folded {
    color: #03befc;
    background: rgba(3, 190, 252, 0.12);
    border-radius: 2px;
  }

  .code-editor-xamirror .xamirror-foldgutter-folded:hover {
    color: #0299cc;
    background: rgba(3, 190, 252, 0.22);
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-foldgutter-open {
    color: #767676;
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-foldgutter-open:hover {
    color: #4fd0ff;
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-foldgutter-folded {
    color: #4fd0ff;
    background: rgba(79, 208, 255, 0.16);
    border-radius: 2px;
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-foldgutter-folded:hover {
    color: #8ce0ff;
    background: rgba(79, 208, 255, 0.28);
  }

  /* Baris yang dilipat: tampilkan penanda "..." kecil yg bisa diklik
     utk buka lipatan lagi, warnanya disamakan dgn aksen biru situs
     (bukan biru bawaan addon spt di foldgutter.css). */
  .code-editor-xamirror .xamirror-foldmarker {
    color: #03befc;
    text-shadow: none;
    font-family: "Courier New", monospace;
    font-weight: bold;
    padding: 0 3px;
  }

  /* === Matching Tag Highlight (addon bawaan: assets/library/xamirror/addon/edit/matchtags.js) ===
     Menyorot LATAR (bukan cuma teks) sepasang tag buka & tutup HTML saat
     kursor berada di salah satunya -- warnanya disamakan dgn aksen biru
     situs (#03befc) spt addon fold & search, cuma dibuat sangat transparan
     supaya teks tag (yg sudah berwarna dari syntax highlight) tetap
     terbaca jelas di atasnya. */
  .code-editor-xamirror .xamirror-matchingtag {
    background: rgba(3, 190, 252, 0.25);
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-matchingtag {
    background: rgba(79, 208, 255, 0.22);
  }

  /* === Active Line Highlight (addon bawaan: assets/library/xamirror/addon/selection/active-line.js) ===
     Baris tempat kursor berada saat ini diberi latar sangat tipis
     supaya gampang dilacak posisinya di tengah kode yg panjang --
     warnanya sengaja SANGAT transparan (beda dgn highlight matching
     tag/fold di atas yg boleh lebih pekat) karena ini nyala TERUS
     menerus mengikuti kursor, bukan cuma sesekali. */
  .code-editor-xamirror .xamirror-activeline-background {
    background: rgba(3, 190, 252, 0.06);
  }

  .code-editor-xamirror.xm-s-monokai .xamirror-activeline-background {
    background: rgba(255, 255, 255, 0.05);
  }

  /* === Selected Text Highlight (addon bawaan: assets/library/xamirror/addon/selection/mark-selection.js) ===
     Lapisan tambahan di ATAS highlight seleksi bawaan browser/xamirror --
     dipakai terutama supaya kontras seleksi di theme "monokai" (yg warna
     seleksi bawaannya agak gelap/kurang kelihatan) lebih jelas, disamakan
     dgn aksen biru situs. */
  .code-editor-xamirror.xm-s-monokai .xamirror-selectedtext {
    background: rgba(79, 208, 255, 0.18);
  }

  /* === Autocomplete Dropdown (addon bawaan: assets/library/xamirror/addon/hint/) ===
     Elemen .xamirror-hints di-append addon ini ke document.body (bukan
     ke dalam .xamirror wrapper), jadi restyle-nya global (tidak perlu
     diawali .code-editor-xamirror). z-index dibuat lebih tinggi dari
     elemen manapun di situs (tertinggi sebelumnya 99999, dipakai loading
     overlay) supaya dropdown-nya tidak pernah tertutup, termasuk saat
     editor sedang di dalam modal Perbesar. Tampilan disamakan dgn gaya
     card/dropdown lain di situs: rounded, shadow lembut, dan item aktif
     pakai aksen biru situs (#03befc) -- bukan biru bawaan addon (#08f). */
  .xamirror-hints {
    z-index: 100000;
    border: 1px solid #ddd;
    border-radius: 6px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
    font-family: "Courier New", monospace;
    font-size: 13px;
  }

  .xamirror-hint {
    padding: 3px 8px;
    border-radius: 3px;
  }

  li.xamirror-hint-active {
    background: #03befc;
    color: #fff;
  }

  body.dark-mode .xamirror-hints {
    background: #2b2b2b;
    border-color: #444;
    color: #eee;
  }

  body.dark-mode .xamirror-hint {
    color: #eee;
  }

  body.dark-mode li.xamirror-hint-active {
    background: #03befc;
    color: #1e1e1e;
  }

  /* === Status Bar (Line/Column) ala Sublime Text ===
     Sekarang HANYA ditampilkan di modal Perbesar (#code-editor-modal-status,
     lihat rule di bawah) -- status bar di editor kecil panel Setting
     (HTML/CSS/JS) sudah dihapus dari HTML, jadi editor kecil kembali
     dibiarkan rounded penuh (tidak perlu diratakan lagi di bawah). */
  .code-editor-status-bar {
    font-family: "Courier New", monospace;
    font-size: 11px;
    line-height: 1.4;
    color: #666;
    background: #eceff1;
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 3px 10px;
    text-align: right;
    user-select: none;
    white-space: nowrap;
    box-sizing: border-box;
  }

  body.dark-mode .code-editor-status-bar {
    color: #aaa;
    background: #2b2b2b;
    border-color: #444;
  }

  /* Status bar versi modal Perbesar -- elemen statis (tidak ikut
     di-reparent), berdiri sendiri di dalam #code-editor-modal-body
     (BUKAN di dalam div#section-html/css/js yang di-reparent).
     TETAP jadi flex-item biasa (bukan absolute-overlay) supaya benar-
     benar keluar sebagai baris SENDIRI di bawah editor (tidak menimpa
     teks kode) -- "order: 5" memaksanya selalu di posisi PALING BAWAH
     di dalam modal-body yang flex-column. Baris ini FULL-WIDTH
     ("justify-content: space-between") supaya grup kiri (search bar +
     status Line/Column) tetap menempel ke pojok KIRI, SEMENTARA badge
     tipe section (HTML/CSS/JavaScript, #code-editor-modal-type) di
     bawah didorong rata ke pojok KANAN -- ujung kanan modal, sejajar
     satu baris dengan Line/Column. */
  #code-editor-modal-bottom-bar {
    order: 5;
    align-self: stretch;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px;
    max-width: 100%;
  }

  #code-editor-modal-bottom-left {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
  }

  #code-editor-modal-status {
    flex: none;
    width: auto;
    text-align: left;
    border: none;
    background: transparent;
    margin-top: 0;
  }

  body.dark-mode #code-editor-modal-status {
    background: transparent;
  }

  /* Badge tipe section (HTML/CSS/JavaScript) -- HANYA muncul di modal
     Perbesar (diisi lewat JS di openCodeEditorModal() berdasarkan
     targetId yg dibuka), sengaja TIDAK ada versi utk editor kecil di
     sidebar panel Setting. Tanpa border/background (transparent) biar
     nyatu/selaras dgn background modal, gaya teksnya disamakan dgn
     status bar Line/Column di sebelahnya biar terasa satu keluarga. */
  .code-editor-modal-type-badge {
    flex: none;
    font-family: "Courier New", monospace;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    color: #666;
    background: transparent;
    border: none;
    padding: 3px 0;
    white-space: nowrap;
    user-select: none;
    box-sizing: border-box;
  }

  body.dark-mode .code-editor-modal-type-badge {
    color: #aaa;
    background: transparent;
  }

  /* === Tab HTML/CSS/JS di dalam modal Perbesar Code Editor ===
     Ditambahkan supaya user bisa GANTI jenis editor (HTML <-> CSS <-> JS)
     LANGSUNG di dalam modal besar tanpa perlu menutup modal & klik icon
     perbesar lain dari panel Setting. "order: -1" memaksa baris tab ini
     SELALU tampil paling atas di dalam #code-editor-modal-body (flex
     column) walau elemen div#section-html/css/js yang di-reparent lewat
     appendChild() bisa masuk BELAKANGAN secara urutan DOM -- lihat juga
     #code-editor-modal-bottom-bar yang pakai trik order serupa (order: 5)
     supaya selalu di baris paling bawah. */
  #code-editor-modal-tabs {
    order: -1;
    align-self: stretch;
    display: flex;
    gap: 4px;
    margin-bottom: 10px;
    border-bottom: 1px solid #ddd;
    flex: none;
  }

  body.dark-mode #code-editor-modal-tabs {
    border-bottom-color: #444;
  }

  .code-editor-modal-tab {
    padding: 8px 16px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: #9a9a9a;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color 0.2s ease, border-color 0.2s ease;
    user-select: none;
  }

  .code-editor-modal-tab:hover:not(.active) {
    color: #333;
  }

  .code-editor-modal-tab.active {
    color: #03befc;
    border-bottom-color: #03befc;
  }

  body.dark-mode .code-editor-modal-tab {
    color: #888;
  }

  body.dark-mode .code-editor-modal-tab:hover:not(.active) {
    color: #ddd;
  }

  body.dark-mode .code-editor-modal-tab.active {
    color: #03befc;
  }
  /* === End Tab HTML/CSS/JS modal Perbesar === */

  /* === Search Bar (Ctrl+F) ala Sublime Text, pakai xamirror addon
     "searchcursor" (assets/library/xamirror/addon/search/searchcursor.js) ===
     Muncul di SEBELAH status bar Line/Column saat editor sedang di
     dalam modal Perbesar (lihat #code-editor-modal-bottom-bar di
     atas), atau mengambang di atas editor kecil panel Setting saat
     search dipicu dari sana (class .search-bar-floating, lihat di
     bawah) -- satu elemen yang sama di-reparent lewat JS tergantung
     konteksnya, persis pola reparenting yg dipakai div#section-html/
     css/js saat openCodeEditorModal(). */
  .code-editor-search-bar {
    display: none;
    align-items: center;
    gap: 4px;
    font-family: "Courier New", monospace;
    font-size: 11px;
    background: rgba(255, 255, 255, 0.35);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    border: 1px solid #ddd;
    border-radius: 3px;
    padding: 5px 6px;
    box-sizing: border-box;
    margin-left: 5px;
    /* Batas lebar maksimum supaya bar TIDAK meluber keluar wadahnya di
       layar sempit -- nilainya "100%" relatif ke containing block-nya
       masing-masing (lihat override per konteks: modal Perbesar di
       bawah pakai calc() thd .modal-body, versi floating panel Setting
       pakai flex:0 1 auto dari parent). Karena flex item di dalamnya
       (icon/count/tombol) dikunci flex-shrink:0, saat bar ini dipaksa
       menyempit oleh max-width, HANYA .code-editor-search-input yang
       ikut mengecil (lihat rule flex-shrink di bawah). */
    max-width: 100%;
  }

  body.dark-mode .code-editor-search-bar {
    background: rgba(30, 30, 30, 0.35);
    border-color: #444;
  }

  .code-editor-search-bar .code-editor-search-icon {
    color: #999;
    font-size: 11px;
    margin-right: 2px;
    flex-shrink: 0;
  }

  body.dark-mode .code-editor-search-bar .code-editor-search-icon {
    color: #777;
  }

  .code-editor-search-bar .code-editor-search-input {
    border: none;
    background: transparent;
    outline: none;
    font-family: inherit;
    font-size: 11px;
    color: #333;
    width: 130px;
    /* Elemen INI yang menyusut duluan kalau ruang tidak cukup (icon,
       counter, dan tombol prev/next/close di sebelahnya dikunci
       flex-shrink:0 supaya tetap utuh & mudah diklik). min-width kecil
       supaya browser tidak membatasi shrink pada lebar minimum bawaan
       <input> (yang biasanya jauh lebih lebar dari yang kita mau). */
    min-width: 32px;
    flex-shrink: 1;
  }

  body.dark-mode .code-editor-search-bar .code-editor-search-input {
    color: #eee;
  }

  .code-editor-search-bar .code-editor-search-count {
    color: #888;
    font-size: 10px;
    min-width: 34px;
    text-align: center;
    user-select: none;
    flex-shrink: 0;
  }

  body.dark-mode .code-editor-search-bar .code-editor-search-count {
    color: #999;
  }

  .code-editor-search-bar .code-editor-search-btn {
    cursor: pointer;
    color: #777;
    font-size: 11px;
    padding: 3px 5px;
    border-radius: 3px;
    transition: all 0.2s ease;
    flex-shrink: 0;
  }

  .code-editor-search-bar .code-editor-search-btn:hover {
    color: #007bff;
    background-color: rgba(0, 123, 255, 0.1);
  }

  body.dark-mode .code-editor-search-bar .code-editor-search-btn {
    color: #aaa;
  }

  body.dark-mode .code-editor-search-bar .code-editor-search-btn:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.08);
  }

  /* Versi "di dalam modal Perbesar" -- dipicu lewat klik
     #code-editor-modal-search-icon (atau Ctrl+F saat editor di dalam
     modal, karena keduanya sama-sama memanggil window.openXamirrorSearch()
     dgn search bar yg sama). Selama bar ini masih menjadi anak dari
     #code-editor-modal-body (lihat openXamirrorSearch() -- direparent
     ke #code-editor-modal-bottom-left saat "inModal"), posisinya
     di-override jadi TERAPUNG persis DI BAWAH icon search, dgn tepi
     kanan bar RATA/sejajar lurus dgn tepi kanan icon search
     (right: 98px, sama persis dgn right icon #code-editor-modal-search-icon
     di atas). Anchor position:relative-nya adalah .modal-body milik
     modal ini (sudah position:relative, lihat rule
     .code-editor-modal-content .modal-body), jadi TIDAK perlu wrapper
     baru. top dihitung dari top icon (57px) + tinggi icon (~13px font
     + padding 4px atas-bawah = ~21px) + jarak kecil 6px => 57+21+6 = 84px.
     Selector di-scope ke "#code-editor-modal-body #xamirror-search-bar"
     (bukan class umum .code-editor-search-bar) supaya HANYA berlaku
     selama elemen ini benar-benar sedang berada di dalam modal --
     versi "mengambang di editor kecil" (.search-bar-floating, dipakai
     saat search dipicu dari panel Setting) tetap position:static
     seperti semula karena saat itu elemen ini sudah dipindah KELUAR
     dari #code-editor-modal-body. */
  #code-editor-modal-body #xamirror-search-bar {
    position: absolute;
    top: 84px;
    right: 98px;
    z-index: 25;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    /* max-width:100% bawaan (rule umum .code-editor-search-bar) mengacu
       ke LEBAR PENUH #code-editor-modal-body (containing block-nya,
       persentase width/max-width elemen absolute selalu dihitung thd
       lebar containing block, TERLEPAS dari offset left/right/top yg
       dipakai utk POSISI-nya). Itu belum cukup di sini karena bar ini
       sudah digeser "right:98px" dari tepi kanan -- kalau max-width
       tetap 100%, sisi kiri bar bisa meluber ke kiri padding
       modal-body saat layar sempit. Override ke calc(100% - 123px):
       98px (jarak ke tepi kanan) + 20px (buffer, menyamai padding kiri
       .code-editor-modal-content .modal-body) + 5px (jarak kiri yg
       diminta user).

       CATATAN: 5px ini SENGAJA dibakar langsung ke max-width, BUKAN
       lewat margin-left. Elemen ini position:absolute dgn cuma "right"
       yg di-set ("left" dibiarkan auto) -- browser MENGHITUNG left
       sendiri lewat rumus left = lebar_containing_block - margin-left
       - width - margin-right - right. Karena "left" itu variabel
       bebas, kenaikan margin-left otomatis "diserap balik" oleh
       pengurangan nilai left hasil hitungan, sehingga posisi tepi box
       di layar TIDAK berubah sama sekali walau margin-left ditambah
       (margin-left di rule umum .code-editor-search-bar tetap berguna
       utk versi floating panel Setting, yg posisinya static/flex biasa
       sehingga margin-left DI SANA bekerja normal). */
    max-width: calc(100% - 123px);
  }

  /* Versi "di editor kecil" panel Setting (bukan dari modal Perbesar)
     -- POSISINYA SEKARANG DISAMAKAN PERSIS dgn versi modal Perbesar
     (lihat #code-editor-modal-body #xamirror-search-bar di atas):
     overlay absolute, mengambang TERAPUNG persis DI BAWAH icon search
     (.code-search-icon, right:90px), dgn tepi kanan bar RATA/sejajar
     lurus dgn tepi kanan icon search itu. Anchor position:relative-nya
     adalah .code-editor-wrap (sudah position:relative, sama seperti
     .modal-body jadi anchor versi modal), jadi search bar ini
     di-reparent lewat JS ke .code-editor-wrap yg bersangkutan (bukan
     lagi ke .settings-option-header seperti sebelumnya).
     top dihitung dgn formula yg sama dgn versi modal: top icon (6px)
     + tinggi icon (~13px font + padding 4px atas-bawah = ~21px) +
     jarak kecil 6px => 6+21+6 = 33px. */
  .code-editor-search-bar.search-bar-floating {
    position: absolute;
    top: 33px;
    right: 90px;
    z-index: 25;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    /* Sama seperti versi modal: dibatasi supaya tidak meluber ke kiri
       wadah saat layar sempit. 90px (jarak ke tepi kanan) + 20px
       (buffer, menyamai padding wadah) + 5px (jarak kiri). */
    max-width: calc(100% - 115px);
  }

  /* Label header tidak boleh ikut menyusut saat search bar muncul di
     sebelahnya -- yg menyusut/pas-kan diri cukup search bar-nya saja
     (lihat min-width:0 & input yg sudah diperpendek di atas). */
  .settings-option-header label {
    flex-shrink: 0;
  }

  /* Highlight semua kemunculan hasil pencarian (kuning), dan highlight
     kemunculan yg sedang aktif dibedakan lewat seleksi teks bawaan
     xamirror (background biru selection), jadi cukup satu class saja
     di sini utk seluruh match. */
  .xamirror-search-match {
    background: rgba(255, 235, 59, 0.55);
    border-radius: 2px;
  }

  body.dark-mode .xamirror-search-match {
    background: rgba(255, 213, 79, 0.35);
  }

  /* Versi OVERLAY (gantinya markText) -- style SAMA PERSIS dgn
     .xamirror-search-match di atas, cuma nama class beda krn
     xamirror otomatis menambah prefix "xm-" di depan nama
     style yg dikembalikan fungsi token() overlay (lihat
     makeSearchOverlay() di script bawah), jadi class yg BENERAN
     ke-render ke DOM adalah "xm-xamirror-search-match-overlay", bukan
     "xamirror-search-match-overlay" polos. */
  .xm-xamirror-search-match-overlay {
    background: rgba(255, 235, 59, 0.55);
    border-radius: 2px;
  }

  body.dark-mode .xm-xamirror-search-match-overlay {
    background: rgba(255, 213, 79, 0.35);
  }

  .editor-content span {
    display: inline;
  }

  /* Italic, bold, underline */
  .editor-content i,
  .editor-content em,
  .cv-preview i,
  .cv-preview em {
    font-style: italic;
  }

  .editor-content b,
  .editor-content strong,
  .cv-preview b,
  .cv-preview strong {
    font-weight: bold;
  }

  .editor-content u,
  .cv-preview u {
    text-decoration: underline;
  }

  /* [FIX: warna teks & underline link "KLIK LINK" tidak bisa diubah
     lewat toolbar]
     Toolbar (warna/bold/italic/dst) membungkus teks dengan <strong>/
     <em>/<span style="color:..."> DI LUAR tag <a> -- bukan menempel
     style langsung ke <a> itu sendiri (lihat HTML hasil export: <a>
     selalu tanpa atribut style sama sekali). Sementara itu browser
     punya default styling KHUSUS untuk <a> (warna link + underline)
     lewat user-agent stylesheet, yang match LANGSUNG ke elemen <a>.
     Dalam cascade CSS, nilai yang match langsung ke suatu elemen
     SELALU menang dibanding nilai yang cuma diwariskan (inherited)
     dari induknya -- sekalipun yang "menang" itu cuma default bawaan
     browser. Itu sebabnya warna dari toolbar & toggle underline-off
     tidak pernah kelihatan efeknya di teks link.
     Fix: paksa <a> di area editor/preview MEWARISI warna dari
     pembungkusnya (color: inherit) dan matikan underline default
     (text-decoration: none) -- dengan begini, warna/format apa pun
     yang ditempel toolbar ke <strong>/<em>/<span> pembungkus otomatis
     "tembus" ke teks link, dan underline hanya muncul kalau user
     sengaja pakai toolbar Underline (yang menghasilkan <u>, sudah
     ditangani rule di atas). */
  .editor-content a,
  .cv-preview a,
  .cv-section a {
    color: inherit;
    text-decoration: none;
  }

  .editor-content a:hover,
  .cv-preview a:hover,
  .cv-section a:hover {
    text-decoration: none;
  }

  /* Heading styles */
  .editor-content h1,
  .cv-preview h1 {
    font-size: 2em;
  }

  .editor-content h2,
  .cv-preview h2 {
    font-size: 1.5em;
  }

  .editor-content h3,
  .cv-preview h3 {
    font-size: 1.17em;
  }

  .editor-content h4,
  .cv-preview h4 {
    font-size: 1em;
  }

  .editor-content h5,
  .cv-preview h5 {
    font-size: 0.83em;
  }

  .editor-content h6,
  .cv-preview h6 {
    font-size: 0.75em;
  }

  .editor-content ul,
  .cv-preview ul {
    list-style-type: disc;
    margin-left: 10px;
    margin-right: 10px;
  }

  .editor-content ol,
  .cv-preview ol {
    list-style-type: decimal;
    margin-left: 10px;
    margin-right: 10px;
  }

    /* [DISAMAKAN] ol dan ul sekarang pakai padding-left yang SAMA PERSIS
       (1.5em, mengikuti nilai bawaan .xa-editor ol di xaeditor.core.css)
       -- ol dan ul digabung dalam satu selector. Catatan: sebelumnya
       ol dan ul sengaja dipisah (18px vs 42px) supaya bullet & nomor
       tetap sejajar rata kiri; menyamakan nilainya di sini bisa
       membuat bullet (ul) tidak lagi sejajar dengan nomor (ol). */
    .editor-content ol,
    .cv-preview ol,
    .cv-section ol,
    .editor-content ul,
    .cv-preview ul,
    .cv-section ul {
      list-style-position: inside;
      padding-left: 1.5em;
      margin-left: 0;
      margin-right: 10px;
    }

  .editor-content li,
  .cv-preview li,
  .cv-section li {
    margin-bottom: 5px;
  }

  /* [TAB-INDENT PREVIEW PARITY]
     xaeditor.core.css SUDAH mendefinisikan padding-left untuk class
     "xa-indent-1"/"xa-indent-2" (ditempel oleh binding Tab custom di
     xaeditor-integration.js, dibatasi MAX_TAB_INDENT_LEVEL = 2) --
     TAPI file itu di-load lewat <link> yang selector-nya semua diawali
     ".xa-editor ..." (lihat xaeditor.core.css), jadi HANYA berlaku di
     dalam kanvas editor. Class "xa-indent-N" pada <p>/<blockquote>/
     heading TETAP ikut terbawa ke HTML hasil export (getEditorHTML()
     tidak membuang class ini -- stripIndentClass() cuma dipanggil utk
     <li>, karena indent level pada <li> sudah dikonversi jadi nesting
     <ul>/<ol> asli oleh normalizeListsForOutput(), bukan lewat class
     ini lagi), tapi TIDAK ADA rule CSS yang membacanya di .cv-preview/
     .cv-section -- makanya baris yang di-Tab-indent tampil rata di
     preview walau sudah menjorok di editor.

     Nilai di bawah ini SENGAJA disalin persis (bukan ditebak) dari
     xaeditor.core.css: ".xa-indent-1{padding-left:3em}" /
     ".xa-indent-2{padding-left:6em}" -- supaya hasil preview & export
     identik pixel-per-pixel dengan yang terlihat di editor. Hanya
     level 1-2 yang disediakan karena itu satu-satunya level yang bisa
     dihasilkan Tab saat ini (MAX_TAB_INDENT_LEVEL = 2 di
     xaeditor-integration.js); naikkan batas itu di sana DAN tambah
     rule level 3+ di sini sekaligus kalau suatu saat batasnya diubah.

     Selector li.xa-indent-N sengaja tetap disertakan (nilai 4.5em/
     7.5em, sama seperti xaeditor.core.css) sebagai jaring pengaman
     kalau suatu saat ada <li> yang lolos tanpa melalui
     normalizeListsForOutput() -- pada alur normal saat ini class
     tersebut sudah dibuang dari <li> sebelum sampai ke preview/export,
     jadi baris li di bawah ini tidak aktif dalam kondisi normal. */
  .editor-content .xa-indent-1,
  .cv-preview .xa-indent-1,
  .cv-section .xa-indent-1 {
    padding-left: 3em;
  }

  .editor-content li.xa-indent-1,
  .cv-preview li.xa-indent-1,
  .cv-section li.xa-indent-1 {
    padding-left: 4.5em;
  }

  .editor-content .xa-indent-2,
  .cv-preview .xa-indent-2,
  .cv-section .xa-indent-2 {
    padding-left: 6em;
  }

  .editor-content li.xa-indent-2,
  .cv-preview li.xa-indent-2,
  .cv-section li.xa-indent-2 {
    padding-left: 7.5em;
  }

  /* Warna marker bullet/nomor (ditulis oleh attributor resmi "listcolor",
     lihat registerListColor() di xaeditor-integration.js) SENGAJA dibaca
     dari custom property --li-marker-color, BUKAN dari properti CSS
     `color` biasa pada <li>.
     Kalau dipasang lewat `color`, browser mewarnai marker via
     currentColor, TAPI properti `color` itu juga inherited ke semua
     teks anak di dalam <li> yang tidak dibungkus <span style="color">
     sendiri -- itu sebabnya teks polos di baris list yang sama ikut
     "kebawa" warna marker padahal tidak pernah diwarnai. Custom
     property tidak punya efek visual apa pun kecuali dibaca eksplisit
     di sini, jadi tidak bocor ke teks anak.

     PENTING: selector di sini SENGAJA tidak mensyaratkan atribut
     [data-list] -- normalizeListsForOutput() (xaeditor-integration.js)
     MEMBUANG atribut data-list itu saat HTML mentah dari editor
     dinormalisasi jadi <ul>/<ol> semantik untuk preview/export. Kalau
     selector di sini ikut mensyaratkan [data-list], rule-nya cuma
     nyala di DOM mentah editor (xa.root, sebelum dinormalisasi) dan
     tidak pernah kena di .cv-preview/.cv-section (HTML hasil export) --
     itu sebabnya sempat kejadian bullet di preview tidak ikut
     berwarna. */
  .editor-content li::marker,
  .cv-preview li::marker,
  .cv-section li::marker {
    color: var(--li-marker-color, currentColor);
  }

  /* Warna bullet/nomor DI DALAM editor (xa-editor, lihat xaeditor.core.css).
     XaEditor TIDAK memakai native list-style/::marker browser di dalam
     area editing -- li di-set `list-style-type: none` dan bullet/angkanya
     digambar lewat pseudo-element ::before milik <span class="xa-ui">
     yang disisipkan di dalam tiap <li data-list="...">:

       .xa-editor li[data-list=bullet] > .xa-ui:before { content: '\2022' }
       .xa-editor li[data-list=ordered] > .xa-ui:before { content: counter(...) }

     Rule `::marker` di atas sama sekali tidak menyentuh elemen ini (tidak
     ada native marker yang dirender di editor), jadi warnanya perlu
     disambungkan terpisah ke pseudo-element yang benar-benar dipakai.
     Pakai custom property yang SAMA (--li-marker-color, ditulis oleh
     attributor resmi "listcolor" -- lihat registerListColor() di
     xaeditor-integration.js) supaya warna
     bullet di editor konsisten dengan warna bullet di preview/section.

     Sengaja HANYA untuk mode bullet & ordered -- checked/unchecked
     (checkbox list) sengaja dikunci abu-abu (#777) oleh xaeditor.core.css
     dan tidak boleh ikut berubah warna. */
  .editor-content li[data-list="bullet"] > .xa-ui::before,
  .editor-content li[data-list="ordered"] > .xa-ui::before {
    color: var(--li-marker-color, currentColor);
  }

  ._left-list {
    text-align: left;
    list-style-position: inside;
  }

  ._center-list {
    text-align: center;
    list-style-position: inside;
  }

  ._right-list {
    text-align: right;
    list-style-position: inside;
  }

  ._justify-list {
    text-align: justify;
    list-style-position: inside;
  }

  ._left {
    text-align: left;
  }

  ._center {
    text-align: center;
  }

  ._right {
    text-align: right;
  }

  ._justify {
    text-align: justify;
  }

  .editor-toolbar {
    position: relative;
    border-top-left-radius: 8px;
    border-top-right-radius: 8px;
    padding: 8px;
    background: #f6f7f9;
  }

  .editor-toolbar button {
    width: 32px;
    height: 28px;
    text-align: center;
    padding: 4px 8px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    cursor: pointer;
    box-sizing: border-box;
  }

  /* ✅ FIX (bug "2 kotak" saat disentuh di HP -- akar masalah SEBENARNYA,
     beda dari fix :hover/:active sebelumnya): browser mobile (terutama
     Android Chrome) menambahkan lapisan highlight semi-transparan BAWAAN
     di atas elemen yang disentuh (tap highlight), independen dari CSS
     background/border kita, dan TIDAK bisa dimatikan lewat
     background/border biasa -- harus lewat -webkit-tap-highlight-color.
     Karena wrapper (.toolbar-video-wrap/.toolbar-link-wrap) dan tombol
     di dalamnya adalah 2 elemen tap-able terpisah, browser bisa
     menggambar highlight bawaan ini ke keduanya sendiri-sendiri ->
     terlihat seperti 2 kotak menumpuk, dan sempat tetap terlihat
     setelah tap (butuh waktu fade-out). Reset di sini berlaku untuk
     SEMUA tombol toolbar & wrapper-nya (bukan cuma Video/Link) supaya
     konsisten -- toolbar penuh sentuhan seperti ini rawan kena efek
     yang sama walau belum tentu semuanya kelihatan separah Video/Link
     (yang paling kena karena strukturnya 2 lapis elemen). */
  .editor-toolbar button,
  .toolbar-video-wrap,
  .toolbar-video-wrap button,
  .toolbar-link-wrap,
  .toolbar-link-wrap button,
  .toolbar-color-btn-wrap,
  .toolbar-color-btn {
    -webkit-tap-highlight-color: transparent;
  }

  .editor-toolbar button i {
    margin: auto;
    font-size: 12px;
  }

  .editor-toolbar button:hover {
    background: rgba(3, 190, 252, 0.25);
    border-color: rgba(3, 190, 252, 0.25);
  }

  .editor-toolbar button.active {
    background-color: #03befc !important;
    color: white !important;
    border-color: #03befc !important;
  }

  /* ===== FIX: Tombol Bullet List & Numbered List menyusut tidak sinkron
     dengan tombol Warna Teks (A) / Warna Latar (paint) di mode HP =====
     Root cause (dibuktikan lewat pengukuran langsung, bukan tebakan):
     saat sebuah <button> punya box-sizing:border-box + flex-basis:0
     (dari flex:1 1 0) DAN punya padding/border sungguhan, browser
     memperlakukan padding+border itu sebagai tambahan ukuran tetap di
     luar proporsi flex-grow -- sehingga tombol ini selalu tampil ~8px
     (padding 3px*2 + border 1px*2) lebih lebar daripada elemen flex
     tetangga (mis. .toolbar-color-btn-wrap) yang sama-sama flex:1 1 0
     tapi tidak punya padding/border sungguhan.
     Fix (padding:0 + border:none + outline negatif sebagai border
     palsu -- lihat update di bawah) SEKARANG DIPINDAH ke dalam
     @media (max-width: 480px), sesuai tempat masalahnya sebenarnya
     terjadi (flex:1 1 0 cuma aktif di mode HP) -- lihat blok itu untuk
     rule-nya. Sebelumnya fix ini ditulis DI LUAR media query (jadi ikut
     aktif juga di desktop), padahal di desktop tombol ini masih pakai
     width:32px tetap (bukan flex:1 1 0) sehingga bug-nya tidak pernah
     muncul di sana. Di desktop sekarang tombol ini TIDAK diberi
     override apa pun lagi, jadi otomatis mewarisi border asli dari
     ".editor-toolbar button" (1px solid #ccc) sama seperti semua
     tombol toolbar lainnya.
     ✅ UPDATE: "border palsu" di mode HP tadinya dibuat dengan
     box-shadow (inset 0 0 0 1px), tapi teknik ini terbukti tetap
     terlihat sedikit lebih tebal/berat dibanding border asli tombol
     lain -- box-shadow dirender lewat pipeline bayangan (ikut
     anti-aliasing) sementara border dirender lewat pipeline garis,
     jadi walau sama-sama "1px" hasilnya tidak 100% identik secara
     visual. Diganti ke `outline` dengan `outline-offset` negatif:
     outline sama seperti box-shadow (tidak ikut dihitung dalam box
     model/flex-basis, jadi fix sinkronisasi lebar tetap aman), tapi
     rendering-nya memakai jalur yang sama dengan border asli sehingga
     hasilnya sekarang konsisten di semua tombol. Lihat rule di dalam
     @media (max-width: 480px) untuk detail. */

  .custom-select {
    position: relative;
    height: 28px;
    width: 100%;
    font-size: 13px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    box-sizing: border-box;
    /* ✅ Fix: sebelumnya .custom-select TIDAK punya box-sizing:border-box
       padahal punya border 1px, sementara .editor-toolbar button (tombol
       biasa seperti Reset/Bold/dst) SUDAH border-box. Akibatnya border
       dropdown ini (dipakai oleh Icon/Emoticon, Font, Size, Spacing)
       dihitung DI LUAR width/min-width yang sudah diset -- ukuran akhirnya
       jadi tidak presisi sama dengan tombol lain saat toolbar disusutkan
       ke mode HP (min-width:18px dst tidak konsisten hasil akhirnya). */
    cursor: pointer;
    user-select: none;
    transition: background 0.2s ease;
    /* ✅ Fix: sebelumnya .select-display (anak in-flow satu-satunya di
       sini -- .select-options posisinya absolute jadi tidak ikut flex)
       hanya block biasa yang nempel di baris paling atas kotak 28px
       ini. Di desktop nyaris tidak kelihatan karena padding+font-nya
       hampir memenuhi 28px. Di mode HP, padding & font-size .select-
       display mengecil tapi tinggi 28px di sini TETAP -- sehingga teks
       terlihat "nempel pojok kiri-atas" dengan ruang kosong di bawah.
       display:flex + align-items:center DI SINI (bukan di .select-
       display sendiri) menjaga .select-display selalu center vertikal
       berapa pun ukurannya, TANPA mengganggu text-overflow:ellipsis
       internal .select-display (yang butuh konteks block, bukan flex,
       untuk memotong nama font panjang jadi "..."). */
    display: flex;
    align-items: center;
  }

  .custom-select:hover {
    background: rgba(3, 190, 252, 0.25);
    border-color: rgba(3, 190, 252, 0.25);
  }

  .custom-select.open {
    background: rgba(3, 190, 252, 0.25);
    border-color: rgba(3, 190, 252, 0.25);
  }

  .custom-select .select-display {
    padding: 6px 10px;
    /* flex:1 supaya tetap mengisi lebar penuh .custom-select (perilaku
       block sebelumnya), min-width:0 WAJIB ada -- default min-width
       flex-item adalah "auto" (=lebar konten), yang akan mencegah
       kotak ini pernah menyusut lebih kecil dari teksnya sendiri,
       sehingga overflow:hidden + text-overflow:ellipsis di bawah
       (rule .toolbar-select/.toolbar-font-family dst) tidak akan
       pernah ke-trigger sama sekali kalau min-width:0 ini tidak ada. */
    flex: 1 1 auto;
    min-width: 0;
  }

  /* ✅ Fix: z-index popup ini SEBELUMNYA cuma 10 -- lebih rendah dari
     nub TrackPoint/.toolbar-trackpoint (z-index:30, lihat rule-nya di
     bawah). Karena posisi nub itu ada di antara baris 2 & 3 toolbar,
     popup dropdown manapun (Paragraph/Heading, Font, Size, Spacing)
     yang tingginya sampai menjangkau area itu akan tertutupi/"tertabrak"
     oleh nub-nya alih-alih tampil menutup di atasnya. Dinaikkan ke atas
     30 (dan disamakan dengan popup toolbar besar lain seperti color
     picker/link/AI Writer yang sudah pakai 2000+) supaya popup dropdown
     SELALU jadi lapisan paling atas dibanding elemen dekoratif toolbar
     manapun, di breakpoint apa pun. */
  .custom-select .select-options {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 2000;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
  }

  .custom-select.open .select-options {
    display: block;
  }

  .custom-select .select-options li {
    padding: 6px 10px;
    transition: background 0.2s ease;
  }

  .custom-select .select-options li:hover {
    background-color: rgba(3, 190, 252, 0.25);
  }

  .button-editor-one {
    display: flex;
    width: 100%;
    position: relative;
    gap: 6px; /* jarak antar blok kiri-kanan, disamakan dgn gap tombol */
  }

  .button-editor-one .one-left {
    margin: 2px 0;
    display: flex;
    justify-content: flex-end;
    width: 50%;
  }

  /* Dropdown heading (Paragraph/H1-H6): disamakan dgn grid tombol
     (4*32 + 3*6 = 146px), bukan ikut melebar 100% kolom, supaya sisi
     kiri & kanannya sejajar dgn blok 4-tombol di baris lain. */
  .button-editor-one .one-left .custom-select {
    width: 146px;
  }

  .button-editor-one .one-right {
    margin: 2px 0;
    display: flex;
    gap: 6px;
    width: 50%;
  }

  /* ✅ FIX: sebelumnya .two-left/.two-right & .three-left/.three-right
     pakai flex "width:50% + gap:6px", yang totalnya SELALU 6px lebih
     lebar dari kontainer (50%+50%+6px > 100%) -- browser terpaksa
     menyusutkan (flex-shrink) kedua kolom utk pas, dan sisa pecahan
     piksel dari penyusutan itu tidak selalu dibulatkan simetris antara
     kolom yang rata-kanan (.two-left/.three-left, dipakai Strike & Font)
     dengan kolom yang rata-kiri (.two-right/.three-right, dipakai Text
     Color & Size) -- akibatnya notch cekungan 9x9px di sudut ke-4 tombol
     itu (lihat #tp-strikethrough-btn::after dkk di bawah) jadi tidak
     sama jaraknya ke nub TrackPoint, walau nilai widthnya "sama-sama
     50%". Diganti ke CSS Grid (grid-template-columns: 1fr 6px 1fr):
     grid TIDAK pernah overflow seperti itu -- track tengah 6px dicadangkan
     duluan sebagai nilai piksel pasti, sisanya baru dibagi rata ke 2
     track 1fr -- sehingga kolom kiri & kanan presisi identik lebarnya di
     kedua baris (dua & tiga), dan pusat track tengah (tempat TrackPoint
     duduk lewat left:50%) selalu tepat di tengah-tengah kedua kolom. */
  .button-editor-two {
    display: grid;
    grid-template-columns: 1fr 6px 1fr;
    width: 100%;
    position: relative;
  }

  .button-editor-two .two-left {
    grid-column: 1;
    margin: 2px 0;
    display: flex;
    justify-content: flex-end;
    gap: 6px;
  }

  .button-editor-two .two-right {
    grid-column: 3;
    margin: 2px 0;
    display: flex;
    gap: 6px;
  }

  .button-editor-three {
    display: grid;
    grid-template-columns: 1fr 6px 1fr;
    width: 100%;
    position: relative;
  }

  .button-editor-three .three-left {
    grid-column: 1;
    margin: 2px 0;
    display: flex;
    justify-content: flex-end;
    gap: 6px;
  }

  .button-editor-three .three-right {
    grid-column: 3;
    margin: 2px 0;
    display: flex;
    gap: 6px;
  }

  .button-editor-four {
    display: flex;
    width: 100%;
    position: relative;
    gap: 6px;
  }

  .button-editor-four .four-left {
    margin: 2px 0;
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    width: 50%;
  }

  .button-editor-four .four-right {
    margin: 2px 0;
    display: flex;
    gap: 6px;
    width: 50%;
  }

  /* Kontrol tambahan: font, size, warna, line-height */
  .toolbar-select {
    width: 92px;
    flex: 0 0 auto;
  }

  .toolbar-select .select-display {
    padding: 6px 8px;
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    box-sizing: border-box;
    /* ✅ Fix: tanpa border-box, width:100% (lihat .toolbar-emoticon
       .select-display) + padding di sini akan DIJUMLAHKAN (bukan
       dipotong dari 100%), jadi kontennya meluber keluar kotak
       .toolbar-emoticon yang sudah menyusut kecil di mode HP --
       inilah sebabnya Icon terlihat tidak ikut mengecil serasi
       dengan tombol Reset/Blockquote di sampingnya. */
  }

  .toolbar-select .select-options {
    max-height: 220px;
    overflow-y: auto;
  }

  .toolbar-select .select-options li {
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Lebar disesuaikan supaya sejajar dengan grid tombol kecil (32px + gap 6px) */
  /* Font: selebar 4 kotak kecil -> 4*32 + 3*6 */
  .toolbar-font-family {
    width: 146px;
  }

  /* Size: selebar 2 kotak kecil -> 2*32 + 1*6 */
  .toolbar-font-size {
    width: 70px;
  }

  .toolbar-font-size .select-display {
    padding: 6px 6px;
    text-align: left;
  }

  /* Spasi (line-height): selebar 2 kotak kecil (dipindah ke three-right) */
  .toolbar-line-height {
    width: 70px;
    height: 28px;
  }

  .toolbar-line-height .select-display {
    padding: 6px 8px;
    font-size: 12px;
    text-align: left;
  }

  /* Panel opsi tetap lebar & mudah dibaca walau kotak tertutup kecil */
  .toolbar-font-size .select-options {
    min-width: 90px;
  }

  /* Panel font dibuat lebih lebar (nama font banyak yang panjang) dan
     berisi kotak pencarian di atas + daftar font yang bisa discroll,
     mengikuti pola yang sama dengan panel emoji.
     PENTING: sama seperti popup Link & Emoticon, position dibuat "fixed"
     (bukan mewarisi "absolute" dari rule dasar .custom-select
     .select-options) supaya popup ini TIDAK ikut terpotong oleh
     overflow-y:auto milik panel .settings-overlay. "right: auto" WAJIB
     ditambahkan di sini untuk menimpa "right: 0" dari rule dasar --
     tanpa ini, dengan position:fixed + left (di-set lewat JS) + right:0
     sekaligus, lebar popup akan dipaksa membentang dari posisi tombol
     sampai ke tepi kanan LAYAR (bukan lagi 200px seperti seharusnya).
     Posisi top/left dihitung & di-set lewat JS (lihat
     positionToolbarFontFamilyPopup() di initToolbarFontFamilyPopupPosition
     pada bagian script) tiap kali popup ini dibuka. */
  .toolbar-font-family .select-options {
    position: fixed;
    right: auto;
    min-width: 200px;
    max-height: none;
    overflow: hidden;
    padding: 8px;
    box-sizing: border-box;
    display: none;
    flex-direction: column;
    z-index: 2000;
  }

  .toolbar-font-family.open .select-options {
    display: flex;
  }

  .toolbar-font-family .font-search-wrap {
    position: relative;
    margin-bottom: 6px;
    flex: 0 0 auto;
  }

  .toolbar-font-family .font-search {
    width: 100%;
    box-sizing: border-box;
    padding: 5px 8px 5px 26px;
    font-size: 12px;
    border: 1px solid rgba(3, 190, 252, 0.6);
    border-radius: 3px;
    outline: none;
  }

  .toolbar-font-family .font-search:focus {
    border-color: rgba(3, 190, 252, 0.6);
  }

  .toolbar-font-family .font-search-icon {
    position: absolute;
    left: 8px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 11px;
    color: #999;
    pointer-events: none;
  }

  .toolbar-font-family .font-list {
    flex: 1 1 auto;
    min-height: 0;
    max-height: 170px;
    overflow-y: auto;
  }

  .toolbar-font-family .font-list li.font-hidden {
    display: none;
  }

  .toolbar-font-family .font-empty {
    display: none;
    width: 100%;
    text-align: center;
    font-size: 12px;
    color: #999;
    padding: 10px 0;
  }

  .toolbar-font-family .font-empty.font-empty-visible {
    display: block;
  }

  body.dark-mode .toolbar-font-family .font-search {
    background: #2b2b2b;
    border-color: #555;
    color: #eee;
  }

  /* ✅ FIX: sebelumnya "border: none" -- tombol ini satu-satunya dari
     ke-4 tombol di sekeliling TrackPoint yang tidak punya garis border
     sama sekali (beda dari Strike/Font/Size yang semuanya border:1px
     solid #ccc). Padahal secara PIKSEL letaknya sudah presisi sama
     (lihat fix .toolbar-color-btn-wrap & grid .two-right/.three-right
     di atas) -- yang bikin masih kelihatan "beda sendiri" (lebih
     menempel ke TrackPoint) adalah border yang hilang ini: sisi tombol
     yang tanpa border jadi menyatu mulus dengan notch cekungnya (tidak
     ada garis pemisah), sementara sisi tombol lain yang berborder
     terlihat jelas berhenti di tepi notch. box-sizing:border-box jadi
     border 1px ini TIDAK mengubah ukuran/posisi kotak 32x28-nya sama
     sekali, murni menyamakan bobot visual. Warna border disamakan per
     mode lewat rule body:not(.dark-mode)/.dark-mode di bawah. */
  .toolbar-color-btn {
    position: relative;
    width: 32px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    cursor: pointer;
    box-sizing: border-box;
    flex: 0 0 auto;
  }

  .toolbar-color-btn:hover {
    background: #eef6ff;
  }

  .toolbar-color-btn i {
    font-size: 12px;
    pointer-events: none;
  }

  .toolbar-color-btn .color-indicator {
    position: absolute;
    bottom: 3px;
    left: 5px;
    right: 5px;
    height: 3px;
    border-radius: 1px;
    pointer-events: none;
    background: #000000;
    outline: 1px solid #999999;
  }

  .toolbar-color-btn[data-role="bg-color"] .color-indicator {
    background: #ffffff;
  }

  /* Tombol Warna Teks (A) & Warna Latar Belakang Teks (paint) di light
     mode dibuat langsung tampil dengan warna "aktif" yang sama seperti
     tombol toolbar lain saat .active (lihat .editor-toolbar button.active
     di atas), tanpa perlu class .active tambahan. Discoped dengan
     "body:not(.dark-mode)" supaya tampilan dark mode-nya tidak ikut
     berubah (tetap putih & abu seperti sebelumnya, lihat rule dasar
     .toolbar-color-btn di atas). */
  body:not(.dark-mode) .toolbar-color-btn {
    background-color: #03befc;
    border-color: #03befc;
    transition:
      background-color 0.15s ease,
      border-color 0.15s ease;
  }

  body:not(.dark-mode) .toolbar-color-btn i {
    color: #ffffff;
  }

  /* Efek hover dibuat halus mengikuti warna background tombolnya sendiri
     (nuansa biru yang sama, sedikit lebih terang), bukan lagi warna hover
     tombol toolbar lain (#eef6ff). */
  body:not(.dark-mode) .toolbar-color-btn:hover {
    background-color: #29c8fc;
  }

  .toolbar-color-btn input[type="color"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    padding: 0;
    border: none;
    /* Native color picker OS/browser dimatikan di sini -- diganti popup
       custom (.toolbar-ccp-popup) yang tampilannya sama persis dengan
       color picker latar belakang section (.ccp-popup). Fungsi (apply
       textColor/textBgColor) tetap sama seperti sebelumnya karena input
       ini tetap ada & tetap memicu event "input"/"change", hanya saja
       nilainya sekarang di-set lewat popup custom, bukan lewat picker
       bawaan browser. */
    pointer-events: none;
  }

  /* ===== Popup Color Picker untuk toolbar (Warna Teks & Warna Latar
     Belakang Teks) -- tombolnya TIDAK diubah, hanya popup yang muncul
     saat diklik yang dibuat sama persis dengan popup color picker latar
     belakang section (lihat .ccp-popup / .ccp-picker-area dkk di atas).
     Class ccp-* di dalamnya sengaja dipakai ulang (bukan diduplikasi)
     supaya tampilannya benar-benar identik. ===== */
  /* ✅ FIX: sebelumnya wrapper ini TIDAK punya width/flex tegas --
     lebarnya dihitung browser lewat "shrink-to-fit" (inline-flex tanpa
     width eksplisit), yang pembulatan sub-pikselnya bisa beda dari
     tombol tetangga (.toolbar-font-size) yang sudah punya width:70px +
     flex:0 0 auto pasti. Akibatnya walau .toolbar-color-btn di
     dalamnya sudah width:32px, wrapper pembungkusnya sendiri (elemen
     yang benar-benar jadi flex-item di .two-right) bisa mendarat di
     lebar yang tidak presisi 32px, sehingga ikon "A" di dalamnya
     kelihatan geser sedikit dibanding notch tombol lain. Width & flex
     di sini SEKARANG disamakan persis dengan .toolbar-color-btn di
     dalamnya supaya wrapper tidak lagi dihitung shrink-to-fit. */
  .toolbar-color-btn-wrap {
    position: relative;
    display: inline-flex;
    width: 32px;
    height: 28px;
    flex: 0 0 auto;
  }

  /* Pembungkus popup dibuat mengikuti gaya panel dropdown font/size/spacing
     (.select-options): border-radius & warna border yang sama, tanpa
     box-shadow.
     PENTING: position dibuat "fixed" (bukan "absolute" relatif ke
     tombolnya) supaya popup ini TIDAK ikut terpotong oleh overflow-y:auto
     milik panel .settings-overlay (parent panel ini otomatis memotong
     (clip) konten yang melebar ke kiri karena overflow-x-nya ikut jadi
     "auto"/terpotong begitu overflow-y diset). Dengan "fixed", posisi
     top/left popup dihitung & di-set lewat JS (lihat
     positionToolbarCcpPopup() di initToolbarCcpColorPicker) tiap kali
     dibuka, mengikuti posisi tombolnya di layar, sehingga popup bisa
     tampil menimpa (di atas) elemen lain tanpa terpotong batas panel. */
  .toolbar-ccp-popup {
    display: none;
    position: fixed;
    width: 232px;
    padding: 12px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    box-sizing: border-box;
    z-index: 2000;
  }

  /* Popup toolbar disusun jadi 1 baris (flex row): area hue-slider +
     kotak saturation/value di kiri, kotak-kotak warna preset (swatch) di
     kanan -- sebelumnya swatch berada di baris terpisah di bawah. */
  .toolbar-color-btn-wrap.open .toolbar-ccp-popup {
    display: flex;
    align-items: flex-start;
    gap: 10px;
  }

  /* Tinggi hue-slider & kotak saturation/value disesuaikan (dinaikkan)
     supaya sejajar dengan tinggi kolom swatch 3-kolom x 9-baris di
     sampingnya (sebelumnya dipendekkan ke 50px karena ada baris field
     HEX/R/G/B di bawahnya; field itu sudah dihapus). */
  .toolbar-ccp-popup .ccp-picker-area {
    flex: 1;
    height: 150px;
    margin-bottom: 0;
  }

  /* Tambahan jarak antara hue-slider & kotak saturation/value (ccp-sv-square)
     khusus di popup toolbar Text Color & Background Color (#txtc-color-popup,
     #txtbg-color-popup), supaya tidak berdempetan -- pola sama seperti yang
     dipakai di #bg-color-popup .ccp-sv-square. Ubah angka margin-left di
     bawah ini kalau mau jarak lebih besar/kecil. */
  .toolbar-ccp-popup .ccp-sv-square {
    margin-left: 6px;
  }

  /* Kolom swatch dipindah ke kanan area hue/sv, disusun 3 kolom supaya
     tetap ramping dan tingginya menyamai .ccp-picker-area di sampingnya. */
  .toolbar-ccp-popup .ccp-swatches {
    flex: 0 0 48px;
    width: 48px;
    grid-template-columns: repeat(3, 1fr);
    align-content: start;
    gap: 3px;
    margin: 0;
  }

  body.dark-mode .toolbar-ccp-popup {
    background-color: #222327;
    border-color: #444;
  }

  /* ===== TrackPoint ala keyboard ThinkPad =====
     Tombol bulat kecil warna merah yang diletakkan tepat di tengah
     persimpangan 4 tombol: Strikethrough (kiri-atas), Warna Teks
     (kanan-atas), Font (kiri-bawah), dan Size (kanan-bawah) -- meniru
     posisi TrackPoint asli yang duduk di antara tombol G/H/B keyboard
     ThinkPad.

     PENTING: posisi/ukuran ke-4 tombol ini SAMA SEKALI TIDAK diubah
     (tidak ada margin/offset tambahan apapun). Supaya TrackPoint tetap
     tidak "menutupi" tombol, sudut yang menghadap TrackPoint dibuat
     menjorok ke DALAM (cekung/concave) -- bukan rounded biasa yang
     membesar keluar -- lewat pseudo-element ::after berukuran 9x9px
     (seperempat lingkaran/quarter-disk, dibuat dengan memberi salah
     satu sudut kotaknya border-radius 100% dari sisinya) yang duduk
     PAS di sudut tombol dan TIDAK menjorok keluar sama sekali (beda
     dari versi sebelumnya yang berupa lingkaran penuh 18px separuh di
     dalam-separuh di luar tombol). Karena seluruh bentuknya ada di
     dalam batas tombol sendiri, border lengkungnya (disamakan dengan
     border tombol toolbar, lihat .editor-toolbar button) tidak pernah
     tumpang tindih / bersentuhan dengan border tombol tetangga --
     masing-masing dari ke-4 tombol punya notch sendiri yang berdiri
     sendiri, persis seperti notch/cutout pada keycap asli ThinkPad.
     Nub TrackPoint sendiri (z-index lebih tinggi) lalu duduk pas di
     celah cekung gabungan ke-4 notch tsb.

     ✅ DIKEMBALIKAN atas permintaan: sudut ke-4 tombol yang menghadap
     TrackPoint (Strikethrough: kanan-bawah, Warna Teks: kiri-bawah,
     Font: kanan-atas, Size: kiri-atas) dibuat melengkung CEKUNG ke luar
     tombol, seolah membungkus nub TrackPoint di tengah persimpangan.

     Teknik: pseudo-element ::after 9x9px persis di sudut tombol yang
     bersangkutan, diwarnai SAMA seperti background di baliknya
     (background .editor-toolbar, bukan background tombol), lalu diberi
     border-radius 100% pada sudut yang menghadap KE DALAM tombol --
     ini membuat kotak 9x9 tsb jadi seperempat-lingkaran (quarter-disk)
     yang "memakan" sudut tombol dengan warna background, sehingga tepi
     tombol yang tersisa tampak melengkung cekung mengikuti busur itu.

     ✅ FIX: patch ini SENGAJA TIDAK diberi border sama sekali (versi
     sebelumnya sempat diberi border 1px di 2 sisi supaya "menyambung"
     dengan border lurus tombol) -- karena radius-nya 100% (mengonsumsi
     SELURUH sisi kotak, tidak menyisakan bagian lurus sama sekali),
     arah singgung (tangent) busur di titik ujungnya (kanan & kiri notch)
     berubah 90° secara MENDADAK dari arah border lurus tombol di
     atasnya/sampingnya -- inilah yang menimbulkan kesan "lancip"/patah
     di ujung kiri-kanan notch, bukan lengkungan mulus. Tanpa border
     sama sekali, patch murni jadi tambalan warna background polos
     (menyatu sempurna dengan warna di baliknya, TIDAK ada garis apapun
     yang bisa terlihat patah), dan border tombol yang tersisa (di luar
     area notch) tetap berhenti rapi di tepi patch tanpa ada sambungan
     canggung.

     ✅ FIX (lancip di ujung notch): posisi absolute (top/right/bottom/
     left) pada patch ini dihitung relatif terhadap PADDING-BOX parent
     (tombolnya), BUKAN border-box -- jadi offset "bottom:0; right:0;"
     dkk sebelumnya berhenti TEPAT DI DALAM garis border 1px tombol,
     tidak ikut menutupinya. Akibatnya border asli tombol (1px, lurus)
     di ujung notch tetap terlihat menyembul sedikit sebagai bekas
     lancip/patah, walau area di dalamnya sudah "dimakan" quarter-disk
     background. Diperbaiki dengan offset -1px (bottom:-1px; right:-1px;
     dst) supaya patch ikut menjorok sampai ke border-box terluar
     tombol, menutupi juga 1px garis border aslinya di area notch --
     border tombol jadi benar-benar berhenti bersih di tepi lengkungan,
     tanpa sisa lancip apapun. */
  #tp-strikethrough-btn {
    position: relative;
  }

  #tp-strikethrough-btn::after,
  #txtc-color-label::after,
  .toolbar-font-family::after,
  .toolbar-font-size::after {
    content: "";
    position: absolute;
    width: 10px;
    height: 10px;
    background: #f5f5f5;
    z-index: 20;
    pointer-events: none;
  }

  /* Strikethrough: sudut KANAN-BAWAH (menghadap TrackPoint) */
  #tp-strikethrough-btn::after {
    bottom: -1px;
    right: -1px;
    border-top-left-radius: 100%;
  }

  /* Warna Teks: sudut KIRI-BAWAH */
  #txtc-color-label::after {
    bottom: -1px;
    left: -1px;
    border-top-right-radius: 100%;
  }

  /* Font: sudut KANAN-ATAS */
  .toolbar-font-family::after {
    top: -1px;
    right: -1px;
    border-bottom-left-radius: 100%;
  }

  /* Size: sudut KIRI-ATAS */
  .toolbar-font-size::after {
    top: -1px;
    left: -1px;
    border-bottom-right-radius: 100%;
  }

  /* Dark mode: background patch ikut background .editor-toolbar dark
     (#414045). Tidak ada border yang perlu di-override lagi. */
  body.dark-mode #tp-strikethrough-btn::after,
  body.dark-mode #txtc-color-label::after,
  body.dark-mode .toolbar-font-family::after,
  body.dark-mode .toolbar-font-size::after {
    background: #414045;
  }

  /* Nub TrackPoint itu sendiri: dibungkus radial-gradient supaya
     terlihat seperti tutup karet kecil, diposisikan absolute relatif ke
     .button-editor-two (parent-nya sudah position:relative) tepat di
     "top: 100%" (batas bawah baris 2 / batas atas baris 3), lalu digeser
     naik & ke kiri setengah dari ukurannya sendiri supaya betul-betul
     center di persimpangan garis horizontal & vertikal ke-4 tombol di
     atas. z-index-nya dibuat lebih tinggi dari ke-4 notch di atas
     supaya nub-nya tampil menimpa lubang cekung gabungan tsb. */
  .toolbar-trackpoint {
    position: absolute;
    top: 100%;
    left: 50%;
    width: 16px;
    height: 16px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    /* Tekstur bintik grip: pola titik timbul (highlight terang di atas-kiri
       tiap titik, bayangan gelap di bawah-kanan) di-tile lurus sejajar
       lewat background-size, ditumpuk di atas gradient bola merah. */
    background-image:
      radial-gradient(circle at 0.6px 0.6px, rgba(255, 255, 255, 0.45) 0.4px, transparent 0.55px),
      radial-gradient(circle at 1.4px 1.4px, rgba(90, 10, 16, 0.55) 0.5px, transparent 0.65px),
      radial-gradient(circle at 35% 30%, #ff7a7a 0%, #e6394a 45%, #a01822 100%);
    background-size: 2.4px 2.4px, 2.4px 2.4px, 100% 100%;
    background-repeat: repeat, repeat, no-repeat;
    background-position: 0 0, 0 0, 0 0;
    border: 1px solid #7a1119;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), inset 0 1px 1px rgba(255, 255, 255, 0.45);
    z-index: 30;
    cursor: pointer;
    transition: transform 0.1s ease;
  }

  .toolbar-trackpoint:active {
    transform: translate(-50%, -50%) scale(0.92);
  }

  body.dark-mode .toolbar-trackpoint {
    border-color: #5c0d12;
  }

  /* TrackPoint yang dinonaktifkan lewat icon settings di pojok kanan
     .editor-container-tabs: dibuat abu-abu (desaturasi), redup, dan
     pointer-events:none supaya benar-benar tidak bisa diklik (baik mouse
     maupun keyboard, lihat JS yang juga men-set tabindex="-1" &
     aria-disabled="true"). */
  .toolbar-trackpoint.disabled {
    /* CATATAN: pointer-events SENGAJA tidak di-set none di sini (beda dari
       versi sebelumnya) supaya nub tetap bisa menerima event mouse/touch
       walau sedang nonaktif -- ini dibutuhkan supaya long-press LANGSUNG di
       atas nub tetap bisa dipakai untuk MENGAKTIFKAN kembali TrackPoint saat
       statusnya disabled. Klik singkat (buka popup AI Writer) tetap diblok,
       tapi lewat guard di JS (bukan lewat CSS), lihat initToolbarAIPopup(). */
    opacity: 0.45;
    filter: grayscale(1);
    cursor: pointer;
  }

  /* Feedback visual selama long-press berlangsung di atas nub TrackPoint:
     nub membesar sedikit + diberi cincin merah supaya user tahu tekanan
     lama sedang terdeteksi dan akan men-toggle enable/disable begitu
     durasi ambang batasnya (lihat JS: TP_LONG_PRESS_MS) tercapai. */
  .toolbar-trackpoint.long-pressing {
    transform: translate(-50%, -50%) scale(1.18);
    box-shadow: 0 0 0 4px rgba(230, 57, 74, 0.35), 0 1px 2px rgba(0, 0, 0, 0.35);
  }
  
  /* ===== Popup AI Writer (trackpoint) =====
     Diklik lewat .toolbar-trackpoint: menampilkan overlay seukuran persis
     .editor-toolbar (position:absolute; inset:0 terhadap .editor-toolbar
     yang sudah position:relative), menutupi seluruh tombol toolbar di
     baliknya, berisi textarea untuk mengetik perintah/prompt ke AI Writer. */
  .toolbar-ai-popup {
    display: none;
    position: absolute;
    inset: 0;
    background: #f5f5f5;
    border-radius: 2px;
    box-sizing: border-box;
    /* Bottom padding dilebihkan sedikit dibanding sisi lain supaya baris
       tombol stop/generate (di paling bawah popup) tidak terasa menempel
       ke tepi bawah popup. */
    padding: 10px 10px 16px 10px;
    z-index: 5000;
  }

  .toolbar-ai-popup.open {
    display: flex;
    flex-direction: column;
  }

  .toolbar-ai-popup-inner {
    width: 100%;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    /* Gap antara judul & kotak prompt diperkecil sedikit (dari 8px) supaya
       kotak prompt naik lebih dekat ke judul. Karena ini flex-column,
       status text & baris kontrol yang ada DI DALAM .toolbar-ai-input-wrap
       otomatis ikut naik bersamanya (normal flow), tidak perlu disesuaikan
       terpisah. */
    gap: 4px;
    box-sizing: border-box;
  }

  .toolbar-ai-popup-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: bold;
    color: #555;
  }

  .toolbar-ai-popup-title i {
    color: #e6394a;
  }

  /* Badge kecil "Beta" di sebelah teks judul "AI Writer" -- warna oranye
     supaya beda aksen dari ikon wand di sebelahnya (#e6394a), menandakan
     status "Beta" secara visual (bukan lagi disamakan dengan warna ikon). */
  .toolbar-ai-beta-badge {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    color: #fff;
    background: #f97316;
    border-radius: 999px;
    padding: 3px 6px;
    white-space: nowrap;
    user-select: none;
  }

  .toolbar-ai-popup-title .toolbar-ai-close-btn {
    margin-left: auto;
    width: auto;
    height: auto;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    background: transparent;
    color: #888;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    position: relative;
    top: -5px;
    right: -3px;
  }

  .toolbar-ai-popup-title .toolbar-ai-close-btn:hover {
    background: transparent;
    color: #333;
  }

  /* Wrapper textarea: dibuat flex-column supaya baris kontrol (dropdown
     model + tombol stop + tombol generate) berada di ALUR DOKUMEN NORMAL,
     tepat di bawah textarea -- BUKAN mengambang (position:absolute) di
     atas textarea. Ini sengaja diubah dari pendekatan overlay lama
     (padding-bottom + absolute) karena itu rapuh: bergantung pada cara
     tiap browser menghitung batas scroll caret, jadi teks kadang masih
     bisa kelihatan "ketiban" tombol. Dengan alur normal begini, tombol
     TIDAK MUNGKIN pernah tertimpa teks apa pun yang terjadi di dalam
     textarea, karena keduanya tidak pernah saling tumpang tindih secara
     geometris sama sekali. */
  .toolbar-ai-input-wrap {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  /* Wrapper relative -- basis posisi absolute utk .toolbar-ai-char-count
     supaya counter-nya menempel persis di pojok kanan-bawah TEXTAREA
     (bukan pojok .toolbar-ai-input-wrap yang lebih besar, yang juga
     memuat status text & baris kontrol di bawah textarea). */
  .toolbar-ai-textarea-wrap {
    position: relative;
    width: 100%;
  }

  .toolbar-ai-prompt-input {
    width: 100%;
    height: 60px;
    min-height: 60px;
    max-height: 60px;
    resize: none;
    /* padding-bottom dilebihkan (18px, dari 8px) supaya baris teks
       terakhir tidak ketiban .toolbar-ai-char-count yang mengambang di
       pojok kanan-bawah. */
    padding: 8px 10px 18px 10px;
    font-family: inherit;
    font-size: 13px;
    color: #808080;
    border: 1px solid #ccc;
    border-radius: 5px;
    box-sizing: border-box;
    outline: none;
    /* Dipaksa eksplisit (jangan andalkan default browser) supaya teks
       SELALU discroll di dalam kotak, tidak pernah meluber keluar box,
       baik secara vertikal maupun horizontal. */
    overflow-y: auto;
    overflow-x: hidden;
    /* Cegah teks panjang tanpa spasi (URL dsb.) meluber ke samping. */
    word-break: break-word;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
    /* Sembunyikan scrollbar tapi tetap bisa discroll */
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE/Edge lama */
  }

  .toolbar-ai-prompt-input::-webkit-scrollbar {
    display: none; /* Chrome, Safari, Edge berbasis Chromium */
  }

  .toolbar-ai-prompt-input:focus {
    border-color: #3399ff;
  }

  /* Counter karakter "0/1000" di pojok kanan-bawah kotak prompt.
     pointer-events:none supaya tetap bisa klik/scroll textarea tepat di
     baliknya; user-select:none supaya tidak ikut ke-select bareng teks
     prompt saat user select-all di textarea. */
  .toolbar-ai-char-count {
    position: absolute;
    right: 8px;
    bottom: 5px;
    font-size: 10px;
    line-height: 1;
    color: #aaa;
    background: transparent;
    pointer-events: none;
    user-select: none;
    font-variant-numeric: tabular-nums;
  }

  /* Warna berubah jadi merah saat mendekati/menyentuh batas 1000 karakter,
     penanda visual tambahan selain angka itu sendiri. */
  .toolbar-ai-char-count.limit-near {
    color: #e6394a;
  }

  .toolbar-ai-input-controls {
    width: 100%;
    height: 28px;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 5px;
    box-sizing: border-box;
    /* Tambahan jarak dari baris teks status di atasnya (di luar `gap` milik
       .toolbar-ai-input-wrap) supaya baris tombol tidak terasa terlalu
       rapat dari atas. */
    margin-top: 4px;
  }

  /* Teks status animasi (bergantian), sekarang diposisikan tepat di bawah
     textarea (SEBELUM baris dropdown model/tombol stop-generate) supaya
     tidak menempel ke batas bawah popup. Disembunyikan (opacity:0) secara
     default, hanya diberi class "visible" via JS SELAMA proses generate
     berlangsung (lihat startStatusAnimation()/stopStatusAnimation() di
     initToolbarAIPopup()). Teksnya sendiri diganti-ganti tiap beberapa
     detik lewat JS, transition opacity di sini yang bikin pergantiannya
     terasa fade in/out, bukan "loncat" tiba-tiba. Dibuat flex row supaya
     badge status (#tp-ai-status-badge) sejajar di sebelah kiri teks. */
  .toolbar-ai-status-text {
    align-self: flex-start;
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 0;
    min-height: 16px;
    font-size: 11px;
    line-height: 1.5;
    color: #999;
    opacity: 0;
    transition: opacity 0.25s ease;
    pointer-events: none;
    user-select: none;
  }

  .toolbar-ai-status-text.visible {
    opacity: 1;
  }

  /* Badge kecil di sebelah kiri teks status.
       - is-processing -> TIDAK ADA teks lagi, cuma titik/lingkaran kecil
         hijau yang berkedip (blink) selama proses generate berjalan.
       - is-failed     -> tetap pill merah berlabel "Failed" seperti semula,
         supaya kegagalan tetap jelas terbaca (bukan cuma warna).
       - is-success    -> pill biru berlabel "Success", muncul sesaat
         setelah proses generate AI selesai BERHASIL (geometri sama persis
         dengan is-failed -- keduanya pill berlabel, bedanya cuma warna &
         teks), lalu ikut memudar seperti is-failed.
     Base rule di bawah ini menyimpan properti bersama (flex, align-items,
     dst); ukuran & bentuknya sendiri (dot bulat vs pill berlabel) diatur
     per-state di masing-masing class supaya keduanya bisa beda geometri. */
  .toolbar-ai-status-badge {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    font-weight: 700;
    line-height: 1.4;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    color: #fff;
    border-radius: 999px;
    white-space: nowrap;
  }

  /* Titik kecil hijau berkedip (bukan pill teks lagi) selama proses
     generate berjalan. Ukurannya tetap kecil (8px) supaya sejajar rapi
     dengan teks status animasi di sebelahnya. */
  .toolbar-ai-status-badge.is-processing {
    width: 8px;
    height: 8px;
    padding: 0;
    background: #1fa34a;
    animation: tp-ai-status-badge-blink 1.1s ease-in-out infinite;
  }

  .toolbar-ai-status-badge.is-failed {
    padding: 2px 6px;
    background: #d92626;
    animation: none;
  }

  /* Pill biru "Success", geometri sama dengan is-failed (padding, radius,
     dst diwarisi dari base rule .toolbar-ai-status-badge di atas) --
     bedanya cuma warna latar, supaya konsisten secara visual dengan
     pasangan gagalnya. */
  .toolbar-ai-status-badge.is-success {
    padding: 2px 6px;
    background: #2563eb;
    animation: none;
  }

  /* Efek "berkedip" untuk titik status processing: opacity naik-turun
     berulang, bukan warna yang berubah, supaya tetap terbaca sebagai hijau
     yang sama sepanjang animasinya. */
  @keyframes tp-ai-status-badge-blink {
    0%,
    100% {
      opacity: 1;
    }
    50% {
      opacity: 0.25;
    }
  }

  .toolbar-ai-status-message {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.5;
  }

  .toolbar-ai-model-select {
    position: relative;
    /* Disamakan dengan geseran naik tombol stop/generate di sebelahnya
       (lihat `top` pada .toolbar-ai-input-controls .toolbar-ai-stop-btn /
       .toolbar-ai-generate-btn) supaya teks dropdown model tetap sejajar
       secara visual dengan kedua tombol itu. Hanya posisi visual yang
       berubah, slot flex aslinya tetap sama sehingga tidak menambah
       tinggi baris ataupun popup. */
    top: -12px;
    flex: 0 0 auto;
    width: 92px;
    height: 22px;
    display: flex;
    align-items: center;
    padding: 0 2px;
    font-size: 10px;
    font-family: inherit;
    color: #555;
    cursor: pointer;
    user-select: none;
  }

  .toolbar-ai-model-select .ai-model-select-display {
    padding: 2px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 3px;
    width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .toolbar-ai-model-select .ai-model-select-display::after {
    content: "";
    flex: 0 0 auto;
    width: 0;
    height: 0;
    border-left: 3px solid transparent;
    border-right: 3px solid transparent;
    border-top: 4px solid currentColor;
    opacity: 0.7;
  }

  .toolbar-ai-model-select .ai-model-select-options {
    display: none;
    position: absolute;
    bottom: 100%;
    left: 0;
    margin: 0 0 4px 0;
    min-width: 110px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    list-style: none;
    padding: 2px 0;
    box-sizing: border-box;
    z-index: 20;
  }

  .toolbar-ai-model-select.open .ai-model-select-options {
    display: block;
  }

  .toolbar-ai-model-select .ai-model-select-options li {
    padding: 5px 10px;
    font-size: 11px;
    white-space: nowrap;
    transition: background 0.2s ease;
  }

  .toolbar-ai-model-select .ai-model-select-options li:hover {
    background-color: #f0f0f0;
  }

  .toolbar-ai-model-select .ai-model-select-options li.selected {
    color: #1a7ff0;
    font-weight: 600;
  }

  .toolbar-ai-model-select.disabled {
    opacity: 0.5;
    pointer-events: none;
  }

  .toolbar-ai-input-controls .toolbar-ai-stop-btn,
  .toolbar-ai-input-controls .toolbar-ai-generate-btn {
    position: relative;
    /* Digeser naik beberapa px supaya tidak terlalu mepet ke batas bawah
       popup, TANPA mengubah tinggi baris (.toolbar-ai-input-controls)
       ataupun ukuran popup itu sendiri -- `top` negatif ini hanya
       menggeser tampilan visual tombolnya, posisi asal di flex layout
       (dan ruang yang dipakainya) tetap sama seperti semula. */
    top: -12px;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid #ccc;
    background: white;
    cursor: pointer;
    box-sizing: border-box;
  }

  .toolbar-ai-input-controls .toolbar-ai-stop-btn i,
  .toolbar-ai-input-controls .toolbar-ai-generate-btn i {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1em;
    height: 1em;
    margin: 0;
    padding: 0;
    line-height: 1;
    font-size: 12px;
    pointer-events: none;
    color: inherit;
  }

  .toolbar-ai-input-controls .toolbar-ai-stop-btn {
    background: #fdeaea;
    color: #d92626;
    border-color: #f3a5a5;
  }

  .toolbar-ai-input-controls .toolbar-ai-stop-btn:hover:not(:disabled) {
    background: #fbd6d6;
  }

  .toolbar-ai-input-controls .toolbar-ai-stop-btn:disabled {
    background: #f2f2f2;
    color: #bbb;
    border-color: #e2e2e2;
    cursor: default;
  }

  .toolbar-ai-input-controls .toolbar-ai-generate-btn {
    background: #1a7ff0;
    border-color: #1a7ff0;
    color: white;
  }

  .toolbar-ai-input-controls .toolbar-ai-generate-btn:hover:not(:disabled) {
    background: #2288ee;
  }

  .toolbar-ai-input-controls .toolbar-ai-generate-btn:disabled {
    background: #a6d1ff;
    border-color: #a6d1ff;
    color: white;
    cursor: default;
  }

  body.dark-mode .toolbar-ai-popup {
    background: #414045;
  }

  body.dark-mode .toolbar-ai-popup-title {
    color: #ddd;
  }

  body.dark-mode .toolbar-ai-status-text {
    color: #888;
  }

  body.dark-mode .toolbar-ai-status-badge.is-processing {
    background: #2fbf5f;
  }

  body.dark-mode .toolbar-ai-status-badge.is-failed {
    background: #e6394a;
  }

  body.dark-mode .toolbar-ai-status-badge.is-success {
    background: #3b82f6;
  }

  body.dark-mode .toolbar-ai-beta-badge {
    background: #fb923c;
  }

  body.dark-mode .toolbar-ai-popup-title .toolbar-ai-close-btn {
    background: transparent;
    color: #aaa;
  }

  body.dark-mode .toolbar-ai-popup-title .toolbar-ai-close-btn:hover {
    background: transparent;
    color: #fff;
  }

  body.dark-mode .toolbar-ai-prompt-input {
    background: #222327;
    color: #ddd;
    border-color: #444;
  }

  body.dark-mode .toolbar-ai-char-count {
    color: #666;
  }

  body.dark-mode .toolbar-ai-char-count.limit-near {
    color: #ff6b6b;
  }

  body.dark-mode .toolbar-ai-model-select {
    color: #ddd;
  }

  body.dark-mode .toolbar-ai-model-select .ai-model-select-options {
    background: #2c2d31;
    border-color: #444;
  }

  body.dark-mode .toolbar-ai-model-select .ai-model-select-options li:hover {
    background-color: #3a3b40;
  }

  body.dark-mode .toolbar-ai-input-controls .toolbar-ai-stop-btn {
    background: #3a2323;
    color: #ff6b6b;
    border-color: #7a3a3a;
  }

  body.dark-mode .toolbar-ai-input-controls .toolbar-ai-stop-btn:hover:not(:disabled) {
    background: #4a2b2b;
    border-color: #a04a4a;
  }

  body.dark-mode .toolbar-ai-input-controls .toolbar-ai-stop-btn:disabled {
    background: #2c2d31;
    color: #6c6d72;
    border-color: #55565c;
  }

  body.dark-mode .toolbar-ai-input-controls .toolbar-ai-generate-btn:not(:disabled) {
    background-color: #1a7ff0;
    border-color: #1a7ff0;
    color: white;
  }

  body.dark-mode .toolbar-ai-input-controls .toolbar-ai-generate-btn:hover:not(:disabled) {
    background-color: #2288ee;
  }

  body.dark-mode .toolbar-ai-input-controls .toolbar-ai-generate-btn:disabled {
    background: #24344a;
    color: #7893ad;
    border-color: #3c5573;
  }

  /* ===== Popup Insert Link (toolbar) =====
     Gaya pembungkusnya mengikuti dropdown font/size/spacing (.select-options
     / .toolbar-ccp-popup): menempel tepat di bawah tombolnya, border-radius
     & warna border sama, tanpa box-shadow.
     PENTING: sama seperti .toolbar-ccp-popup, position dibuat "fixed"
     (bukan "absolute" relatif ke tombolnya) supaya popup ini TIDAK ikut
     terpotong oleh overflow-y:auto milik panel .settings-overlay (parent
     panel ini otomatis memotong (clip) konten yang melebar karena
     overflow-x-nya ikut jadi "auto"/terpotong begitu overflow-y diset) --
     inilah sebabnya sisi kanan popup ini dulu terlihat "tertutup"/tidak
     terlihat begitu layar/panel diperkecil, padahal lebar popup sendiri
     sudah menyusut dengan benar. Posisi top/left dihitung & di-set lewat
     JS (lihat positionToolbarLinkPopup() di initToolbarLinkPopup) tiap
     kali dibuka, mengikuti posisi tombolnya di layar, sehingga popup bisa
     tampil menimpa (di atas) elemen lain tanpa terpotong batas panel. */
  .toolbar-link-wrap {
    position: relative;
    display: inline-flex;
    height: 28px;
    box-sizing: border-box;
    /* ✅ Fix: disamakan dgn .editor-toolbar button & .custom-select
       (lihat komentar box-sizing di .custom-select) supaya wrapper Link
       ikut konsisten menyusut mengikuti min-width/max-width mode HP,
       sejajar dengan tombol Reset/Icon/lainnya di baris yang sama. */
  }

  .toolbar-link-popup {
    display: none;
    position: fixed;
    width: 240px;
    padding: 8px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    box-sizing: border-box;
    z-index: 2000;
    gap: 6px;
    flex-direction: column;
    align-items: stretch;
  }

  .toolbar-link-wrap.open .toolbar-link-popup {
    display: flex;
  }

  .link-popup-row {
    display: flex;
    gap: 6px;
    align-items: center;
  }

  .link-newtab-check {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: #666;
    cursor: pointer;
    user-select: none;
    padding: 0 2px;
  }

  .link-newtab-check input[type="checkbox"] {
    margin: 0;
    width: 13px;
    height: 13px;
    cursor: pointer;
    accent-color: #3399ff;
  }

  body.dark-mode .link-newtab-check {
    color: #bbb;
  }

  .link-url-input {
    flex: 1;
    min-width: 0;
    height: 28px;
    padding: 4px 8px;
    font-size: 12px;
    border: 1px solid rgba(3, 190, 252, 0.6);
    border-radius: 3px;
    box-sizing: border-box;
    font-family: inherit;
  }

  .link-url-input:focus {
    outline: none;
    border-color: rgba(3, 190, 252, 0.6);
  }

  .link-url-input:disabled {
    background: #f5f5f5;
    color: #555;
    cursor: default;
  }

  .link-popup-actions {
    display: flex;
    gap: 4px;
    flex: 0 0 auto;
  }

  .link-action-btn {
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    cursor: pointer;
    padding: 0;
    box-sizing: border-box;
    flex: 0 0 auto;
  }

  .link-action-btn i {
    font-size: 11px;
    pointer-events: none;
  }

  .link-action-btn:hover {
    background: #e6e6e6;
  }

  #link-save-btn {
    color: #2f9e44;
    border-color: #b2f2bb;
  }

  #link-save-btn:hover {
    background: #ebfbee;
  }

  /* Samakan border & hover tombol Simpan Link dengan pola warna toolbar
     (border seperti input search/link, hover seperti tombol toolbar
     lain), khusus mode default -- dark mode tetap pakai tampilan lama. */
  body:not(.dark-mode) #link-save-btn {
    border-color: rgba(3, 190, 252, 0.6);
  }

  body:not(.dark-mode) #link-save-btn:hover {
    background: rgba(3, 190, 252, 0.25);
  }

  .link-action-btn-remove {
    color: #e03131;
    border-color: #ffc9c9;
  }

  .link-action-btn-remove:hover {
    background: #fff5f5;
  }

  /* Sembunyikan tombol Simpan saat sedang menampilkan link yang sudah ada
     (mode "lihat"), dan sembunyikan Edit/Hapus saat sedang mengetik link
     baru / mode edit -- diatur lewat class "editing" pada wrapper. */
  .toolbar-link-wrap:not(.editing) #link-save-btn {
    display: none;
  }

  .toolbar-link-wrap.editing #link-edit-btn,
  .toolbar-link-wrap.editing #link-remove-btn {
    display: none;
  }

  /* Saat belum ada link sama sekali (insert baru), tombol Hapus tidak
     relevan -- disembunyikan lewat class "no-link" pada wrapper. */
  .toolbar-link-wrap.no-link #link-remove-btn {
    display: none;
  }

  body.dark-mode .toolbar-link-popup {
    background-color: #222327;
    border-color: #444;
  }

  body.dark-mode .link-url-input {
    background-color: #1b1c1f;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .link-url-input:disabled {
    background-color: #2a2a2e;
    color: #aaa;
  }

  body.dark-mode .link-action-btn {
    background-color: #2a2a2e;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .link-action-btn:hover {
    background-color: #333;
  }

  /* Samakan border & hover tombol Simpan Link dengan tombol Edit Link
     (yang memakai gaya dasar .link-action-btn) khusus di dark mode. */
  body.dark-mode #link-save-btn {
    border-color: #444;
  }

  body.dark-mode #link-save-btn:hover {
    background: #333;
  }

  /* ===== Popup Insert Video (toolbar) =====
     Strukturnya SAMA PERSIS dengan popup Insert Link di atas (input URL
     + tombol Simpan/Edit/Hapus, tanpa box-shadow, dropdown-style), cuma
     beda 2 hal:
     1) Menempel rata dari sisi KANAN tombolnya (melebar ke KIRI), bukan
        dari sisi kiri seperti Link -- lihat positionToolbarVideoPopup(),
        polanya sama seperti positionToolbarCcpPopup() (rect.right -
        popupWidth).
     2) Tidak ada checkbox "Buka di tab baru" (tidak relevan utk video). */
  .toolbar-video-wrap {
    position: relative;
    display: inline-flex;
    height: 28px;
    box-sizing: border-box;
  }

  .toolbar-video-popup {
    display: none;
    position: fixed;
    width: 240px;
    padding: 8px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    box-sizing: border-box;
    z-index: 2000;
    gap: 6px;
    flex-direction: column;
    align-items: stretch;
  }

  .toolbar-video-wrap.open .toolbar-video-popup {
    display: flex;
  }

  .video-popup-row {
    display: flex;
    gap: 6px;
    align-items: center;
  }

  .video-url-input {
    flex: 1;
    min-width: 0;
    height: 28px;
    padding: 4px 8px;
    font-size: 12px;
    border: 1px solid rgba(3, 190, 252, 0.6);
    border-radius: 3px;
    box-sizing: border-box;
    font-family: inherit;
  }

  .video-url-input:focus {
    outline: none;
    border-color: rgba(3, 190, 252, 0.6);
  }

  .video-url-input:disabled {
    background: #f5f5f5;
    color: #555;
    cursor: default;
  }

  .video-popup-actions {
    display: flex;
    gap: 4px;
    flex: 0 0 auto;
  }

  .video-action-btn {
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: white;
    border: 1px solid #ccc;
    border-radius: 3px;
    cursor: pointer;
    padding: 0;
    box-sizing: border-box;
    flex: 0 0 auto;
  }

  .video-action-btn i {
    font-size: 11px;
    pointer-events: none;
  }

  .video-action-btn:hover {
    background: #e6e6e6;
  }

  #video-save-btn {
    color: #2f9e44;
    border-color: #b2f2bb;
  }

  #video-save-btn:hover {
    background: #ebfbee;
  }

  body:not(.dark-mode) #video-save-btn {
    border-color: rgba(3, 190, 252, 0.6);
  }

  body:not(.dark-mode) #video-save-btn:hover {
    background: rgba(3, 190, 252, 0.25);
  }

  .video-action-btn-remove {
    color: #e03131;
    border-color: #ffc9c9;
  }

  .video-action-btn-remove:hover {
    background: #fff5f5;
  }

  /* Sembunyikan tombol Simpan saat sedang menampilkan video yang sudah
     ada (mode "lihat"), dan sembunyikan Edit/Hapus saat sedang mengetik
     URL baru / mode edit -- sama seperti pola popup Link. */
  .toolbar-video-wrap:not(.editing) #video-save-btn {
    display: none;
  }

  .toolbar-video-wrap.editing #video-edit-btn,
  .toolbar-video-wrap.editing #video-remove-btn {
    display: none;
  }

  /* Saat belum ada video sama sekali (insert baru), tombol Hapus tidak
     relevan -- disembunyikan lewat class "no-video" pada wrapper. */
  .toolbar-video-wrap.no-video #video-remove-btn {
    display: none;
  }

  body.dark-mode .toolbar-video-popup {
    background-color: #222327;
    border-color: #444;
  }

  body.dark-mode .video-url-input {
    background-color: #1b1c1f;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .video-url-input:disabled {
    background-color: #2a2a2e;
    color: #aaa;
  }

  body.dark-mode .video-action-btn {
    background-color: #2a2a2e;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .video-action-btn:hover {
    background-color: #333;
  }

  body.dark-mode #video-save-btn {
    border-color: #444;
  }

  body.dark-mode #video-save-btn:hover {
    background: #333;
  }

  /* Emoticon: selebar 1 kotak kecil, ikon rata tengah, panel opsi berisi
     kotak pencarian + grid emoji yang bisa discroll */
  .toolbar-emoticon {
    width: 32px;
    height: 28px;
  }

  .toolbar-emoticon .select-display {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
  }

  .toolbar-emoticon .select-display i {
    display: block;
  }

  /* PENTING: popup ini (seperti popup Link & popup warna) awalnya
     mewarisi "position: absolute" dari rule dasar .custom-select
     .select-options, relatif terhadap tombolnya sendiri. Karena
     tombolnya berada di dalam panel .settings-overlay yang punya
     overflow-y:auto (yang membuat overflow-x ikut otomatis "terpotong"
     juga sesuai spesifikasi CSS), popup absolute ini tidak hanya
     berpotensi terpotong tapi elemen absolute yang meluber dari batas
     panel tetap dihitung sebagai "scrollable overflow" oleh browser --
     sehingga begitu popup ini dibuka, panel/toolbar terlihat "bergeser"
     sendiri (auto-scroll horizontal) alih-alih popup-nya yang menyusuaikan
     diri. Sama seperti popup Link & warna, solusinya: position dibuat
     "fixed" (lolos dari containing block/overflow panel manapun), lalu
     posisi top/left dihitung & di-set lewat JS (lihat
     positionToolbarEmoticonPopup() & initToolbarEmoticonPopupPosition()
     di bagian script) tiap kali popup ini dibuka. */
  .toolbar-emoticon .select-options {
    width: 200px;
    height: 150px;
    padding: 8px;
    box-sizing: border-box;
    position: fixed;
    z-index: 2000;
    /* Panel dibuat flex-column: kotak pencarian ukuran tetap di atas,
       grid emoji mengisi sisa ruang & yang scroll (bukan panel luarnya),
       supaya total ukuran panel persis 200x150px tanpa 2 scrollbar. */
    display: none;
    flex-direction: column;
    overflow: hidden;
  }

  .toolbar-emoticon.open .select-options {
    display: flex;
  }

  .toolbar-emoticon .emoji-search-wrap {
    position: relative;
    margin-bottom: 6px;
    flex: 0 0 auto;
  }

  .toolbar-emoticon .emoji-search {
    width: 100%;
    box-sizing: border-box;
    padding: 5px 8px 5px 26px;
    font-size: 12px;
    border: 1px solid rgba(3, 190, 252, 0.6);
    border-radius: 3px;
    outline: none;
  }

  .toolbar-emoticon .emoji-search:focus {
    border-color: rgba(3, 190, 252, 0.6);
  }

  .toolbar-emoticon .emoji-search-icon {
    position: absolute;
    left: 8px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 11px;
    color: #999;
    pointer-events: none;
  }

  /* Baris "sering digunakan": 5 emoji teratas, dipisahkan garis dari
     grid emoji lengkap di bawahnya. Disembunyikan (tidak makan tempat)
     selama belum ada emoji yang pernah dipakai. */
  .toolbar-emoticon .emoji-favorites {
    display: none;
    flex: 0 0 auto;
    flex-wrap: wrap;
    padding-bottom: 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid #ddd;
  }

  .toolbar-emoticon .emoji-favorites.emoji-favorites-visible {
    display: flex;
  }

  .toolbar-emoticon .emoji-grid {
    display: flex;
    flex-wrap: wrap;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }

  .toolbar-emoticon .emoji-item {
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    cursor: pointer;
    border-radius: 3px;
    transition: background 0.15s ease;
  }

  .toolbar-emoticon .emoji-item:hover {
    background-color: rgba(3, 190, 252, 0.25);
  }

  .toolbar-emoticon .emoji-item.emoji-hidden {
    display: none;
  }

  .toolbar-emoticon .emoji-empty {
    display: none;
    width: 100%;
    text-align: center;
    font-size: 12px;
    color: #999;
    padding: 10px 0;
  }

  .toolbar-emoticon .emoji-empty.emoji-visible {
    display: block;
  }

  /* Toggle Dark Mode */
  .dark-toggle {
    position: absolute;
    top: 50%;
    right: 10px;
    transform: translateY(-50%);
    width: 30px;
    height: 16px;
  }

  .dark-toggle input {
    opacity: 0;
    width: 0;
    height: 0;
  }

  .dark-toggle .slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    border-radius: 20px;
    transition: 0.3s;
  }

  .dark-toggle .slider::before {
    content: "";
    position: absolute;
    height: 12px;
    width: 12px;
    left: 2px;
    bottom: 2px;
    background-color: white;
    border-radius: 50%;
    transition: 0.3s;
  }

  .dark-toggle input:checked + .slider {
    box-shadow: 0 0 0 0.5px #555555;
    background-color: #000;
  }

  .dark-toggle input:checked + .slider::before {
    transform: translateX(14px);
  }

  body.dark-mode ._top-header {
    background-color: #222327 !important;
    color: #ddd !important;
    border-bottom: 1px solid #444;
  }

  body.dark-mode ._main-list,
  body.dark-mode .history-container,
  body.dark-mode .settings-overlay,
  body.dark-mode ._section-sidebar,
  body.dark-mode .settings-group,
  body.dark-mode .visual-editor-container {
    background-color: #222327 !important;
    color: #ddd !important;
    border-color: #333 !important;
  }

  body.dark-mode ._sidebar-header,
  body.dark-mode ._sidebar-footer {
    background-color: #414045 !important;
    color: #ddd !important;
    border-color: #333 !important;
  }

  body.dark-mode ._main-list h3,
  body.dark-mode .settings-header h4,
  body.dark-mode ._sidebar-header ._logo,
  body.dark-mode ._top-header ._user-name,
  body.dark-mode ._sidebar-footer i,
  body.dark-mode ._menu-item input,
  body.dark-mode .settings-group h5,
  body.dark-mode .settings-option label {
    color: #ccc !important;
  }

  body.dark-mode ._sidebar-footer i {
    border: 1px solid #555;
  }

  body.dark-mode ._menu-item,
  body.dark-mode ._section-options li,
  body.dark-mode .controls button,
  body.dark-mode .editor-pane,
  body.dark-mode .style-control-group select,
  body.dark-mode .settings-input {
    background-color: #2a2a2e !important;
    color: #ddd !important;
    border-color: #444 !important;
  }

  body.dark-mode ._menu-item.placeholder {
    background-color: #222327 !important;
  }

  body.dark-mode ._section-options li:hover,
  body.dark-mode .controls button:hover {
    background-color: #333 !important;
  }

  body.dark-mode .settings-tabs {
    border-bottom-color: #3c3c3c;
  }

  body.dark-mode .tab-button {
    color: #7a7a7a;
  }

  body.dark-mode .tab-button:hover:not(.active) {
    color: #e0e0e0;
  }

  body.dark-mode .tab-button.active {
    color: #ffffff;
  }

  body.dark-mode .code-editor {
    background-color: #1f1f1f;
    color: #ddd;
    border-color: #444;
  }

  body.dark-mode .history-item {
    background-color: transparent;
    color: #ccc;
  }

  body.dark-mode .history-item:hover {
    background-color: #333;
    color: #fff;
  }

  body.dark-mode .btn {
    background-color: #2a2a2e;
    color: #ddd;
    border: 1px solid #444;
    padding: 8px 16px;
    border-radius: 6px;
    transition: all 0.2s ease;
  }

  body.dark-mode .btn:hover {
    background-color: #333;
    color: #fff;
    border-color: #555;
    cursor: pointer;
  }

  body.dark-mode ._save-button,
  body.dark-mode ._publish-button,
  body.dark-mode ._cancel-setting-button,
  body.dark-mode ._cancel-section-button,
  body.dark-mode ._save-setting-button {
    background-color: #18191c;
    color: #ddd;
    border: 1px solid #555;
  }

  body.dark-mode ._save-button:hover,
  body.dark-mode ._publish-button:hover,
  body.dark-mode ._cancel-setting-button:hover,
  body.dark-mode ._cancel-section-button:hover,
  body.dark-mode ._save-setting-button:hover {
    background-color: #333;
    color: #fff;
  }

  body.dark-mode .loading-overlay {
    background-color: rgba(30, 30, 30, 0.9);
  }

  body.dark-mode .editor-container-tabs {
    background: #1a1b1e;
  }

  body.dark-mode .editor-container-tabs::after {
    display: none;
  }

  @media (hover: hover) and (pointer: fine) {
    body.dark-mode ._sidebar-footer i:hover:not(.active) {
      color: #3fa9f5;
      background: #1a1b1e;
    }
  }

  body.dark-mode ._sidebar-footer i.preview-mode-icon.active {
    color: #3fa9f5;
    background-color: #222327;
    border: 1px solid #555;
  }

  body.dark-mode .confirm-toast {
    background-color: #1e1e1e;
    color: #ddd;
    border: 1px solid #666;
    box-shadow: 0 0 8px rgba(255, 255, 255, 0.1);
  }

  body.dark-mode .close-confirm-toast {
    color: #ccc;
  }

  body.dark-mode .close-confirm-toast:hover {
    color: #fff;
  }

  body.dark-mode .confirm-toast-button.add {
    background-color: #1e1e1e;
    color: #28a745;
    border: 1px solid #28a745;
  }

  body.dark-mode .confirm-toast-button.add:hover {
    background-color: #28a745;
    color: #fff;
  }

  body.dark-mode .confirm-toast-button.duplicate {
    background-color: #1e1e1e;
    color: #007bff;
    border: 1px solid #007bff;
  }

  body.dark-mode .confirm-toast-button.duplicate:hover {
    background-color: #007bff;
    color: #fff;
  }

  body.dark-mode .confirm-toast-button.delete {
    background-color: #1e1e1e;
    color: #dc3545;
    border: 1px solid #dc3545;
  }

  body.dark-mode .confirm-toast-button.delete:hover {
    background-color: #dc3545;
    color: #fff;
  }

  body.dark-mode ._menu-item .settings {
    color: #ccc;
  }

  body.dark-mode .editor-tab.active {
    /* Disamakan dengan pola tombol Warna Teks/Warna Latar Belakang Teks
       (.toolbar-color-btn) di dark mode: background tombol itu TETAP
       putih di dark mode (tidak di-override, lihat rule dasar
       .toolbar-color-btn), sementara icon-nya jadi HITAM (lihat
       "body.dark-mode .toolbar-color-btn i" di bawah) supaya tetap
       kontras di atas putih. Pill tab aktif sekarang ikut background
       putih yang sama, jadi teksnya ikut disamakan jadi gelap --
       sebelumnya putih (#ffffff), yang di rule baru di bawah bikin
       teks tak terlihat karena background pill juga jadi putih. */
    color: #1a1b1e;
    /* FIX: rule dasar ".editor-tab.active" (default mode) sekarang pakai
       text-shadow PUTIH (cocok untuk teks putih di atas pill cyan).
       Di dark mode teksnya gelap lagi (#1a1b1e di atas pill putih), jadi
       trik fake-bold-nya perlu shadow GELAP juga -- kalau tidak
       di-override di sini, shadow putih warisan dari rule dasar akan
       tampak pudar/salah warna di atas teks gelap. Nilai ini sama
       persis dengan text-shadow default mode SEBELUM perubahan warna
       tab aktif di atas. */
    text-shadow: 0 0 0.4px rgba(26, 27, 30, 0.6), 0 0 0.4px rgba(26, 27, 30, 0.6);
  }

  body.dark-mode .editor-tab {
    color: #7a7a7a;
  }

  body.dark-mode .editor-tab:hover:not(.active) {
    color: #e0e0e0;
  }

  body.dark-mode .editor-tabs-indicator {
    /* Disamakan dengan warna background tombol Warna Teks/Warna Latar
       Belakang Teks (.toolbar-color-btn) di dark mode -- tombol itu
       TIDAK punya override background khusus dark mode, jadi tetap
       putih (warna dasarnya di rule ".toolbar-color-btn"). Sebelumnya
       pill ini #414045 (abu-abu gelap). */
    background: #ffffff;
  }

  body.dark-mode .visual-editor-container {
    background-color: #222327;
    border-color: #333;
  }

  body.dark-mode .visual-editor {
    background-color: #222327;
  }

  body.dark-mode .editor-toolbar {
    background-color: #414045;
    border-color: #444;
  }

  body.dark-mode .style-controls {
    background-color: #202024;
    border-color: #444;
  }

  body.dark-mode .style-controls label {
    color: #fff;
  }

  body.dark-mode .editor-toolbar button {
    background-color: #222327;
    color: #ddd;
    border-color: #444;
  }

  body.dark-mode .editor-toolbar button:hover {
    background-color: rgba(255, 255, 255, 0.08);
  }

  body.dark-mode .editor-toolbar button.active {
    background-color: #ffffff !important;
    color: #222327 !important;
    border-color: #ccc !important;
  }

  /* Icon "A" (Warna Teks) & icon paint/fill-drip (Warna Latar Belakang
     Teks) di dark mode dibuat hitam supaya tetap terlihat jelas. */
  body.dark-mode .toolbar-color-btn {
    border-color: #444;
  }

  body.dark-mode .toolbar-color-btn i {
    color: #000000;
  }

  /* Hover icon A & Paint di dark mode: background dasarnya tetap putih,
     jadi cukup digelapkan sedikit saja saat hover (bukan mengikuti pola
     hover tombol toolbar lain yang menerangkan dari latar gelap). */
  body.dark-mode .toolbar-color-btn:hover {
    background: #f2f2f2;
  }

  /* Background & warna teks area editor di dark mode -- mengikuti
     referensi (di sana diatur lewat "body.dark-mode .editor-content"
     karena backdrop belum ada; di sini disesuaikan ke
     ".editor-content-backdrop" karena itu yang sekarang jadi elemen
     "kanvas" & scroll container-nya). ".editor-content" di dalamnya
     tetap transparent (lihat rule dasarnya) supaya warna ini
     kelihatan tembus, dan warna teks default (#ddd) ikut mewarisi ke
     isi konten seperti di referensi. */
  body.dark-mode .editor-content-backdrop {
    background-color: #222327;
    color: #ddd;
  }

  body.dark-mode #html-content .xa-editor.xa-blank > *:first-child::before {
    color: #777;
  }

  body.dark-mode .custom-select {
    background-color: #222327;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .custom-select:hover {
    background-color: #333;
  }

  body.dark-mode .custom-select .select-options {
    background-color: #222327;
    border-color: #444;
  }

  body.dark-mode .custom-select .select-options li:hover {
    background-color: #444;
  }

  body.dark-mode .toolbar-emoticon .emoji-search {
    background-color: #222327;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .toolbar-emoticon .emoji-item:hover {
    background-color: #444;
  }

  body.dark-mode ._main-list::-webkit-scrollbar {
    width: 12px;
  }

  body.dark-mode ._main-list::-webkit-scrollbar-track {
    background: #1e1e1e;
    border-radius: 6px;
  }

  body.dark-mode ._main-list::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #1e1e1e;
  }

  body.dark-mode ._main-list::-webkit-scrollbar-thumb:hover {
    background-color: #666;
  }

  body.dark-mode ._section-sidebar::-webkit-scrollbar {
    width: 12px;
  }

  body.dark-mode ._section-sidebar::-webkit-scrollbar-track {
    background: #1e1e1e;
    border-radius: 6px;
  }

  body.dark-mode ._section-sidebar::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #1e1e1e;
  }

  body.dark-mode ._section-sidebar::-webkit-scrollbar-thumb:hover {
    background-color: #666;
  }

  body.dark-mode .settings-overlay::-webkit-scrollbar {
    width: 12px;
  }

  body.dark-mode .settings-overlay::-webkit-scrollbar-track {
    background: #1e1e1e;
    border-radius: 6px;
  }

  body.dark-mode .settings-overlay::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #1e1e1e;
  }

  body.dark-mode .settings-overlay::-webkit-scrollbar-thumb:hover {
    background-color: #666;
  }

  body.dark-mode .history-body::-webkit-scrollbar {
    width: 12px;
  }

  body.dark-mode .history-body::-webkit-scrollbar-track {
    background: #1e1e1e;
    border-radius: 6px;
  }

  body.dark-mode .history-body::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #1e1e1e;
  }

  body.dark-mode .history-body::-webkit-scrollbar-thumb:hover {
    background-color: #666;
  }

  /* Scrollbar khusus editor konten di dark mode: track TETAP transparent
     (sama seperti mode default -- lihat rule dasarnya), cuma warna thumb
     yang diganti lebih terang supaya tetap kontras & terlihat jelas di
     atas background gelap (#222327). */
  body.dark-mode .editor-content-backdrop {
    scrollbar-color: #666 transparent; /* Firefox */
  }

  body.dark-mode .editor-content-backdrop::-webkit-scrollbar-track {
    background: transparent;
  }

  body.dark-mode .editor-content-backdrop::-webkit-scrollbar-thumb {
    background-color: #666;
    border-radius: 4px;
  }

  body.dark-mode .editor-content-backdrop::-webkit-scrollbar-thumb:hover {
    background-color: #888;
  }

  body.dark-mode ._main-list,
  body.dark-mode ._section-sidebar,
  body.dark-mode .settings-overlay,
  body.dark-mode .history-body {
    scrollbar-width: auto;
    scrollbar-color: #555 #1e1e1e;
  }

  body.dark-mode .language-container {
    background-color: #222327;
    border-color: #444;
    color: #ddd;
  }

  body.dark-mode .language-option:hover {
    background-color: #333;
  }

  body.dark-mode .language-option.active {
    background-color: #1e1f22;
    color: #3fa9f5;
  }

  body.dark-mode .language-toggle:hover,
  body.dark-mode .language-toggle:hover i,
  body.dark-mode .language-toggle:hover span {
    color: #3fa9f5;
  }

  body.dark-mode .language-toggle.active,
  body.dark-mode .language-toggle.active i,
  body.dark-mode .language-toggle.active span {
    color: #3fa9f5;
  }

  body.dark-mode .language-toggle.active {
    background-color: rgba(63, 169, 245, 0.1);
  }

  .preview-frame {
    margin: 30px auto 0;
    position: relative;
    width: 100%;
    padding: 0;
    background: #ffffff;
    border: none;
    border-radius: 0;
    box-shadow: none;
    box-sizing: border-box;
    flex: 1;
    order: 1;
    overflow: hidden;
    /*transition: all 0.3s ease;*/
    z-index: 1;
    height: calc(100vh - 30px);
  }

  .preview-frame.desktop-mode {
    max-width: 100%;
  }

  .preview-frame.tablet-mode {
    max-width: 768px;
  }

  .preview-frame.mobile-mode {
    max-width: 360px;
  }

  /* ✅ Simulasi mode Tablet/HP di layar PC SUNGGUHAN (>768px), khusus
     saat ruang yang tersedia DI SAMPING sidebar (window.innerWidth -
     lebar sidebar saat ini, lihat getPcAvailableWidth() di JS) lebih
     sempit dari ukuran asli mode yang dipilih (768px Tablet / 360px
     HP) -- mis. jendela PC sempit, atau sidebar sedang terbuka
     sehingga sisa ruangnya < 768/360px. Class ini ditambahkan/dilepas
     oleh applyMobileDeviceSimulation().
     Beda dengan simulasi mode HP/Tablet sungguhan (yang men-transform
     .preview-frame itu sendiri via position:fixed di luar flow), di
     sini .preview-frame TETAP flex item biasa (max-width:100% supaya
     ikut menyesuaikan lebar flex yang benar2 tersedia & tidak
     bentrok/dorong sidebar) -- yang di-scale hanya .cv-preview di
     dalamnya, supaya @container query tetap membaca breakpoint
     768/360px (ukuran layar yang "sesungguhnya" utk mode itu) namun
     visualnya tetap muat pas di ruang yang tersedia. */
  .preview-frame.pc-sim-scaled {
    max-width: 100% !important;
    margin: 30px 0 0 0 !important;
  }

  .preview-frame.pc-sim-scaled > .cv-preview {
    width: var(--sim-width) !important;
    min-width: 0 !important;
    max-width: none !important;
    height: var(--sim-height) !important;
    transform-origin: top left !important;
    transform: var(--sim-transform, none) !important;
  }

  body.sidebar-collapsed ._main-list,
  body.sidebar-collapsed ._sidebar-header,
  body.sidebar-collapsed ._sidebar-footer,
  body.sidebar-collapsed ._section-sidebar,
  body.sidebar-collapsed .settings-overlay {
    display: none !important;
  }

  body.sidebar-collapsed .preview-frame {
    width: 100% !important;
    max-width: 100% !important;
    margin: 30px 0 0 0 !important;
  }

  /* 🐞 FIX: rule di atas (specificity 0,2,1) selalu menang lawan
     .preview-frame.tablet-mode / .mobile-mode (specificity 0,2,0),
     jadi max-width mode device ke-override jadi 100% begitu sidebar
     di-hide. Tambahkan versi ber-prefix "body.sidebar-collapsed" di
     sini juga supaya specificity-nya naik jadi 0,3,0 -- menang lawan
     rule di atas -- sehingga max-width tablet/mobile tetap konsisten
     dengan icon mode yang aktif, baik sidebar show maupun hide. */
  body.sidebar-collapsed .preview-frame.tablet-mode {
    max-width: 768px !important;
    margin: 30px auto 0 !important;
  }

  body.sidebar-collapsed .preview-frame.mobile-mode {
    max-width: 360px !important;
    margin: 30px auto 0 !important;
  }

  /* ✅ Floating-toggle sekarang boleh muncul di SEMUA ukuran layar
     (desktop/PC, tablet, maupun HP) -- bukan cuma mode HP seperti
     sebelumnya. Aturan dasarnya: sembunyi secara default, lalu
     ditampilkan bergantian dengan tombol toggle-sidebar-icon di
     footer sidebar -- floating-toggle tampil ketika sidebar
     TERTUTUP (body.sidebar-collapsed), dan tersembunyi lagi ketika
     sidebar dibuka kembali (baik lewat klik floating-toggle itu
     sendiri maupun lewat toggle-sidebar-icon di footer). Rule
     "body.sidebar-collapsed .floating-toggle" di bawah ini SENGAJA
     diletakkan di luar @media supaya berlaku di semua breakpoint;
     rule duplikatnya yang ada di dalam @media (max-width:768px) dan
     @media (max-width:480px) tetap dibiarkan (redundant, tidak
     masalah) sebagai jaga-jaga. */
  .floating-toggle {
    display: none !important;
  }

  body.sidebar-collapsed .floating-toggle {
    display: flex !important;
  }

  body.dark-mode .floating-toggle {
    background-color: #495057;
  }

  /* Modal Section */
  .modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 10000;
  }

  .modal-content {
    background: white;
    border-radius: 8px;
    width: 90%;
    max-width: 450px;
    max-height: 70vh;
    display: flex;
    flex-direction: column;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  }

  body.dark-mode .modal-content {
    background: #222327;
    color: #ddd;
    border: 1px solid #444;
  }

  .modal-header {
    padding: 20px;
    border-bottom: 1px solid #ddd;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  body.dark-mode .modal-header {
    border-bottom-color: #444;
  }

  .modal-header h4 {
    margin: 0;
    color: #333;
  }

  body.dark-mode .modal-header h4 {
    color: #ddd;
  }

  .close-modal {
    font-size: 24px;
    cursor: pointer;
    color: #999;
    transition: color 0.2s;
  }

  .close-modal:hover {
    color: #666;
  }

  body.dark-mode .close-modal {
    color: #ccc;
  }

  body.dark-mode .close-modal:hover {
    color: #fff;
  }

  .modal-body {
    padding: 0;
    flex: 1;
    overflow-y: auto;
  }

  .section-options-list {
    list-style: none;
    padding: 0;
    margin: 0;
  }

  .section-options-list li {
    display: flex;
    align-items: center;
    padding: 12px 20px;
    cursor: pointer;
    transition: background-color 0.2s;
    border-bottom: 1px solid #f0f0f0;
  }

  body.dark-mode .section-options-list li {
    border-bottom-color: #333;
    color: #ddd;
  }

  .section-options-list li:hover {
    background-color: #f5f5f5;
  }

  body.dark-mode .section-options-list li:hover {
    background-color: #333;
  }

  .section-options-list li i {
    margin-right: 12px;
    font-size: 16px;
    width: 20px;
    text-align: center;
  }

  .section-options-list li span {
    flex: 1;
  }

  .modal-footer {
    padding: 15px 20px;
    border-top: 1px solid #ddd;
    display: flex;
    justify-content: flex-end;
  }

  body.dark-mode .modal-footer {
    border-top-color: #444;
  }

  .modal-cancel-btn {
    padding: 8px 16px;
    background: #6c757d;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.2s;
  }

  .modal-cancel-btn:hover {
    background: #5a6268;
  }

  body.dark-mode .modal-cancel-btn {
    background: #495057;
  }

  body.dark-mode .modal-cancel-btn:hover {
    background: #343a40;
  }

  /* Tombol aksi merusak (mis. "Buang Perubahan & Keluar") -- warna sama
     dengan .confirm-toast-button.delete supaya konsisten di seluruh app
     untuk aksi yang tidak bisa dibatalkan. */
  .modal-btn-danger {
    padding: 8px 16px;
    background: #dc3545;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-weight: bold;
    margin-left: 8px;
    transition: background-color 0.2s;
  }

  .modal-btn-danger:hover {
    background: #c82333;
  }

  body.dark-mode .modal-btn-danger {
    background: #b02a37;
  }

  body.dark-mode .modal-btn-danger:hover {
    background: #8f232d;
  }

  /* Modal Konfirmasi Keluar Halaman (Unsaved Changes) -- markup TIDAK
     pakai .modal-header standar (lihat index.php): icon + judul + pesan
     semuanya di dalam .modal-body, disusun flex-column & center supaya
     semuanya rata tengah (bukan rata kiri seperti modal lain). */
  .unsaved-changes-modal-content {
    max-width: 420px;
    /* Override max-height:70vh bawaan .modal-content generik (didesain
       utk modal lain yang kontennya panjang & perlu scroll internal,
       mis. code editor). Modal ini isinya pendek & tetap (icon+judul+
       pesan+tombol) -- dalam kondisi normal TIDAK PERNAH sampai
       setinggi 70vh, jadi 70vh disini cuma bikin body ke-squeeze &
       discroll/terpotong secara tidak perlu, terutama di HP saat
       footer melebar (tombol ditumpuk vertikal). Dinaikkan ke 95vh
       (bukan dihapus total) semata sebagai jaring pengaman kalau ada
       viewport ekstrem pendek (mis. HP landscape) -- overflow-y:auto
       di sini (bukan di .modal-body) supaya kalau SAMPAI kepepet,
       seluruh modal (termasuk footer/tombol) yang discroll bareng,
       bukan cuma teksnya doang yang terpotong sementara tombol tetap
       terlihat utuh. */
    max-height: 95vh;
    overflow-y: auto;
  }

  .unsaved-changes-modal-content .modal-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 28px 24px 20px;
    /* Override flex:1 + overflow-y:auto bawaan .modal-body generik --
       di sini body TIDAK perlu ikut "mengisi sisa ruang lalu discroll"
       sendiri (scroll sudah dipindah ke level .unsaved-changes-modal-content
       di atas sbg jaring pengaman terakhir), cukup setinggi kontennya
       sendiri (flex:0 0 auto) tanpa scrollbar internal di sini. */
    flex: 0 0 auto;
    overflow: visible;
  }

  .unsaved-changes-icon {
    font-size: 48px;
    color: #f0ad4e;
    margin-bottom: 14px;
  }

  body.dark-mode .unsaved-changes-icon {
    color: #ffc861;
  }

  .unsaved-changes-title {
    margin: 0 0 10px;
    color: #333;
    font-size: 18px;
  }

  body.dark-mode .unsaved-changes-title {
    color: #ddd;
  }

  .unsaved-changes-message {
    padding: 0;
    margin: 0;
    color: #333;
    line-height: 1.5;
  }

  body.dark-mode .unsaved-changes-message {
    color: #ddd;
  }

  /* Footer modal ini ikut di-center supaya sejalan dgn konten di atasnya
     (footer generik lain pakai justify-content:flex-end, tapi khusus
     modal ini kita override). */
  .unsaved-changes-modal-content .modal-footer {
    justify-content: center;
  }

  /* Samakan lebar kedua tombol (Stay & Leave/Continue) di modal ini --
     dibuat scoped ke .unsaved-changes-modal-content saja supaya TIDAK
     memengaruhi .modal-cancel-btn / .modal-btn-danger generik yang
     dipakai modal lain (mis. confirm-toast). Pakai flex:1 (bukan width
     tetap) supaya tetap proporsional di 3 bahasa (ID/SU/EN) yang
     panjang teksnya beda-beda -- lebar dua tombol selalu sama persis
     karena sama-sama mengambil separuh ruang footer, apa pun isinya. */
  .unsaved-changes-modal-content .modal-footer .modal-cancel-btn,
  .unsaved-changes-modal-content .modal-footer .modal-btn-danger {
    flex: 1;
    text-align: center;
  }

  /* Mode HP (<=480px): teks tombol yang lebih panjang (mis. "Tetap di
     Halaman" di ID) bisa wrap jadi 2 baris di layar sempit, sementara
     tombol satunya ("Lanjutkan"/"Continue") tetap 1 baris -- hasilnya
     tinggi kedua tombol jadi tidak sama (flex stretch tidak selalu bisa
     diandalkan pada elemen <button> di semua browser, mis. Safari/iOS).
     Solusinya: di layar sempit, susun tombol VERTIKAL & full-width
     alih-alih sejajar horizontal, supaya masalah "tinggi harus sama"
     tidak relevan lagi -- tiap tombol boleh setinggi apa pun sesuai
     kontennya sendiri tanpa terlihat janggal berdampingan. */
  @media (max-width: 480px) {
    .unsaved-changes-modal-content .modal-footer {
      flex-direction: column;
      gap: 8px;
    }

    .unsaved-changes-modal-content .modal-footer .modal-cancel-btn,
    .unsaved-changes-modal-content .modal-footer .modal-btn-danger {
      width: 100%;
      margin-left: 0; /* reset margin-left:8px bawaan .modal-btn-danger,
                          tidak relevan lagi saat tombol ditumpuk vertikal */
    }
  }

  /* Style untuk Made With ExaBox di modal footer */
  .made-with-exabox {
    font-size: 12px;
    color: #666;
    display: flex;
    align-items: center;
    gap: 5px;
    margin-left: auto; /* Dorong ke kanan */
    user-select: none;
  }

  .made-with-exabox i {
    color: #007bff;
    font-size: 14px;
  }

  .made-with-exabox:hover {
    color: #007bff;
  }

  .made-with-exabox:hover i {
    transform: scale(1.1);
    transition: transform 0.2s ease;
  }

  .modal-footer {
    padding: 15px 20px;
    border-top: 1px solid #ddd;
    display: flex;
    justify-content: flex-end;
    align-items: center;
  }

  body.dark-mode .made-with-exabox {
    color: #ccc;
  }

  body.dark-mode .made-with-exabox:hover {
    color: #3fa9f5;
  }

  body.dark-mode .modal-footer {
    border-top-color: #444;
  }

  body.dark-mode .modal-body::-webkit-scrollbar {
    width: 12px;
  }

  body.dark-mode .modal-body::-webkit-scrollbar-track {
    background: #1e1e1e;
    border-radius: 6px;
  }

  body.dark-mode .modal-body::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #1e1e1e;
  }

  body.dark-mode .modal-body::-webkit-scrollbar-thumb:hover {
    background-color: #666;
  }

  body.dark-mode .modal-body {
    scrollbar-width: auto;
    scrollbar-color: #555 #1e1e1e;
  }

  ._menu-item.color-effect .hamburger {
    color: #007bff !important;
  }

  body.dark-mode ._menu-item.color-effect .hamburger {
    color: #3fa9f5 !important;
  }

  @media (max-width: 768px) {
    body {
      flex-direction: column;
      overflow-x: hidden;
      height: 100vh;
    }

    .header-center-toggle {
      left: auto;
      right: 46px;
      transform: translateY(-50%);
    }

    ._main-list,
    ._sidebar-header,
    ._sidebar-footer,
    ._section-sidebar,
    .settings-overlay {
      width: 385px !important;
      max-width: 385px !important;
    }

    .preview-frame {
      width: 100% !important;
      max-width: 100% !important;
      margin-left: 0 !important;
      margin-right: 0 !important;
    }

    ._sidebar-header {
      position: fixed;
      top: 30px;
      right: 0;
      height: 70px;
      z-index: 2;
      background: #ffffff !important;
    }

    ._main-list {
      position: fixed;
      top: 100px;
      right: 0;
      height: calc(100% - 130px) !important;
      flex: none;
      background: #f7f7f7 !important;
      margin-top: 0 !important;
      z-index: 2;
    }

    ._sidebar-footer {
      position: fixed !important;
      top: auto !important;
      bottom: 0 !important;
      right: 0 !important;
      height: 30px !important;
      z-index: 2;
      background: #ffffff !important;
    }

    .settings-overlay ._sidebar-footer,
    ._section-sidebar ._sidebar-footer {
      display: none !important;
    }

    ._section-sidebar,
    .settings-overlay {
      top: 100px !important;
      height: calc(100% - 100px) !important;
    }

    .preview-frame {
      position: fixed;
      width: 100% !important;
      margin-top: 30px !important;
      top: 0 !important;
      height: calc(100% - 30px) !important;
      z-index: 1;
    }

    .floating-toggle {
      top: auto !important;
      bottom: 20px !important;
      transform: none !important;
    }

    /* ✅ State ketika sidebar TIDAK collapsed - SIDEBAR VISIBLE
       (tanpa animasi geser, langsung ditampilkan) */
    body:not(.sidebar-collapsed) ._main-list,
    body:not(.sidebar-collapsed) ._sidebar-header,
    body:not(.sidebar-collapsed) ._sidebar-footer {
      display: flex !important;
    }

    /* ✅ Mode Tablet (481px-768px): preview CV SELALU tampil (tidak lagi
       di-hide saat sidebar terbuka). Preview-frame posisinya fixed
       full-width di lapisan belakang (z-index lebih rendah), sedangkan
       sidebar (._main-list/._sidebar-header/._sidebar-footer) fixed di
       atasnya (z-index lebih tinggi, hanya menutupi sisi kanan selebar
       385px). Jadi toggle show/hide sidebar TIDAK perlu lagi mengubah
       apa pun pada preview-frame -- preview yang terlihat di belakang
       sidebar itu memang sengaja selalu ditampilkan. */
    .preview-frame {
      display: block !important;
    }

    /* ✅ State ketika sidebar collapsed - SIDEBAR DISEMBUNYIKAN
       (langsung hide, bukan digeser ke kanan) */
    body.sidebar-collapsed ._main-list,
    body.sidebar-collapsed ._sidebar-header,
    body.sidebar-collapsed ._sidebar-footer {
      display: none !important;
    }

    /* ✅ Mode Tablet (481px - 768px): floating-toggle SEKARANG ikut muncul
       begitu sidebar collapsed (preview CV full layar), sama seperti mode
       HP di bawah (@media max-width:480px). Rule ini diletakkan di block
       max-width:768px (mencakup tablet DAN hp) supaya tombolnya menyambung
       terus dari lebar tablet turun ke lebar hp tanpa jeda -- tidak perlu
       lagi rule terpisah khusus 480px, tapi rule di block 480px tetap
       dibiarkan ada (redundant, tidak masalah) sebagai jaga-jaga. */
    body.sidebar-collapsed .floating-toggle {
      display: flex !important;
    }

    /* DRAGGING STATES */
    body.has-dragging ._main-list,
    body.has-dragging ._sidebar-header,
    body.has-dragging ._sidebar-footer {
      opacity: 0 !important;
      pointer-events: none;
      transition: opacity 0.1s ease;
      background: transparent !important;
      border: none !important;
    }

    body.has-dragging ._menu-item.dragging {
      opacity: 0 !important;
      background: transparent !important;
      border: none !important;
      box-shadow: none !important;
    }

    body.dark-mode.has-dragging ._main-list,
    body.dark-mode.has-dragging ._sidebar-header,
    body.dark-mode.has-dragging ._sidebar-footer {
      opacity: 0 !important;
      background: transparent !important;
      border: none !important;
    }

    body.dark-mode.has-dragging ._menu-item.dragging {
      opacity: 0 !important;
      background: transparent !important;
      border: none !important;
      box-shadow: none !important;
    }

    body.has-dragging .preview-frame {
      display: block !important;
      transform: translateX(0) !important;
      margin-top: 30px !important;
      z-index: 1;
    }

    ._menu-item.dragging {
      transform: scale(1.1) !important;
    }

    ._menu-item.scale-feedback {
      transform: scale(1.03);
      transition: transform 0.3s ease;
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
      z-index: 1;
    }

    body.dark-mode ._menu-item.scale-feedback {
      box-shadow: 0 2px 6px rgba(255, 255, 255, 0.15);
      background-color: #2a2a2e;
      border-color: #444;
    }
  }

  /* ✅ Mode Tablet sungguhan (481px-768px): mode HP & mode PC yang
     dipilih lewat ikon HP/Tablet/PC di footer sekarang ikut
     disimulasikan (sebelumnya cuma mode Tablet & Desktop di layar HP
     sungguhan <=480px yang punya perlakuan ini -- lihat block
     @media(max-width:480px) di bawah).

     Kenapa perlu blok terpisah (bukan digabung ke dalam
     @media(max-width:768px) di atas)? Supaya kondisi "min-width:481px"
     memastikan rule di sini TIDAK PERNAH menyentuh device sungguhan HP
     (<=480px) sama sekali -- menghindari tabrakan/rebutan spesifisitas
     dengan rule simulasi mode Tablet/PC yang sudah ada di block
     @media(max-width:480px), karena keduanya sama-sama menyasar class
     ".desktop-mode" (hanya beda kategori device sungguhan yang
     ditarget). Dengan @media terpisah, browser otomatis hanya
     menjalankan salah satu blok sesuai lebar layar sungguhan yang
     aktif -- tidak perlu lagi "adu" specificity/urutan antar block.

     Nilai --sim-width/--sim-height/--sim-transform diisi oleh JS
     (applyMobileDeviceSimulation()) -- lihat komentar lengkap di sana.

     Preview di mode Tablet sungguhan SELALU tampil penuh layar di
     belakang sidebar (baik saat sidebar show maupun hide -- beda
     dengan mode HP sungguhan yang preview-nya di-hide total saat
     sidebar show), makanya rule di sini sengaja dibuat berlaku utk
     KEDUA state sidebar (body.sidebar-collapsed maupun
     body:not(.sidebar-collapsed)), bukan cuma saat collapsed. */
  @media (min-width: 481px) and (max-width: 768px) {
    body.sidebar-collapsed .preview-frame.mobile-mode,
    body.sidebar-collapsed .preview-frame.desktop-mode,
    body:not(.sidebar-collapsed) .preview-frame.mobile-mode,
    body:not(.sidebar-collapsed) .preview-frame.desktop-mode {
      left: 0 !important;
      right: auto !important;
      margin-left: 0 !important;
      margin-right: 0 !important;
      width: var(--sim-width, 100%) !important;
      max-width: none !important;
      height: var(--sim-height, calc(100% - 30px)) !important;
      transform-origin: top left !important;
      transform: var(--sim-transform, none) !important;
    }

    /* ✅ Kasus mode simulasi yang baseWidth-nya LEBIH SEMPIT dari layar
       Tablet sungguhan (mis. mode HP/360px disimulasikan di layar
       Tablet 481-768px) -- ditandai lewat class ".sim-centered" yang
       ditambahkan JS (applyMobileDeviceSimulation()) ketika scale
       hasil hitungnya >= 1. TIDAK di-scale-up sama sekali (transform
       cuma translateX untuk nge-center, BUKAN scale()), supaya "mode
       HP" tetap kelihatan proporsional/natural seperti device HP asli
       -- bukan diregangkan penuh lebar layar Tablet. Sengaja ditulis
       SETELAH rule di atas (specificity-nya sama, 0,3,1) supaya menang
       lewat urutan cascade saat class ".sim-centered" aktif. */
    body.sidebar-collapsed .preview-frame.sim-centered,
    body:not(.sidebar-collapsed) .preview-frame.sim-centered {
      left: 50% !important;
      right: auto !important;
      margin-left: 0 !important;
      margin-right: 0 !important;
      width: var(--sim-width, 100%) !important;
      max-width: none !important;
      height: calc(100% - 30px) !important;
      transform-origin: top center !important;
      transform: translateX(-50%) !important;
    }
  }

  @media (max-width: 480px) {
    /* ✅ Mode HP (<=480px): sidebar full-width (100%), bukan lagi 385px
       fixed seperti di mode tablet, supaya benar-benar penuh layar. */
    ._main-list,
    ._sidebar-header,
    ._sidebar-footer,
    ._section-sidebar,
    .settings-overlay {
      width: 100% !important;
      max-width: 100% !important;
    }

    /* ✅ Fix: .editor-content-backdrop punya max-width:360px hardcoded
       (lihat rule dasarnya, class .editor-content-backdrop di atas)
       yang TIDAK ikut melebar walau .editor-toolbar & .settings-overlay
       di atasnya sudah full-width 100% di mode HP. Override di sini
       supaya kotak konten editor (area contenteditable) selalu
       mengikuti 100% lebar toolbar, bukan mentok di 360px lalu
       "mengambang" di tengah lewat margin:0 auto bawaan rule dasarnya. */
    .editor-content-backdrop {
      max-width: 100% !important;
    }

    ._main-list {
      margin-top: 0 !important;
      top: 100px !important;
      height: calc(100% - 160px) !important;
    }

    /* ✅ Mode HP: logo Exa Box digeser ke atas & tombol Simpan/Publish
       digeser ke bawah SECARA VERTIKAL di dalam header yang sama
       (tinggi header TETAP 70px, tidak berubah). Posisi tombol tetap
       position:absolute dengan jarak dari kanan yang sama persis
       seperti semula (right:120px untuk Simpan, right:20px untuk
       Publish) -- hanya ditambahkan "bottom" supaya tombol turun
       menempel ke bawah header, sementara logo diberi align-self ke
       atas supaya naik menempel ke atas header. Dengan begini logo &
       tombol tidak lagi sejajar di baris yang sama (tidak bertabrakan
       secara horizontal) tanpa mengubah tinggi header maupun posisi
       horizontal tombol. Logo digeser naik 5px lagi (margin-top 4px →
       -1px) dan tombol digeser turun 5px lagi (bottom 15px → 10px)
       supaya jaraknya makin lega dan tidak tabrakan di layar sempit. */
    ._sidebar-header {
      align-items: flex-start !important;
      padding-top: 6px !important;
      padding-bottom: 6px !important;
    }

    ._sidebar-header ._logo {
      margin-top: -1px;
    }

    ._sidebar-header ._save-button,
    ._sidebar-header ._publish-button,
    ._sidebar-header ._cancel-section-button,
    ._sidebar-header ._cancel-setting-button,
    ._sidebar-header ._save-setting-button {
      top: auto !important;
      bottom: 10px !important;
    }

    ._sidebar-footer {
      height: 60px !important;
      top: auto !important;
      bottom: 0 !important;
      display: flex !important;
      align-items: center !important;
      /* ✅ Mode HP: posisi tombol footer disamakan dengan mode tablet/PC
         (space-between -- ikon HP/Tablet/PC di kiri, toggle sidebar di
         kanan), bukan flex-end seperti sebelumnya. */
      justify-content: space-between !important;
      padding: 0 12px !important;
    }

    ._sidebar-footer i {
      font-size: 24px;
    }

    /* ✅ Mode HP: tombol icon mode HP/Tablet/PC (.left) sekarang ikut
       ditampilkan di footer, sama seperti mode tablet/PC (sebelumnya
       disembunyikan lewat display:none). */
    ._sidebar-footer .left {
      display: flex !important;
      align-items: center !important;
    }

    ._sidebar-footer .right {
      flex: 0 0 auto;
    }

    /* ✅ Mode HP (<=480px): floating-toggle muncul saat sidebar collapsed
       (preview CV full layar, sidebar disembunyikan). Rule yang sama juga
       sudah aktif di block @media max-width:768px (mencakup rentang
       tablet 481px-768px) supaya tombolnya konsisten tampil dari lebar
       tablet sampai lebar hp -- rule di sini sengaja tetap dipertahankan
       sebagai duplikat/jaga-jaga. Bergantian dengan tombol
       toggle-sidebar-icon di footer (._sidebar-footer .right) yang
       tampil saat sidebar TIDAK collapsed untuk menampilkan preview CV. */
    body.sidebar-collapsed .floating-toggle {
      display: flex !important;
    }

    .settings-overlay ._sidebar-footer,
    ._section-sidebar ._sidebar-footer {
      display: none !important;
    }

    ._section-sidebar,
    .settings-overlay {
      top: 100px !important;
      height: calc(100% - 100px) !important;
    }

    .preview-frame {
      margin-top: 30px !important;
      top: 0 !important;
      height: calc(100% - 30px) !important;
    }

    body.sidebar-collapsed .preview-frame.mobile-mode {
      width: 100% !important;
      max-width: 100% !important;
      margin-left: 0 !important;
      margin-right: 0 !important;
    }

    /* ✅ Mode HP asli (<=480px): tablet-mode & desktop-mode disimulasikan
       lewat CSS custom properties (--sim-width/--sim-height/--sim-scale)
       yang nilainya di-set oleh JS (applyMobileDeviceSimulation()).
       Sengaja pakai custom property + rule bercompound-selector
       (.preview-frame.tablet-mode / .desktop-mode -- 2 class) di sini,
       BUKAN cuma inline style dari JS, supaya spesifisitasnya otomatis
       lebih tinggi & pasti menang dibanding rule umum ".preview-frame"
       (1 class, width:100%!important) di atas -- tidak perlu berharap
       inline-style menang lewat "adu !important" yang riskan meleset.
       left/right/margin di-pin eksplisit ke 0 supaya box precisely
       nempel dari pojok kiri-atas viewport (titik awal transform-scale),
       tidak mengikuti posisi "static" bawaan flex yang bisa meleset.

       🐞 FIX: sebelumnya selector di sini cuma ".preview-frame.desktop-mode"
       (2 class -> specificity 0,2,0), padahal preview baru KELIHATAN di
       HP justru saat body sudah punya class "sidebar-collapsed", dan rule
       "body.sidebar-collapsed .preview-frame { width:100%!important; ... }"
       (lihat di atas, dekat .floating-toggle) punya specificity 0,2,1
       (elemen body + 2 class) -- LEBIH TINGGI dari rule simulasi ini,
       walau sama-sama !important dan rule simulasi ini ditulis belakangan.
       Akibatnya width/max-width/margin preview selalu KEMBALI ke 100% versi
       "body.sidebar-collapsed", meng-override var(--sim-width) di sini --
       preview jadi lebar asli 768px/1280px dikalikan scale tapi DIHITUNG
       dari width yang sudah salah (100% viewport), jadi keliatan cuma
       nempel setengah kiri layar, sisanya kosong. Fix: tambahkan prefix
       "body.sidebar-collapsed" di sini juga supaya specificity-nya naik
       jadi 0,3,1 -- pasti menang lawan rule 0,2,1 di atas, sekaligus
       tetap benar secara semantik (preview memang cuma tampil saat
       sidebar collapsed di mode HP). */
    body.sidebar-collapsed .preview-frame.desktop-mode,
    body.sidebar-collapsed .preview-frame.tablet-mode,
    body.has-dragging .preview-frame.desktop-mode,
    body.has-dragging .preview-frame.tablet-mode {
      left: 0 !important;
      right: auto !important;
      margin-left: 0 !important;
      margin-right: 0 !important;
      width: var(--sim-width, 100%) !important;
      max-width: none !important;
      height: var(--sim-height, calc(100% - 30px)) !important;
      transform-origin: top left !important;
      transform: var(--sim-transform, none) !important;
    }

    /* ✅ Jaga-jaga (belum pernah kejadian di rentang device HP <=480px
       dengan konstanta lebar simulasi saat ini, karena baseWidth Tablet
       768px & PC 1280px selalu lebih lebar dari layar HP -- scale
       selalu <1). Ditambahkan supaya konsisten dengan block Tablet
       481-768px di atas kalau suatu saat konstanta MOBILE_SIM_* berubah
       dan skenario scale>=1 muncul juga di sini -- lihat penjelasan
       lengkap soal ".sim-centered" di block @media(min-width:481px). */
    body.sidebar-collapsed .preview-frame.sim-centered {
      left: 50% !important;
      right: auto !important;
      margin-left: 0 !important;
      margin-right: 0 !important;
      width: var(--sim-width, 100%) !important;
      max-width: none !important;
      height: calc(100% - 30px) !important;
      transform-origin: top center !important;
      transform: translateX(-50%) !important;
    }

    body:not(.sidebar-collapsed) .preview-frame {
      display: none !important;
      margin-top: 30px !important;
    }

    body:not(.sidebar-collapsed) ._main-list,
    body:not(.sidebar-collapsed) ._sidebar-header,
    body:not(.sidebar-collapsed) ._sidebar-footer {
      display: flex !important;
    }

    body.has-dragging ._main-list,
    body.has-dragging ._sidebar-header,
    body.has-dragging ._sidebar-footer {
      opacity: 0 !important;
      pointer-events: none;
      transition: opacity 0.1s ease;
      background: transparent !important;
      border: none !important;
    }

    body.has-dragging ._menu-item.dragging {
      opacity: 0 !important;
      background: transparent !important;
      border: none !important;
      box-shadow: none !important;
    }

    body.dark-mode.has-dragging ._main-list,
    body.dark-mode.has-dragging ._sidebar-header,
    body.dark-mode.has-dragging ._sidebar-footer {
      opacity: 0 !important;
      background: transparent !important;
      border: none !important;
    }

    body.dark-mode.has-dragging ._menu-item.dragging {
      opacity: 0 !important;
      background: transparent !important;
      border: none !important;
      box-shadow: none !important;
    }

    body.has-dragging .preview-frame {
      display: block !important;
      transform: translateX(0) !important;
      margin-top: 30px !important;
      top: 0 !important;
      height: calc(100% - 30px) !important;
      z-index: 1;
    }

    ._menu-item.dragging {
      transform: scale(1.1) !important;
    }

    ._menu-item.scale-feedback {
      transform: scale(1.05);
      transition: transform 0.3s ease;
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
      z-index: 1;
    }

    body.dark-mode ._menu-item.scale-feedback {
      box-shadow: 0 2px 6px rgba(255, 255, 255, 0.15);
      background-color: #2a2a2e;
      border-color: #444;
    }

    /* ✅ Mode HP: .history-container & .language-container semula punya
       "width" TETAP dalam px (215px & 140px) sementara posisinya "left"
       juga tetap dalam px (140px & 180px). Di layar HP yang sempit,
       left + width bisa melebihi lebar viewport sehingga box-nya
       terpotong/tertutup di sisi kanan. Di sini "width" diubah jadi
       "max-width" yang dihitung dari sisa ruang viewport (100vw
       dikurangi posisi left-nya dan sedikit margin aman di kanan),
       supaya lebarnya otomatis mengecil & fleksibel mengikuti layar
       (bukan lagi ukuran kaku) dan selalu pas di dalam batas layar. */
    .history-container {
      width: auto !important;
      max-width: calc(100vw - 150px) !important;
    }

    .language-container {
      width: auto !important;
      max-width: calc(100vw - 190px) !important;
    }

    /* ========================================================
       ✅ EDITOR TOOLBAR RESPONSIVE (Mode HP, <=480px)
       Semua elemen toolbar (dropdown Heading, tombol align/bold/
       italic/underline/dst, warna teks & bg, list, dropdown Font,
       Size, Spacing, insert image/link/emoji, superscript, dst)
       semula punya "width" TETAP dalam px (lihat rule dasarnya:
       .editor-toolbar button, .toolbar-select, .toolbar-font-family,
       .toolbar-font-size, .toolbar-line-height, .toolbar-emoticon,
       .toolbar-color-btn, .button-editor-one .one-left .custom-select)
       yang dirancang pas untuk kolom 146px (asumsi layar cukup
       lebar). Di sini "width" diubah jadi "max-width" (batas atas
       saja) + "flex:1 1 0" + "min-width" kecil, supaya tiap tombol/
       dropdown BISA mengecil fleksibel mengikuti sisa ruang yang
       benar-benar tersedia di layar HP manapun (termasuk yang
       sangat sempit), bukan lagi ukuran kaku. Container baris
       (.one-left/.one-right/.two-left/dst) TETAP flex row tanpa
       wrap, jadi tiap baris tombol tetap sejajar & tidak pecah ke
       baris baru -- hanya ukurannya yang menyusut bersama-sama. */
    .editor-toolbar {
      padding: 6px 10px;
    }

    /* ⚠️ PENTING: gap antar tombol dalam satu baris SENGAJA TIDAK
       diubah (tetap default 6px, lihat .button-editor-X .X-left/
       .X-right di rule dasar) supaya total lebar maksimum tiap baris
       tetap konsisten 146px (4*32px tombol + 3*6px gap = 146px, sama
       dengan lebar dropdown Paragraph/Font yang 146px, dan Size+
       Spacing = 70+70+6 = 146px). Kalau gap-nya beda antar baris,
       baris yang isinya banyak tombol jadi punya total lebar lebih
       kecil daripada baris yang isinya cuma 1 dropdown -- padahal
       keduanya rata kanan/kiri ke garis tengah yang sama -- sehingga
       terlihat "tidak sejajar" persis seperti yang dilaporkan. */

    /* Tombol ikon biasa: Align, Bold/Italic/Underline/Strike, Bullet/
       Numbered List, Insert Image, Reset Format, Superscript/
       Subscript, Blockquote, Insert Video, dll. */
    .editor-toolbar button {
      width: auto;
      max-width: 32px;
      flex: 1 1 0;
      min-width: 18px;
      /* ✅ Fix: height TIDAK ikut diubah (tetap 28px, sama seperti rule
         dasar/desktop) supaya sejajar dengan elemen toolbar lain yang
         juga tetap 28px di mode HP (.custom-select/dropdown Paragraph-
         Font-Size-Spacing, .toolbar-color-btn, .toolbar-link-wrap,
         .toolbar-emoticon). Sebelumnya di sini height diubah ke 26px
         sendirian -- selisih 2px itu yang bikin satu baris toolbar
         terlihat "tidak sama rata tingginya". Hanya lebar/padding/font
         yang boleh menyusut di mode HP, tinggi semua elemen toolbar
         tetap seragam. */
      padding: 3px;
    }

    /* ===== FIX: Tombol Bullet List & Numbered List menyusut tidak
       sinkron dengan tombol Warna Teks (A) / Warna Latar (paint) di
       mode HP =====
       Root cause (dibuktikan lewat pengukuran langsung, bukan tebakan):
       saat sebuah <button> punya box-sizing:border-box + flex-basis:0
       (dari flex:1 1 0 di atas) DAN punya padding/border sungguhan,
       browser memperlakukan padding+border itu sebagai tambahan ukuran
       tetap di luar proporsi flex-grow -- sehingga tombol ini selalu
       tampil ~8px (padding 3px*2 + border 1px*2) lebih lebar daripada
       elemen flex tetangga (mis. .toolbar-color-btn-wrap) yang sama-
       sama flex:1 1 0 tapi tidak punya padding/border sungguhan. Sudah
       diuji: begitu padding & border tombol ini dihilangkan, lebarnya
       sinkron 100% dengan tombol lain di semua lebar layar.
       Fix: padding & border SUNGGUHAN dihapus (tidak lagi menyumbang ke
       box model), lalu diganti tampilan visualnya dengan box-shadow
       inset (border palsu) + flexbox centering untuk ikon -- box-shadow
       murni visual, TIDAK dihitung dalam box model/flex-basis, sehingga
       tombol ini akhirnya menyusut identik dengan tombol toolbar lain
       tanpa perlu div pembungkus apa pun.
       SENGAJA di-scope di sini (dalam @media max-width:480px), BUKAN di
       luar media query lagi -- karena flex:1 1 0 (akar masalahnya) juga
       cuma aktif di sini. Di luar breakpoint ini tombol memakai
       width:32px tetap, jadi cukup border asli seperti tombol lain
       (lihat rule dasar ".editor-toolbar button"), tanpa perlu box-
       shadow palsu. */
    /* ✅ FIX (border tombol Bullet/Numbered List terlihat lebih tebal
       dibanding tombol toolbar lain di mode HP): sebelumnya "border
       palsu" di sini dibuat pakai box-shadow (inset 0 0 0 1px), yang
       secara render browser memang tampak sedikit lebih tebal/berat
       dibanding border asli (beda pipeline rendering: box-shadow ikut
       anti-aliasing bayangan, border tidak). Diganti ke outline dengan
       outline-offset negatif: outline TIDAK dihitung dalam box model/
       flex-basis sama sekali (persis seperti tujuan box-shadow di sini),
       tapi rendering-nya memakai jalur yang sama seperti border asli,
       jadi hasilnya konsisten 1px dengan tombol lain, tidak lagi
       tampak lebih tebal. Fix sinkronisasi lebar (alasan awal border
       asli dihapus) tetap terjaga karena outline tetap di luar box
       model. */
    .editor-toolbar button[data-command="insertUnorderedList"],
    .editor-toolbar button[data-command="insertOrderedList"] {
      padding: 0;
      border: none;
      outline: 1px solid #ccc;
      outline-offset: -1px;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .editor-toolbar button[data-command="insertUnorderedList"]:hover,
    .editor-toolbar button[data-command="insertOrderedList"]:hover {
      outline-color: rgba(3, 190, 252, 0.25);
    }

    .editor-toolbar button[data-command="insertUnorderedList"].active,
    .editor-toolbar button[data-command="insertOrderedList"].active {
      outline-color: #03befc;
    }

    body.dark-mode .editor-toolbar button[data-command="insertUnorderedList"],
    body.dark-mode .editor-toolbar button[data-command="insertOrderedList"] {
      outline-color: #444;
    }

    body.dark-mode .editor-toolbar button[data-command="insertUnorderedList"].active,
    body.dark-mode .editor-toolbar button[data-command="insertOrderedList"].active {
      outline-color: #ccc;
    }

    /* ✅ Ukuran ikon disamakan untuk SEMUA tombol kecil toolbar (bukan
       cuma <button> biasa) supaya saat layar mode HP mengecil, semua
       ikon menyusut kompak ke ukuran yang sama persis. Sebelumnya
       ".toolbar-color-btn i" (Warna Teks & Warna Latar, elemennya
       <label> bukan <button> sehingga TIDAK tertangkap selector
       ".editor-toolbar button i") tetap 12px mengikuti rule dasar
       desktop (lihat ".toolbar-color-btn i" di atas) sementara ikon
       tombol lain sudah mengecil ke 11px -- akibatnya 2 ikon warna itu
       terlihat sedikit lebih besar sendiri dibanding ikon tombol
       Align/Bold/List/dst di baris yang sama. */
    .editor-toolbar button i,
    .toolbar-color-btn i {
      font-size: 11px;
    }

    /* Tombol Warna Teks & Warna Latar (label, bukan <button>).
       PENTING: yang jadi flex-item langsung di dalam .two-right adalah
       .toolbar-color-btn-wrap (pembungkus popup), BUKAN .toolbar-color-btn
       itu sendiri -- sama seperti pola .toolbar-link-wrap di bawah.
       Kalau aturan menyusut hanya dipasang di .toolbar-color-btn tanpa
       wrapper-nya ikut diberi flex/max-width/min-width, wrapper tidak
       ikut menyusut mengikuti sisa ruang, sehingga 2 tombol ini terlihat
       tidak sejajar dengan tombol ol/ul di sampingnya yang menyusut.
       ✅ FIX (4 tombol di .two-right -- Warna Teks, Warna Latar, OL, UL
       -- tidak ikut mengecil di mode HP, padahal baris .two-left di
       sebelahnya mengecil normal): wrapper ini masih membawa
       "width: 32px" (nilai pasti/definite) dari rule dasar desktop
       (lihat .toolbar-color-btn-wrap di atas, dibuat sengaja fixed di
       sana supaya presisi shrink-to-fit) yang TIDAK pernah dicabut di
       sini -- beda dengan ".editor-toolbar button" yang eksplisit
       di-reset ke "width: auto" di mode HP. Akibatnya wrapper ini
       (dan ruang kolom .two-right yang menampungnya, termasuk OL/UL
       yang berbagi baris dengannya) ikut dianggap butuh ruang minimum
       sebesar width tetap tsb saat browser menghitung penyusutan,
       sehingga gagal menyusut sinkron seperti baris .two-left.
       "width: auto" di sini menghapus nilai pasti itu supaya flex-basis
       (dari "flex: 1 1 0") yang sepenuhnya menentukan ukurannya, persis
       seperti tombol biasa. */
    .toolbar-color-btn-wrap {
      width: auto;
      max-width: 32px;
      flex: 1 1 0;
      min-width: 18px;
    }

    .toolbar-color-btn-wrap .toolbar-color-btn {
      max-width: 100%;
      width: 100%;
      min-width: 0;
      flex: 1 1 0;
    }

    /* Wrapper tombol Insert Link (membungkus 1 tombol 32px) */
    .toolbar-link-wrap {
      max-width: 32px;
      flex: 1 1 0;
      min-width: 18px;
      padding: 3px;
      border: 1px solid #ccc;
      border-radius: 3px;
      background: white;
      box-sizing: border-box;
      align-items: center;
      justify-content: center;
      /* ✅ Fix (menggantikan versi sebelumnya): padding:3px + border:1px
         di wrapper ini TETAP dipertahankan (perlu, supaya box model-nya
         match dengan Reset/Blockquote saat flex-shrink dihitung).
         BEDANYA dari versi lama: border sekarang dibuat NYATA/terlihat
         (#ccc, bukan transparent) dan background putih dipindah ke
         wrapper ini juga.
         Sebelumnya border+background "terlihat" itu ada di TOMBOL DI
         DALAM wrapper (#link-btn), yang ukurannya cuma 100% dari ruang
         SISA wrapper setelah dikurangi padding+border wrapper -- alhasil
         kotak yang benar-benar terlihat mata (putih+bergaris) untuk
         tombol Link jadi lebih KECIL & "mengambang" di tengah slotnya
         (24x20 di dalam slot 32x28), tidak nempel pas ke tepi seperti
         tombol Reset/Blockquote/dll yang mengisi penuh slotnya. Dengan
         memindahkan border+background yang terlihat ke WRAPPER (elemen
         yang ukurannya sudah disamakan lewat flex/max-width/min-width
         di atas), sekarang cuma ada SATU lapis kotak yang terlihat, dan
         itu langsung mengisi penuh slot 32x28 -- persis seperti tombol
         toolbar lain, sinkron saat toolbar disusutkan di mode HP. */
    }

    .toolbar-link-wrap:hover {
      background: rgba(3, 190, 252, 0.25);
      border-color: rgba(3, 190, 252, 0.25);
    }

    body.dark-mode .toolbar-link-wrap {
      background-color: #222327;
      border-color: #444;
    }

    body.dark-mode .toolbar-link-wrap:hover {
      background-color: rgba(255, 255, 255, 0.08);
    }

    .toolbar-link-wrap button {
      max-width: 100%;
      width: 100%;
      height: 100%;
      flex: none;
      padding: 0;
      border: none;
      background: transparent;
      /* ✅ Tombol di dalam wrapper sekarang MURNI elemen fungsional
         (area klik + ikon), tanpa lapisan visual sendiri (background/
         border-nya sengaja transparent/none) -- karena background+border
         yang terlihat sudah dipindah ke .toolbar-link-wrap di atas.
         "flex:none" (0 0 auto) membuat tombol ini murni ikut ukuran
         wrapper-nya lewat width/height:100%, tanpa lapisan grow/shrink
         kedua yang bisa bikin ukurannya menyimpang lagi. */
    }

    body.dark-mode .toolbar-link-wrap button {
      color: #ddd;
    }

    /* ✅ FIX (bug: "2 kotak" bertumpuk saat tombol disentuh di mode HP):
       ".editor-toolbar button:hover" (rule global, spesifisitas class+
       pseudo-class+element) TETAP menempel ke tombol #link-btn di dalam
       wrapper ini walau sudah di-reset "background:transparent" di rule
       ".toolbar-link-wrap button" di atas -- soalnya reset itu HANYA
       untuk state normal, bukan state :hover. Akibatnya saat disentuh,
       tombol DALAM ikut jadi biru (rgba(3,190,252,0.25)) DI ATAS wrapper
       LUAR yang juga sudah biru dari ".toolbar-link-wrap:hover" -- dua
       lapis warna menumpuk, terlihat seperti "2 kotak" biru bersarang.
       Solusi: matikan juga background/border tombol dalam khusus state
       :hover/:active, supaya warna hover HANYA tampil sekali di wrapper. */
    .toolbar-link-wrap button:hover,
    .toolbar-link-wrap button:active {
      background: transparent !important;
      border-color: transparent !important;
    }

    /* Wrapper tombol Insert Video (membungkus 1 tombol 32px) -- SAMA
       PERSIS dengan pola .toolbar-link-wrap di atas. Wrapper ini
       ("toolbar-video-wrap") baru ditambahkan belakangan (fitur popup
       Insert Video) dan SEMPAT TIDAK dimasukkan ke sini, sehingga di
       mode HP tombolnya tidak ikut menyusut/menyamakan tinggi seperti
       tombol toolbar lain (tetap ukuran desktop, terlihat "beda
       sendiri"). Aturan di bawah ini menyamakan perilakunya persis
       dengan .toolbar-link-wrap supaya ikut kompak & sejajar. */
    .toolbar-video-wrap {
      max-width: 32px;
      flex: 1 1 0;
      min-width: 18px;
      padding: 3px;
      border: 1px solid #ccc;
      border-radius: 3px;
      background: white;
      box-sizing: border-box;
      align-items: center;
      justify-content: center;
    }

    .toolbar-video-wrap:hover {
      background: rgba(3, 190, 252, 0.25);
      border-color: rgba(3, 190, 252, 0.25);
    }

    body.dark-mode .toolbar-video-wrap {
      background-color: #222327;
      border-color: #444;
    }

    body.dark-mode .toolbar-video-wrap:hover {
      background-color: rgba(255, 255, 255, 0.08);
    }

    .toolbar-video-wrap button {
      max-width: 100%;
      width: 100%;
      height: 100%;
      flex: none;
      padding: 0;
      border: none;
      background: transparent;
    }

    body.dark-mode .toolbar-video-wrap button {
      color: #ddd;
    }

    /* ✅ FIX (bug: "2 kotak" bertumpuk saat tombol disentuh di mode HP)
       -- persis akar masalah & solusi yang sama dengan
       ".toolbar-link-wrap button:hover" di atas, cuma untuk tombol
       Insert Video. Lihat komentar lengkap di sana. */
    .toolbar-video-wrap button:hover,
    .toolbar-video-wrap button:active {
      background: transparent !important;
      border-color: transparent !important;
    }

    /* Dropdown Heading (Paragraph/H1-H6) di baris pertama */
    .button-editor-one .one-left .custom-select {
      width: auto;
      max-width: 146px;
      flex: 1 1 0;
      min-width: 60px;
    }

    /* ✅ Fix: popup isi dropdown Heading (.toolbar-heading-select
       .select-options -- daftar "Paragraph/Heading 1-6") SEBELUMNYA
       mewarisi "position: absolute; left:0; right:0" dari rule dasar
       .custom-select .select-options (lihat di atas), yang artinya
       lebar popup selalu dipaksa sama persis dengan lebar tombol
       induknya. Di mode HP tombolnya sendiri boleh menyusut sampai
       serendah 60px (lihat rule persis di atas), sehingga popup ikut
       menyusut ke 60px juga saat dibuka -- teks "Heading 1".."Heading 6"
       jadi terlihat kecil/terpotong padahal seharusnya tetap terbaca
       normal.
       Solusi CSS MURNI (tanpa JS): tetap "position: absolute" seperti
       rule dasar (bukan "fixed" -- posisinya tetap relatif ke tombolnya
       sendiri via top:100% bawaan, jadi otomatis selalu menempel pas di
       bawah tombol di breakpoint manapun, termasuk tablet & PC, tanpa
       perlu dihitung ulang lewat JS). Yang diubah hanya "right: 0" jadi
       "right: auto" (tetap "left: 0" seperti rule dasar) + lebar tetap
       146px (ukuran normal dropdown ini, lihat .button-editor-one
       .one-left .custom-select di luar @media) -- dianchor dari KIRI
       (left:0) supaya sisi kiri popup selalu menempel pas di batas kiri
       tombolnya sendiri (sesuai permintaan), berapa pun lebar tombolnya
       saat itu, tanpa perlu JS. max-width dipasang sebagai jaga-jaga di
       HP yang sangat sempit supaya popup tidak sampai keluar tepi layar
       kanan. */
    .toolbar-heading-select .select-options {
      left: 0;
      right: auto;
      width: 146px;
      max-width: calc(100vw - 16px);
      max-height: 260px;
      overflow-y: auto;
    }

    /* Dropdown umum toolbar (Font, Size, Spacing, Emoticon) */
    .toolbar-select {
      width: auto;
      flex: 1 1 0;
      min-width: 34px;
    }

    .toolbar-font-family {
      max-width: 146px;
      min-width: 60px;
    }

    .toolbar-font-size,
    .toolbar-line-height {
      max-width: 70px;
      min-width: 40px;
    }

    .toolbar-emoticon {
      width: auto;
      max-width: 32px;
      flex: 1 1 0;
      min-width: 18px;
      padding: 3px;
      box-sizing: border-box;
      /* ✅ Fix: sama seperti .toolbar-link-wrap di atas -- padding:3px
         disamakan dengan ".editor-toolbar button" supaya perhitungan
         shrink flex-basis:0 tidak lagi bias (lihat komentar lengkap
         di .toolbar-link-wrap). */
    }

    .toolbar-select .select-display {
      padding: 5px 4px;
      font-size: 11px;
    }

    .toolbar-font-size .select-display,
    .toolbar-line-height .select-display {
      padding: 5px 3px;
    }

    /* ✅ Isi dropdown pembungkus Emoji & Link tetap punya width tetap
       (200px / 240px, lihat rule dasarnya) supaya tampilan grid/form di
       dalamnya tidak berantakan -- HANYA ditambahkan max-width di sini
       (bukan mengganti width) supaya begitu layar HP-nya lebih sempit
       dari lebar tetap tersebut, kotak popup ikut menyusut mengikuti
       sisa lebar layar (tidak overflow ke luar viewport), tanpa
       mengubah lebar "normal"-nya di layar yang masih cukup lebar. */
    .toolbar-emoticon .select-options {
      max-width: calc(100vw - 20px);
    }

    .toolbar-font-family .select-options {
      max-width: calc(100vw - 20px);
    }

    .toolbar-link-popup {
      max-width: calc(100vw - 20px);
    }

    /* Popup Insert Video -- sama persis dengan .toolbar-link-popup di
       atas, supaya ikut menyusut mengikuti lebar layar HP dan tidak
       overflow ke luar viewport. */
    .toolbar-video-popup {
      max-width: calc(100vw - 20px);
    }

    /* ✅ DIKEMBALIKAN: notch cekung ke-4 tombol ikut diperkecil di mode
       HP (dari 10x10px ke 7x7px, offset -1px pada rule dasar tetap
       kepakai) supaya proporsinya tetap wajar mengikuti nub TrackPoint
       yang juga diperkecil (16px -> 11px) di bawah. */
    #tp-strikethrough-btn::after,
    #txtc-color-label::after,
    .toolbar-font-family::after,
    .toolbar-font-size::after {
      width: 7px;
      height: 7px;
    }

    .toolbar-trackpoint {
      width: 11px;
      height: 11px;
    }
  }

  /* Style Controls - Layout lebih rapi */
.style-controls {
  display: grid;
  grid-template-columns: 1fr;
  gap: 15px;
  padding: 15px;
  max-width: 100%;
  box-sizing: border-box;
  background: #f6f7f9;
  border-radius: 8px;
  margin-bottom: 15px;
  overflow-y: auto;
  overflow-x: hidden;
}

.style-control-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.style-control-group label {
  font-size: 12px;
  font-weight: 500;
  color: #555;
  margin-bottom: 0;
}

.style-control-group select,
.style-control-group input[type="text"] {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 12px;
  background: white;
  transition: all 0.2s ease;
  box-sizing: border-box;
}

.style-control-group select:focus,
.style-control-group input[type="text"]:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
}

.color-picker {
  display: flex;
  align-items: center;
  gap: 8px;
}

.color-picker input[type="color"] {
  width: 36px;
  height: 36px;
  padding: 2px;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  background: white;
}

.color-picker span {
  font-size: 12px;
  font-family: monospace;
  color: #666;
  background: white;
  padding: 4px 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  flex: 1;
}

/* ===== Baris Background Color & Image Background (bersebelahan) =====
   Trigger (tombol) bg-color & bg-image ditampilkan 50/50 bersebelahan
   kiri-kanan. Popup/dropdown isinya sengaja DIPINDAH ke luar (jadi
   sibling dari row, bukan anak .ccp/.bgimg lagi) supaya saat dibuka bisa
   tampil FULL WIDTH tanpa terkunci ke lebar kolom 50%-nya.
   PENTING: popup tetap position:static (in-flow), BUKAN position:absolute,
   supaya tingginya otomatis mendorong layout (mengikuti tinggi konten)
   dan tidak terpotong/scroll aneh oleh overflow-y:auto milik
   .style-controls / .editor-pane (ini penyebab bug "scroll style control"
   sebelumnya saat popup dicoba dibuat absolute). */
.bg-style-row {
  display: flex;
  gap: 10px;
  max-width: 100%;
}

.bg-style-row .style-control-group {
  flex: 1 1 0;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
}

/* ===== Custom Color Picker (ccp) ===== */
.ccp {
  position: relative;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

.ccp-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: 100%;
  padding: 6px 8px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  box-sizing: border-box;
  font-size: 12px;
  color: #666;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

.ccp-trigger:hover {
  background-color: rgba(3, 190, 252, 0.25);
  border-color: #ccc;
}

/* Penanda halus tombol mana yang sedang aktif antara "Warna Latar" dan
   "Gambar Latar" -- keduanya berperilaku seperti 2 tab yang saling
   eksklusif (klik salah satu trigger otomatis menutup yang lain, lihat
   listener klik di initCcpColorPicker/initBgImageControl), bukan
   dropdown biasa yang bisa tertutup sendiri. Jadi highlight berbasis
   class "open" ini pas: begitu tombol diklik, class "open" pindah ke
   dirinya dan trigger langsung ter-highlight; begitu tab satunya
   diklik, highlight ini otomatis pindah ke sana. */
.ccp.open .ccp-trigger {
  background-color: #03befc;
  color: #fff;
}

.ccp-trigger-swatch {
  width: 22px;
  height: 22px;
  border-radius: 4px;
  box-shadow: 0 0 0 1px rgba(196, 196, 196, 0.6);
  flex-shrink: 0;
  background-image:
    linear-gradient(45deg, #ccc 25%, transparent 25%),
    linear-gradient(-45deg, #ccc 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ccc 75%),
    linear-gradient(-45deg, transparent 75%, #ccc 75%);
  background-size: 8px 8px;
  background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
}

.ccp-trigger-swatch::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 3px;
  background-color: inherit;
}

.ccp-trigger-value {
  flex: 1;
  font-family: monospace;
  font-size: 10px;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ccp-trigger-caret {
  font-size: 10px;
  color: #999;
  transition: transform 0.2s ease;
}

.ccp.open .ccp-trigger-caret {
  transform: rotate(180deg);
}

.ccp-native-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.ccp-popup {
  display: none;
  position: static;
  width: 100%;
  max-width: 100%;
  margin-top: 8px;
  padding: 12px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 8px;
  box-shadow: none;
  box-sizing: border-box;
}

.ccp.open .ccp-popup {
  display: block;
}

/* Untuk popup bg-color & bg-image yang sudah dipindah keluar dari
   .ccp/.bgimg (lihat markup #bg-color-popup / #bg-image-popup di HTML),
   tampilkan berdasarkan class "open" pada trigger-nya lewat :has(). */
.style-controls:has(#bg-color-ccp.open) #bg-color-popup {
  display: block;
}

.style-controls:has(#bg-image-bgimg.open) #bg-image-popup {
  display: block;
}

/* Perkecil lebar tombol slider hue & alpha khusus di dropdown
   Background Color (tab Design) supaya tidak sebesar slider default. */
#bg-color-popup .ccp-hue-slider,
#bg-color-popup .ccp-alpha-slider {
  width: 12px;
}

/* Tambahan jarak antara slider alpha & kotak saturation/value (ccp-sv-square)
   khusus di dropdown Background Color (tab Design), supaya tidak menempel.
   Ubah angka margin-left di bawah ini kalau mau jarak lebih besar/kecil. */
#bg-color-popup .ccp-sv-square {
  margin-left: 6px;
}

.ccp-picker-area {
  display: flex;
  gap: 10px;
  height: 140px;
  max-width: 100%;
  margin-bottom: 12px;
}

/* Slider warna (hue) diletakkan di sebelah KIRI kotak saturation/value */
.ccp-hue-slider {
  position: relative;
  width: 16px;
  height: 100%;
  border-radius: 8px;
  cursor: pointer;
  flex-shrink: 0;
  background: linear-gradient(
    to bottom,
    #ff0000 0%,
    #ffff00 17%,
    #00ff00 33%,
    #00ffff 50%,
    #0000ff 67%,
    #ff00ff 83%,
    #ff0000 100%
  );
}

.ccp-hue-thumb,
.ccp-alpha-thumb {
  position: absolute;
  left: -3px;
  right: -3px;
  height: 6px;
  border-radius: 3px;
  border: 2px solid white;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
  pointer-events: none;
  transform: translateY(-3px);
}

/* Slider transparansi (alpha) diletakkan di samping slider warna (hue) */
.ccp-alpha-slider {
  position: relative;
  width: 16px;
  height: 100%;
  border-radius: 8px;
  cursor: pointer;
  flex-shrink: 0;
  /* Bikin stacking context sendiri supaya urutan tumpuk (z-index) antara
     lapisan checkerboard/gradient (::after) dan tombolnya konsisten di
     semua browser -- tanpa ini tombol bisa "ketutup" lapisan gradient. */
  isolation: isolate;
  /* Pola kotak-kotak (checkerboard) sebagai latar transparansi */
  background-image:
    linear-gradient(45deg, #ccc 25%, transparent 25%),
    linear-gradient(-45deg, #ccc 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ccc 75%),
    linear-gradient(-45deg, transparent 75%, #ccc 75%);
  background-color: #fff;
  background-size: 8px 8px;
  background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
}

.ccp-alpha-slider::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 8px;
  z-index: 1;
  /* --ccp-alpha-color diisi lewat JS (format "r, g, b") mengikuti warna
     yang sedang dipilih, dari solid (atas) ke transparan (bawah) */
  background: linear-gradient(
    to bottom,
    rgba(var(--ccp-alpha-color, 0, 0, 0), 1) 0%,
    rgba(var(--ccp-alpha-color, 0, 0, 0), 0) 100%
  );
}

.ccp-alpha-thumb {
  z-index: 2;
}

.ccp-sv-square {
  position: relative;
  flex: 1;
  height: 100%;
  border-radius: 8px;
  cursor: crosshair;
  background-color: #ff0000;
  background-image:
    linear-gradient(to top, #000000, rgba(0, 0, 0, 0)),
    linear-gradient(to right, #ffffff, rgba(255, 255, 255, 0));
}

.ccp-sv-thumb {
  position: absolute;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  margin-top: -6px;
  border-radius: 50%;
  border: 2px solid white;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

.ccp-fields {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr 1fr;
  gap: 6px;
  max-width: 100%;
  box-sizing: border-box;
  margin-bottom: 12px;
}

.ccp-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.ccp-field label {
  font-size: 10px;
  color: #999;
  text-align: center;
  text-transform: uppercase;
}

.ccp-field input {
  width: 100%;
  padding: 5px 4px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 11px;
  text-align: center;
  font-family: monospace;
  box-sizing: border-box;
}

.ccp-field input:focus {
  outline: none;
  border-color: rgba(171, 178, 191, 0.6);
  box-shadow: 0 0 4px 0 rgba(171, 178, 191, 0.5);
}

.ccp-field-rgb input::-webkit-outer-spin-button,
.ccp-field-rgb input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.ccp-swatches {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  gap: 5px;
  max-width: 100%;
  box-sizing: border-box;
}

.ccp-swatch {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 4px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  cursor: pointer;
  box-sizing: border-box;
  transition: transform 0.1s ease;
}

.ccp-swatch:hover {
  transform: scale(1.15);
}

.ccp-swatch.active {
  box-shadow: 0 0 0 2px white, 0 0 0 3px #abb2bf;
}

/* ===== Image Background control (bgimg) ===== */
.bgimg {
  position: relative;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

.bgimg-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: 100%;
  padding: 6px 8px;
  background: #fbfbfc;
  border: 1px solid var(--pst-border);
  border-radius: 4px;
  cursor: pointer;
  box-sizing: border-box;
  font-size: 12px;
  color: #666;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

.bgimg-trigger:hover {
  background-color: rgba(3, 190, 252, 0.25);
}

/* Sama seperti .ccp.open .ccp-trigger di atas -- highlight tab yang
   sedang aktif. */
.bgimg.open .bgimg-trigger {
  background-color: #03befc;
  color: #fff;
}

.bgimg-trigger-thumb {
  width: 22px;
  height: 22px;
  border-radius: 4px;
  box-shadow: 0 0 0 1px rgba(196, 196, 196, 0.6);
  flex-shrink: 0;
  background-color: #f1f1f1;
  background-size: cover;
  background-position: center;
  background-image:
    linear-gradient(45deg, #ddd 25%, transparent 25%),
    linear-gradient(-45deg, #ddd 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ddd 75%),
    linear-gradient(-45deg, transparent 75%, #ddd 75%);
  background-size: 8px 8px;
  background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
}

.bgimg-trigger-thumb.has-image {
  background-image: var(--bgimg-thumb-url, none);
  background-size: cover;
  background-position: center;
  box-shadow: none;
}

.bgimg-trigger-value {
  flex: 1;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bgimg-trigger-caret {
  font-size: 10px;
  color: #999;
  transition: transform 0.2s ease;
}

.bgimg.open .bgimg-trigger-caret {
  transform: rotate(180deg);
}

.bgimg-native-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.bgimg-popup {
  display: none;
  width: 100%;
  max-width: 100%;
  margin-top: 8px;
  padding: 12px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 8px;
  box-sizing: border-box;
}

.bgimg.open .bgimg-popup {
  display: block;
}

.bgimg-preview-wrap {
  display: none;
  position: relative;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  height: 90px;
  margin-bottom: 10px;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid #ddd;
  background-color: #f1f1f1;
  background-image:
    linear-gradient(45deg, #ddd 25%, transparent 25%),
    linear-gradient(-45deg, #ddd 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ddd 75%),
    linear-gradient(-45deg, transparent 75%, #ddd 75%);
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
}

.bgimg-preview-wrap.has-image {
  display: block;
  border: none;
}

.bgimg-preview-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Tombol icon mata di tengah preview -- muncul saat hover, buka lightbox
   untuk melihat gambar yang diupload dalam ukuran penuh saat diklik. */
.bgimg-preview-view-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.2s ease, background 0.2s ease, transform 0.15s ease;
}

.bgimg-preview-wrap:hover .bgimg-preview-view-btn {
  opacity: 1;
}

.bgimg-preview-view-btn:hover {
  background: rgba(0, 0, 0, 0.75);
  transform: translate(-50%, -50%) scale(1.08);
}

/* ===== Lightbox: lihat gambar Image Background dalam ukuran penuh ===== */
.bgimg-lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10001;
  align-items: center;
  justify-content: center;
}

.bgimg-lightbox.open {
  display: flex;
}

.bgimg-lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
}

.bgimg-lightbox-content {
  position: relative;
  max-width: 90vw;
  max-height: 85vh;
  display: flex;
}

.bgimg-lightbox-content img {
  max-width: 90vw;
  max-height: 85vh;
  object-fit: contain;
  border-radius: 6px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
}

/* Sebelumnya bulat (border-radius: 50%) & mengambang di LUAR gambar
   (top/right negatif -18px, menabrak tepi gambar dari luar). Sekarang
   kotak dengan sudut membulat, posisinya DI DALAM batas gambar --
   top/right positif supaya tombol duduk beberapa piksel dari tepi
   gambar, bukan menggantung keluar. border-radius disamakan persis
   dengan border-radius gambar modal (".bgimg-lightbox-content img",
   6px di atas) supaya lengkungan sudut tombol & gambar konsisten. */
.bgimg-lightbox-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 34px;
  height: 34px;
  border-radius: 0;
  border: none;
  background: transparent;
  color: #fff;
  font-size: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: none;
  transition: transform 0.15s ease;
}

.bgimg-lightbox-close:hover {
  transform: scale(1.1);
}

/* Dark mode: overlay lightbox sama-sama gelap, jadi warna icon X cukup
   putih di kedua mode -- tidak perlu override khusus lagi sekarang
   karena kotak background sudah dihilangkan (transparent). */
body.dark-mode .bgimg-lightbox-close {
  background: transparent;
  color: #fff;
}

/* ===== Drag & drop zone untuk upload gambar background ===== */
.bgimg-dropzone {
  position: relative;
  width: 100%;
  max-width: 100%;
  padding: 10px;
  border: 1.5px dashed #ddd;
  border-radius: 6px;
  background-color: #f6f9fc;
  transition: border-color 0.2s ease, background-color 0.2s ease;
  box-sizing: border-box;
}

.bgimg-dropzone.dragover {
  border-color: #339af0;
  background-color: rgba(51, 154, 240, 0.06);
}

.bgimg-drop-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 8px;
  font-size: 11px;
  color: #999;
  text-align: center;
}

/* Hint statis batas format/ukuran upload (mis. "PNG/JPEG/WEBP, maks. 8MB"),
   ditampilkan proaktif SEBELUM user memilih file -- bukan cuma lewat toast
   error setelah upload ditolak -- supaya batasnya kelihatan dari awal.
   Dibuat lebih kecil & redup dari .bgimg-drop-hint supaya tidak lebih
   menonjol dari instruksi utama "drag & drop". */
.bgimg-limit-hint {
  margin-top: 2px;
  font-size: 10px;
  color: #bbb;
  text-align: center;
}

.bgimg-drop-overlay {
  display: none;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 6px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.92);
  color: #339af0;
  font-size: 12px;
  font-weight: 600;
  pointer-events: none;
}

.bgimg-dropzone.dragover .bgimg-drop-overlay {
  display: flex;
}

.bgimg-drop-overlay i {
  font-size: 20px;
}

.bgimg-actions {
  display: flex;
  gap: 8px;
  max-width: 100%;
  box-sizing: border-box;
}

.bgimg-action-btn {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 8px;
  font-size: 12px;
  border-radius: 4px;
  border: 1px solid #ddd;
  background: #f7f7f7;
  color: #444;
  cursor: pointer;
  transition: all 0.2s ease;
}

.bgimg-action-btn span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bgimg-action-btn:hover {
  border-color: #ccc;
  background: #eee;
}

.bgimg-remove-btn {
  color: #e03131;
  border-color: #f3c2c2;
  background: #fff5f5;
}

.bgimg-remove-btn:hover {
  background: #ffe3e3;
  border-color: #e8a8a8;
}

.bgimg-uploading {
  margin-top: 10px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #666;
}

/* Dark Mode - Custom Color Picker */
body.dark-mode .ccp-trigger {
  background-color: #1e1e1e;
  border-color: #444;
  color: #ddd;
}

body.dark-mode .ccp-trigger:hover {
  background-color: rgba(255, 255, 255, 0.08);
}

body.dark-mode .ccp.open .ccp-trigger {
  background-color: #6c757d;
  color: #ffffff;
  border-color: #6c757d;
}

body.dark-mode .ccp-trigger-swatch {
  box-shadow: 0 0 0 1px rgba(92, 92, 92, 0.8);
}

body.dark-mode .ccp-popup {
  background-color: #2a2a2e;
  border-color: #444;
}

body.dark-mode .ccp-field input {
  background-color: #1e1e1e;
  border-color: #444;
  color: #ddd;
}

body.dark-mode .ccp-field label {
  color: #aaa;
}

body.dark-mode .ccp-swatch.active {
  box-shadow: 0 0 0 2px #2a2a2e, 0 0 0 3px #abb2bf;
}

/* Dark Mode - Image Background control */
body.dark-mode .bgimg-trigger {
  background-color: #202024;
  border-color: #444;
  color: #ddd;
}

body.dark-mode .bgimg-trigger:hover {
  background-color: rgba(255, 255, 255, 0.08);
  color: #fff;
}

body.dark-mode .bgimg.open .bgimg-trigger {
  background-color: #6c757d;
  color: #ffffff;
  border-color: #6c757d;
}

body.dark-mode .bgimg-trigger-thumb {
  box-shadow: 0 0 0 1px rgba(92, 92, 92, 0.8);
}

body.dark-mode .bgimg-popup {
  background-color: #2a2a2e;
  border-color: #444;
}

body.dark-mode .bgimg-action-btn {
  background-color: #1e1e1e;
  border-color: #444;
  color: #ddd;
}

body.dark-mode .bgimg-action-btn:hover {
  border-color: #444;
  background-color: rgba(255, 255, 255, 0.08);
}

body.dark-mode .bgimg-remove-btn {
  background-color: #2e1e1e;
  border-color: #6b2b2b;
  color: #ff8787;
}

body.dark-mode .bgimg-remove-btn:hover {
  background-color: #3a1f1f;
  border-color: #a55;
}

body.dark-mode .bgimg-uploading {
  color: #aaa;
}

body.dark-mode .bgimg-dropzone {
  border-color: #444;
  background-color: #34343a;
}

body.dark-mode .bgimg-dropzone.dragover {
  border-color: #3fa9f5;
  background-color: rgba(63, 169, 245, 0.08);
}

body.dark-mode .bgimg-drop-hint {
  color: #888;
}

body.dark-mode .bgimg-limit-hint {
  color: #6b6b70;
}

body.dark-mode .bgimg-drop-overlay {
  background: rgba(30, 30, 30, 0.92);
  color: #3fa9f5;
}

/* Separator */
.style-separator {
  border-top: 1px solid #ddd;
  margin: 15px 0;
}

/* Margin & Padding Section */
.spacing-section {
  margin-bottom: 20px;
  padding: 0 5px;
}

.spacing-title {
  display: block;
  margin-bottom: 12px;
  font-weight: 600;
  font-size: 13px;
  color: #333;
}

.spacing-title i {
  margin-right: 6px;
  width: 16px;
  color: #007bff;
}

.spacing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}

.spacing-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.spacing-item label {
  font-size: 11px;
  font-weight: 500;
  color: #666;
  text-align: center;
}

.spacing-item input {
  width: 100%;
  padding: 6px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 12px;
  text-align: center;
  background: white;
  transition: all 0.2s ease;
  box-sizing: border-box;
}

.spacing-item input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
}

/* Dark Mode Styles */
body.dark-mode .style-control-group label,
body.dark-mode .spacing-title {
  color: #ddd;
}

body.dark-mode .style-control-group select,
body.dark-mode .style-control-group input[type="text"],
body.dark-mode .spacing-item input,
body.dark-mode .color-picker span {
  background-color: #1e1e1e;
  border-color: #444;
  color: #ddd;
}

body.dark-mode .style-control-group select:focus,
body.dark-mode .style-control-group input[type="text"]:focus,
body.dark-mode .spacing-item input:focus {
  border-color: #3fa9f5;
  box-shadow: 0 0 0 2px rgba(63, 169, 245, 0.1);
}

/* Field custom color picker (hex/rgb/alpha) dikecualikan dari rule fokus
   biru di atas, supaya highlight-nya tetap konsisten #abb2bf di dark mode
   juga (sama seperti field RGB & tombol trigger/swatch). */
body.dark-mode .ccp-field input:focus {
  border-color: rgba(171, 178, 191, 0.45);
  box-shadow: 0 0 4px 0 rgba(171, 178, 191, 0.35);
}

body.dark-mode .color-picker input[type="color"] {
  background-color: #1e1e1e;
  border-color: #444;
}

body.dark-mode .spacing-item label {
  color: #aaa;
}

body.dark-mode .style-separator {
  border-top-color: #444;
}

/* ===== Section Padding (per-device: HP / Tablet / PC) =====
   Widget ditaruh di bawah bg-color & bg-image (lihat markup
   ".padding-style-group" di HTML, persis setelah ".bgimg-popup"). Tab
   device (.padding-device-tab) tampilannya sengaja dibuat mirip ikon
   preview-mode di footer sidebar supaya terasa satu kesatuan, tapi
   class-nya sengaja dibedakan (BUKAN "preview-mode-icon") supaya tidak
   ikut ke-toggle oleh querySelectorAll(".preview-mode-icon") di
   setPreviewMode(). Sinkronisasi dua arah ditangani lewat JS
   (syncPaddingControlsWithMode & event klik tab, lihat kode JS). */
.padding-style-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
  padding-top: 20px;
  border-top: 2px dashed #ddd;
}

.padding-style-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.padding-style-header label {
  font-size: 12px;
  font-weight: 500;
  color: #555;
}

.padding-reset-wrap {
  grid-area: reset;
  display: flex;
  justify-content: flex-end;
  margin-top: 4px;
}

.padding-reset-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 0 8px;
  height: 18px;
  border: none;
  border-radius: 999px;
  background: #eef0f3;
  color: #999;
  font-size: 9px;
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease;
}

.padding-reset-btn:hover {
  color: #fff;
  background: #03befc;
}

.padding-reset-btn:hover i {
  color: #fff;
}

.padding-reset-btn i {
  font-size: 9px;
}

.padding-device-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
  background: #eee;
  border-radius: 6px;
  padding: 3px;
}

.padding-device-tab {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 26px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: #777;
  font-size: 12px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.padding-device-tab:hover {
  color: #333;
  background: rgba(3, 190, 252, 0.25);
}

.padding-device-tab.active {
  background: #03befc;
  color: #fff;
}

.padding-box-grid {
  display: grid;
  /* minmax(0, 1fr) -- BUKAN cuma "1fr" -- supaya kolom BOLEH menyusut
     lebih kecil dari min-content isinya (input angka + stepper satuan +
     slider di dalam .padding-field). Tanpa minmax(0, ...) ini, grid
     "1fr" default-nya minmax(auto, 1fr): kolom dipaksa selebar konten
     terlebarnya dan bisa melebihi lebar .padding-box-grid sendiri --
     lalu ikut memaksa .editor-pane melebar/scroll horizontal (lihat
     komentar overflow-y/overflow-x di .toolbar-ccp-popup & .style-controls
     di atas, .editor-pane otomatis kena overflow-x:auto begitu
     overflow-y:auto diset). Pola perbaikan sama seperti yang sudah
     dipakai di ".bg-style-row .style-control-group" (min-width:0). */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas:
    "top top"
    "left right"
    "bottom bottom"
    "reset reset";
  row-gap: 10px;
  column-gap: 10px;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 12px;
  max-width: 100%;
  box-sizing: border-box;
}

.padding-field[data-side="top"] { grid-area: top; }
.padding-field[data-side="left"] { grid-area: left; }
.padding-field[data-side="right"] { grid-area: right; }
.padding-field[data-side="bottom"] { grid-area: bottom; }

/* Field "Bottom" sengaja dibuat beda dari 3 field lainnya (top/left/right
   masih pakai gap:6px bawaan ".padding-field" di bawah): jarak antara
   input angka (di dalam .padding-field-label) & slider-nya (.padding-
   range-input) dibuat 5px, bukan 6px, sesuai permintaan khusus untuk
   field ini saja. */
.padding-field[data-side="bottom"] {
  gap: 5px;
}

/* Field Top & Bottom: teks label ("Atas"/"Bawah") TETAP di kiri seperti
   field lain -- yang dipindah ke tengah HANYA bagian input angkanya
   (.padding-field-value). Dipakai grid 3 kolom (1fr auto 1fr) supaya
   kolom tengah (tempat input, lebar "auto" mengikuti kontennya) selalu
   berada pas di tengah row, karena kolom kiri & kanan sama-sama "1fr"
   (seimbang) -- kolom kanan sengaja dibiarkan kosong hanya sebagai
   penyeimbang visual, bukan diisi apa pun. */
.padding-field[data-side="top"] .padding-field-label,
.padding-field[data-side="bottom"] .padding-field-label {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 12px;
}

.padding-field[data-side="top"] .padding-field-label > span:first-child,
.padding-field[data-side="bottom"] .padding-field-label > span:first-child {
  grid-column: 1;
  justify-self: start;
}

.padding-field[data-side="top"] .padding-field-label > .padding-field-value,
.padding-field[data-side="bottom"] .padding-field-label > .padding-field-value {
  grid-column: 2;
}

/* Field Right: teks "Kanan" tetap di KANAN, kotak input digeser ke KIRI
   (kebalikan dari field Top/Left/Bottom) -- row-reverse membalik urutan
   TAMPILAN saja (posisi di DOM/HTML tidak diubah). Karena ini mendorong
   kotak input Right ke tengah (berdekatan dengan kotak input Left di
   sisi lain), jarak antar-kolom Left/Right di ".padding-box-grid"
   sengaja dilebarkan (lihat column-gap di sana) supaya kedua kotak
   tetap ada jaraknya, tidak nempel. */
.padding-field[data-side="right"] .padding-field-label {
  flex-direction: row-reverse;
}

.padding-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* min-width:0 -- grid item ini secara default punya min-width:auto
     (= min-content isinya: input angka + stepper satuan + slider), jadi
     grid "minmax(0, 1fr)" di .padding-box-grid tidak bisa benar-benar
     menyusutkannya kecuali di-override di sini juga. Tanpa ini, widget
     Padding melebar melewati batas sidebar & memicu overflow-x pada
     .editor-pane (lihat komentar di .padding-box-grid). Pola sama seperti
     ".bg-style-row .style-control-group { min-width: 0; }" di atas. */
  min-width: 0;
}

.padding-field-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2px;
  min-width: 0;
}

/* FIX: selector ini SEBELUMNYA "descendant" biasa (".padding-field-label
   span:first-child" tanpa ">"), sehingga ikut menyasar span APA PUN yang
   kebetulan first-child dari parent-nya masing-masing di mana pun di
   dalam .padding-field-label -- termasuk ".padding-unit-display" (teks
   satuan "px" di stepper panah atas/bawah) yang letaknya first-child dari
   ".padding-unit-stepper", 2 level lebih dalam. Akibatnya teks unit ikut
   ke-uppercase ("px" -> "PX") & ikut font-size/letter-spacing label,
   membesarkan kotak .padding-field-value di sekitarnya. Sekarang pakai
   child combinator ">" supaya HANYA span yang jadi anak LANGSUNG
   .padding-field-label (yaitu span label "Top"/"Left"/"Right"/"Bottom")
   yang kena rule ini -- konsisten dengan pola yang sudah dipakai di rule
   grid-column field Top/Bottom di atas (".padding-field-label > span:first-child"). */
.padding-field-label > span:first-child {
  font-size: 11px;
  font-weight: 500;
  color: #777;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.padding-field-value {
  display: flex;
  align-items: center;
  gap: 4px;
  background: #f5f5f5;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 2px 3px;
}

.padding-number-input {
  width: 40px;
  border: none;
  background: transparent;
  font-size: 12px;
  text-align: right;
  padding: 2px 3px;
  -moz-appearance: textfield;
  height: 16px;
  line-height: 16px;
  box-sizing: border-box;
}

.padding-number-input::-webkit-outer-spin-button,
.padding-number-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.padding-number-input:focus {
  outline: none;
}

/* Stepper satuan padding (menggantikan dropdown <select> lama): teks
   satuan aktif (.padding-unit-display) + dua tombol panah kecil bersusun
   vertikal (.padding-unit-arrows > .padding-unit-arrow-btn) untuk maju/
   mundur bergilir (cycle) di antara px/%/rem/em/vh/vw/pt/ch. Nilai satuan
   sesungguhnya tetap disimpan di <input type="hidden"> terpisah (tidak
   perlu di-style, tidak pernah tampak). */
.padding-unit-stepper {
  display: flex;
  align-items: center;
  gap: 2px;
}

.padding-unit-display {
  font: inherit;
  font-size: 10px;
  color: #999;
  min-width: 18px;
  text-align: right;
  user-select: none;
  height: 16px;
  line-height: 16px;
  box-sizing: border-box;
}

.padding-unit-arrows {
  display: flex;
  flex-direction: column;
  line-height: 0;
}

.padding-unit-arrow-btn {
  border: none;
  background: transparent;
  color: #999;
  font-size: 8px;
  line-height: 1;
  padding: 1px 2px;
  margin: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Perbesar tampilan ikon panah saja (transform tidak mempengaruhi
   ukuran box/layout tombol, jadi kotak input & tombol tetap sama
   persis seperti semula). */
.padding-unit-arrow-btn i {
  transform: scale(1.6);
}

.padding-unit-arrow-btn:hover,
.padding-unit-arrow-btn:focus {
  color: #555;
  outline: none;
}

.padding-unit-arrow-btn:active {
  color: #03befc;
}

.padding-range-input {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  border-radius: 2px;
  background: #ddd;
  outline: none;
  cursor: pointer;
  margin: 5px 0;
}

.padding-range-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #03befc;
  cursor: pointer;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
}

.padding-range-input::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #03befc;
  cursor: pointer;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
}

body.dark-mode .padding-style-group {
  border-top-color: #444;
}

body.dark-mode .padding-style-header label {
  color: #ddd;
}

body.dark-mode .padding-reset-btn {
  background: #303034;
  color: #888;
}

body.dark-mode .padding-reset-btn:hover {
  color: #fff;
  background: #4a4a52;
}

body.dark-mode .padding-reset-btn:hover i {
  color: #fff;
}

body.dark-mode .padding-device-tabs {
  background: #1e1e1e;
}

body.dark-mode .padding-device-tab {
  color: #aaa;
}

body.dark-mode .padding-device-tab:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.08);
}

body.dark-mode .padding-device-tab.active {
  background: #4a4a52;
  color: #fff;
}

body.dark-mode .padding-box-grid {
  background: #1e1e1e;
  border-color: #444;
}

body.dark-mode .padding-field-label > span:first-child {
  color: #aaa;
}

body.dark-mode .padding-field-value {
  background: #2a2a2e;
  border-color: #444;
}

body.dark-mode .padding-number-input {
  color: #ddd;
}

body.dark-mode .padding-unit-display {
  color: #888;
}

body.dark-mode .padding-unit-arrow-btn {
  color: #888;
}

body.dark-mode .padding-unit-arrow-btn:hover,
body.dark-mode .padding-unit-arrow-btn:focus {
  color: #fff;
}

body.dark-mode .padding-unit-arrow-btn:active {
  color: #4a4a52;
}

body.dark-mode .padding-range-input {
  background: #444;
}

body.dark-mode .padding-range-input::-webkit-slider-thumb {
  background: #4a4a52;
}

body.dark-mode .padding-range-input::-moz-range-thumb {
  background: #4a4a52;
}

/* History Loading Indicator */
.history-loading {
    text-align: center;
    padding: 10px !important;
    color: #999;
    font-size: 13px;
    cursor: default !important;
}

.history-loading:hover {
    background-color: transparent !important;
    transform: none !important;
}

.history-loading i {
    margin-right: 8px;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* Dark Mode */
body.dark-mode .history-loading {
    color: #777;
}

/* ✅ Search History (dark mode) -- input & icon disamakan dengan
   pola input gelap lain di app (background #2a2a2e, border #444).
   Border fokus SENGAJA dibuat #777 (sama dengan .history-search-icon),
   bukan aksen biru #03befc seperti di light mode -- supaya di dark
   mode warnanya lebih netral/menyatu, tidak menyala biru mencolok. */
body.dark-mode .history-search-input {
    background-color: #2a2a2e;
    color: #ddd;
    border-color: #444;
}

body.dark-mode .history-search-input:focus {
    border-color: #777;
}

body.dark-mode .history-search-input::placeholder {
    color: #777;
}

body.dark-mode .history-search-icon {
    color: #777;
}

body.dark-mode .history-search-clear {
    color: #777;
}

body.dark-mode .history-search-clear:hover {
    color: #ddd;
}

  /* ============================================================
     Dropdown "Font" (toolbar teks): sebelumnya tiap <li> punya
     style="font-family: ..." inline supaya preview nama font
     tampil dalam font aslinya. Dipindah ke sini, di-scope ke
     .font-list (satu-satunya <ul> dengan class ini di dokumen)
     supaya tidak bentrok dengan data-value di dropdown lain.
     ============================================================ */
  .font-list li[data-value="Arial, sans-serif"] {
    font-family: Arial, sans-serif;
  }
  .font-list li[data-value="'Arial Black', sans-serif"] {
    font-family: 'Arial Black', sans-serif;
  }
  .font-list li[data-value="'Arial Narrow', sans-serif"] {
    font-family: 'Arial Narrow', sans-serif;
  }
  .font-list li[data-value="Helvetica, sans-serif"] {
    font-family: Helvetica, sans-serif;
  }
  .font-list li[data-value="'Times New Roman', serif"] {
    font-family: 'Times New Roman', serif;
  }
  .font-list li[data-value="Times, serif"] {
    font-family: Times, serif;
  }
  .font-list li[data-value="'Courier New', monospace"] {
    font-family: 'Courier New', monospace;
  }
  .font-list li[data-value="Courier, monospace"] {
    font-family: Courier, monospace;
  }
  .font-list li[data-value="Georgia, serif"] {
    font-family: Georgia, serif;
  }
  .font-list li[data-value="Verdana, sans-serif"] {
    font-family: Verdana, sans-serif;
  }
  .font-list li[data-value="'Trebuchet MS', sans-serif"] {
    font-family: 'Trebuchet MS', sans-serif;
  }
  .font-list li[data-value="Tahoma, sans-serif"] {
    font-family: Tahoma, sans-serif;
  }
  .font-list li[data-value="Impact, sans-serif"] {
    font-family: Impact, sans-serif;
  }
  .font-list li[data-value="'Comic Sans MS', cursive"] {
    font-family: 'Comic Sans MS', cursive;
  }
  .font-list li[data-value="Calibri, sans-serif"] {
    font-family: Calibri, sans-serif;
  }
  .font-list li[data-value="Cambria, serif"] {
    font-family: Cambria, serif;
  }
  .font-list li[data-value="Candara, sans-serif"] {
    font-family: Candara, sans-serif;
  }
  .font-list li[data-value="Corbel, sans-serif"] {
    font-family: Corbel, sans-serif;
  }
  .font-list li[data-value="Consolas, monospace"] {
    font-family: Consolas, monospace;
  }
  .font-list li[data-value="Constantia, serif"] {
    font-family: Constantia, serif;
  }
  .font-list li[data-value="'Segoe UI', sans-serif"] {
    font-family: 'Segoe UI', sans-serif;
  }
  .font-list li[data-value="'Century Gothic', sans-serif"] {
    font-family: 'Century Gothic', sans-serif;
  }
  .font-list li[data-value="'Franklin Gothic Medium', sans-serif"] {
    font-family: 'Franklin Gothic Medium', sans-serif;
  }
  .font-list li[data-value="'Lucida Sans Unicode', sans-serif"] {
    font-family: 'Lucida Sans Unicode', sans-serif;
  }
  .font-list li[data-value="'Lucida Console', monospace"] {
    font-family: 'Lucida Console', monospace;
  }
  .font-list li[data-value="'Palatino Linotype', serif"] {
    font-family: 'Palatino Linotype', serif;
  }
  .font-list li[data-value="'Book Antiqua', serif"] {
    font-family: 'Book Antiqua', serif;
  }
  .font-list li[data-value="'Bookman Old Style', serif"] {
    font-family: 'Bookman Old Style', serif;
  }
  .font-list li[data-value="Garamond, serif"] {
    font-family: Garamond, serif;
  }
  .font-list li[data-value="Baskerville, serif"] {
    font-family: Baskerville, serif;
  }
  .font-list li[data-value="Didot, serif"] {
    font-family: Didot, serif;
  }
  .font-list li[data-value="Rockwell, serif"] {
    font-family: Rockwell, serif;
  }
  .font-list li[data-value="Copperplate, serif"] {
    font-family: Copperplate, serif;
  }
  .font-list li[data-value="Papyrus, cursive"] {
    font-family: Papyrus, cursive;
  }
  .font-list li[data-value="Futura, sans-serif"] {
    font-family: Futura, sans-serif;
  }
  .font-list li[data-value="Geneva, sans-serif"] {
    font-family: Geneva, sans-serif;
  }
  .font-list li[data-value="'Gill Sans', sans-serif"] {
    font-family: 'Gill Sans', sans-serif;
  }
  .font-list li[data-value="Optima, sans-serif"] {
    font-family: Optima, sans-serif;
  }
  .font-list li[data-value="Perpetua, serif"] {
    font-family: Perpetua, serif;
  }
  .font-list li[data-value="Monaco, monospace"] {
    font-family: Monaco, monospace;
  }
  .font-list li[data-value="Menlo, monospace"] {
    font-family: Menlo, monospace;
  }
  .font-list li[data-value="'Andale Mono', monospace"] {
    font-family: 'Andale Mono', monospace;
  }

  /* Dropdown "Paragraph" (format heading): sebelumnya style="font-size:
     ..." inline per <li> untuk preview ukuran heading di dropdown.
     Di-scope ke .toolbar-heading-select .select-options (satu-
     satunya di dokumen) supaya tidak bentrok data-value lain. */
  .toolbar-heading-select .select-options li[data-value="p"] {
    font-size: 1em;
  }
  .toolbar-heading-select .select-options li[data-value="h1"] {
    font-size: 2em;
  }
  .toolbar-heading-select .select-options li[data-value="h2"] {
    font-size: 1.5em;
  }
  .toolbar-heading-select .select-options li[data-value="h3"] {
    font-size: 1.17em;
  }
  .toolbar-heading-select .select-options li[data-value="h4"] {
    font-size: 1em;
  }
  .toolbar-heading-select .select-options li[data-value="h5"] {
    font-size: 0.83em;
  }
  .toolbar-heading-select .select-options li[data-value="h6"] {
    font-size: 0.75em;
  }

  /* Elemen yang sebelumnya disembunyikan lewat style="display:none"/
     "display: none" inline (kelas dasarnya sendiri pakai display:flex,
     jadi override ini perlu spesifisitas ID). JS (app.js) SELALU set
     .style.display secara eksplisit saat show/hide (showAddSectionModal,
     setBgImageValue, dst), jadi override JS tetap menang seperti biasa --
     rule ini cuma menggantikan kondisi AWAL sebelum JS jalan. */
  #add-section-modal,
  #code-editor-modal,
  #bg-image-remove-btn,
  #bg-image-uploading {
    display: none;
  }
/* =========================================================================
   PAGE SETTINGS TAB -- tab "Settings" di dalam settings-editor.
   Redesign: dari 1 panel panjang bertumpuk (banyak divider) menjadi
   kumpulan CARD yang bisa dibuka/tutup (accordion). Info Halaman &
   Metadata terbuka dari awal karena paling sering diisi; Opsi Halaman,
   Analytics, dan Others (yang lebih jarang disentuh) mulai tertutup
   supaya panel tidak terasa panjang & padat saat pertama dibuka.
   Setiap card diberi ikon warna berbeda (lihat --pst-c-* di bawah)
   sebagai penanda visual cepat -- bukan dekorasi, tapi bantu orang
   scan "ini soal apa" tanpa baca judul dulu.
   Panel induknya (.settings-editor) lebar tetap 385px, jadi semua di
   bawah ini tetap 1 kolom (bertumpuk).
   Interaksi (counter, sinkron preview, tab, dropdown, upload, copy,
   & buka/tutup card) ada di js/settings.js.
   ========================================================================= */

.pst-wrap {
  --pst-bg: #f6f7f9;
  --pst-card: #ffffff;
  --pst-border: #e9eaed;
  --pst-text: #26272b;
  --pst-muted: #8b8d94;
  --pst-blue: #007bff;
  --pst-blue-soft: #eaf3ff;
  --pst-violet: #7c3aed;
  --pst-violet-soft: #f3ecff;
  --pst-amber: #c2760c;
  --pst-amber-soft: #fdf1de;
  --pst-indigo: #4f46e5;
  --pst-indigo-soft: #edecfd;
  --pst-slate: #52565f;
  --pst-slate-soft: #eef0f2;

  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 13px;
  color: var(--pst-text);
  background: var(--pst-bg);
  border-radius: 8px;
  padding: 12px;
  box-sizing: border-box;
}

.pst-wrap * {
  box-sizing: border-box;
}

/* ---- Card shell ---- */
.pst-card {
  background: var(--pst-card);
  border: 1px solid var(--pst-border);
  border-radius: 4px;
  overflow: hidden;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.pst-card-open {
  border-color: #dfe2e8;
  box-shadow: 0 2px 10px rgba(20, 20, 30, 0.045);
}

.pst-card-header {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 14px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}

.pst-card-icon {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
}

.pst-icon-info { background: var(--pst-blue-soft); color: var(--pst-blue); }
.pst-icon-seo { background: var(--pst-violet-soft); color: var(--pst-violet); }
.pst-icon-options { background: var(--pst-amber-soft); color: var(--pst-amber); }
.pst-icon-analytics { background: var(--pst-indigo-soft); color: var(--pst-indigo); }
.pst-icon-others { background: var(--pst-slate-soft); color: var(--pst-slate); }

.pst-card-title {
  flex: 1;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--pst-text);
}

.pst-card-chevron {
  flex: none;
  font-size: 11px;
  color: var(--pst-muted);
  transition: transform 0.2s ease;
}

.pst-card-open .pst-card-chevron {
  transform: rotate(180deg);
}

.pst-card-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.pst-card-open .pst-card-body {
  grid-template-rows: 1fr;
}

.pst-card-body-inner {
  overflow: hidden;
  min-height: 0;
}

.pst-card-body-content {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 2px 14px 16px;
}

/* ---- Fields (shared) ---- */
.pst-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pst-field-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.pst-field-label-row label,
.pst-field-label-single {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--pst-text);
}

.pst-required {
  color: #e0483f;
}

.pst-char-count {
  font-size: 11px;
  color: var(--pst-muted);
  flex: none;
}

.pst-char-count.pst-char-count-limit {
  color: #e0483f;
  font-weight: 700;
}

.pst-input,
.pst-textarea {
  width: 100%;
  border: 1px solid var(--pst-border);
  background: #fbfbfc;
  border-radius: 4px;
  padding: 9px 11px;
  font-size: 13px;
  font-family: inherit;
  color: var(--pst-text);
}

.pst-input::placeholder,
.pst-textarea::placeholder {
  color: #b3b5bc;
}

.pst-input:focus,
.pst-textarea:focus {
  outline: none;
  border-color: rgba(171, 178, 191, 0.6);
  background: #fff;
  box-shadow: 0 0 4px 0 rgba(171, 178, 191, 0.5);
}

.pst-textarea {
  resize: vertical;
  line-height: 1.45;
}

.pst-textarea-code {
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 12px;
}

.pst-current-url {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--pst-muted);
}

.pst-current-url i {
  font-size: 10px;
}

.pst-current-url span:last-child {
  color: var(--pst-blue);
  word-break: break-all;
}

/* ---- Multiselect (dummy dropdown) ---- */
.pst-multiselect {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  border: 1px solid var(--pst-border);
  background: #fbfbfc;
  border-radius: 4px;
  padding: 9px 11px;
  cursor: pointer;
}

.pst-multiselect.pst-open {
  border-color: rgba(171, 178, 191, 0.6);
  background: #fff;
  box-shadow: 0 0 4px 0 rgba(171, 178, 191, 0.5);
}

.pst-multiselect.pst-open i {
  transform: rotate(180deg);
}

.pst-multiselect i {
  font-size: 10px;
  color: var(--pst-muted);
}

.pst-multiselect-placeholder {
  font-size: 12.5px;
  color: #a7a9b0;
}

.pst-multiselect-disabled,
.pst-field-disabled {
  opacity: 0.55;
  pointer-events: none;
}

.pst-badge-soon {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--pst-muted);
  background: #eee;
  border-radius: 20px;
  padding: 2px 8px;
  flex: none;
}

/* ---- SEO score (lives in the Metadata card header) ---- */
.pst-seo-score {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
}

.pst-seo-score-bar {
  width: 44px;
  height: 5px;
  border-radius: 3px;
  background: #ececf0;
  overflow: hidden;
}

.pst-seo-score-fill {
  display: block;
  height: 100%;
  width: 50%;
  background: linear-gradient(90deg, #ffb020, #ffd166);
  border-radius: 3px;
}

.pst-seo-score-fill.pst-score-good {
  background: linear-gradient(90deg, #16a34a, #4ade80);
}

.pst-seo-score-fill.pst-score-bad {
  background: linear-gradient(90deg, #dc2626, #f87171);
}

.pst-seo-score-value {
  font-size: 11px;
  font-weight: 700;
  color: var(--pst-muted);
  min-width: 26px;
  text-align: right;
}

/* ---- Image upload (Meta image / Favicon) ---- */
.pst-image-row {
  display: flex;
  gap: 10px;
}

.pst-image-field {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

.pst-image-upload {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  height: 76px;
  border: 1.5px dashed #d6d8dd;
  border-radius: 4px;
  color: var(--pst-muted);
  cursor: pointer;
  background: #fbfbfc;
  overflow: hidden;
}

.pst-image-upload:hover {
  border-color: var(--pst-blue);
  background: var(--pst-blue-soft);
  color: var(--pst-blue);
}

.pst-image-upload i {
  font-size: 15px;
}

.pst-image-upload span {
  font-size: 11px;
  font-weight: 600;
}

.pst-image-upload.pst-has-image {
  border-style: solid;
  padding: 0;
}

.pst-image-upload.pst-has-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pst-image-upload.pst-has-image i,
.pst-image-upload.pst-has-image span {
  display: none;
}

/* ---- Preview block (nested inside Metadata card) ---- */
.pst-preview-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 14px;
  border-top: 2px dashed var(--pst-border);
}

.pst-preview-tabs {
  position: relative;
  display: flex;
  background: var(--pst-slate-soft);
  border-radius: 4px;
  padding: 3px;
  gap: 3px;
}

.pst-preview-tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: none;
  background: none;
  border-radius: 4px;
  padding: 7px 8px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--pst-muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.pst-preview-tab i {
  font-size: 11px;
}

.pst-preview-tab.active {
  background: #fff;
  color: var(--pst-text);
  box-shadow: 0 1px 3px rgba(20, 20, 30, 0.08);
}

.pst-preview-content {
  display: none;
}

.pst-preview-content.active {
  display: block;
}

.pst-google-preview {
  border: 1px solid var(--pst-border);
  border-radius: 4px;
  padding: 12px;
  background: #fff;
}

.pst-google-url {
  font-size: 11.5px;
  color: #145c2c;
  margin-bottom: 2px;
}

.pst-google-title {
  font-size: 14px;
  color: #0d0a8c;
  margin-bottom: 3px;
  line-height: 1.3;
}

.pst-google-desc {
  font-size: 12px;
  color: #4d5156;
  line-height: 1.45;
}

.pst-social-preview {
  border: 1px solid var(--pst-border);
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
}

.pst-social-image {
  height: 110px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: var(--pst-slate-soft);
  color: var(--pst-muted);
}

.pst-social-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pst-social-image i {
  font-size: 18px;
}

.pst-social-image span {
  font-size: 11px;
  font-weight: 600;
}

.pst-social-body {
  padding: 10px 12px;
  border-top: 1px solid var(--pst-border);
}

.pst-social-domain {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--pst-muted);
  margin-bottom: 3px;
}

.pst-social-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--pst-text);
  margin-bottom: 3px;
  line-height: 1.3;
}

.pst-social-desc {
  font-size: 11.5px;
  color: var(--pst-muted);
  line-height: 1.4;
}

/* ---- Toggle rows / switches ---- */
.pst-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--pst-text);
}

.pst-switch {
  position: relative;
  flex: none;
  display: inline-block;
  width: 36px;
  height: 20px;
}

.pst-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.pst-switch .pst-slider {
  position: absolute;
  inset: 0;
  background: #dcdee3;
  border-radius: 20px;
  cursor: pointer;
  transition: background 0.15s ease;
}

.pst-switch .pst-slider::before {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  left: 2px;
  top: 2px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.15s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.pst-switch input:checked + .pst-slider {
  background: var(--pst-blue);
}

.pst-switch input:checked + .pst-slider::before {
  transform: translateX(16px);
}

/* ---- Links / text buttons ---- */
.pst-link-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  align-self: flex-start;
  border: none;
  background: none;
  color: var(--pst-blue);
  font-size: 12px;
  font-weight: 600;
  padding: 0;
  cursor: pointer;
}

.pst-link-btn i {
  font-size: 10px;
}

.pst-link-btn:hover,
.pst-link-text:hover {
  text-decoration: underline;
}

.pst-link-text {
  font-size: 11.5px;
  color: var(--pst-blue);
  text-decoration: none;
}

.pst-field-actions {
  display: flex;
  gap: 6px;
  flex: none;
}

.pst-text-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid var(--pst-border);
  background: #fbfbfc;
  color: var(--pst-text);
  font-size: 11px;
  font-weight: 600;
  padding: 5px 9px;
  border-radius: 4px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.pst-text-btn:hover {
  background-color: rgba(3, 190, 252, 0.25);
}

.pst-text-btn i {
  font-size: 10px;
}

.pst-text-btn.pst-copied {
  border-color: #16a34a;
  color: #16a34a;
  background: #f0fdf4;
}

/* ---- Dark mode ---- */
body.dark-mode .pst-wrap {
  --pst-bg: #202024;
  --pst-card: #2a2a2e;
  --pst-border: #3a3a3f;
  --pst-text: #e9e9ec;
  --pst-muted: #96969c;
  --pst-blue-soft: #16283d;
  --pst-violet-soft: #2a2340;
  --pst-amber-soft: #332a19;
  --pst-indigo-soft: #23213d;
  --pst-slate-soft: #333338;
}

body.dark-mode .pst-card-open {
  border-color: #45454b;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}

body.dark-mode .pst-input,
body.dark-mode .pst-textarea,
body.dark-mode .pst-multiselect {
  background: #202024;
  color: var(--pst-text);
}

body.dark-mode .pst-input:focus,
body.dark-mode .pst-textarea:focus,
body.dark-mode .pst-multiselect.pst-open {
  background: #26262b;
  border-color: rgba(171, 178, 191, 0.45);
  box-shadow: 0 0 4px 0 rgba(171, 178, 191, 0.35);
}

body.dark-mode .pst-preview-tabs {
  background: #202024;
}

body.dark-mode .pst-preview-tab.active {
  background: #33333a;
  color: #fff;
}

body.dark-mode .pst-google-preview,
body.dark-mode .pst-social-preview {
  background: #202024;
  border-color: var(--pst-border);
}

body.dark-mode .pst-google-title {
  color: #8ab4f8;
}

body.dark-mode .pst-social-body {
  border-top-color: var(--pst-border);
}

body.dark-mode .pst-social-image {
  background: #202024;
}

body.dark-mode .pst-image-upload {
  background: #202024;
  border-color: #45454b;
}

body.dark-mode .pst-image-upload:hover {
  background-color: rgba(255, 255, 255, 0.08);
  color: #fff;
}

body.dark-mode .pst-text-btn {
  background: #202024;
}

body.dark-mode .pst-text-btn:hover {
  background-color: rgba(255, 255, 255, 0.08);
  color: #fff;
}

body.dark-mode .pst-badge-soon {
  background: #333338;
}