<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:gd="http://schemas.google.com/g/2005" xmlns:georss="http://www.georss.org/georss" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-9000860041661554971</atom:id><lastBuildDate>Tue, 10 Nov 2026 05:29:04 +0000</lastBuildDate><category>herbal</category><category>download</category><category>ebook</category><category>islami</category><category>Forex</category><category>Marketiva</category><category>converter</category><category>Cinta</category><category>El-shirazy</category><category>Habiburahman</category><category>Investasi Menurut Hukum Islam</category><category>Kang Abik</category><category>Novel</category><category>Online</category><category>Trading</category><category>VALUTA</category><category>doa</category><category>#pemimpinmerdekabelajar #refleksimerdekabelajar</category><category>18 Cara Merawat Komputer/Laptop Agar Tetap Awet Dan Tidak Rusak</category><category>ASING</category><category>Apa</category><category>Ayat</category><category>BATASAN RIBA DALAM SYARIAH</category><category>Bank konvensional vs Bank Syariah</category><category>Basic</category><category>Batasan Mengambil Keuntungan dalam Islam</category><category>Bertasbih</category><category>Bertasbih II</category><category>CARI</category><category>COMMODITIES</category><category>Cara</category><category>Cara Daftar Google Adsense Indonesia untuk menghasilkan uang dari internet</category><category>Cara Download Video Di YouTube Tanpa Software</category><category>Cara Investasi yang benar dan aman</category><category>Cara Memasang Iklan Adsense di Tengah Posting Secara Otomatis</category><category>Cara Memperbaiki Harddisk Tidak Terbaca Komputer</category><category>Cara Mendapatkan Uang dari Internet  seo</category><category>Cara Mendapatkan Uang dari Internet Bagi Anda yang Suka Bermain Game</category><category>Cara Mendapatkan Uang dari Internet Jika Anda Memahami Teknik SEO</category><category>Cara Mendapatkan Uang dari Internet Jika Anda Memiliki Modal Memadai</category><category>Cara Mendapatkan Uang dari Internet Jika Anda Punya Waktu Memadai</category><category>Cara Mendapatkan Uang dari Internet dengan Membuat Program/Software</category><category>Cara Mendapatkan Uang dari Internet dengan Toko Online</category><category>Cara Meningkatkan lalu lintas Pengunjung Blog / Website Dengan Cepat</category><category>Cara Mudah Membuat Akun PayPal Tanpa Kartu Kredit</category><category>Cara atasi Bluetooth Laptop tidak bekerja</category><category>Cara mengetahui spesifikasi komputer di linux</category><category>Cara mudah Download Video Di YouTube</category><category>DI INTERNET</category><category>DOWNLOAD SOAL-SOAL LATIHAN PRAKTEK EXCEL</category><category>Deposit</category><category>Deposito Syariah</category><category>Download ebook islami bagian 5</category><category>Download internet download accelator IrfanView</category><category>Dunia</category><category>Edensor</category><category>FUND</category><category>Forex dalam hukum Islam</category><category>HOBI YANG MENGHASILKAN UANG</category><category>Hukum Investasi  Menurut Syariat Islam</category><category>Hukum Tansaksi Valas dan Spekulasi Kurs Mata Uang</category><category>INDEXES</category><category>INVESTASI SYARIAH MENGUNTUNGKAN DUNIA DAN AKHIRAT</category><category>Instrumen Pasar Modal Syariah</category><category>Investasi dalam pandangan islam (syariah)</category><category>Jenis Investasi Berdasarkan Syariah</category><category>Join</category><category>Jual Beli dalam Syariah</category><category>KAMUS ISTILAH FOREX</category><category>Kenapa Kamu Harus Punya PayPal</category><category>Kumpulan Perintah Linux di Terminal Lengkap</category><category>MARKETIVA AGEA MUDAHNYA BERTRANSAKSI FOREX</category><category>MATA UANG</category><category>MENGENAL SAHAM (1)</category><category>MENGENAL SAHAM (2)</category><category>MENGUMPULKAM MODAL</category><category>MENYIAPKAN DANA HAJI</category><category>MONEY GAME</category><category>Materi</category><category>Membuat blog jadi 3 kolom</category><category>Mengetahui Kapasitas Memory</category><category>Menghasilkan Uang dari Internet dengan ClixSense</category><category>Menghasilkan Uang dari Internet dengan WHAFF!</category><category>Modul penyelesain masalah akuntansi dengan excel</category><category>Musik Klasik Bagi Bayi 1</category><category>Musik Klasik Bagi Bayi 2</category><category>Musik Klasik Bagi Bayi 3</category><category>Nama</category><category>Novel Ayat-Ayat Cinta</category><category>PELATIHAN PEMANFAATAN TIK DAN LITERASASI DIGITAL DI SMK N 8 SOLO KERJASAMA PUSTEKOM KEMDIKBUD DAN KOGTIK</category><category>PRODUK SIMPANAN DI BANK</category><category>Pasar Modal Syariah</category><category>Penyebab Blue Screen Windows dan Solusi Cara Mengatasinya</category><category>Processor</category><category>REKSA DANA (1)</category><category>REKSA DANA (2)</category><category>Resiko dalam Investasi</category><category>SAW</category><category>SENI BACA ALQURAN</category><category>SIKLUS FINANSIAL ANDA</category><category>STRATEGI FOREX</category><category>Secara</category><category>Singkat</category><category>Soal</category><category>Spekulasi di Pasar Modal</category><category>Strategy</category><category>TEMPAT</category><category>Tantangan Investasi Syariah di Pasar Modal</category><category>Tata Cara Jual Beli dalam Islam</category><category>Tinjauan Syariat Terhadap Jual-Beli Kredit</category><category>Tips Jitu Trading</category><category>Tips Memperbaiki Komputer Yang Macet Hang</category><category>Tradding</category><category>Transaksi</category><category>Tutorial</category><category>Tutorial Cara</category><category>UANG</category><category>Witdrawl</category><category>ahmed</category><category>aisyah</category><category>akm</category><category>alam</category><category>alquran</category><category>andrea</category><category>andrea hirata</category><category>ansav</category><category>antivirus</category><category>aqiqah</category><category>audio</category><category>avi</category><category>bagian</category><category>bepergian</category><category>berhijab</category><category>berjilbab</category><category>dan</category><category>dan Hardisk pada PC</category><category>dan Hardisk pada PC Mengetahui Kapasitas Memory</category><category>dan Referensi Jawaban</category><category>deedat</category><category>dogma</category><category>download converter</category><category>download lagu Letto - Lethologica (2009)</category><category>download laskar pelangi karya andrea hirata</category><category>download modul praktek SPSS</category><category>download setup alqur'an in word</category><category>download soal-soal pilihan Ganda TKJ (teknik Jaringan dan Komputer)</category><category>engkau</category><category>fiqih</category><category>flv</category><category>hamil</category><category>hirata</category><category>ilmu</category><category>iman</category><category>injil</category><category>islam</category><category>jual beli kredit dalam islam</category><category>karpov</category><category>karya andrea hirata</category><category>keajaiban</category><category>kristen</category><category>kristus</category><category>laskar pelangi 3</category><category>laut</category><category>lintas</category><category>maryamah</category><category>matematika</category><category>mati</category><category>menghalangi</category><category>menghasilkan uang dari internet dengan ProBux</category><category>misteri</category><category>mpeg</category><category>muallaf</category><category>muhammmad</category><category>nabawiyah</category><category>naskah</category><category>nusantar</category><category>orientasi</category><category>pcmav</category><category>pemuda</category><category>pendeta</category><category>penduduk</category><category>perdebatan</category><category>perjalanan</category><category>quran</category><category>rahasia</category><category>sang pemimpi</category><category>sedang</category><category>sejarah</category><category>semesta</category><category>sirah</category><category>smadav</category><category>surga</category><category>swaramuslim</category><category>tawakal</category><category>wanita</category><category>wav</category><category>wma</category><title>INVESTASI DEMI HIDUP YANG LEBIH BAIK</title><description>"Berbuat BAIK adalah investasi"-
"Kesehatan adalah investasi"-
"Ilmu adalah investasi"--

"BerInvestasilah yang bisa Menjamin Kebahagian Kehidupan Dunia Akhirat"</description><link>http://kitab-investasi.blogspot.com/</link><managingEditor>noreply@blogger.com (Unknown)</managingEditor><generator>Blogger</generator><openSearch:totalResults>771</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><language>en-us</language><itunes:explicit>yes</itunes:explicit><copyright>Bussines for you</copyright><itunes:keywords>reksadana,saham,forex,Bank,Tabungan,Forex,Pulsa,Elektronik,HP,Bisnis,Bussine</itunes:keywords><itunes:summary>Islamic canon law investment for calm old age, happy, Very Rich, Barokah if died entered heaven Reksadana , Saham, Bank, Tabungan, Forex, Pulsa Elektronik</itunes:summary><itunes:subtitle>Investasi syariah</itunes:subtitle><itunes:category text="Business"><itunes:category text="Investing"/></itunes:category><itunes:author>abang_cool</itunes:author><itunes:owner><itunes:email>nof_rizal07@yahoo.co.id</itunes:email><itunes:name>abang_cool</itunes:name></itunes:owner><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-5438142829766460482</guid><pubDate>Mon, 09 Nov 2026 01:38:11 +0000</pubDate><atom:updated>2026-11-09T08:38:11.936+07:00</atom:updated><title>Aplikasi Pengolah Presentasi Profesional</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Aplikasi Pengolah Presentasi Profesional - Informatika Kelas X SMK&lt;/title&gt;
&lt;style&gt;
body{margin:0;background:#E9ECFA}
#ppRoot{--brand:#3730A3;--brand-dark:#1E1B6B;--surface:#EEF0FF;--paper:#FFFFFF;--ink:#111827;--ink-soft:#374151;--line:#C7CBE8;--amber:#F59E0B;--green:#15803D;--red:#B91C1C;--yellow:#B45309}#ppRoot *{box-sizing:border-box}#ppRoot{background:#E9ECFA;color:var(--ink);font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;line-height:1.6;font-size:16px;padding:12px 0;text-align:left}#ppRoot .page-shell{max-width:1100px;margin:0 auto;background:#fff;border-radius:14px;box-shadow:0 8px 32px rgba(30,27,107,.18);border:1px solid var(--line);overflow:hidden}#ppRoot header.hero{background:var(--brand-dark);color:#fff;padding:34px 5% 30px}#ppRoot .kop{text-align:center;font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;color:#C7D2FE;margin-bottom:8px;font-weight:700}#ppRoot h1.title{font-weight:800;font-size:clamp(1.5rem,3.6vw,2.3rem);text-align:center;line-height:1.3;margin:0;color:#fff}#ppRoot .subtitle{text-align:center;margin:10px auto 0;color:#E0E7FF;font-size:1rem;max-width:70ch}#ppRoot .meta-strip{margin-top:24px;display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;background:rgba(255,255,255,.25);border:1px solid rgba(255,255,255,.25)}#ppRoot .meta-cell{background:var(--brand);padding:12px 14px;text-align:center}#ppRoot .meta-cell .k{font-size:.66rem;letter-spacing:.08em;text-transform:uppercase;color:#C7D2FE;font-weight:700}#ppRoot .meta-cell .v{margin-top:4px;font-size:.9rem;font-weight:700;color:#fff}#ppRoot nav.toc{display:flex;flex-wrap:wrap;gap:6px;padding:12px 5%;background:var(--surface);border-bottom:1px solid var(--line)}#ppRoot nav.toc a{font-size:.8rem;font-weight:700;color:var(--brand-dark);text-decoration:none;background:#fff;border:1px solid var(--line);border-radius:999px;padding:4px 12px}#ppRoot nav.toc a.go{background:var(--amber);border-color:var(--amber);color:#1F1300}#ppRoot .section{padding:30px 5%;border-bottom:1px solid #E5E7EB;background:#fff;color:var(--ink)}#ppRoot .section-tag{font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-soft);margin-bottom:6px;font-weight:700}#ppRoot h2.section-title{font-size:clamp(1.2rem,2.2vw,1.6rem);font-weight:800;color:var(--brand-dark);margin:0 0 14px}#ppRoot h3.sub-title{font-size:1.05rem;font-weight:800;color:var(--brand-dark);margin:22px 0 10px;display:flex;align-items:center;gap:9px}#ppRoot h3.sub-title:before{content:"";width:9px;height:9px;background:var(--amber);display:inline-block;border-radius:2px;flex:none}#ppRoot .section p{color:#1F2937;max-width:80ch;margin:0 0 10px}#ppRoot .section ul,#ppRoot .section ol{margin:6px 0 14px 1.3em;padding:0;max-width:80ch}#ppRoot .section li{margin-bottom:6px;color:#1F2937}#ppRoot .section strong{color:var(--brand-dark)}#ppRoot .note{font-size:.84rem;color:var(--ink-soft)}#ppRoot .callout{border-left:4px solid var(--brand);background:var(--surface);padding:14px 18px;border-radius:0 8px 8px 0;margin:14px 0;max-width:80ch;color:#1F2937}#ppRoot .callout .lbl{font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--brand-dark);font-weight:800;margin-bottom:6px}#ppRoot .callout.warn{border-left-color:var(--red);background:#FEE9E9}#ppRoot .callout.warn .lbl{color:#991B1B}#ppRoot .card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px;margin:16px 0 6px}#ppRoot .card{background:#fff;border:1.5px solid var(--line);border-radius:10px;padding:16px 18px}#ppRoot .card .tag{font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;color:#fff;background:var(--brand);padding:3px 8px;border-radius:4px;display:inline-block;margin-bottom:10px;font-weight:700}#ppRoot .card h4{font-size:.98rem;margin:0 0 6px;color:var(--brand-dark)}#ppRoot .card p{font-size:.88rem;margin:0}#ppRoot .table-scroll{overflow-x:auto;margin:14px 0}#ppRoot table.compare{width:100%;border-collapse:collapse;font-size:.9rem;min-width:460px}#ppRoot table.compare th,#ppRoot table.compare td{border:1px solid var(--line);padding:9px 12px;text-align:left;color:#1F2937;vertical-align:top}#ppRoot table.compare th{background:var(--brand);color:#fff;font-size:.76rem;letter-spacing:.04em;text-transform:uppercase}#ppRoot table.compare tr:nth-child(even) td{background:var(--surface)}#ppRoot .summary-box{background:var(--brand-dark);color:#fff;border-radius:10px;padding:22px 24px}#ppRoot .summary-box h4{color:#C7D2FE;font-size:.85rem;letter-spacing:.08em;text-transform:uppercase;margin:0 0 12px}#ppRoot .section .summary-box ul{margin:0 0 0 1.1em;max-width:none}#ppRoot .section .summary-box li{color:#EEF2FF;margin-bottom:7px}#ppRoot .hier{border:1.5px solid var(--line);border-radius:10px;padding:16px;background:var(--surface);max-width:760px}#ppRoot .hb{background:#fff;border:1.5px solid var(--brand);border-radius:8px;padding:8px 12px;text-align:center;color:#1F2937}#ppRoot .hb b{display:block;color:var(--brand-dark)}#ppRoot .hb span{font-size:.8rem;color:var(--ink-soft)}#ppRoot .hb.h1{background:var(--brand);border-color:var(--brand)}#ppRoot .hb.h1 b,#ppRoot .hb.h1 span{color:#fff}#ppRoot .hrow{display:flex;gap:10px;flex-wrap:wrap}#ppRoot .hrow .hb{flex:1;min-width:120px}#ppRoot .arr{text-align:center;font-size:.8rem;color:var(--ink-soft);margin:6px 0;font-weight:700}#ppRoot .steps-num{counter-reset:step}#ppRoot .section .steps-num li{counter-increment:step;list-style:none;position:relative;padding-left:34px;margin-bottom:12px}#ppRoot .steps-num li:before{content:counter(step);position:absolute;left:0;top:0;font-weight:800;color:#fff;background:var(--brand);width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.8rem}#ppRoot .student-info{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;margin:12px 0}#ppRoot .field label,#ppRoot .fl{display:block;font-size:.74rem;letter-spacing:.05em;text-transform:uppercase;color:var(--brand-dark);margin-bottom:5px;font-weight:800}#ppRoot .field input,#ppRoot .field select,#ppRoot .field textarea{width:100%;padding:10px 12px;border-radius:8px;border:1.5px solid #9CA3AF;background:#fff;color:#111827;font-size:.95rem;font-family:inherit}#ppRoot .field input:focus,#ppRoot .field select:focus{outline:3px solid #A5B4FC;border-color:var(--brand)}#ppRoot .field.chk label{display:flex;gap:8px;align-items:center;text-transform:none;letter-spacing:0;font-size:.9rem;font-weight:600;color:#1F2937;margin:6px 0}#ppRoot .field.chk input{width:auto}#ppRoot .task-panel{border:2px dashed var(--brand);border-radius:12px;padding:20px 22px;background:#FAFAFF;margin:18px 0}#ppRoot .task-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:10px}#ppRoot .task-head .badge{font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;background:var(--amber);color:#1F1300;padding:4px 10px;border-radius:4px;font-weight:800}#ppRoot .task-head h3{font-size:1.15rem;margin:0;color:var(--brand-dark)}#ppRoot .task-head .pts{margin-left:auto;font-weight:800;color:var(--brand-dark);font-size:.9rem}#ppRoot .form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:12px;margin:10px 0}#ppRoot .item-row{border:1px solid var(--line);background:#fff;border-radius:10px;padding:12px 14px;margin:10px 0}#ppRoot .item-row .q{font-weight:700;color:#1F2937;margin:0 0 8px;font-size:.92rem}#ppRoot .item-row .form-grid{margin:0}#ppRoot .primary-btn{font-family:inherit;font-weight:800;font-size:.92rem;padding:12px 24px;border-radius:8px;border:2px solid var(--brand);cursor:pointer;background:var(--brand);color:#fff}#ppRoot .primary-btn:hover{filter:brightness(1.1)}#ppRoot .primary-btn.ghost{background:#fff;color:var(--brand)}#ppRoot .primary-btn.sm{padding:8px 16px;font-size:.85rem}#ppRoot .primary-btn:disabled{opacity:.5;cursor:not-allowed}#ppRoot .task-actions{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0;align-items:center}#ppRoot .msgline{font-size:.88rem;font-weight:700;color:#92400E}#ppRoot .msgline.err{color:var(--red)}#ppRoot .msgline.ok{color:var(--green)}#ppRoot .slide-prev{position:relative;width:100%;max-width:520px;aspect-ratio:16/9;border:2px solid #6B7280;border-radius:6px;overflow:hidden;padding:5% 6%;background:#E5E7EB;color:#6B7280}#ppRoot .slide-prev .t{font-weight:700;line-height:1.15;margin:0 0 3%}#ppRoot .slide-prev ul{margin:0;padding-left:1.1em}#ppRoot .slide-prev li{margin:0 0 1%;color:inherit}#ppRoot .ratio{font-weight:800;font-size:.9rem;margin:8px 0}#ppRoot .ratio.ok{color:var(--green)}#ppRoot .ratio.mid{color:var(--yellow)}#ppRoot .ratio.bad{color:var(--red)}#ppRoot .thumbs{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin:10px 0}#ppRoot .thumb{position:relative;aspect-ratio:16/9;border:1.5px solid #6B7280;border-radius:6px;background:#fff;padding:8px;font-size:.75rem;color:#1F2937}#ppRoot .th-title{font-weight:800}#ppRoot .th-foot{position:absolute;left:8px;bottom:5px;font-size:.62rem;color:#374151;max-width:70%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#ppRoot .th-num{position:absolute;right:8px;bottom:5px;font-size:.66rem;font-weight:800;color:#374151}#ppRoot .th-logo{position:absolute;right:6px;top:6px;width:20px;height:20px;border-radius:50%;background:var(--brand);color:#fff;font-size:.65rem;font-weight:800;display:flex;align-items:center;justify-content:center}#ppRoot .hide{display:none!important}#ppRoot .result-panel{display:none;margin-top:18px;background:#fff;border:2px solid var(--green);border-radius:10px;padding:22px 24px}#ppRoot .result-panel.show{display:block}#ppRoot .score-lbl{font-size:.74rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft);font-weight:800}#ppRoot .score{font-size:2.6rem;font-weight:800;color:var(--green)}#ppRoot .detail{font-size:.9rem;color:#1F2937}#ppRoot table.rubrik{width:100%;border-collapse:collapse;margin-top:8px;font-size:.88rem;min-width:420px}#ppRoot table.rubrik th,#ppRoot table.rubrik td{border:1px solid #C9CCE0;padding:9px 11px;text-align:left;color:#1F2937}#ppRoot table.rubrik th{background:var(--brand-dark);color:#fff;font-size:.76rem;letter-spacing:.04em;text-transform:uppercase}#ppRoot table.rubrik tr:nth-child(even) td{background:#F1F2FA}#ppRoot table.final td.ok{background:#DCFCE7;color:#14532D;font-weight:700}#ppRoot table.final td.mid{background:#FEF3C7;color:#78350F;font-weight:700}#ppRoot table.final td.bad{background:#FEE2E2;color:#7F1D1D;font-weight:700}#ppRoot footer{background:var(--surface);text-align:center;padding:20px 5% 26px;font-size:.74rem;color:var(--ink-soft);border-top:1px solid var(--line);font-weight:700}@media(max-width:700px){#ppRoot header.hero{padding:26px 5% 22px}#ppRoot .section{padding:22px 5%}#ppRoot .task-panel{padding:16px 14px}}#ppRoot .gslides{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-top:10px}#ppRoot .gslides canvas{width:100%;height:auto;border:2px solid #6B7280;border-radius:6px;background:#E5E7EB;display:block}#ppRoot .msg-priv{background:#FEF3C7;border-left:4px solid #B45309;color:#78350F;padding:10px 14px;border-radius:0 8px 8px 0;font-size:.88rem;font-weight:600;margin:8px 0}#ppRoot .item-row .qs{font-weight:400;color:var(--ink-soft)}#ppRoot .field textarea{resize:vertical;min-height:70px}#ppRoot .field input[type=file]{padding:8px;background:#fff}#ppRoot .field input[type=range]{padding:0}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="ppRoot"&gt;
&lt;div class="page-shell"&gt;
&lt;header class="hero"&gt;
  &lt;div class="kop"&gt;Informatika - Fase E - Kelas X SMK&lt;/div&gt;
  &lt;h1 class="title"&gt;Aplikasi Pengolah Presentasi Profesional&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Murid mampu merancang presentasi yang komunikatif menggunakan tema, slide master, multimedia, animasi, transisi, hyperlink, dan fitur kolaborasi.&lt;/p&gt;
  &lt;div class="meta-strip"&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Elemen CP&lt;/div&gt;&lt;div class="v"&gt;Teknologi Informasi dan Komunikasi&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK Kelas X&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;1 Pertemuan - 4 JP&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Model&lt;/div&gt;&lt;div class="v"&gt;Penjelasan, Praktik &amp;amp; Evaluasi Individu&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;nav class="toc" aria-label="Daftar isi"&gt;
  &lt;a href="#pp-tujuan"&gt;Tujuan&lt;/a&gt;&lt;a href="#pp-komunikatif"&gt;Komunikatif&lt;/a&gt;&lt;a href="#pp-tema"&gt;Tema&lt;/a&gt;&lt;a href="#pp-master"&gt;Slide Master&lt;/a&gt;&lt;a href="#pp-multimedia"&gt;Multimedia&lt;/a&gt;&lt;a href="#pp-animasi"&gt;Animasi&lt;/a&gt;&lt;a href="#pp-transisi"&gt;Transisi&lt;/a&gt;&lt;a href="#pp-hyperlink"&gt;Hyperlink&lt;/a&gt;&lt;a href="#pp-kolaborasi"&gt;Kolaborasi&lt;/a&gt;&lt;a href="#pp-alur"&gt;Alur Kerja&lt;/a&gt;&lt;a href="#pp-rangkuman"&gt;Rangkuman&lt;/a&gt;&lt;a href="#pp-catatan"&gt;Buku Catatan&lt;/a&gt;&lt;a href="#pp-praktik" class="go"&gt;Praktik&lt;/a&gt;
&lt;/nav&gt;

&lt;main&gt;
  &lt;section class="section" id="pp-tujuan"&gt;
    &lt;div class="section-tag"&gt;Capaian &amp;amp; Tujuan Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Ke Mana Materi Ini Menuju&lt;/h2&gt;
    &lt;p&gt;Murid mampu &lt;strong&gt;merancang presentasi yang komunikatif&lt;/strong&gt; menggunakan &lt;strong&gt;tema, slide master, multimedia, animasi, transisi, hyperlink,&lt;/strong&gt; dan &lt;strong&gt;fitur kolaborasi&lt;/strong&gt;.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Indikator Ketercapaian&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Memilih tema, warna, dan font yang &lt;strong&gt;terbaca jelas&lt;/strong&gt; (kontras cukup, ukuran huruf memadai).&lt;/li&gt;
      &lt;li&gt;Mengatur tampilan yang berulang (footer, nomor slide, logo, posisi judul) melalui &lt;strong&gt;Slide Master&lt;/strong&gt;, bukan slide demi slide.&lt;/li&gt;
      &lt;li&gt;Memilih dan menyisipkan &lt;strong&gt;multimedia&lt;/strong&gt; yang sesuai tujuan pesan serta aksesibel.&lt;/li&gt;
      &lt;li&gt;Menerapkan &lt;strong&gt;animasi&lt;/strong&gt; yang bermakna dan &lt;strong&gt;transisi&lt;/strong&gt; yang konsisten.&lt;/li&gt;
      &lt;li&gt;Membuat &lt;strong&gt;hyperlink&lt;/strong&gt; dan tombol navigasi untuk presentasi yang tidak harus berurutan.&lt;/li&gt;
      &lt;li&gt;Berkolaborasi: berbagi akses dengan peran yang tepat, memberi komentar, memakai riwayat versi, dan mengekspor hasil.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3 class="sub-title"&gt;Rincian Kegiatan (4 JP x 45 menit)&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Tahap&lt;/th&gt;&lt;th&gt;Kegiatan&lt;/th&gt;&lt;th&gt;Waktu&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pendahuluan&lt;/td&gt;&lt;td&gt;Apersepsi: "Mengapa presentasi yang isinya bagus bisa tetap membosankan?" Penyampaian tujuan.&lt;/td&gt;&lt;td&gt;15 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Eksplorasi Konsep&lt;/td&gt;&lt;td&gt;Membaca materi: presentasi komunikatif, tema, slide master, multimedia, animasi, transisi, hyperlink, kolaborasi.&lt;/td&gt;&lt;td&gt;55 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Demonstrasi&lt;/td&gt;&lt;td&gt;Guru mendemonstrasikan Slide Master, Animation Pane, dan tautan navigasi pada aplikasi presentasi.&lt;/td&gt;&lt;td&gt;25 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Praktik Individu&lt;/td&gt;&lt;td&gt;Murid mengerjakan Tugas A-F pada Studio Presentasi di halaman ini.&lt;/td&gt;&lt;td&gt;45 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Evaluasi &amp;amp; Pengumpulan&lt;/td&gt;&lt;td&gt;Sistem menilai otomatis, skor terkirim ke guru, murid mengunduh bukti PDF 1 lembar.&lt;/td&gt;&lt;td&gt;20 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Refleksi dan penguatan konsep.&lt;/td&gt;&lt;td&gt;10 menit&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Keterkaitan dengan 8 Profil Lulusan&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Dimensi Profil Lulusan&lt;/th&gt;&lt;th&gt;Wujud dalam pembelajaran ini&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Komunikasi&lt;/td&gt;&lt;td&gt;Menyampaikan gagasan secara runtut, jelas, dan menarik melalui slide.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kolaborasi&lt;/td&gt;&lt;td&gt;Menyusun dan menelaah presentasi bersama dengan peran akses yang tepat.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kreativitas&lt;/td&gt;&lt;td&gt;Memilih tema, multimedia, dan animasi yang orisinal dan bermakna.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penalaran Kritis&lt;/td&gt;&lt;td&gt;Menilai kapan sebuah efek membantu pesan dan kapan justru mengganggu.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kemandirian&lt;/td&gt;&lt;td&gt;Mengerjakan tugas praktik pribadi dan memperbaikinya berdasarkan hasil penilaian.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kewargaan&lt;/td&gt;&lt;td&gt;Menghargai hak cipta dan mencantumkan sumber gambar, audio, dan video.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-komunikatif"&gt;
    &lt;div class="section-tag"&gt;Konsep Dasar&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Presentasi yang Komunikatif&lt;/h2&gt;
    &lt;p&gt;Presentasi bukan dokumen yang dipindahkan ke layar. Slide adalah &lt;strong&gt;alat bantu visual&lt;/strong&gt;; pesan utama tetap disampaikan oleh penyaji. Presentasi disebut &lt;strong&gt;komunikatif&lt;/strong&gt; bila audiens dapat menangkap pesan intinya dengan cepat, mengingatnya, dan terdorong bertindak.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Struktur yang Dianjurkan&lt;/h3&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Pembuka&lt;/span&gt;&lt;h4&gt;Judul, tujuan, agenda&lt;/h4&gt;&lt;p&gt;Perkenalkan topik, jelaskan manfaat bagi audiens, tampilkan agenda singkat (3-5 butir).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Isi&lt;/span&gt;&lt;h4&gt;Satu ide per slide&lt;/h4&gt;&lt;p&gt;Setiap slide memuat satu pesan. Judul slide berupa kalimat pesan, bukan sekadar topik ("Penjualan naik 20%", bukan "Data Penjualan").&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Penutup&lt;/span&gt;&lt;h4&gt;Simpulan &amp;amp; ajakan&lt;/h4&gt;&lt;p&gt;Ringkas 3 poin kunci, sebutkan langkah lanjut, sertakan sumber dan kontak.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Prinsip Desain Slide&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Prinsip&lt;/th&gt;&lt;th&gt;Aturan praktis&lt;/th&gt;&lt;th&gt;Kesalahan umum&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kontras&lt;/td&gt;&lt;td&gt;Rasio kontras teks terhadap latar minimal &lt;strong&gt;4,5 : 1&lt;/strong&gt;.&lt;/td&gt;&lt;td&gt;Teks abu muda di latar putih; teks kuning di latar putih.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ukuran huruf&lt;/td&gt;&lt;td&gt;Judul &lt;strong&gt;&amp;ge; 32 pt&lt;/strong&gt;, isi &lt;strong&gt;18-28 pt&lt;/strong&gt;. Jika butuh huruf lebih kecil dari 18 pt, isinya terlalu banyak.&lt;/td&gt;&lt;td&gt;Satu slide berisi paragraf panjang berhuruf 12 pt.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konsistensi&lt;/td&gt;&lt;td&gt;Maksimal &lt;strong&gt;2 jenis font&lt;/strong&gt; dan satu palet warna di seluruh slide.&lt;/td&gt;&lt;td&gt;Setiap slide berbeda font dan warna.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kepadatan&lt;/td&gt;&lt;td&gt;Maksimal sekitar 6 baris per slide; gunakan kata kunci, bukan kalimat penuh.&lt;/td&gt;&lt;td&gt;Membaca slide kata demi kata.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ruang kosong&lt;/td&gt;&lt;td&gt;Sisakan margin; ruang kosong membantu mata fokus.&lt;/td&gt;&lt;td&gt;Elemen menempel ke tepi slide.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Hierarki&lt;/td&gt;&lt;td&gt;Judul &amp;gt; subjudul &amp;gt; isi dibedakan lewat ukuran, tebal, dan warna.&lt;/td&gt;&lt;td&gt;Semua teks berukuran sama.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;div class="callout"&gt;&lt;div class="lbl"&gt;Uji 3 Detik&lt;/div&gt;Tampilkan sebuah slide selama 3 detik lalu sembunyikan. Jika audiens tidak dapat menyebutkan pesan utamanya, slide itu perlu disederhanakan.&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-tema"&gt;
    &lt;div class="section-tag"&gt;Fitur 1&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Tema (Theme)&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Tema&lt;/strong&gt; adalah satu paket rancangan yang berisi &lt;strong&gt;skema warna, pasangan font (judul &amp;amp; isi), efek, dan tata letak&lt;/strong&gt;. Dengan satu klik, seluruh slide berganti tampilan dengan konsisten.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Komponen Tema&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Warna tema&lt;/strong&gt;: palet latar, teks, dan aksen. Gunakan aturan 60-30-10: 60% warna dominan (latar), 30% warna pendukung, 10% warna aksen untuk hal penting.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Font tema&lt;/strong&gt;: satu font untuk judul (heading) dan satu untuk isi (body). Pilih font yang bersih dan terbaca (misalnya Calibri, Arial, Georgia). Hindari font dekoratif seperti Comic Sans atau Impact untuk presentasi resmi.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Efek &amp;amp; latar&lt;/strong&gt;: gaya bayangan dan latar. Gunakan seperlunya.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Varian&lt;/strong&gt;: variasi warna dari satu tema yang sama.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3 class="sub-title"&gt;Memilih Tema yang Tepat&lt;/h3&gt;
    &lt;ol&gt;
      &lt;li&gt;Tentukan &lt;strong&gt;konteks&lt;/strong&gt;: ruangan gelap dengan proyektor cocok dengan latar gelap dan teks terang; ruangan terang atau dicetak cocok dengan latar terang dan teks gelap.&lt;/li&gt;
      &lt;li&gt;Pastikan &lt;strong&gt;kontras teks-latar&lt;/strong&gt; memenuhi 4,5 : 1 (standar aksesibilitas WCAG tingkat AA untuk teks biasa).&lt;/li&gt;
      &lt;li&gt;Sesuaikan dengan &lt;strong&gt;audiens dan topik&lt;/strong&gt; (formal untuk laporan, lebih ekspresif untuk promosi).&lt;/li&gt;
      &lt;li&gt;Uji dengan &lt;strong&gt;melihat dari jarak&lt;/strong&gt;: kecilkan tampilan layar hingga 50%; jika masih terbaca, aman.&lt;/li&gt;
    &lt;/ol&gt;
    &lt;div class="callout warn"&gt;&lt;div class="lbl"&gt;Perhatian&lt;/div&gt;Warna tidak boleh menjadi satu-satunya pembawa makna (misalnya hanya "merah = salah, hijau = benar"). Tambahkan tanda lain seperti ikon atau teks agar murid dengan buta warna tetap memahami.&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-master"&gt;
    &lt;div class="section-tag"&gt;Fitur 2&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Slide Master&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Slide Master&lt;/strong&gt; adalah "cetakan induk" yang mengatur tampilan semua slide: font, warna, posisi judul, footer, nomor slide, dan logo. Mengubah master berarti mengubah seluruh slide sekaligus, sehingga presentasi tetap &lt;strong&gt;konsisten&lt;/strong&gt; dan pengerjaan lebih &lt;strong&gt;efisien&lt;/strong&gt;.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Hierarki: Master - Layout - Slide&lt;/h3&gt;
    &lt;div class="hier"&gt;
      &lt;div class="hb h1"&gt;&lt;b&gt;Slide Master&lt;/b&gt;&lt;span&gt;Aturan paling atas: font, warna, logo, nomor slide untuk semua layout&lt;/span&gt;&lt;/div&gt;
      &lt;div class="arr"&gt;&amp;darr; diwariskan ke&lt;/div&gt;
      &lt;div class="hrow"&gt;
        &lt;div class="hb h2"&gt;&lt;b&gt;Layout Judul&lt;/b&gt;&lt;span&gt;Judul + subjudul&lt;/span&gt;&lt;/div&gt;
        &lt;div class="hb h2"&gt;&lt;b&gt;Layout Judul &amp;amp; Isi&lt;/b&gt;&lt;span&gt;Judul + daftar poin&lt;/span&gt;&lt;/div&gt;
        &lt;div class="hb h2"&gt;&lt;b&gt;Layout Dua Konten&lt;/b&gt;&lt;span&gt;Judul + 2 kolom&lt;/span&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="arr"&gt;&amp;darr; dipakai oleh&lt;/div&gt;
      &lt;div class="hrow"&gt;
        &lt;div class="hb h3"&gt;&lt;b&gt;Slide 1&lt;/b&gt;&lt;/div&gt;&lt;div class="hb h3"&gt;&lt;b&gt;Slide 2&lt;/b&gt;&lt;/div&gt;&lt;div class="hb h3"&gt;&lt;b&gt;Slide 3&lt;/b&gt;&lt;/div&gt;&lt;div class="hb h3"&gt;&lt;b&gt;Slide 4&lt;/b&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Apa yang Diatur di Master&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Font &amp;amp; ukuran judul/isi&lt;/strong&gt; serta warna teks.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Placeholder footer, tanggal, dan nomor slide&lt;/strong&gt; (aktifkan lewat Insert &amp;gt; Header &amp;amp; Footer).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Logo sekolah&lt;/strong&gt; atau elemen dekoratif yang muncul di semua slide.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Posisi judul&lt;/strong&gt; dan area isi pada setiap layout.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Bullet&lt;/strong&gt; dan jarak antarbaris.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3 class="sub-title"&gt;Langkah Umum&lt;/h3&gt;
    &lt;ol&gt;
      &lt;li&gt;Buka tampilan master: &lt;strong&gt;View &amp;gt; Slide Master&lt;/strong&gt; (PowerPoint) atau &lt;strong&gt;Slide &amp;gt; Edit theme&lt;/strong&gt; (Google Slides).&lt;/li&gt;
      &lt;li&gt;Pilih slide paling atas (master) untuk perubahan yang berlaku di semua layout, atau pilih satu layout untuk perubahan khusus.&lt;/li&gt;
      &lt;li&gt;Ubah font, warna, logo, dan posisi placeholder.&lt;/li&gt;
      &lt;li&gt;Tutup tampilan master (&lt;strong&gt;Close Master View&lt;/strong&gt;) dan periksa hasilnya pada slide.&lt;/li&gt;
      &lt;li&gt;Simpan sebagai &lt;strong&gt;template&lt;/strong&gt; (.potx) bila ingin dipakai lagi.&lt;/li&gt;
    &lt;/ol&gt;
    &lt;div class="callout warn"&gt;&lt;div class="lbl"&gt;Kesalahan Umum&lt;/div&gt;Memindahkan judul atau menempel logo satu per satu pada tiap slide. Selain lama, hasilnya mudah tidak sejajar. Jika perubahan berlaku untuk banyak slide, lakukan di &lt;strong&gt;Slide Master&lt;/strong&gt;.&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-multimedia"&gt;
    &lt;div class="section-tag"&gt;Fitur 3&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Multimedia&lt;/h2&gt;
    &lt;p&gt;Multimedia adalah gabungan teks, gambar, grafik, audio, dan video. Pilih media berdasarkan &lt;strong&gt;pesan yang ingin disampaikan&lt;/strong&gt;, bukan sekadar untuk menghias.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Media&lt;/th&gt;&lt;th&gt;Paling tepat untuk&lt;/th&gt;&lt;th&gt;Tips&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Gambar / foto&lt;/td&gt;&lt;td&gt;Menunjukkan objek nyata, suasana, atau bukti&lt;/td&gt;&lt;td&gt;Resolusi cukup tetapi tidak berlebihan; potong (crop) bagian yang tidak perlu; beri &lt;strong&gt;teks alternatif&lt;/strong&gt;.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Diagram / SmartArt&lt;/td&gt;&lt;td&gt;Alur proses, hierarki, siklus, perbandingan&lt;/td&gt;&lt;td&gt;Maksimal 5-7 elemen; satu gagasan per diagram.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Grafik (chart)&lt;/td&gt;&lt;td&gt;Tren dan perbandingan angka&lt;/td&gt;&lt;td&gt;Pilih jenis yang cocok: garis untuk tren, batang untuk perbandingan, lingkaran untuk bagian dari keseluruhan (sedikit kategori).&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Video&lt;/td&gt;&lt;td&gt;Demonstrasi, wawancara, proses bergerak&lt;/td&gt;&lt;td&gt;Durasi singkat (di bawah 2 menit); atur mulai &lt;em&gt;saat diklik&lt;/em&gt; agar penyaji memegang kendali; format MP4 (H.264).&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekaman layar (screen recording)&lt;/td&gt;&lt;td&gt;Langkah-langkah memakai aplikasi di komputer&lt;/td&gt;&lt;td&gt;Rekam area yang perlu saja; beri narasi; sisipkan sebagai video.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Audio&lt;/td&gt;&lt;td&gt;Narasi atau musik latar&lt;/td&gt;&lt;td&gt;Volume pelan; hindari musik yang bersaing dengan suara penyaji; format MP3 atau M4A.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Aksesibilitas, Berkas, dan Etika&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Teks alternatif (alt text)&lt;/strong&gt;: deskripsi singkat gambar agar pembaca layar dapat membacakannya. Contoh: "Murid mengerjakan tugas di laboratorium komputer". Di PowerPoint: klik kanan gambar &amp;gt; View Alt Text.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Sematkan (embed) atau tautkan (link)?&lt;/strong&gt; Media yang disematkan ikut tersimpan di dalam berkas sehingga tetap bisa diputar saat berkas dipindah ke komputer lain. Media yang hanya ditautkan akan hilang jika berkas aslinya tidak ikut dibawa.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Ukuran berkas&lt;/strong&gt;: kompres gambar dan media (PowerPoint: File &amp;gt; Info &amp;gt; Compress Media/Pictures) agar mudah dikirim dan tidak lambat.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Hak cipta&lt;/strong&gt;: gunakan gambar, musik, dan video milik sendiri atau berlisensi terbuka (misalnya Creative Commons), dan &lt;strong&gt;cantumkan sumbernya&lt;/strong&gt; pada slide referensi.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Cadangan&lt;/strong&gt;: siapkan rencana jika video gagal diputar (misalnya tangkapan layar atau berkas lokal).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-animasi"&gt;
    &lt;div class="section-tag"&gt;Fitur 4&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Animasi&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Animasi&lt;/strong&gt; adalah gerakan yang diterapkan pada &lt;strong&gt;objek di dalam slide&lt;/strong&gt; (teks, gambar, bentuk, grafik). Tujuannya &lt;strong&gt;mengarahkan perhatian dan mengatur alur penjelasan&lt;/strong&gt;, bukan pamer efek.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Empat Kategori Animasi&lt;/h3&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Entrance&lt;/span&gt;&lt;h4&gt;Masuk&lt;/h4&gt;&lt;p&gt;Objek muncul ke layar. Contoh: Appear, Fade, Wipe, Fly In. Dipakai untuk menampilkan poin satu per satu.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Emphasis&lt;/span&gt;&lt;h4&gt;Penekanan&lt;/h4&gt;&lt;p&gt;Objek yang sudah tampil diberi sorotan. Contoh: Pulse, Grow/Shrink. Dipakai untuk menegaskan angka atau kata kunci.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Exit&lt;/span&gt;&lt;h4&gt;Keluar&lt;/h4&gt;&lt;p&gt;Objek meninggalkan layar. Contoh: Disappear, Fade, Fly Out. Dipakai agar slide tidak penuh.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Motion Path&lt;/span&gt;&lt;h4&gt;Jalur Gerak&lt;/h4&gt;&lt;p&gt;Objek bergerak mengikuti lintasan. Dipakai untuk menunjukkan perpindahan, misalnya aliran data.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Pemicu (Start) Animasi&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Pemicu&lt;/th&gt;&lt;th&gt;Arti&lt;/th&gt;&lt;th&gt;Cocok untuk&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;On Click (Saat diklik)&lt;/td&gt;&lt;td&gt;Efek berjalan ketika penyaji mengklik&lt;/td&gt;&lt;td&gt;Poin yang dijelaskan satu per satu&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;With Previous (Bersamaan dengan sebelumnya)&lt;/td&gt;&lt;td&gt;Efek berjalan bersamaan dengan efek sebelumnya&lt;/td&gt;&lt;td&gt;Judul dan gambar yang muncul bersama&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;After Previous (Setelah sebelumnya)&lt;/td&gt;&lt;td&gt;Efek berjalan otomatis setelah efek sebelumnya selesai&lt;/td&gt;&lt;td&gt;Judul yang muncul otomatis begitu slide tampil&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Mengelola Animasi&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Buka &lt;strong&gt;Animation Pane&lt;/strong&gt; (Animations &amp;gt; Animation Pane) untuk melihat, mengurutkan, dan mengatur durasi setiap efek.&lt;/li&gt;
      &lt;li&gt;Untuk daftar poin, pilih &lt;strong&gt;Effect Options &amp;gt; By Paragraph&lt;/strong&gt; agar poin muncul satu per satu.&lt;/li&gt;
      &lt;li&gt;Atur &lt;strong&gt;Duration&lt;/strong&gt; (umumnya 0,3-0,7 detik) dan &lt;strong&gt;Delay&lt;/strong&gt; seperlunya.&lt;/li&gt;
      &lt;li&gt;Gunakan &lt;strong&gt;Animation Painter&lt;/strong&gt; untuk menyalin pengaturan animasi ke objek lain.&lt;/li&gt;
      &lt;li&gt;Fitur &lt;strong&gt;Morph&lt;/strong&gt; (PowerPoint 365) menganimasikan perubahan objek antar dua slide secara halus.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;div class="callout"&gt;&lt;div class="lbl"&gt;Aturan Animasi Bermakna&lt;/div&gt;Tanyakan: "Apakah efek ini membantu audiens memahami urutan atau menyorot hal penting?" Jika tidak, hapus. Cukup 1-2 jenis efek halus yang dipakai berulang.&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-transisi"&gt;
    &lt;div class="section-tag"&gt;Fitur 5&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Transisi&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Transisi&lt;/strong&gt; adalah efek perpindahan &lt;strong&gt;dari satu slide ke slide berikutnya&lt;/strong&gt;. Transisi bekerja pada tingkat slide, sedangkan animasi bekerja pada tingkat objek di dalam slide.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Transisi&lt;/th&gt;&lt;th&gt;Animasi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bekerja pada&lt;/td&gt;&lt;td&gt;Seluruh slide&lt;/td&gt;&lt;td&gt;Objek tertentu di dalam slide&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Terjadi saat&lt;/td&gt;&lt;td&gt;Berpindah slide&lt;/td&gt;&lt;td&gt;Selama slide ditampilkan&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tujuan&lt;/td&gt;&lt;td&gt;Penghubung antar bagian&lt;/td&gt;&lt;td&gt;Mengarahkan perhatian dalam satu slide&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Menu (PowerPoint)&lt;/td&gt;&lt;td&gt;Tab Transitions&lt;/td&gt;&lt;td&gt;Tab Animations&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Pengaturan Transisi&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Jenis efek&lt;/strong&gt;: pilih yang halus (Fade, Push, Wipe, Morph).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Duration&lt;/strong&gt;: sekitar 0,5-1,5 detik. Lebih lama terasa menunggu.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Apply To All&lt;/strong&gt;: terapkan satu transisi untuk semua slide agar konsisten.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Advance Slide&lt;/strong&gt;: &lt;em&gt;On Mouse Click&lt;/em&gt; (penyaji mengendalikan) atau &lt;em&gt;After&lt;/em&gt; (otomatis setelah beberapa detik, untuk tayangan kiosk).&lt;/li&gt;
    &lt;/ul&gt;
    &lt;div class="callout warn"&gt;&lt;div class="lbl"&gt;Hindari&lt;/div&gt;Efek berlebihan (Cube, Vortex, Honeycomb) atau transisi acak yang berbeda di setiap slide. Efek itu mengalihkan perhatian dari isi dan terkesan kurang profesional.&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-hyperlink"&gt;
    &lt;div class="section-tag"&gt;Fitur 6&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Hyperlink dan Navigasi&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Hyperlink&lt;/strong&gt; adalah tautan yang membawa audiens ke tujuan tertentu saat diklik. Dengan hyperlink, presentasi dapat dibuat &lt;strong&gt;non-linear&lt;/strong&gt;: penyaji melompat ke bagian yang diminta tanpa menekan "berikutnya" berkali-kali.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Jenis tautan&lt;/th&gt;&lt;th&gt;Tujuan&lt;/th&gt;&lt;th&gt;Contoh pemakaian&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Slide dalam dokumen&lt;/td&gt;&lt;td&gt;Slide lain di presentasi yang sama&lt;/td&gt;&lt;td&gt;Butir agenda menuju slide pembahasan; tombol "Kembali ke Agenda"&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Halaman web&lt;/td&gt;&lt;td&gt;Alamat URL (diawali https://)&lt;/td&gt;&lt;td&gt;Sumber referensi, video, formulir&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Alamat email&lt;/td&gt;&lt;td&gt;Membuka aplikasi email (mailto:)&lt;/td&gt;&lt;td&gt;Slide "Hubungi Kami"&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Berkas lain&lt;/td&gt;&lt;td&gt;Dokumen atau berkas di komputer/drive&lt;/td&gt;&lt;td&gt;Lampiran data (pastikan berkas ikut dibawa)&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Cara Membuat&lt;/h3&gt;
    &lt;ol&gt;
      &lt;li&gt;Pilih teks, gambar, atau bentuk yang akan dijadikan tautan.&lt;/li&gt;
      &lt;li&gt;Tekan &lt;strong&gt;Ctrl + K&lt;/strong&gt; (Insert &amp;gt; Link) lalu pilih tujuan: &lt;em&gt;Place in This Document&lt;/em&gt; untuk slide, atau ketik URL/alamat email.&lt;/li&gt;
      &lt;li&gt;Untuk tombol navigasi, gunakan &lt;strong&gt;Insert &amp;gt; Shapes &amp;gt; Action Buttons&lt;/strong&gt; (PowerPoint) atau bentuk biasa yang ditautkan ke slide tujuan.&lt;/li&gt;
      &lt;li&gt;Uji tautan dalam mode &lt;strong&gt;Slide Show&lt;/strong&gt;, bukan hanya di mode edit.&lt;/li&gt;
    &lt;/ol&gt;
    &lt;div class="callout"&gt;&lt;div class="lbl"&gt;Praktik Baik&lt;/div&gt;
      &lt;ul style="margin-bottom:0"&gt;
        &lt;li&gt;Pakai teks tautan yang &lt;strong&gt;deskriptif&lt;/strong&gt; ("Panduan resmi Kemendikdasmen"), jangan "klik di sini".&lt;/li&gt;
        &lt;li&gt;Setiap slide tujuan sebaiknya punya tombol &lt;strong&gt;kembali&lt;/strong&gt; ke agenda.&lt;/li&gt;
        &lt;li&gt;Jangan menautkan ke berkas lokal yang tidak ikut dibawa ke komputer lain.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-kolaborasi"&gt;
    &lt;div class="section-tag"&gt;Fitur 7&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fitur Kolaborasi&lt;/h2&gt;
    &lt;p&gt;Presentasi kelompok dikerjakan bersama pada &lt;strong&gt;satu berkas daring&lt;/strong&gt; (misalnya PowerPoint di OneDrive atau Google Slides), sehingga tidak ada lagi berkas "final_revisi_baru2.pptx" yang tercecer.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Peran Akses&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Peran&lt;/th&gt;&lt;th&gt;Hak&lt;/th&gt;&lt;th&gt;Diberikan kepada&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Editor (Dapat mengedit)&lt;/td&gt;&lt;td&gt;Mengubah isi, menambah dan menghapus slide&lt;/td&gt;&lt;td&gt;Anggota kelompok yang ikut menyusun&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Komentator (Dapat berkomentar)&lt;/td&gt;&lt;td&gt;Memberi komentar dan saran tanpa mengubah isi&lt;/td&gt;&lt;td&gt;Guru atau teman penelaah&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pelihat (Hanya lihat)&lt;/td&gt;&lt;td&gt;Membaca dan menayangkan saja&lt;/td&gt;&lt;td&gt;Audiens umum, kelas lain&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;p&gt;Prinsip &lt;strong&gt;hak akses minimum&lt;/strong&gt;: beri akses sesuai kebutuhan saja. Hindari menyetel tautan "siapa saja dapat mengedit".&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Alat Kolaborasi&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Co-authoring&lt;/strong&gt;: beberapa orang mengedit berkas yang sama secara bersamaan; kursor tiap orang tampak berwarna berbeda.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Komentar &amp;amp; @mention&lt;/strong&gt;: tulis catatan pada objek tertentu dan sebut nama teman agar ia mendapat pemberitahuan. Tandai komentar &lt;em&gt;resolved&lt;/em&gt; bila sudah ditindaklanjuti.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Riwayat versi (Version History)&lt;/strong&gt;: melihat siapa mengubah apa dan &lt;strong&gt;memulihkan&lt;/strong&gt; versi sebelumnya bila terjadi kesalahan.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Presenter View &amp;amp; catatan penyaji&lt;/strong&gt;: penyaji melihat catatan dan slide berikutnya, audiens hanya melihat slide.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Ekspor&lt;/strong&gt;: simpan sebagai &lt;strong&gt;PDF&lt;/strong&gt; untuk dibagikan kepada orang yang tidak memiliki aplikasi presentasi atau untuk dicetak.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3 class="sub-title"&gt;Etika Kerja Kelompok&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Bagi tugas per slide/bagian dengan jelas di awal.&lt;/li&gt;
      &lt;li&gt;Ikuti Slide Master yang sudah disepakati; jangan mengubah tema tanpa diskusi.&lt;/li&gt;
      &lt;li&gt;Tulis komentar yang sopan, spesifik, dan membangun.&lt;/li&gt;
      &lt;li&gt;Jangan menghapus pekerjaan teman; gunakan komentar atau riwayat versi.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-peta"&gt;
    &lt;div class="section-tag"&gt;Peta Menu&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Di Mana Menemukannya?&lt;/h2&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Fitur&lt;/th&gt;&lt;th&gt;Microsoft PowerPoint&lt;/th&gt;&lt;th&gt;Google Slides&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tema&lt;/td&gt;&lt;td&gt;Design &amp;gt; Themes / Variants&lt;/td&gt;&lt;td&gt;Slide &amp;gt; Change theme&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Slide Master&lt;/td&gt;&lt;td&gt;View &amp;gt; Slide Master&lt;/td&gt;&lt;td&gt;Slide &amp;gt; Edit theme&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Gambar / Video / Audio&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Pictures / Video / Audio&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Image / Video / Audio&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekaman layar&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Screen Recording&lt;/td&gt;&lt;td&gt;Tidak bawaan; rekam dengan aplikasi lain lalu sisipkan videonya&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Animasi&lt;/td&gt;&lt;td&gt;Animations &amp;gt; Add Animation, Animation Pane&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Animation (panel Motion)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Transisi&lt;/td&gt;&lt;td&gt;Transitions &amp;gt; pilih efek, Duration, Apply To All&lt;/td&gt;&lt;td&gt;Slide &amp;gt; Transition (panel Motion)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Hyperlink&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Link (Ctrl + K); Insert &amp;gt; Action&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Link (Ctrl + K)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bagikan &amp;amp; komentar&lt;/td&gt;&lt;td&gt;Share; Review &amp;gt; New Comment&lt;/td&gt;&lt;td&gt;Share; Insert &amp;gt; Comment&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Riwayat versi&lt;/td&gt;&lt;td&gt;File &amp;gt; Info &amp;gt; Version History&lt;/td&gt;&lt;td&gt;File &amp;gt; Version history&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Presenter View&lt;/td&gt;&lt;td&gt;Slide Show &amp;gt; Presenter View&lt;/td&gt;&lt;td&gt;Present &amp;gt; Presenter view&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ekspor PDF&lt;/td&gt;&lt;td&gt;File &amp;gt; Export &amp;gt; Create PDF/XPS&lt;/td&gt;&lt;td&gt;File &amp;gt; Download &amp;gt; PDF document&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
    &lt;p class="note"&gt;Nama menu dapat sedikit berbeda antarversi. Gunakan kotak pencarian "Tell me / Search the menus" bila menu tidak ditemukan.&lt;/p&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-alur"&gt;
    &lt;div class="section-tag"&gt;Langkah Kerja&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Alur Merancang Presentasi&lt;/h2&gt;
    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;strong&gt;Tentukan tujuan dan audiens.&lt;/strong&gt; Apa satu pesan utama dan apa yang diharapkan dilakukan audiens setelahnya?&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Susun storyboard.&lt;/strong&gt; Tulis judul-pesan tiap slide di kertas atau outline sebelum membuka aplikasi.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Pilih tema dan atur Slide Master.&lt;/strong&gt; Warna, font, footer, nomor slide, dan logo diatur sekali di master.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Isi konten.&lt;/strong&gt; Satu ide per slide, kata kunci, ukuran huruf terbaca.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Tambahkan multimedia&lt;/strong&gt; yang mendukung pesan, lengkap dengan teks alternatif dan sumber.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Atur animasi, transisi, dan hyperlink&lt;/strong&gt; secukupnya dan konsisten.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Bagikan untuk telaah.&lt;/strong&gt; Minta komentar teman/guru, perbaiki, lalu latihan dengan Presenter View dan siapkan PDF cadangan.&lt;/li&gt;
    &lt;/ol&gt;
    &lt;h3 class="sub-title"&gt;Daftar Periksa Akhir&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Periksa&lt;/th&gt;&lt;th&gt;Standar&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kontras teks-latar&lt;/td&gt;&lt;td&gt;Minimal 4,5 : 1&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ukuran huruf&lt;/td&gt;&lt;td&gt;Judul 32 pt atau lebih; isi 18 pt atau lebih&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Jumlah font&lt;/td&gt;&lt;td&gt;Maksimal 2 jenis&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Footer, nomor slide, logo&lt;/td&gt;&lt;td&gt;Diatur melalui Slide Master&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Multimedia&lt;/td&gt;&lt;td&gt;Relevan, beralt text, bersumber jelas, disematkan&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Animasi &amp;amp; transisi&lt;/td&gt;&lt;td&gt;Bermakna, halus, konsisten, 0,5-1,5 detik&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Hyperlink&lt;/td&gt;&lt;td&gt;Sudah diuji di mode Slide Show; ada tombol kembali&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kolaborasi&lt;/td&gt;&lt;td&gt;Peran akses tepat; komentar ditindaklanjuti; PDF tersedia&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-rangkuman"&gt;
    &lt;div class="section-tag"&gt;Penguatan Konsep&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rangkuman Materi&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;h4&gt;Poin-Poin Kunci&lt;/h4&gt;
      &lt;ul&gt;
        &lt;li&gt;Presentasi komunikatif: satu ide per slide, teks ringkas, pesan utama mudah ditangkap dalam beberapa detik.&lt;/li&gt;
        &lt;li&gt;Tema mengatur warna, font, dan efek; pilih kontras minimal 4,5 : 1, judul 32 pt atau lebih, isi 18-28 pt, maksimal 2 font.&lt;/li&gt;
        &lt;li&gt;Slide Master mengatur elemen berulang (footer, nomor slide, logo, posisi judul) untuk semua slide sekaligus.&lt;/li&gt;
        &lt;li&gt;Multimedia dipilih sesuai pesan: screen recording untuk langkah aplikasi, grafik untuk tren angka, diagram untuk alur; beri alt text, sematkan media, cantumkan sumber.&lt;/li&gt;
        &lt;li&gt;Animasi bekerja pada objek (Masuk, Penekanan, Keluar, Jalur Gerak) dengan pemicu On Click, With Previous, atau After Previous.&lt;/li&gt;
        &lt;li&gt;Transisi bekerja antar slide; pilih yang halus, 0,5-1,5 detik, dan terapkan ke semua slide.&lt;/li&gt;
        &lt;li&gt;Hyperlink (slide, web, email, berkas) dan tombol kembali membuat presentasi non-linear dan mudah dinavigasi.&lt;/li&gt;
        &lt;li&gt;Kolaborasi: peran Editor, Komentator, Pelihat; komentar; riwayat versi untuk memulihkan; ekspor PDF untuk berbagi.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-catatan"&gt;
    &lt;div class="section-tag"&gt;Literasi &amp;amp; Refleksi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Buku Catatan: Rangkumanku&lt;/h2&gt;
    &lt;p&gt;Tulis rangkuman dengan &lt;strong&gt;kata-katamu sendiri&lt;/strong&gt; pada tiap pokok bahasan. Menulis ulang membantu kamu mengingat dan memahami. Catatan tersimpan otomatis di perangkat ini (tidak dikirim ke guru), dapat diunduh sebagai PDF atau teks, dan boleh juga kamu salin ke buku tulis.&lt;/p&gt;
    &lt;div id="ppNotes"&gt;&lt;/div&gt;
    &lt;div class="task-actions"&gt;
      &lt;button type="button" class="primary-btn" data-act="notePdf"&gt;Unduh Catatan (PDF)&lt;/button&gt;
      &lt;button type="button" class="primary-btn ghost" data-act="noteTxt"&gt;Unduh Catatan (.txt)&lt;/button&gt;
      &lt;button type="button" class="primary-btn ghost" data-act="noteClr"&gt;Hapus Semua Catatan&lt;/button&gt;
      &lt;span id="ppNoteMsg" class="msgline" role="status" aria-live="polite"&gt;&lt;/span&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pp-praktik"&gt;
    &lt;div class="section-tag"&gt;Praktik &amp;amp; Evaluasi Individu&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Studio Presentasi: Tugas Pribadi&lt;/h2&gt;
    &lt;p&gt;Kerjakan &lt;strong&gt;mandiri&lt;/strong&gt; tujuh tugas praktik (A-G) berikut. Tugas A-F merancang bagian presentasi "Literasi Digital di Sekolah"; Tugas G meminta kamu membuat &lt;strong&gt;presentasi pengenalan diri&lt;/strong&gt; lengkap dengan foto dan gambar latar. Sistem menilai otomatis dengan skor maksimal &lt;strong&gt;100&lt;/strong&gt;, mengirim skor ke guru, dan menyediakan bukti &lt;strong&gt;PDF 1 lembar&lt;/strong&gt;.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Isi Data Diri&lt;/h3&gt;
    &lt;div class="student-info"&gt;
      &lt;div class="field"&gt;&lt;label for="ppAbsen"&gt;No. Absen&lt;/label&gt;&lt;input type="number" id="ppAbsen" data-in="g" min="1" max="60" inputmode="numeric" placeholder="Contoh: 12"&gt;&lt;/div&gt;
      &lt;div class="field"&gt;&lt;label for="ppNama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="ppNama" data-in="g" maxlength="80" placeholder="Ketik nama lengkap"&gt;&lt;/div&gt;
      &lt;div class="field"&gt;&lt;label for="ppKelas"&gt;Kelas&lt;/label&gt;
        &lt;select id="ppKelas" data-in="g"&gt;&lt;option value=""&gt;-- Pilih Kelas --&lt;/option&gt;&lt;option value="X TB1"&gt;X TB1&lt;/option&gt;&lt;option value="X TB2"&gt;X TB2&lt;/option&gt;&lt;option value="X TO1"&gt;X TO1&lt;/option&gt;&lt;option value="X TO2"&gt;X TO2&lt;/option&gt;&lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;&lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div id="ppTasks"&gt;&lt;/div&gt;
    &lt;div class="task-actions"&gt;
      &lt;button type="button" class="primary-btn" data-act="grade"&gt;Hitung Nilai &amp;amp; Kirim ke Guru&lt;/button&gt;
      &lt;button type="button" class="primary-btn ghost" data-act="reset"&gt;Ulangi Semua Jawaban&lt;/button&gt;
      &lt;span id="ppMsg" class="msgline" role="status" aria-live="polite"&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class="result-panel" id="ppResult"&gt;
      &lt;div class="score-lbl"&gt;Nilai Praktik (Otomatis)&lt;/div&gt;
      &lt;div class="score" id="ppScore"&gt;0&lt;/div&gt;
      &lt;p id="ppDetail" class="detail"&gt;&lt;/p&gt;
      &lt;h3 class="sub-title"&gt;Rincian Penilaian&lt;/h3&gt;
      &lt;p class="note"&gt;Hijau = benar, kuning = sebagian, merah = perlu diperbaiki. Perbaiki jawaban yang merah/kuning, lalu tekan tombol hitung lagi.&lt;/p&gt;
      &lt;div class="table-scroll" id="ppTable"&gt;&lt;/div&gt;
      &lt;div class="task-actions"&gt;
        &lt;button type="button" class="primary-btn ghost" data-act="pdf"&gt;Unduh Bukti (PDF - 1 Lembar)&lt;/button&gt;
        &lt;button type="button" class="primary-btn ghost" data-act="resend"&gt;Kirim Ulang Skor ke Guru&lt;/button&gt;
        &lt;span id="ppMsg2" class="msgline" role="status" aria-live="polite"&gt;&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Format Penilaian&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;&lt;table class="rubrik"&gt;
      &lt;tr&gt;&lt;th&gt;Tugas&lt;/th&gt;&lt;th&gt;Yang dinilai&lt;/th&gt;&lt;th&gt;Bobot&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;A. Tema &amp;amp; Keterbacaan&lt;/td&gt;&lt;td&gt;Kontras warna, ukuran judul dan isi, jenis font&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;B. Slide Master&lt;/td&gt;&lt;td&gt;Footer, nomor slide, logo, penerapan ke semua slide, tempat mengubah posisi judul&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;C. Multimedia&lt;/td&gt;&lt;td&gt;Pemilihan media, pemutaran video, teks alternatif, sematkan media&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;D. Animasi &amp;amp; Transisi&lt;/td&gt;&lt;td&gt;Kategori dan pemicu animasi, jenis, durasi, dan penerapan transisi&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;E. Hyperlink&lt;/td&gt;&lt;td&gt;Tautan ke slide, web, email, dan tombol kembali&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;F. Kolaborasi&lt;/td&gt;&lt;td&gt;Peran akses, riwayat versi, ekspor PDF&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;G. Presentasi Pengenalan Diri&lt;/td&gt;&lt;td&gt;Foto diri, gambar latar, keterbacaan teks di atas gambar, kelengkapan profil, teks alternatif, sumber gambar&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;&lt;/div&gt;
  &lt;/section&gt;
&lt;/main&gt;
&lt;footer&gt;APLIKASI PENGOLAH PRESENTASI PROFESIONAL - INFORMATIKA - FASE E KELAS X SMK&lt;/footer&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
(function(){
if(window.__ppInit){return;} window.__ppInit=true;
var ENDPOINT='https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec';
var JUDUL='Aplikasi Pengolah Presentasi Profesional';
var JSPDF='https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
var AUTOT='https://cdn.jsdelivr.net/npm/jspdf-autotable@3.8.2/dist/jspdf.plugin.autotable.min.js';
var LAST=null, APPLIED=false;

function $(id){return document.getElementById(id);}
function esc(s){return String(s==null?'':s).replace(/[&amp;&lt;&gt;"']/g,function(c){return {'&amp;':'&amp;amp;','&lt;':'&amp;lt;','&gt;':'&amp;gt;','"':'&amp;quot;',"'":'&amp;#39;'}[c];});}
function val(id){var e=$(id);return e?String(e.value):'';}
function chk(id){var e=$(id);return !!(e&amp;&amp;e.checked);}
function setMsg(id,t,cls){var e=$(id);if(!e)return;e.textContent=t||'';e.className='msgline'+(cls?' '+cls:'');}

/* ---------- data ---------- */
var PAL=[['p1','Navy dan Putih','#1E3A8A','#FFFFFF'],['p2','Putih dan Abu Gelap','#FFFFFF','#1F2937'],['p3','Kuning Muda dan Putih','#FEF08A','#FFFFFF'],['p4','Biru Langit dan Biru Muda','#38BDF8','#7DD3FC'],['p5','Abu Sedang dan Abu Terang','#9CA3AF','#D1D5DB'],['p6','Hijau Tua dan Krem','#14532D','#FEF3C7'],['p7','Jingga dan Putih','#F97316','#FFFFFF'],['p8','Marun dan Putih','#7F1D1D','#FFFFFF']];
var FONTS=['Calibri','Arial','Georgia','Times New Roman','Verdana','Comic Sans MS','Impact'];
var FORMAL=['Calibri','Arial','Georgia','Times New Roman','Verdana'];
var CAT=[['masuk','Masuk (Entrance)'],['tekan','Penekanan (Emphasis)'],['keluar','Keluar (Exit)']];
var EFEK={masuk:['Appear','Fade','Wipe','Fly In','Zoom'],tekan:['Pulse','Grow/Shrink','Spin','Teeter'],keluar:['Disappear','Fade','Fly Out']};
var TRG=[['click','Saat diklik (On Click)'],['with','Bersamaan dengan sebelumnya (With Previous)'],['after','Otomatis setelah sebelumnya (After Previous)']];
var TRANS=['Fade','Push','Wipe','Morph','Cube','Vortex','Honeycomb','Acak (Random)'];
var SMOOTH=['Fade','Push','Wipe','Morph'];
var MEDIA=[['foto','Foto / gambar'],['diagram','Diagram / SmartArt'],['grafik','Grafik (Chart)'],['video','Video'],['rekam','Rekaman layar (screen recording)'],['audio','Audio']];
var LTYPE=[['slide','Slide dalam dokumen'],['web','Halaman web'],['email','Alamat email'],['file','Berkas lain']];
var ROLE=[['editor','Editor (dapat mengedit)'],['komentar','Komentator (dapat berkomentar)'],['lihat','Pelihat (hanya lihat)']];

/* ---------- builders ---------- */
function opt(v,t){return '&lt;option value="'+esc(v)+'"&gt;'+esc(t)+'&lt;/option&gt;';}
function sel(id,opts,ph,da){return '&lt;select id="'+id+'"'+(da?' data-in="'+da+'"':'')+'&gt;'+(ph?opt('',ph):'')+opts.map(function(o){return Array.isArray(o)?opt(o[0],o[1]):opt(o,o);}).join('')+'&lt;/select&gt;';}
function fld(label,html,cls){return '&lt;div class="field'+(cls?' '+cls:'')+'"&gt;&lt;label&gt;'+label+'&lt;/label&gt;'+html+'&lt;/div&gt;';}
function head(l,t,p){return '&lt;div class="task-head"&gt;&lt;span class="badge"&gt;Tugas '+l+'&lt;/span&gt;&lt;h3&gt;'+t+'&lt;/h3&gt;&lt;span class="pts"&gt;'+p+' poin&lt;/span&gt;&lt;/div&gt;';}
function slideOpts(){return [['1','Slide 1'],['2','Slide 2'],['3','Slide 3'],['4','Slide 4'],['5','Slide 5']];}

function taskA(){
  return '&lt;div class="task-panel"&gt;'+head('A','Tema &amp; Keterbacaan',16)
  +'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Presentasi "Literasi Digital di Sekolah" akan ditayangkan dengan proyektor di aula. Atur tema agar teks terbaca jelas dari bangku paling belakang. Perhatikan standar pada materi (kontras, ukuran huruf, jenis font).&lt;/p&gt;'
  +'&lt;div class="form-grid"&gt;'
  +fld('Palet warna (latar dan teks)',sel('ppPal',PAL.map(function(p){return [p[0],p[1]];}),'-- pilih palet --','a'))
  +fld('Ukuran font judul (pt)','&lt;input type="number" id="ppTJ" min="10" max="80" value="24" data-in="a"&gt;')
  +fld('Ukuran font isi (pt)','&lt;input type="number" id="ppTI" min="8" max="48" value="14" data-in="a"&gt;')
  +fld('Font judul',sel('ppFJ',FONTS,'-- pilih font --','a'))
  +fld('Font isi',sel('ppFI',FONTS,'-- pilih font --','a'))
  +'&lt;/div&gt;&lt;div class="ratio" id="ppRatio"&gt;&lt;/div&gt;&lt;div class="slide-prev" id="ppPrevA"&gt;&lt;div class="t"&gt;Literasi Digital di Sekolah&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Kenali berita hoaks&lt;/li&gt;&lt;li&gt;Lindungi data pribadi&lt;/li&gt;&lt;li&gt;Hormati hak cipta&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;';
}
function taskB(){
  return '&lt;div class="task-panel"&gt;'+head('B','Slide Master',16)
  +'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Sekolah meminta semua slide memuat nama sekolah di bagian bawah, nomor slide, dan logo sekolah. Atur pada &lt;strong&gt;Slide Master&lt;/strong&gt;, lalu terapkan ke semua slide.&lt;/p&gt;'
  +'&lt;div class="form-grid"&gt;'
  +fld('Teks footer','&lt;input type="text" id="ppFoot" maxlength="60" placeholder="Contoh: SMK Negeri 1 Contoh" data-in="m"&gt;')
  +fld('Elemen master','&lt;label&gt;&lt;input type="checkbox" id="ppNum" data-in="m"&gt; Tampilkan nomor slide&lt;/label&gt;&lt;label&gt;&lt;input type="checkbox" id="ppLogo" data-in="m"&gt; Tampilkan logo sekolah&lt;/label&gt;','chk')
  +'&lt;/div&gt;'
  +'&lt;div class="item-row"&gt;&lt;p class="q"&gt;Pertanyaan: judul di semua slide perlu dipindah ke posisi tengah atas. Di mana perubahan itu paling efisien dilakukan?&lt;/p&gt;'+sel('ppMQ',[['master','Di Slide Master'],['tiap','Pada tiap slide satu per satu'],['sorter','Di tampilan Slide Sorter'],['notes','Di tampilan Notes']],'-- pilih jawaban --')+'&lt;/div&gt;'
  +'&lt;div class="task-actions"&gt;&lt;button type="button" class="primary-btn sm" data-act="applyMaster"&gt;Terapkan ke Semua Slide&lt;/button&gt;&lt;span id="ppThumbMsg" class="msgline"&gt;&lt;/span&gt;&lt;/div&gt;'
  +'&lt;div class="thumbs" id="ppThumbs"&gt;&lt;/div&gt;&lt;/div&gt;';
}
function taskC(){
  var sc=['Menunjukkan langkah-langkah menginstal sebuah aplikasi di layar komputer.','Membandingkan penjualan empat triwulan agar tren naik-turunnya mudah terlihat.','Menjelaskan alur pendaftaran murid baru dari langkah 1 sampai 5 secara berurutan.'];
  var h=head('C','Multimedia',12)+'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Pilih media yang paling tepat untuk tiap kebutuhan, lalu atur pengaturannya.&lt;/p&gt;';
  sc.forEach(function(s,i){h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;'+(i+1)+'. '+s+'&lt;/p&gt;'+sel('ppM'+(i+1),MEDIA,'-- pilih media --')+'&lt;/div&gt;';});
  h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;4. Video demonstrasi harus diputar tepat ketika penyaji selesai menjelaskan. Kapan video dimulai?&lt;/p&gt;'+sel('ppVS',[['click','Saat diklik (penyaji mengendalikan)'],['auto','Otomatis begitu slide tampil']],'-- pilih --')+'&lt;/div&gt;';
  h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;5. Foto di slide 4 harus dapat dibacakan oleh pembaca layar (screen reader).&lt;/p&gt;&lt;div class="form-grid"&gt;'+fld('Pengaturan','&lt;label&gt;&lt;input type="checkbox" id="ppAlt"&gt; Tambahkan teks alternatif (alt text)&lt;/label&gt;','chk')+fld('Isi teks alternatif (minimal 10 karakter)','&lt;input type="text" id="ppAltT" maxlength="120" placeholder="Deskripsikan isi foto"&gt;')+'&lt;/div&gt;&lt;/div&gt;';
  h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;6. Berkas akan dipindahkan ke komputer lain dan video harus tetap dapat diputar. Pilih cara memasukkan video:&lt;/p&gt;'+sel('ppEmb',[['embed','Sematkan (embed) di dalam berkas'],['link','Tautkan ke berkas video di komputer asal']],'-- pilih --')+'&lt;/div&gt;';
  return '&lt;div class="task-panel"&gt;'+h+'&lt;/div&gt;';
}
function effRow(i,q,extra){
  return '&lt;div class="item-row"&gt;&lt;p class="q"&gt;'+q+'&lt;/p&gt;&lt;div class="form-grid"&gt;'+fld('Kategori',sel('ppCat'+i,CAT,'-- pilih --','cat'))+fld('Efek',sel('ppEf'+i,[],'-- pilih kategori dulu --'))+fld('Pemicu',sel('ppTg'+i,TRG,'-- pilih --'))+(extra||'')+'&lt;/div&gt;&lt;/div&gt;';
}
function taskD(){
  return '&lt;div class="task-panel"&gt;'+head('D','Animasi &amp; Transisi',16)
  +'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Atur animasi pada slide 3 sesuai kebutuhan masing-masing objek, lalu atur transisi seluruh presentasi.&lt;/p&gt;'
  +effRow(1,'1. Judul slide harus muncul otomatis begitu slide tampil.')
  +effRow(2,'2. Poin-poin muncul satu per satu setiap penyaji mengklik.',fld('Opsi efek','&lt;label&gt;&lt;input type="checkbox" id="ppPara"&gt; Per paragraf (By Paragraph)&lt;/label&gt;','chk'))
  +effRow(3,'3. Angka "85%" perlu ditegaskan agar menarik perhatian, tetapi tetap berada di layar.')
  +'&lt;div class="item-row"&gt;&lt;p class="q"&gt;4. Transisi antar slide&lt;/p&gt;&lt;div class="form-grid"&gt;'+fld('Jenis transisi',sel('ppTrT',TRANS,'-- pilih --'))+fld('Durasi (detik)','&lt;input type="number" id="ppTrD" min="0" max="10" step="0.1" value="3"&gt;')+fld('Penerapan','&lt;label&gt;&lt;input type="checkbox" id="ppTrA"&gt; Terapkan ke semua slide (Apply To All)&lt;/label&gt;','chk')+'&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
}
function linkRow(i,q,slideDefault){
  return '&lt;div class="item-row"&gt;&lt;p class="q"&gt;'+q+'&lt;/p&gt;&lt;div class="form-grid"&gt;'+fld('Jenis tautan',sel('ppLt'+i,LTYPE,'-- pilih --','lt'))
   +'&lt;div class="field" id="ppLdS'+i+'"&gt;&lt;label&gt;Tujuan slide&lt;/label&gt;'+sel('ppLs'+i,slideOpts(),'-- pilih slide --')+'&lt;/div&gt;'
   +'&lt;div class="field hide" id="ppLdT'+i+'"&gt;&lt;label&gt;Tujuan (URL / alamat email)&lt;/label&gt;&lt;input type="text" id="ppLx'+i+'" maxlength="150" placeholder="Ketik tujuan tautan"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
}
function taskE(){
  return '&lt;div class="task-panel"&gt;'+head('E','Hyperlink',12)
  +'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Slide 2 adalah &lt;strong&gt;Agenda&lt;/strong&gt;; slide 3 berjudul &lt;strong&gt;Profil Sekolah&lt;/strong&gt;. Buat tautan untuk keempat elemen berikut. URL harus diawali https:// dan alamat email diketik tanpa awalan.&lt;/p&gt;'
  +linkRow(1,'1. Butir agenda "Profil Sekolah" harus membawa audiens ke slide pembahasannya.')
  +linkRow(2,'2. Butir "Panduan Resmi" membuka situs web resmi (contoh: https://www.kemendikdasmen.go.id).')
  +linkRow(3,'3. Butir "Hubungi Kami" membuka aplikasi email ke alamat guru.')
  +linkRow(4,'4. Tombol "Kembali ke Agenda" pada slide 3 harus membawa audiens kembali ke Agenda.')
  +'&lt;/div&gt;';
}
function taskF(){
  var q=[['ppR1','Teman satu kelompok yang ikut menyusun slide.'],['ppR2','Guru yang hanya memberi umpan balik tanpa mengubah isi.'],['ppR3','Kelas lain yang hanya akan menonton dan membaca.']];
  var h=head('F','Kolaborasi',8)+'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Presentasi kelompok dikerjakan pada satu berkas daring. Tentukan peran akses dan fitur yang tepat.&lt;/p&gt;';
  q.forEach(function(x,i){h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;'+(i+1)+'. Peran akses untuk: '+x[1]+'&lt;/p&gt;'+sel(x[0],ROLE,'-- pilih peran --')+'&lt;/div&gt;';});
  h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;4. Setelah perubahan besar isi presentasi menjadi kacau, fitur untuk mengembalikan isi ke keadaan sebelumnya adalah:&lt;/p&gt;'+sel('ppF4',[['riwayat','Riwayat versi (Version History)'],['spell','Pemeriksa ejaan'],['sorter','Slide Sorter'],['present','Presenter View']],'-- pilih --')+'&lt;/div&gt;';
  h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;5. Presentasi akan dikirim kepada orang yang tidak memiliki aplikasi presentasi. Format yang tepat:&lt;/p&gt;'+sel('ppF5',[['pdf','PDF'],['pptx','Berkas asli (.pptx) saja'],['potx','Template (.potx)'],['mp4','Video MP4 tanpa teks']],'-- pilih --')+'&lt;/div&gt;';
  return '&lt;div class="task-panel"&gt;'+h+'&lt;/div&gt;';
}

/* ---------- Tugas G: presentasi pengenalan diri ---------- */
var GP=null,GB=null;
var SOLID=[['#1E3A8A','Biru Tua'],['#14532D','Hijau Tua'],['#7F1D1D','Marun'],['#111827','Hitam Kebiruan'],['#FFFFFF','Putih']];
function taskG(){
  return '&lt;div class="task-panel"&gt;'+head('G','Presentasi Pengenalan Diri',20)
  +'&lt;p&gt;&lt;strong&gt;Skenario:&lt;/strong&gt; Buat 3 slide perkenalan dirimu: &lt;strong&gt;Slide 1&lt;/strong&gt; judul dan foto, &lt;strong&gt;Slide 2&lt;/strong&gt; tentang saya, &lt;strong&gt;Slide 3&lt;/strong&gt; motto. Unggah foto dirimu, pakai gambar sebagai &lt;strong&gt;latar slide&lt;/strong&gt;, dan pastikan teks tetap terbaca di atas gambar.&lt;/p&gt;'
  +'&lt;div class="msg-priv"&gt;Privasi: foto dan gambar hanya diproses di perangkatmu. Tidak diunggah ke server dan tidak dikirim ke guru. Gunakan foto yang sopan dan pantas untuk sekolah.&lt;/div&gt;'
  +'&lt;div class="form-grid"&gt;'
  +fld('Foto diri (JPG/PNG/WebP, maks. 8 MB)','&lt;input type="file" id="ppGPhoto" accept="image/jpeg,image/png,image/webp" data-in="gf"&gt;')
  +fld('Teks alternatif foto (min. 10 karakter)','&lt;input type="text" id="ppGAlt" maxlength="120" placeholder="Contoh: Murid berseragam tersenyum di depan kelas" data-in="g"&gt;')
  +fld('Gambar latar slide (JPG/PNG/WebP, maks. 8 MB)','&lt;input type="file" id="ppGBg" accept="image/jpeg,image/png,image/webp" data-in="gf"&gt;')
  +fld('Sumber gambar latar','&lt;input type="text" id="ppGSrc" maxlength="80" placeholder="Contoh: Foto milik sendiri / Unsplash - nama fotografer" data-in="g"&gt;')
  +fld('Warna latar bila tanpa gambar',sel('ppGSolid',SOLID,'','g'))
  +fld('Warna teks',sel('ppGTx',[['#FFFFFF','Putih'],['#111827','Gelap']],'','g'))
  +fld('Lapisan di atas gambar (overlay)',sel('ppGOv',[['dark','Gelap'],['light','Terang']],'','g'))
  +fld('Kekuatan overlay: &lt;span id="ppGOvV"&gt;0&lt;/span&gt;%','&lt;input type="range" id="ppGOp" min="0" max="80" step="5" value="0" data-in="g"&gt;')
  +'&lt;/div&gt;'
  +'&lt;div class="form-grid"&gt;'
  +fld('Nama panggilan','&lt;input type="text" id="ppGNick" maxlength="30" data-in="g" placeholder="Contoh: Ani"&gt;')
  +fld('Hobi','&lt;input type="text" id="ppGHobi" maxlength="50" data-in="g" placeholder="Contoh: Desain grafis"&gt;')
  +fld('Cita-cita','&lt;input type="text" id="ppGCita" maxlength="50" data-in="g" placeholder="Contoh: Network engineer"&gt;')
  +fld('Motto hidup','&lt;input type="text" id="ppGMotto" maxlength="90" data-in="g" placeholder="Contoh: Belajar sedikit tapi konsisten"&gt;')
  +'&lt;/div&gt;'
  +'&lt;div class="task-actions"&gt;&lt;button type="button" class="primary-btn sm ghost" data-act="clrPhoto"&gt;Hapus Foto&lt;/button&gt;&lt;button type="button" class="primary-btn sm ghost" data-act="clrBg"&gt;Hapus Gambar Latar&lt;/button&gt;&lt;span id="ppGMsg" class="msgline"&gt;&lt;/span&gt;&lt;/div&gt;'
  +'&lt;div class="gslides"&gt;&lt;canvas id="ppGC1" width="960" height="540" aria-label="Pratinjau slide 1"&gt;&lt;/canvas&gt;&lt;canvas id="ppGC2" width="960" height="540" aria-label="Pratinjau slide 2"&gt;&lt;/canvas&gt;&lt;canvas id="ppGC3" width="960" height="540" aria-label="Pratinjau slide 3"&gt;&lt;/canvas&gt;&lt;/div&gt;&lt;/div&gt;';
}
function coverDraw(ctx,img,x,y,w,h){var r=Math.max(w/img.width,h/img.height),iw=img.width*r,ih=img.height*r;ctx.save();ctx.beginPath();ctx.rect(x,y,w,h);ctx.clip();ctx.drawImage(img,x+(w-iw)/2,y+(h-ih)/2,iw,ih);ctx.restore();}
function wrapText(ctx,t,x,y,maxW,lh,maxLines){var words=String(t).split(/\s+/),line='',n=0;for(var i=0;i&lt;words.length;i++){var test=line?line+' '+words[i]:words[i];if(ctx.measureText(test).width&gt;maxW&amp;&amp;line){ctx.fillText(line,x,y);y+=lh;line=words[i];n++;if(n&gt;=maxLines-1){break;}}else{line=test;}}ctx.fillText(line,x,y);return y;}
function circlePhoto(ctx,cx,cy,r){
  ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.closePath();ctx.clip();
  if(GP){coverDraw(ctx,GP,cx-r,cy-r,r*2,r*2);}else{ctx.fillStyle='#D1D5DB';ctx.fillRect(cx-r,cy-r,r*2,r*2);ctx.fillStyle='#374151';ctx.font='bold '+Math.round(r/4)+'px sans-serif';ctx.textAlign='center';ctx.fillText('FOTO DIRI',cx,cy+r/12);}
  ctx.restore();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.lineWidth=6;ctx.strokeStyle=val('ppGTx')||'#FFFFFF';ctx.stroke();
}
function drawSlide(n){
  var c=$('ppGC'+n);if(!c)return;var ctx=c.getContext('2d'),W=960,H=540,tx=val('ppGTx')||'#FFFFFF';
  ctx.clearRect(0,0,W,H);ctx.fillStyle=val('ppGSolid')||'#1E3A8A';ctx.fillRect(0,0,W,H);
  if(GB){coverDraw(ctx,GB,0,0,W,H);var op=num(val('ppGOp'))/100;if(op&gt;0){ctx.fillStyle=(val('ppGOv')==='light'?'rgba(255,255,255,':'rgba(0,0,0,')+op+')';ctx.fillRect(0,0,W,H);}}
  ctx.fillStyle=tx;ctx.textAlign='left';ctx.textBaseline='alphabetic';
  var nama=val('ppNama').trim()||'Nama Murid',kelas=val('ppKelas')||'Kelas',nick=val('ppGNick').trim(),hobi=val('ppGHobi').trim(),cita=val('ppGCita').trim(),motto=val('ppGMotto').trim();
  if(n===1){
    ctx.font='bold 58px sans-serif';ctx.fillText('Perkenalan Diri',60,170);
    ctx.font='bold 42px sans-serif';wrapText(ctx,nama,60,250,520,50,2);
    ctx.font='28px sans-serif';ctx.fillText(kelas+(val('ppAbsen')?'  |  No. Absen '+val('ppAbsen'):''),60,340);
    circlePhoto(ctx,760,270,150);
  }else if(n===2){
    ctx.font='bold 52px sans-serif';ctx.fillText('Tentang Saya',60,110);
    ctx.font='34px sans-serif';
    ctx.fillText('Panggilan : '+(nick||'...'),60,230);
    wrapText(ctx,'Hobi : '+(hobi||'...'),60,300,620,42,2);
    wrapText(ctx,'Cita-cita : '+(cita||'...'),60,390,620,42,2);
    circlePhoto(ctx,810,130,80);
  }else{
    ctx.font='bold 52px sans-serif';ctx.fillText('Motto Hidupku',60,120);
    ctx.font='italic 40px sans-serif';wrapText(ctx,'"'+(motto||'...')+'"',60,250,840,54,4);
    ctx.font='28px sans-serif';ctx.fillText('Terima kasih',60,470);
  }
  var src=val('ppGSrc').trim();
  if(GB&amp;&amp;src){ctx.font='16px sans-serif';ctx.fillStyle=tx;ctx.textAlign='right';ctx.fillText('Sumber latar: '+src,W-20,H-16);}
}
function drawAll(){drawSlide(1);drawSlide(2);drawSlide(3);var v=$('ppGOvV');if(v)v.textContent=val('ppGOp')||'0';}
function loadImg(file,done,fail){
  if(!/^image\/(jpeg|png|webp)$/.test(file.type)){fail('Format harus JPG, PNG, atau WebP.');return;}
  if(file.size&gt;8*1024*1024){fail('Ukuran gambar maksimal 8 MB.');return;}
  var rd=new FileReader();
  rd.onerror=function(){fail('Gambar tidak dapat dibaca.');};
  rd.onload=function(){var im=new Image();im.onerror=function(){fail('Berkas bukan gambar yang valid.');};
    im.onload=function(){var m=1280,r=Math.min(1,m/Math.max(im.width,im.height)),cv=document.createElement('canvas');cv.width=Math.max(1,Math.round(im.width*r));cv.height=Math.max(1,Math.round(im.height*r));cv.getContext('2d').drawImage(im,0,0,cv.width,cv.height);done(cv);};
    im.src=rd.result;};
  rd.readAsDataURL(file);
}
function onGFile(el){
  var f=el.files&amp;&amp;el.files[0];if(!f)return;var isP=el.id==='ppGPhoto';
  loadImg(f,function(cv){if(isP){GP=cv;}else{GB=cv;}setMsg('ppGMsg',(isP?'Foto':'Gambar latar')+' berhasil dimuat.','ok');drawAll();},function(m){el.value='';setMsg('ppGMsg',m,'err');});
}
function renderTasks(){
  var t=$('ppTasks');if(!t)return;
  t.innerHTML=taskA()+taskB()+taskC()+taskD()+taskE()+taskF()+taskG();
  APPLIED=false;GP=null;GB=null;updPrevA();renderThumbs();drawAll();
}

/* ---------- warna &amp; preview ---------- */
function lum(h){var c=[1,3,5].map(function(i){return parseInt(h.substr(i,2),16)/255;}).map(function(v){return v&lt;=0.03928?v/12.92:Math.pow((v+0.055)/1.055,2.4);});return 0.2126*c[0]+0.7152*c[1]+0.0722*c[2];}
function ratio(a,b){var l1=lum(a),l2=lum(b);return (Math.max(l1,l2)+0.05)/(Math.min(l1,l2)+0.05);}
function palOf(id){return PAL.filter(function(p){return p[0]===id;})[0]||null;}
function num(v){var n=parseFloat(String(v).replace(',','.'));return isNaN(n)?0:n;}
function updPrevA(){
  var el=$('ppPrevA');if(!el)return;
  var p=palOf(val('ppPal')),tj=num(val('ppTJ')),ti=num(val('ppTI'));
  el.style.background=p?p[2]:'#E5E7EB';el.style.color=p?p[3]:'#6B7280';
  var sc=el.clientWidth/960;
  var t=el.querySelector('.t'),ul=el.querySelector('ul');
  t.style.fontSize=Math.max(6,tj*sc)+'px';ul.style.fontSize=Math.max(5,ti*sc)+'px';
  t.style.fontFamily=val('ppFJ')?'"'+val('ppFJ')+'",sans-serif':'inherit';ul.style.fontFamily=val('ppFI')?'"'+val('ppFI')+'",sans-serif':'inherit';
  var r=$('ppRatio');
  if(!p){r.textContent='Rasio kontras: pilih palet warna dahulu.';r.className='ratio';return;}
  var x=ratio(p[2],p[3]);
  r.textContent='Rasio kontras: '+x.toFixed(1)+' : 1 '+(x&gt;=4.5?'(memenuhi, minimal 4,5 : 1)':'(belum memenuhi, minimal 4,5 : 1)');
  r.className='ratio '+(x&gt;=4.5?'ok':x&gt;=3?'mid':'bad');
}
function renderThumbs(){
  var box=$('ppThumbs');if(!box)return;var h='',foot=val('ppFoot').trim(),n=chk('ppNum'),lg=chk('ppLogo');
  for(var i=1;i&lt;=5;i++){h+='&lt;div class="thumb"&gt;&lt;div class="th-title"&gt;Slide '+i+'&lt;/div&gt;'+(APPLIED?((lg?'&lt;span class="th-logo"&gt;S&lt;/span&gt;':'')+(foot?'&lt;div class="th-foot"&gt;'+esc(foot)+'&lt;/div&gt;':'')+(n?'&lt;span class="th-num"&gt;'+i+'&lt;/span&gt;':'')):'')+'&lt;/div&gt;';}
  box.innerHTML=h;
  setMsg('ppThumbMsg',APPLIED?'Master diterapkan ke 5 slide.':'Belum diterapkan. Tekan tombol untuk melihat hasilnya.',APPLIED?'ok':'');
}
function fillEf(i){var c=val('ppCat'+i),s=$('ppEf'+i);if(!s)return;s.innerHTML=c?(EFEK[c].map(function(e){return opt(e,e);}).join('')):opt('','-- pilih kategori dulu --');}
function togLink(i){var t=val('ppLt'+i);$('ppLdS'+i).classList.toggle('hide',t!=='slide'&amp;&amp;t!=='');$('ppLdT'+i).classList.toggle('hide',t==='slide'||t==='');
  var inp=$('ppLx'+i);if(inp)inp.placeholder=t==='web'?'https://contoh.go.id':t==='email'?'nama@sekolah.sch.id':'Nama berkas';}

/* ---------- penilaian ---------- */
function lab(list,v){var r=list.filter(function(x){return x[0]===v;})[0];return r?r[1]:'(belum dipilih)';}
function ev(){
  var R=[];
  function add(t,aspek,jawab,max,got,ket){R.push({t:t,aspek:aspek,jawab:jawab,max:max,got:got,ket:ket});}
  /* A */
  var p=palOf(val('ppPal')),r=p?ratio(p[2],p[3]):0;
  add('A','Kontras teks-latar',p?p[1]+' ('+r.toFixed(1)+':1)':'(belum dipilih)',6,!p?0:r&gt;=4.5?6:r&gt;=3?3:0,'Minimal 4,5:1');
  var tj=num(val('ppTJ')),ti=num(val('ppTI'));
  add('A','Ukuran judul',tj+' pt',3,tj&gt;=32?3:0,'Minimal 32 pt');
  add('A','Ukuran isi',ti+' pt',3,(ti&gt;=18&amp;&amp;ti&lt;=28)?3:0,'18-28 pt');
  var fj=val('ppFJ'),fi=val('ppFI');
  add('A','Font judul',fj||'(belum dipilih)',2,FORMAL.indexOf(fj)&gt;=0?2:0,'Font formal &amp; terbaca');
  add('A','Font isi',fi||'(belum dipilih)',2,FORMAL.indexOf(fi)&gt;=0?2:0,'Font formal &amp; terbaca');
  /* B */
  var ft=val('ppFoot').trim();
  add('B','Footer nama sekolah',ft||'(kosong)',3,(ft.length&gt;=3&amp;&amp;APPLIED)?3:0,'Isi footer &amp; terapkan');
  add('B','Nomor slide',chk('ppNum')?'Aktif':'Tidak aktif',3,(chk('ppNum')&amp;&amp;APPLIED)?3:0,'Aktifkan &amp; terapkan');
  add('B','Logo sekolah',chk('ppLogo')?'Aktif':'Tidak aktif',3,(chk('ppLogo')&amp;&amp;APPLIED)?3:0,'Aktifkan &amp; terapkan');
  add('B','Diterapkan ke semua slide',APPLIED?'Ya':'Belum',4,APPLIED?4:0,'Tekan tombol terapkan');
  add('B','Tempat ubah posisi judul',lab([['master','Slide Master'],['tiap','Tiap slide'],['sorter','Slide Sorter'],['notes','Notes']],val('ppMQ')),3,val('ppMQ')==='master'?3:0,'Slide Master');
  /* C */
  var M=[['rekam','Menginstal aplikasi'],['grafik','Tren penjualan'],['diagram','Alur pendaftaran']];
  M.forEach(function(m,i){var v=val('ppM'+(i+1));add('C','Media: '+m[1],lab(MEDIA,v),2,v===m[0]?2:0,lab(MEDIA,m[0]));});
  add('C','Pemutaran video',val('ppVS')==='click'?'Saat diklik':val('ppVS')==='auto'?'Otomatis':'(belum dipilih)',2,val('ppVS')==='click'?2:0,'Saat diklik');
  var at=val('ppAltT').trim();
  add('C','Teks alternatif',chk('ppAlt')?(at||'(kosong)'):'Tidak aktif',2,(chk('ppAlt')&amp;&amp;at.length&gt;=10)?2:0,'Aktif + min. 10 karakter');
  add('C','Cara memasukkan video',val('ppEmb')==='embed'?'Sematkan (embed)':val('ppEmb')==='link'?'Tautan':'(belum dipilih)',2,val('ppEmb')==='embed'?2:0,'Sematkan (embed)');
  /* D */
  function eff(i){return lab(CAT,val('ppCat'+i))+(val('ppEf'+i)?' - '+val('ppEf'+i):'')+'; '+lab(TRG,val('ppTg'+i)).replace(/ \(.*\)/,'');}
  var c1=val('ppCat1'),g1=val('ppTg1');
  add('D','Animasi judul',eff(1),3,(c1==='masuk'?1:0)+((g1==='after'||g1==='with')?2:0),'Masuk; otomatis');
  var c2=val('ppCat2'),g2=val('ppTg2');
  add('D','Animasi poin-poin',eff(2)+(chk('ppPara')?'; per paragraf':''),4,(c2==='masuk'?1:0)+(g2==='click'?2:0)+(chk('ppPara')?1:0),'Masuk; saat diklik; per paragraf');
  add('D','Animasi angka 85%',eff(3),3,val('ppCat3')==='tekan'?3:0,'Penekanan');
  var tt=val('ppTrT'),td=num(val('ppTrD'));
  add('D','Jenis transisi',tt||'(belum dipilih)',2,SMOOTH.indexOf(tt)&gt;=0?2:0,'Fade/Push/Wipe/Morph');
  add('D','Durasi transisi',td+' detik',2,(td&gt;=0.5&amp;&amp;td&lt;=1.5)?2:0,'0,5-1,5 detik');
  add('D','Terapkan ke semua slide',chk('ppTrA')?'Ya':'Tidak',2,chk('ppTrA')?2:0,'Apply To All');
  /* E */
  function lk(i){var t=val('ppLt'+i);return lab(LTYPE,t)+(t==='slide'?(val('ppLs'+i)?' - slide '+val('ppLs'+i):''):(val('ppLx'+i)?' - '+val('ppLx'+i).trim():''));}
  var t1=val('ppLt1');add('E','Agenda ke Profil Sekolah',lk(1),3,(t1==='slide'?1:0)+((t1==='slide'&amp;&amp;val('ppLs1')==='3')?2:0),'Slide 3');
  var t2=val('ppLt2'),u2=val('ppLx2').trim();add('E','Tautan situs web',lk(2),3,(t2==='web'?1:0)+((t2==='web'&amp;&amp;/^https:\/\/[a-z0-9-]+(\.[a-z0-9-]+)+(\/\S*)?$/i.test(u2))?2:0),'Jenis web + URL https://');
  var t3=val('ppLt3'),u3=val('ppLx3').trim();add('E','Tautan email',lk(3),3,(t3==='email'?1:0)+((t3==='email'&amp;&amp;/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(u3))?2:0),'Jenis email + alamat valid');
  var t4=val('ppLt4');add('E','Tombol kembali ke Agenda',lk(4),3,(t4==='slide'?1:0)+((t4==='slide'&amp;&amp;val('ppLs4')==='2')?2:0),'Slide 2');
  /* F */
  var RL=function(v){return lab(ROLE,v);};
  add('F','Peran teman sekelompok',RL(val('ppR1')),2,val('ppR1')==='editor'?2:0,'Editor');
  add('F','Peran guru',RL(val('ppR2')),2,val('ppR2')==='komentar'?2:0,'Komentator');
  add('F','Peran kelas lain',RL(val('ppR3')),2,val('ppR3')==='lihat'?2:0,'Pelihat');
  add('F','Memulihkan isi',lab([['riwayat','Riwayat versi'],['spell','Pemeriksa ejaan'],['sorter','Slide Sorter'],['present','Presenter View']],val('ppF4')),1,val('ppF4')==='riwayat'?1:0,'Riwayat versi');
  add('F','Format untuk dibagikan',lab([['pdf','PDF'],['pptx','.pptx'],['potx','.potx'],['mp4','Video MP4']],val('ppF5')),1,val('ppF5')==='pdf'?1:0,'PDF');

  /* G */
  var gal=val('ppGAlt').trim(),gsrc=val('ppGSrc').trim(),gop=num(val('ppGOp')),gtx=val('ppGTx'),gov=val('ppGOv');
  add('G','Foto diri diunggah',GP?'Ya':'Belum',4,GP?4:0,'Unggah foto');
  add('G','Gambar latar dipakai',GB?'Ya':'Belum',3,GB?3:0,'Unggah gambar latar');
  var legible=GB&amp;&amp;gop&gt;=40&amp;&amp;((gov==='dark'&amp;&amp;gtx==='#FFFFFF')||(gov==='light'&amp;&amp;gtx==='#111827'));
  add('G','Teks terbaca di atas gambar',GB?(gov==='dark'?'Overlay gelap ':'Overlay terang ')+gop+'%, teks '+(gtx==='#FFFFFF'?'putih':'gelap'):'(tanpa gambar latar)',4,legible?4:0,'Overlay &gt;=40% dan teks kontras');
  var fl=['ppGNick','ppGHobi','ppGCita','ppGMotto'].filter(function(i){return val(i).trim().length&gt;=3;}).length;
  add('G','Isi profil (panggilan, hobi, cita-cita, motto)',fl+' dari 4 terisi',4,fl,'Isi semua kolom');
  add('G','Teks alternatif foto',gal||'(kosong)',2,(GP&amp;&amp;gal.length&gt;=10)?2:0,'Min. 10 karakter + foto');
  add('G','Sumber gambar latar',gsrc||'(kosong)',3,(GB&amp;&amp;gsrc.length&gt;=3)?3:0,'Cantumkan sumber');
  var total=0;R.forEach(function(x){total+=x.got;});
  return {rows:R,total:total};
}
function stat(x){return x.got&gt;=x.max?'ok':x.got&gt;0?'mid':'bad';}
function statL(x){return x.got&gt;=x.max?'Benar':x.got&gt;0?'Sebagian':'Perbaiki';}
function renderResult(res){
  $('ppScore').textContent=res.skor;
  var tun=res.skor&gt;=75;
  $('ppDetail').innerHTML='&lt;strong&gt;'+esc(res.nama)+'&lt;/strong&gt; - '+esc(res.kelas)+' - No. Absen '+esc(res.noAbsen)+'. '+(tun?'Predikat: &lt;strong&gt;Tuntas&lt;/strong&gt; (KKM 75).':'Predikat: &lt;strong&gt;Belum tuntas&lt;/strong&gt; (KKM 75). Perbaiki jawaban merah/kuning lalu hitung ulang.');
  var h='&lt;table class="compare final"&gt;&lt;tr&gt;&lt;th&gt;Tugas&lt;/th&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Jawabanmu&lt;/th&gt;&lt;th&gt;Skor&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;';
  res.rows.forEach(function(x){h+='&lt;tr&gt;&lt;td&gt;'+x.t+'&lt;/td&gt;&lt;td&gt;'+esc(x.aspek)+'&lt;/td&gt;&lt;td&gt;'+esc(x.jawab)+'&lt;/td&gt;&lt;td&gt;'+x.got+' / '+x.max+'&lt;/td&gt;&lt;td class="'+stat(x)+'"&gt;'+statL(x)+'&lt;/td&gt;&lt;/tr&gt;';});
  $('ppTable').innerHTML=h+'&lt;/table&gt;';
  $('ppResult').className='result-panel show';
}

/* ---------- kirim ---------- */
function sendScore(res){
  var payload={noAbsen:res.noAbsen,nama:res.nama,kelas:res.kelas,skor:res.skor,judulMateri:JUDUL};
  return fetch(ENDPOINT,{method:'POST',mode:'no-cors',headers:{'Content-Type':'text/plain;charset=utf-8'},body:JSON.stringify(payload)});
}
function doSend(res,msgId){
  setMsg(msgId,'Mengirim skor ke guru...','');
  return sendScore(res).then(function(){setMsg(msgId,'Skor '+res.skor+' sudah dikirim ke guru (peramban tidak dapat membaca balasan server; bila ragu, tanyakan guru).','ok');},function(){setMsg(msgId,'Gagal terkirim (koneksi). Tekan "Kirim Ulang Skor ke Guru" atau tunjukkan skor ini kepada guru.','err');});
}

/* ---------- PDF ---------- */
function loadJs(src){return new Promise(function(ok,no){var s=document.createElement('script');s.src=src;s.onload=ok;s.onerror=function(){no(new Error('Gagal memuat pustaka PDF'));};document.head.appendChild(s);});}
function ensurePdf(){
  var ready=function(){return window.jspdf&amp;&amp;window.jspdf.jsPDF&amp;&amp;window.jspdf.jsPDF.API&amp;&amp;window.jspdf.jsPDF.API.autoTable;};
  if(ready())return Promise.resolve();
  return (window.jspdf?Promise.resolve():loadJs(JSPDF)).then(function(){return ready()?0:loadJs(AUTOT);});
}
function makePdf(res){
  var jsPDF=window.jspdf.jsPDF,doc=new jsPDF({unit:'mm',format:'a4'}),mx=12,y=15;
  doc.setFont('helvetica','bold');doc.setFontSize(13);doc.setTextColor(20,20,60);doc.text('HASIL TUGAS PRAKTIK INDIVIDU',mx,y);y+=6;
  doc.setFontSize(10);doc.setTextColor(55,48,163);doc.text(JUDUL,mx,y);y+=5.5;
  doc.setFont('helvetica','normal');doc.setFontSize(9.5);doc.setTextColor(40,40,50);
  doc.text('Kelas: '+res.kelas+'   |   No. Absen: '+res.noAbsen+'   |   Nama: '+res.nama,mx,y);y+=5.5;
  doc.setFont('helvetica','bold');doc.setFontSize(11);doc.setTextColor(21,128,61);
  doc.text('Skor Akhir (Otomatis): '+res.skor+' / 100   -   '+(res.skor&gt;=75?'TUNTAS':'BELUM TUNTAS')+' (KKM 75)',mx,y);y+=4;
  doc.autoTable({startY:y,head:[['Tugas','Aspek','Jawaban','Skor','Status']],
    body:res.rows.map(function(x){return [x.t,x.aspek,String(x.jawab).replace(/[^\x20-\x7E]/g,'-'),x.got+'/'+x.max,statL(x)];}),
    theme:'grid',styles:{fontSize:7,cellPadding:1.1,overflow:'linebreak'},headStyles:{fillColor:[55,48,163],textColor:255,fontSize:7.5},
    columnStyles:{0:{cellWidth:12},1:{cellWidth:50},2:{cellWidth:70},3:{cellWidth:16},4:{cellWidth:24}},margin:{left:mx,right:mx},
    didParseCell:function(d){if(d.section==='body'&amp;&amp;d.column.index===4){var x=res.rows[d.row.index],s=stat(x);d.cell.styles.fillColor=s==='ok'?[220,252,231]:s==='mid'?[254,243,199]:[254,226,226];d.cell.styles.textColor=s==='ok'?[20,83,45]:s==='mid'?[120,53,15]:[127,29,29];d.cell.styles.fontStyle='bold';}}});
  var y2=doc.lastAutoTable.finalY+5;
  try{doc.setFont('helvetica','bold');doc.setFontSize(8);doc.setTextColor(30,27,107);doc.text('Slide pengenalan diri buatan murid (pratinjau)',mx,y2);y2+=2;
    for(var k=1;k&lt;=3;k++){var cv=$('ppGC'+k);if(cv){doc.addImage(cv.toDataURL('image/jpeg',0.75),'JPEG',mx+(k-1)*62,y2,58,32.6);}}
    y2+=32.6+6;}catch(e){}
  doc.setFont('helvetica','normal');doc.setFontSize(8);doc.setTextColor(90,90,100);
  doc.text('Dokumen ini dicetak otomatis oleh sistem sebagai bukti hasil tugas praktik individu.',mx,y2);y2+=8;
  doc.setFontSize(9.5);doc.setTextColor(20,20,30);
  doc.text('......................, '+new Date().toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric'}),mx,y2);y2+=12;
  doc.text('( '+res.nama+' )',mx,y2);y2+=4.5;doc.setFontSize(8.5);doc.setTextColor(90,90,100);doc.text('Murid',mx,y2);
  var pages=doc.getNumberOfPages();
  doc.save('Hasil-Praktik-Presentasi-'+String(res.nama).replace(/[^a-z0-9]+/gi,'_')+'.pdf');
  return pages;
}


/* ---------- Buku catatan ---------- */
var NOTE_Q=[['tema','Tema','Apa itu tema dan bagaimana memilih warna serta font yang terbaca?'],['master','Slide Master','Apa fungsi Slide Master dan apa saja yang diatur di dalamnya?'],['multi','Multimedia','Media apa untuk kebutuhan apa? Apa itu alt text dan embed?'],['anim','Animasi','Sebutkan 4 kategori animasi dan 3 jenis pemicunya.'],['trans','Transisi','Apa bedanya transisi dengan animasi? Apa pengaturan yang baik?'],['link','Hyperlink','Jenis tautan apa saja dan mengapa perlu tombol kembali?'],['kolab','Kolaborasi','Apa perbedaan peran Editor, Komentator, dan Pelihat?'],['refl','Refleksi','Hal paling berguna yang kupelajari dan yang masih ingin kuperdalam:']];
var NKEY='ppNotes_v1';
function noteLoad(){try{return JSON.parse(localStorage.getItem(NKEY)||'{}')||{};}catch(e){return {};}}
function noteSave(o){try{localStorage.setItem(NKEY,JSON.stringify(o));return true;}catch(e){return false;}}
function renderNotes(){
  var box=$('ppNotes');if(!box)return;var d=noteLoad(),h='';
  NOTE_Q.forEach(function(q){h+='&lt;div class="item-row"&gt;&lt;p class="q"&gt;'+esc(q[1])+' &lt;span class="qs"&gt;- '+esc(q[2])+'&lt;/span&gt;&lt;/p&gt;&lt;div class="field"&gt;&lt;textarea id="ppN_'+q[0]+'" rows="3" maxlength="1500" data-in="note" placeholder="Tulis dengan kata-katamu sendiri..."&gt;'+esc(d[q[0]]||'')+'&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;';});
  box.innerHTML=h;
}
function noteAll(){var o={};NOTE_Q.forEach(function(q){o[q[0]]=val('ppN_'+q[0]);});return o;}
function noteText(){
  var o=noteAll(),t='BUKU CATATAN - '+JUDUL+'\n'+(val('ppNama').trim()?'Nama: '+val('ppNama').trim()+'  ':'')+(val('ppKelas')?'Kelas: '+val('ppKelas'):'')+'\n\n';
  NOTE_Q.forEach(function(q){t+=q[1].toUpperCase()+'\n'+(o[q[0]].trim()||'(belum ditulis)')+'\n\n';});return t;
}
function dlBlob(name,blob){var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=name;document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(a.href);a.remove();},500);}
function notePdf(){
  var jsPDF=window.jspdf.jsPDF,doc=new jsPDF({unit:'mm',format:'a4'}),mx=15,y=18,W=180;
  doc.setFont('helvetica','bold');doc.setFontSize(14);doc.setTextColor(30,27,107);doc.text('BUKU CATATAN: RANGKUMANKU',mx,y);y+=6;
  doc.setFontSize(10);doc.setTextColor(55,48,163);doc.text(JUDUL,mx,y);y+=5.5;
  doc.setFont('helvetica','normal');doc.setTextColor(40,40,50);doc.text((val('ppNama').trim()?'Nama: '+val('ppNama').trim()+'   ':'')+(val('ppKelas')?'Kelas: '+val('ppKelas'):''),mx,y);y+=8;
  var o=noteAll(),clean=function(s){return String(s).replace(/[^\x20-\x7E -ÿ\n]/g,'?');};
  NOTE_Q.forEach(function(q){
    var lines=doc.splitTextToSize(clean(o[q[0]].trim()||'(belum ditulis)'),W);
    if(y+6+lines.length*5&gt;285){doc.addPage();y=18;}
    doc.setFont('helvetica','bold');doc.setFontSize(10.5);doc.setTextColor(30,27,107);doc.text(q[1],mx,y);y+=5;
    doc.setFont('helvetica','normal');doc.setFontSize(10);doc.setTextColor(30,30,40);doc.text(lines,mx,y);y+=lines.length*5+4;
  });
  doc.save('Catatan-Presentasi-'+(val('ppNama').trim().replace(/[^a-z0-9]+/gi,'_')||'murid')+'.pdf');
}
/* ---------- aksi ---------- */
function identity(){
  var no=parseInt(val('ppAbsen'),10),nama=val('ppNama').trim().replace(/\s+/g,' '),kelas=val('ppKelas');
  if(!no||no&lt;1||no&gt;60){return {err:'Isi No. Absen dengan angka 1-60.'};}
  if(nama.length&lt;3){return {err:'Isi nama lengkap (minimal 3 huruf).'};}
  if(!kelas){return {err:'Pilih kelas terlebih dahulu.'};}
  return {noAbsen:String(no),nama:nama,kelas:kelas};
}
var ACT={
  grade:function(){
    var id=identity();if(id.err){setMsg('ppMsg',id.err,'err');var f=$('ppAbsen');if(f&amp;&amp;f.scrollIntoView){f.scrollIntoView({block:'center'});}return;}
    var e=ev();LAST={noAbsen:id.noAbsen,nama:id.nama,kelas:id.kelas,skor:e.total,rows:e.rows};
    setMsg('ppMsg','');renderResult(LAST);doSend(LAST,'ppMsg2');
    var r=$('ppResult');if(r&amp;&amp;r.scrollIntoView){r.scrollIntoView({behavior:'smooth',block:'start'});}
  },
  resend:function(){if(!LAST){setMsg('ppMsg2','Hitung nilai terlebih dahulu.','err');return;}doSend(LAST,'ppMsg2');},
  pdf:function(){
    if(!LAST){setMsg('ppMsg2','Hitung nilai terlebih dahulu.','err');return;}
    setMsg('ppMsg2','Menyiapkan PDF...','');
    ensurePdf().then(function(){makePdf(LAST);setMsg('ppMsg2','PDF 1 lembar berhasil dibuat.','ok');}).catch(function(){setMsg('ppMsg2','Pustaka PDF gagal dimuat. Pastikan internet aktif lalu coba lagi.','err');});
  },
  notePdf:function(){setMsg('ppNoteMsg','Menyiapkan PDF...','');ensurePdf().then(function(){notePdf();setMsg('ppNoteMsg','Catatan PDF berhasil dibuat.','ok');}).catch(function(){setMsg('ppNoteMsg','Pustaka PDF gagal dimuat. Pastikan internet aktif.','err');});},
  noteTxt:function(){dlBlob('Catatan-Presentasi.txt',new Blob([noteText()],{type:'text/plain;charset=utf-8'}));setMsg('ppNoteMsg','Catatan teks diunduh.','ok');},
  noteClr:function(){if(window.confirm('Hapus semua catatan?')){try{localStorage.removeItem(NKEY);}catch(e){}renderNotes();setMsg('ppNoteMsg','Catatan dihapus.','');}},
  applyMaster:function(){APPLIED=true;renderThumbs();},
  clrPhoto:function(){GP=null;var f=$('ppGPhoto');if(f)f.value='';drawAll();setMsg('ppGMsg','Foto dihapus.','');},
  clrBg:function(){GB=null;var f=$('ppGBg');if(f)f.value='';drawAll();setMsg('ppGMsg','Gambar latar dihapus.','');},
  reset:function(){renderTasks();$('ppResult').className='result-panel';LAST=null;setMsg('ppMsg','Jawaban dikosongkan.','');}
};
var INP={
  a:function(){updPrevA();},
  g:function(){drawAll();},
  note:function(){var ok=noteSave(noteAll());setMsg('ppNoteMsg',ok?'Tersimpan otomatis di perangkat ini.':'Tidak dapat menyimpan otomatis (penyimpanan peramban dinonaktifkan); unduh catatanmu.',ok?'ok':'err');},
  gf:function(el){onGFile(el);},
  m:function(){APPLIED=false;renderThumbs();},
  cat:function(el){fillEf(el.id.replace('ppCat',''));},
  lt:function(el){togLink(el.id.replace('ppLt',''));}
};
function inRoot(el){while(el){if(el.id==='ppRoot')return true;el=el.parentNode;}return false;}
document.addEventListener('click',function(e){
  var el=e.target;while(el&amp;&amp;el!==document){if(el.getAttribute&amp;&amp;el.getAttribute('data-act')){if(inRoot(el)&amp;&amp;ACT[el.getAttribute('data-act')]){e.preventDefault();ACT[el.getAttribute('data-act')](el);}return;}el=el.parentNode;}
});
function onIn(e){var t=e.target;if(!t||!t.getAttribute||!inRoot(t))return;var n=t.getAttribute('data-in');if(n&amp;&amp;INP[n])INP[n](t);}
document.addEventListener('input',onIn);document.addEventListener('change',onIn);
window.addEventListener('resize',function(){updPrevA();});
function boot(){if($('ppTasks')&amp;&amp;!$('ppTasks').firstChild){renderTasks();}if($('ppNotes')&amp;&amp;!$('ppNotes').firstChild){renderNotes();}}
boot();
document.addEventListener('DOMContentLoaded',boot);window.addEventListener('load',boot);
window.__pp={ev:ev,makePdf:makePdf,ACT:ACT};
})();

&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/11/aplikasi-pengolah-presentasi-profesional.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-4812121963474317711</guid><pubDate>Mon, 09 Nov 2026 01:24:09 +0000</pubDate><atom:updated>2026-11-09T08:24:09.242+07:00</atom:updated><title>Menganalisis Kaidah Kebahasaan Teks Resensi &amp; Menulis Teks Resensi</title><description>&lt;style&gt;
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&amp;family=Source+Sans+3:wght@400;500;600;700&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;display=swap');
.epb{
  --blueprint:#0F2A3D;
  --blueprint-deep:#0A1E2C;
  --panel:#4A2E12;
  --paper:#F3F0E6;
  --ink:#132029;
  --amber:#F5A623;
  --amber-deep:#C97F12;
  --mint:#6EC6CA;
  --line:rgba(234,242,245,0.16);
  --off-white:#EAF2F5;
  --muted:#9FB4C2;
}
.epb *{box-sizing:border-box;}
.epb{scroll-behavior:smooth; background:#0A1F2E;}
.epb{
  margin:0;
  position:relative;
  overflow:hidden;
  background:var(--blueprint);
  color:var(--off-white);
  font-family:'Source Sans 3',sans-serif;
  line-height:1.65;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 34px 34px;
}
.epb h1, .epb h2, .epb h3, .epb h4{font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.02em; margin:0 0 0.5em;}
.epb .mono{font-family:'IBM Plex Mono',monospace;}
.epb a{color:var(--amber);}
.epb .crop{position:absolute; width:22px; height:22px; z-index:5; opacity:.55; pointer-events:none;}
.epb .crop::before, .epb .crop::after{content:"";position:absolute;background:var(--mint);}
.epb .crop::before{width:100%;height:1px;top:50%;left:0;}
.epb .crop::after{width:1px;height:100%;left:50%;top:0;}
.epb .crop.tl{top:14px;left:14px;}
.epb .crop.tr{top:14px;right:14px;}
.epb .crop.bl{bottom:14px;left:14px;}
.epb .crop.br{bottom:14px;right:14px;}
.epb header.cover{
  position:relative;
  padding:40px 24px 36px;
  border-bottom:1px solid var(--line);
  overflow:hidden;
}
.epb .doc-code{font-family:'IBM Plex Mono',monospace; color:var(--amber); letter-spacing:0.15em; font-size:0.78rem; text-transform:uppercase; margin-bottom:18px; display:flex; gap:10px; align-items:center;}
.epb .doc-code::before{content:"◆"; color:var(--amber);}
.epb .cover h1{font-size:clamp(1.6rem,7vw,2.5rem); color:var(--off-white); max-width:16ch; line-height:1.05;}
.epb .cover .subtitle{color:var(--muted); font-size:1.05rem; max-width:60ch; margin-top:14px;}
.epb .meta-strip{
  margin-top:38px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px dashed var(--line); border-bottom:1px dashed var(--line);
}
.epb .meta-strip div{padding:14px 16px; border-right:1px dashed var(--line);}
.epb .meta-strip div:last-child{border-right:none;}
.epb .meta-strip .k{font-family:'IBM Plex Mono',monospace; font-size:0.68rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.1em;}
.epb .meta-strip .v{font-size:0.98rem; color:var(--off-white); margin-top:4px; font-weight:600;}
.epb .stamp{
  position:absolute; top:40px; right:20px; width:104px; height:104px;
  border:2.5px solid var(--amber); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  transform:rotate(-11deg);
  color:var(--amber); font-family:'IBM Plex Mono',monospace;
  text-align:center; font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase;
  opacity:0.9;
}
.epb .stamp .ring{position:absolute; inset:8px; border:1px dashed var(--amber); border-radius:50%;}
.epb .stamp b{display:block; font-family:'Oswald',sans-serif; font-size:1.05rem; letter-spacing:0.06em; margin-top:4px;}

@media (max-width:760px){.epb .stamp{display:none;}
.epb }


.epb main{max-width:920px; margin:0 auto; padding:0 20px 60px;}
.epb .section{padding-top:64px;}
.epb .eyebrow{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; margin-bottom:10px; display:flex; align-items:center; gap:10px;}
.epb .eyebrow::after{content:""; flex:1; height:1px; background:var(--line);}
.epb h2.section-title{font-size:1.7rem; color:var(--off-white);}
.epb p{color:#DDE7EC;}
.epb .lead{color:var(--muted); font-size:1.02rem; max-width:72ch;}
.epb .panel{
  background:var(--panel); border:1px solid var(--line); border-radius:2px;
  padding:26px 28px; margin:22px 0;
}
.epb .panel.tp{border-left:4px solid var(--mint);}
.epb .panel.warn{border-left:4px solid var(--amber);}
.epb .tp-box{display:grid; gap:14px; margin-top:18px;}
.epb .tp-item{display:flex; gap:14px; align-items:flex-start;}
.epb .tp-item .idx{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; min-width:34px;}
.epb .flow{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; margin-top:20px;}
.epb .flow .step{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px; position:relative;}
.epb .flow .step .tag{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase;}
.epb .flow .step h4{font-size:1rem; margin-top:8px; color:var(--off-white);}
.epb .flow .step .time{position:absolute; top:16px; right:16px; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; color:var(--amber);}
.epb .grid-3{display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:14px; margin-top:16px;}
.epb .card{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px;}
.epb .card h4{color:var(--amber); font-size:0.95rem;}
.epb .criteria{display:grid; gap:16px; margin-top:20px;}
.epb .crit{
  background:var(--blueprint-deep); border:1px solid var(--line);
  display:grid; grid-template-columns:64px 1fr; gap:0;
}
.epb .crit .num{
  background:var(--amber); color:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace;
  font-weight:700; display:flex; align-items:center; justify-content:center; font-size:1.05rem;
}
.epb .crit .body{padding:18px 20px;}
.epb .crit h4{color:var(--off-white); font-size:1.02rem; margin-bottom:6px;}
.epb .crit .q{margin-top:10px; padding-top:10px; border-top:1px dashed var(--line); font-family:'IBM Plex Mono',monospace; font-size:0.86rem; color:var(--amber);}
.epb .timeline{margin-top:24px; position:relative; padding-left:34px;}
.epb .timeline::before{content:""; position:absolute; left:11px; top:6px; bottom:6px; width:2px; background:var(--line);}
.epb .tl-item{position:relative; padding-bottom:26px;}
.epb .tl-item:last-child{padding-bottom:0;}
.epb .tl-item::before{content:""; position:absolute; left:-34px; top:2px; width:22px; height:22px; border-radius:50%; background:var(--blueprint); border:2px solid var(--amber);}
.epb .tl-item h4{color:var(--off-white); font-size:1rem; margin-bottom:4px;}
.epb .doc-sample{
  background:var(--paper); color:var(--ink); border-radius:2px; padding:0; margin-top:22px;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .doc-sample .doc-head{background:var(--ink); color:var(--paper); padding:12px 22px; font-family:'IBM Plex Mono',monospace; font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; display:flex; justify-content:space-between;}
.epb .doc-sample .doc-body{padding:26px 28px 30px;}
.epb .doc-sample h3{font-family:'Oswald',sans-serif; color:var(--ink); font-size:1.25rem; text-transform:none; letter-spacing:0;}
.epb .doc-sample ol{padding-left:22px;}
.epb .doc-sample li{margin-bottom:9px;}
.epb .flag{
  display:inline-block; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; font-weight:600;
  background:#D94E2A; color:#fff; padding:1px 7px; border-radius:2px; margin-left:6px; vertical-align:middle;
}
.epb .doc-sample .note-margin{margin-top:6px; font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:#8A3A1F; padding-left:8px; border-left:2px solid #D94E2A;}
.epb .legend{display:flex; flex-wrap:wrap; gap:10px 20px; margin-top:16px;}
.epb .legend span{font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:var(--muted); display:flex; align-items:center; gap:8px;}
.epb .legend .dot{width:10px; height:10px; border-radius:50%; display:inline-block;}
.epb table{width:100%; border-collapse:collapse; margin-top:18px; font-size:0.94rem;}
.epb th, .epb td{border:1px solid var(--line); padding:10px 12px; text-align:left; vertical-align:top;}
.epb th{background:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.06em; color:var(--amber);}
.epb .summary-box{
  border:1px solid var(--amber); background:linear-gradient(180deg, rgba(245,166,35,0.08), transparent);
  padding:24px 26px; margin-top:24px;
}
.epb .summary-box ul{margin:10px 0 0; padding-left:20px;}
.epb .summary-box li{margin-bottom:8px;}
.epb .task-header{
  background:var(--amber); color:var(--blueprint-deep); padding:22px 26px; margin-top:20px;
  display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;
}
.epb .task-header h2{color:var(--blueprint-deep); font-size:1.4rem; margin:0;}
.epb .task-header .badge{font-family:'IBM Plex Mono',monospace; font-size:0.8rem; background:var(--blueprint-deep); color:var(--amber); padding:6px 12px; border-radius:2px;}
.epb .steps-num{counter-reset:step; margin-top:18px; list-style:none; padding:0;}
.epb .steps-num li{counter-increment:step; position:relative; padding-left:44px; margin-bottom:16px;}
.epb .steps-num li::before{content:counter(step,decimal-leading-zero); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; font-size:0.95rem; border:1px solid var(--amber); width:30px; height:26px; display:flex; align-items:center; justify-content:center;}
.epb .rubric-scale{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:16px;}
.epb .rubric-scale div{background:var(--blueprint-deep); border:1px solid var(--line); padding:12px; text-align:center;}
.epb .rubric-scale .n{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:1.3rem; font-weight:700;}
.epb .rubric-scale .l{font-size:0.78rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.05em;}
.epb .notebook{
  background:repeating-linear-gradient(var(--paper), var(--paper) 33px, #D8D3C2 34px);
  color:var(--ink); border-radius:2px; padding:28px 30px; margin-top:20px; position:relative;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .notebook::before{content:""; position:absolute; left:46px; top:0; bottom:0; width:1.5px; background:#C0453A; opacity:0.5;}
.epb .notebook .content{padding-left:24px;}
.epb .notebook h3{color:var(--ink); font-family:'Oswald',sans-serif; letter-spacing:0.02em; margin-bottom:10px;}
.epb .notebook ul{padding-left:20px;}
.epb footer{border-top:1px solid var(--line); padding:24px 20px 40px; color:var(--muted); font-size:0.86rem;}
.epb footer .doc-code{margin-bottom:10px;}
.epb .divider-dash{border:none; border-top:1px dashed var(--line); margin:44px 0 0;}
.epb::selection{background:var(--amber); color:var(--blueprint-deep);}

&lt;/style&gt;
&lt;div class="epb"&gt;
&lt;div class="crop tl"&gt;&lt;/div&gt;&lt;div class="crop tr"&gt;&lt;/div&gt;&lt;div class="crop bl"&gt;&lt;/div&gt;&lt;div class="crop br"&gt;&lt;/div&gt;

&lt;header class="cover"&gt;
  &lt;div class="stamp"&gt;&lt;div class="ring"&gt;&lt;/div&gt;&lt;span&gt;Modul Ajar&lt;b&gt;Terverifikasi&lt;/b&gt;Fase F&lt;/span&gt;&lt;/div&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/RESENSI-2 — 3 JP · 1 Pertemuan&lt;/div&gt;
  &lt;h1&gt;Menganalisis Kaidah Kebahasaan Teks Resensi &amp;amp; Menulis Teks Resensi&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Materi Bahasa Indonesia Kelas XI SMK/MAK — melatih murid menganalisis kaidah kebahasaan teks resensi (kata sifat evaluatif, konjungsi, kalimat opini, kalimat rekomendasi, ragam baku) lalu menulis resensi buku fiksi secara logis dan sistematis. Tugas individu dinilai otomatis dan dapat langsung diunduh sebagai PDF.&lt;/p&gt;

  &lt;div class="meta-strip"&gt;
    &lt;div&gt;&lt;div class="k"&gt;Mata Pelajaran&lt;/div&gt;&lt;div class="v"&gt;Bahasa Indonesia&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK/MAK — Kelas XI&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Elemen&lt;/div&gt;&lt;div class="v"&gt;Membaca, Memirsa &amp;amp; Menulis&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;3 JP (3 × 45 menit)&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;

  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="eyebrow"&gt;01 — Identitas Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Capaian &amp;amp; Tujuan Pembelajaran&lt;/h2&gt;

    &lt;div class="panel tp"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Capaian Pembelajaran (Elemen Membaca–Memirsa &amp;amp; Menulis)&lt;/div&gt;
      &lt;p&gt;Murid mampu menganalisis kaidah kebahasaan teks resensi serta menulis teks resensi buku fiksi dengan struktur lengkap, argumentasi yang beralasan, dan bahasa baku yang komunikatif.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Tujuan Pembelajaran&lt;/div&gt;
      &lt;p style="font-size:1.05rem; color:var(--off-white); font-weight:600;"&gt;Murid mampu menganalisis kaidah kebahasaan teks resensi serta menulis teks resensi buku fiksi secara logis dan sistematis.&lt;/p&gt;

      &lt;div class="tp-box"&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.1&lt;/span&gt;&lt;p&gt;Murid dapat mengidentifikasi kata sifat evaluatif dan menjelaskan fungsinya dalam menyampaikan penilaian.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.2&lt;/span&gt;&lt;p&gt;Murid dapat menganalisis konjungsi (urutan, sebab-akibat, pertentangan, konsesi) yang membangun alur penalaran resensi.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.3&lt;/span&gt;&lt;p&gt;Murid dapat membedakan kalimat fakta, kalimat opini, dan kalimat rekomendasi serta menggunakan ragam bahasa baku.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.4&lt;/span&gt;&lt;p&gt;Murid dapat menggunakan unsur intrinsik fiksi (tema, alur, tokoh, latar, sudut pandang, amanat) sebagai bahan penilaian.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.5&lt;/span&gt;&lt;p&gt;Murid dapat menulis resensi buku fiksi yang runtut: identitas, orientasi, sinopsis, evaluasi, dan rekomendasi.&lt;/p&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="eyebrow" style="margin-top:26px; margin-bottom:6px;"&gt;8 Profil Lulusan yang Dikembangkan&lt;/div&gt;
    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Penalaran Kritis&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menilai karya fiksi dengan alasan yang masuk akal, bukan sekadar selera.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kreativitas&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menulis resensi dengan pembuka menarik dan diksi yang hidup.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Komunikasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menyampaikan penilaian secara santun, jelas, dan sistematis.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kemandirian&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menulis, memeriksa nilai otomatis, dan memperbaiki tulisan sendiri.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-a"&gt;
    &lt;div class="eyebrow"&gt;02 — Materi Inti A&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Peta Kaidah Kebahasaan Teks Resensi&lt;/h2&gt;
    &lt;p class="lead"&gt;Kaidah kebahasaan adalah ciri bahasa yang membuat sebuah teks berfungsi sesuai tujuannya. Karena resensi bertujuan menilai dan merekomendasikan, bahasanya harus mampu menyampaikan penilaian secara beralasan, runtut, dan santun.&lt;/p&gt;

    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Kaidah&lt;/th&gt;&lt;th&gt;Fungsi dalam Resensi&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata sifat evaluatif&lt;/td&gt;&lt;td&gt;Menyatakan penilaian terhadap karya&lt;/td&gt;&lt;td&gt;menarik, apik, memikat, datar, bertele-tele&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi urutan dan sebab-akibat&lt;/td&gt;&lt;td&gt;Menyusun sinopsis dan alasan penilaian&lt;/td&gt;&lt;td&gt;kemudian, akhirnya, karena, sehingga&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi pertentangan dan konsesi&lt;/td&gt;&lt;td&gt;Menyeimbangkan kelebihan dan kekurangan&lt;/td&gt;&lt;td&gt;namun, tetapi, meskipun, sayangnya&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat fakta&lt;/td&gt;&lt;td&gt;Menyampaikan data karya dan isi cerita&lt;/td&gt;&lt;td&gt;Novel ini terdiri atas 232 halaman.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat opini&lt;/td&gt;&lt;td&gt;Menyampaikan pertimbangan peresensi&lt;/td&gt;&lt;td&gt;Alur cerita terasa lambat pada bagian tengah.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat rekomendasi&lt;/td&gt;&lt;td&gt;Mengarahkan pilihan pembaca&lt;/td&gt;&lt;td&gt;Novel ini layak dibaca oleh remaja.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ragam bahasa baku&lt;/td&gt;&lt;td&gt;Menjaga kesantunan dan kredibilitas&lt;/td&gt;&lt;td&gt;tidak, sangat, sebuah (bukan nggak, banget, sebiji)&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-b"&gt;
    &lt;div class="eyebrow"&gt;03 — Materi Inti B&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kata Sifat Evaluatif dan Diksi Penilaian&lt;/h2&gt;
    &lt;p class="lead"&gt;Kata sifat evaluatif adalah kata yang menunjukkan nilai baik atau buruk menurut peresensi. Penilaian yang efektif memakai kata yang tepat dan selalu diikuti alasan.&lt;/p&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Nada Positif&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;menarik, memikat, apik, mengalir, realistis, menyentuh, inspiratif, segar, mendalam, koheren&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Nada Negatif&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;datar, lambat, bertele-tele, klise, mudah ditebak, membingungkan, dangkal, janggal, berlebihan&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Nada Netral&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;sederhana, ringan, padat, beragam, lugas, kronologis&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Dari Penilaian Kosong ke Penilaian Beralasan&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:34%;"&gt;Kurang Efektif&lt;/th&gt;&lt;th&gt;Lebih Efektif (Ada Alasan)&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Novel ini bagus.&lt;/td&gt;&lt;td&gt;Novel ini memikat karena konfliknya berkembang bertahap dan tokohnya bertindak sesuai wataknya.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ceritanya membosankan.&lt;/td&gt;&lt;td&gt;Bagian tengah terasa lambat sebab beberapa adegan serupa diulang tanpa perkembangan konflik.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bahasanya keren banget.&lt;/td&gt;&lt;td&gt;Bahasa penulis mengalir dan kaya gambaran, misalnya saat melukiskan suasana kampung sehabis hujan.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;div class="panel warn" style="margin-top:22px;"&gt;
      &lt;p style="margin:0 0 6px;"&gt;&lt;b style="color:var(--off-white);"&gt;Rumus penilaian beralasan:&lt;/b&gt;&lt;/p&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Penilaian + Alasan + Contoh.&lt;/b&gt; Misalnya: “Tokoh Laras terasa realistis (penilaian) karena ia sempat ragu sebelum mengambil keputusan (alasan), misalnya ketika memilih meninggalkan kampung (contoh).”&lt;/p&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Fakta, Opini, dan Rekomendasi&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:20%;"&gt;Jenis&lt;/th&gt;&lt;th&gt;Ciri&lt;/th&gt;&lt;th&gt;Penanda&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Fakta&lt;/td&gt;&lt;td&gt;Dapat dibuktikan dari buku&lt;/td&gt;&lt;td&gt;angka, nama, tempat, peristiwa&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Opini&lt;/td&gt;&lt;td&gt;Bergantung pada penilaian peresensi&lt;/td&gt;&lt;td&gt;kata sifat evaluatif, terasa, tampak, menurut peresensi&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekomendasi&lt;/td&gt;&lt;td&gt;Saran akhir kepada pembaca&lt;/td&gt;&lt;td&gt;layak dibaca, direkomendasikan, patut dicoba, disarankan&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-c"&gt;
    &lt;div class="eyebrow"&gt;04 — Materi Inti C&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Konjungsi sebagai Penyusun Penalaran&lt;/h2&gt;
    &lt;p class="lead"&gt;Konjungsi membuat resensi mengalir logis. Sinopsis memerlukan konjungsi urutan; evaluasi memerlukan konjungsi sebab-akibat dan pertentangan.&lt;/p&gt;

    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:24%;"&gt;Jenis&lt;/th&gt;&lt;th&gt;Contoh Konjungsi&lt;/th&gt;&lt;th&gt;Kalimat Contoh dalam Resensi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Urutan&lt;/td&gt;&lt;td&gt;kemudian, selanjutnya, setelah itu, akhirnya&lt;/td&gt;&lt;td&gt;Akhirnya, tokoh utama memilih pulang dan memperbaiki hubungan dengan ayahnya.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Sebab-akibat&lt;/td&gt;&lt;td&gt;karena, sebab, sehingga, oleh karena itu&lt;/td&gt;&lt;td&gt;Penokohan terasa kuat karena watak tokoh digambarkan lewat tindakan.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pertentangan&lt;/td&gt;&lt;td&gt;namun, tetapi, akan tetapi, sayangnya&lt;/td&gt;&lt;td&gt;Latar kampung tergambar jelas. Namun, konflik antartokoh diselesaikan terlalu cepat.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konsesi&lt;/td&gt;&lt;td&gt;meskipun, walaupun, kendati&lt;/td&gt;&lt;td&gt;Meskipun alurnya lambat, novel ini menyuguhkan amanat yang menyentuh.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Simpulan&lt;/td&gt;&lt;td&gt;dengan demikian, secara keseluruhan, singkatnya&lt;/td&gt;&lt;td&gt;Secara keseluruhan, novel ini layak dibaca oleh remaja.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-d"&gt;
    &lt;div class="eyebrow"&gt;05 — Materi Inti D&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kalimat Opini, Rekomendasi, dan Kebakuan&lt;/h2&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kalimat Pengantar&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Memperkenalkan karya: “Novel ini mengisahkan…”, “Buku ini menghadirkan…”&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kalimat Opini&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;“Alur cerita terasa lambat…”, “Penulis tampak kurang mendalami latar…”, “Menurut peresensi…”&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kalimat Rekomendasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;“Novel ini layak dibaca oleh…”, “Buku ini direkomendasikan bagi…”&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Mengubah Bahasa Tidak Baku menjadi Baku&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:42%;"&gt;Tidak Baku&lt;/th&gt;&lt;th&gt;Baku&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ceritanya seru banget, gue suka.&lt;/td&gt;&lt;td&gt;Ceritanya sangat seru dan menarik.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penulisnya nggak jelasin latar dengan detil.&lt;/td&gt;&lt;td&gt;Penulis tidak menjelaskan latar dengan rinci.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bukunya bikin kita pengen terus baca.&lt;/td&gt;&lt;td&gt;Buku ini membuat pembaca ingin terus membaca.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pokoknya wajib baca!&lt;/td&gt;&lt;td&gt;Buku ini layak dibaca oleh remaja.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;div class="panel tp" style="margin-top:22px;"&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Sudut pandang peresensi:&lt;/b&gt; utamakan penyebutan orang ketiga (penulis, tokoh, peresensi) agar terkesan objektif. Hindari kata ganti seperti “gue” dan “lo”, serta ungkapan menyerang pribadi pengarang. Kritiklah karyanya, bukan orangnya.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-e"&gt;
    &lt;div class="eyebrow"&gt;06 — Materi Inti E&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Unsur Intrinsik Fiksi sebagai Bahan Penilaian&lt;/h2&gt;
    &lt;p class="lead"&gt;Penilaian terhadap buku fiksi lebih kuat bila bersandar pada unsur intrinsiknya. Pilih dua atau tiga unsur untuk dinilai secara mendalam.&lt;/p&gt;

    &lt;div class="criteria"&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;1&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Tema dan Amanat&lt;/h4&gt;&lt;p&gt;Gagasan pokok dan pesan moral yang hendak disampaikan.&lt;/p&gt;&lt;div class="q"&gt;Tanyakan: apakah tema relevan dan amanat tersampaikan wajar?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;2&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Alur&lt;/h4&gt;&lt;p&gt;Rangkaian peristiwa dari pengenalan, konflik, klimaks, hingga penyelesaian.&lt;/p&gt;&lt;div class="q"&gt;Tanyakan: apakah urutan peristiwa runtut dan memikat?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;3&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Tokoh dan Penokohan&lt;/h4&gt;&lt;p&gt;Siapa pelaku cerita dan bagaimana wataknya digambarkan.&lt;/p&gt;&lt;div class="q"&gt;Tanyakan: apakah tindakan tokoh sesuai wataknya?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;4&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Latar&lt;/h4&gt;&lt;p&gt;Tempat, waktu, dan suasana cerita.&lt;/p&gt;&lt;div class="q"&gt;Tanyakan: apakah latar tergambar hidup dan mendukung cerita?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;5&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Sudut Pandang dan Gaya Bahasa&lt;/h4&gt;&lt;p&gt;Siapa yang bercerita dan bagaimana bahasa dipakai.&lt;/p&gt;&lt;div class="q"&gt;Tanyakan: apakah gaya bahasa mengalir dan konsisten?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-f"&gt;
    &lt;div class="eyebrow"&gt;07 — Materi Inti F&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Langkah Menulis Resensi Buku Fiksi&lt;/h2&gt;

    &lt;div class="timeline"&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;01 · Pilih dan Baca Karya&lt;/h4&gt;&lt;p&gt;Pilih novel atau kumpulan cerpen yang benar-benar sudah dibaca. Catat identitas buku dari halaman judul.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;02 · Catat Unsur Intrinsik&lt;/h4&gt;&lt;p&gt;Tulis tema, alur, tokoh, latar, sudut pandang, dan amanat dalam catatan singkat.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;03 · Tentukan Kelebihan dan Kekurangan&lt;/h4&gt;&lt;p&gt;Cari sedikitnya satu kelebihan dan satu kekurangan beserta contoh dari buku.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;04 · Susun Kerangka&lt;/h4&gt;&lt;p&gt;Urutkan: identitas, orientasi, sinopsis, evaluasi, rekomendasi.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;05 · Kembangkan dengan Kaidah&lt;/h4&gt;&lt;p&gt;Gunakan kata sifat evaluatif, konjungsi pertentangan dan sebab-akibat, kalimat opini beralasan, serta kalimat rekomendasi.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;06 · Sunting&lt;/h4&gt;&lt;p&gt;Periksa kebakuan kata, ejaan, huruf kapital, dan tanda baca. Pastikan sinopsis tidak membocorkan seluruh akhir cerita.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Kerangka dan Kalimat Pembuka yang Dapat Dipakai&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:22%;"&gt;Bagian&lt;/th&gt;&lt;th&gt;Contoh Pola Kalimat&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Orientasi&lt;/td&gt;&lt;td&gt;Novel ini mengisahkan … / Karya ini menghadirkan cerita tentang …&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Sinopsis&lt;/td&gt;&lt;td&gt;Tokoh utama bernama …, seorang …, yang menghadapi … Kemudian … Akhirnya …&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kelebihan&lt;/td&gt;&lt;td&gt;Kelebihan novel ini terletak pada … karena …&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kekurangan&lt;/td&gt;&lt;td&gt;Namun, … terasa … sebab …&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekomendasi&lt;/td&gt;&lt;td&gt;Secara keseluruhan, novel ini layak dibaca oleh …&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="contoh"&gt;
    &lt;div class="eyebrow"&gt;08 — Contoh Teks &amp;amp; Analisis&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Contoh Resensi Fiksi Beranotasi Kaidah&lt;/h2&gt;
    &lt;p class="lead"&gt;Data buku pada contoh bersifat rekaan untuk keperluan pembelajaran.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Contoh Resensi Novel&lt;/span&gt;&lt;span&gt;Dokumen-01&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Pulang untuk Memperbaiki yang Retak&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;&lt;b&gt;Judul buku:&lt;/b&gt; Langit Sehabis Magrib · &lt;b&gt;Penulis:&lt;/b&gt; Laras Ayuningtyas · &lt;b&gt;Penerbit:&lt;/b&gt; Kata Pelita · &lt;b&gt;Tahun terbit:&lt;/b&gt; 2021 · &lt;b&gt;Tebal:&lt;/b&gt; 232 halaman&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Novel ini mengisahkan hubungan seorang anak dengan ayahnya di sebuah kampung nelayan.&lt;span class="flag"&gt;KALIMAT PENGANTAR&lt;/span&gt; Penulis menghadirkan cerita yang sederhana, tetapi&lt;span class="flag"&gt;KONJ. PERTENTANGAN&lt;/span&gt; sarat makna.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Nara, gadis perantau yang bekerja di kota, terpaksa pulang karena ayahnya jatuh sakit. Kemudian,&lt;span class="flag"&gt;KONJ. URUTAN&lt;/span&gt; ia mendapati perahu milik ayahnya hampir dijual untuk menutup utang. Akhirnya,&lt;span class="flag"&gt;KONJ. URUTAN&lt;/span&gt; Nara memilih bertahan di kampung dan belajar melaut bersama para nelayan.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Kelebihan novel ini terletak pada latar kampung yang tergambar hidup&lt;span class="flag"&gt;KATA SIFAT EVALUATIF&lt;/span&gt; karena&lt;span class="flag"&gt;KONJ. SEBAB&lt;/span&gt; penulis melukiskan suara ombak dan aroma ikan asin secara rinci. Tokoh Nara pun terasa realistis&lt;span class="flag"&gt;OPINI BERALASAN&lt;/span&gt; sebab ia sempat ragu sebelum mengambil keputusan. Namun,&lt;span class="flag"&gt;KONJ. PERTENTANGAN&lt;/span&gt; alur pada bagian tengah terasa lambat&lt;span class="flag"&gt;OPINI&lt;/span&gt; sehingga&lt;span class="flag"&gt;KONJ. AKIBAT&lt;/span&gt; pembaca mudah merasa jenuh. Meskipun&lt;span class="flag"&gt;KONSESI&lt;/span&gt; demikian, amanat tentang keberanian berdamai dengan keluarga tersampaikan dengan menyentuh.&lt;/p&gt;
        &lt;p style="margin:10px 0 0; color:#132029 !important;"&gt;Secara keseluruhan,&lt;span class="flag"&gt;KONJ. SIMPULAN&lt;/span&gt; novel ini layak dibaca&lt;span class="flag"&gt;KALIMAT REKOMENDASI&lt;/span&gt; oleh remaja dan siapa pun yang ingin memahami arti pulang.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Rekapitulasi Kaidah yang Ditemukan&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:28%;"&gt;Kaidah&lt;/th&gt;&lt;th&gt;Temuan pada Teks&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata sifat evaluatif&lt;/td&gt;&lt;td&gt;sederhana, sarat makna, hidup, realistis, lambat, menyentuh&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi urutan&lt;/td&gt;&lt;td&gt;kemudian, akhirnya&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi sebab-akibat&lt;/td&gt;&lt;td&gt;karena, sebab, sehingga&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi pertentangan dan konsesi&lt;/td&gt;&lt;td&gt;tetapi, namun, meskipun demikian&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat opini beralasan&lt;/td&gt;&lt;td&gt;“Tokoh Nara pun terasa realistis sebab ia sempat ragu…”&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat rekomendasi&lt;/td&gt;&lt;td&gt;“…novel ini layak dibaca oleh remaja…”&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ragam baku&lt;/td&gt;&lt;td&gt;Seluruh kata baku; tidak ada bahasa gaul&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="eyebrow"&gt;09 — Ringkasan Materi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Poin-Poin Penting&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;ul&gt;
        &lt;li&gt;Kaidah kebahasaan resensi: kata sifat evaluatif, konjungsi, kalimat opini, kalimat rekomendasi, dan ragam baku.&lt;/li&gt;
        &lt;li&gt;Penilaian yang baik beralasan: penilaian, alasan, contoh.&lt;/li&gt;
        &lt;li&gt;Konjungsi urutan menyusun sinopsis; konjungsi sebab-akibat dan pertentangan menyusun evaluasi yang seimbang.&lt;/li&gt;
        &lt;li&gt;Unsur intrinsik fiksi (tema, alur, tokoh, latar, sudut pandang, amanat) menjadi bahan penilaian.&lt;/li&gt;
        &lt;li&gt;Struktur resensi fiksi: identitas, orientasi, sinopsis, evaluasi (kelebihan dan kekurangan), rekomendasi.&lt;/li&gt;
        &lt;li&gt;Gunakan bahasa baku, santun, dan kritik karyanya, bukan pengarangnya.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="tugas-kelompok"&gt;
    &lt;div class="eyebrow"&gt;10 — Lembar Kerja&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Kelompok: Menganalisis Kaidah Kebahasaan Resensi&lt;/h2&gt;
      &lt;span class="badge"&gt;Durasi ± 60 menit&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Tujuan tugas:&lt;/b&gt; Murid menemukan dan menjelaskan kaidah kebahasaan pada sebuah resensi fiksi, lalu memperbaiki kalimat yang belum baku.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Resensi untuk Dianalisis&lt;/span&gt;&lt;span&gt;Dokumen-02&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Persahabatan di Ujung Jalan&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;&lt;b&gt;Judul buku:&lt;/b&gt; Peta Kecil Rangga · &lt;b&gt;Penulis:&lt;/b&gt; Mira Salsabila · &lt;b&gt;Penerbit:&lt;/b&gt; Gema Aksara · &lt;b&gt;Tahun terbit:&lt;/b&gt; 2020&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Novel ini menceritakan Rangga, siswa SMK yang menemukan peta tua di gudang sekolahnya. Kemudian, ia mengajak dua sahabatnya menelusuri petunjuk di peta itu. Akhirnya, mereka menemukan rahasia yang mengubah cara pandang mereka tentang persahabatan.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Kelebihan novel ini terletak pada tokohnya yang realistis karena mereka bertengkar dan berdamai layaknya remaja sungguhan. Bahasanya juga mengalir dan menarik. Tetapi, beberapa adegan petualangan terasa terburu-buru sehingga pembaca kurang bisa menikmati ketegangannya. Penulisnya nggak jelasin latar sekolah dengan detil, jadi gambarannya kurang hidup.&lt;/p&gt;
        &lt;p style="margin:10px 0 0; color:#132029 !important;"&gt;Meskipun begitu, novel ini layak dibaca oleh remaja yang menyukai cerita petualangan dan persahabatan.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;p class="lead" style="margin-top:14px;"&gt;Data buku pada teks ini rekaan untuk keperluan latihan.&lt;/p&gt;

    &lt;h4 style="color:var(--off-white); margin-top:24px;"&gt;Petunjuk Pengerjaan&lt;/h4&gt;
    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;b&gt;Bentuk kelompok&lt;/b&gt; beranggotakan 4–5 orang sesuai pembagian dari guru.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Baca resensi&lt;/b&gt; di atas bersama-sama, lalu tandai kata sifat evaluatif dan konjungsi.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Isi Tabel Identifikasi Kaidah&lt;/b&gt; berikut contoh dan fungsinya.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Temukan kalimat tidak baku&lt;/b&gt; lalu tulis perbaikannya pada Tabel Penyuntingan.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Nilai&lt;/b&gt; apakah setiap penilaian sudah beralasan; tulis satu saran perbaikan.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Presentasikan&lt;/b&gt; hasil maksimal 5 menit per kelompok.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Tabel Identifikasi Kaidah&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:28%;"&gt;Kaidah&lt;/th&gt;&lt;th style="width:36%;"&gt;Temuan pada Teks&lt;/th&gt;&lt;th&gt;Fungsi dalam Resensi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata sifat evaluatif&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi urutan&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi sebab-akibat&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi pertentangan/konsesi&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat opini&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat rekomendasi&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Tabel Penyuntingan Kalimat Tidak Baku&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:50%;"&gt;Kalimat Asli&lt;/th&gt;&lt;th&gt;Perbaikan Baku&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Rubrik Penilaian Kelompok&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Aspek Penilaian&lt;/th&gt;&lt;th&gt;Skor 4 (Sangat Baik)&lt;/th&gt;&lt;th&gt;Skor 3 (Baik)&lt;/th&gt;&lt;th&gt;Skor 2 (Cukup)&lt;/th&gt;&lt;th&gt;Skor 1 (Kurang)&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan identifikasi kaidah&lt;/td&gt;&lt;td&gt;Seluruh kaidah ditemukan tepat&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Sebagian keliru&lt;/td&gt;&lt;td&gt;Tidak tepat&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penjelasan fungsi&lt;/td&gt;&lt;td&gt;Jelas dan runtut&lt;/td&gt;&lt;td&gt;Cukup jelas&lt;/td&gt;&lt;td&gt;Dangkal&lt;/td&gt;&lt;td&gt;Tidak ada&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penyuntingan kalimat&lt;/td&gt;&lt;td&gt;Seluruh perbaikan baku dan tepat&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Sebagian tepat&lt;/td&gt;&lt;td&gt;Tidak tepat&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kerja sama dan presentasi&lt;/td&gt;&lt;td&gt;Semua aktif, presentasi jelas&lt;/td&gt;&lt;td&gt;Sebagian besar aktif&lt;/td&gt;&lt;td&gt;Hanya sebagian aktif&lt;/td&gt;&lt;td&gt;Didominasi satu orang&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="tugas-individu"&gt;
    &lt;div class="eyebrow"&gt;11 — Penutup Pembelajaran&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Individu: Menulis Teks Resensi Buku Fiksi&lt;/h2&gt;
      &lt;span class="badge"&gt;Dikerjakan Mandiri · Dinilai Otomatis · Unduh PDF&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Petunjuk:&lt;/b&gt; Pilih satu novel atau kumpulan cerpen yang sudah kamu baca. Isi identitas buku, lalu tulis resensi (minimal 150 kata) dengan struktur lengkap dan kaidah kebahasaan yang telah dipelajari. Penilaian muncul langsung saat kamu menulis. Setelah yakin, isi No. Absen dan Nama, lalu tekan tombol kirim untuk mengirim nilai dan mengunduh PDF.&lt;/p&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div class="eyebrow" style="margin-bottom:8px;"&gt;Aspek yang Dinilai Otomatis (Total 100, KKM 75)&lt;/div&gt;
      &lt;p style="margin:0; font-size:0.92rem;"&gt;Identitas karya dan judul 10 · Struktur resensi 20 · Unsur intrinsik 10 · Kaidah kebahasaan 30 · Penilaian beralasan 10 · Panjang tulisan 10 · Kalimat dan ejaan dasar 10.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div style="display:grid; grid-template-columns:1fr 2fr; gap:14px; margin-bottom:18px;"&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;No. Absen&lt;/label&gt;
          &lt;input id="mc-absen" type="text" inputmode="numeric" placeholder="Contoh: 12" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Nama Lengkap&lt;/label&gt;
          &lt;input id="mc-nama" type="text" placeholder="Tulis nama lengkap" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div style="display:grid; grid-template-columns:1fr 1fr; gap:14px; margin-bottom:16px;"&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Judul Buku&lt;/label&gt;
          &lt;input id="mc-bjudul" type="text" placeholder="Judul novel/kumpulan cerpen" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Penulis&lt;/label&gt;
          &lt;input id="mc-penulis" type="text" placeholder="Nama penulis" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Penerbit&lt;/label&gt;
          &lt;input id="mc-penerbit" type="text" placeholder="Nama penerbit" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Tahun Terbit&lt;/label&gt;
          &lt;input id="mc-tahun" type="text" inputmode="numeric" placeholder="Contoh: 2021" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Judul Resensi&lt;/label&gt;
      &lt;input id="mc-judul" type="text" placeholder="Contoh: Pulang untuk Memperbaiki yang Retak" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem; margin-bottom:16px;"&gt;

      &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Teks Resensi (minimal 150 kata)&lt;/label&gt;
      &lt;textarea id="mc-teks" rows="14" placeholder="Tulis resensimu di sini..." style="width:100%; padding:12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:1rem; line-height:1.6; resize:vertical;"&gt;&lt;/textarea&gt;
      &lt;p id="mc-stats" class="mono" style="font-size:0.8rem; color:var(--muted); margin:6px 0 0;"&gt;0 kata · 0 kalimat&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div style="display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;"&gt;
        &lt;div class="eyebrow" style="margin:0;"&gt;Penilaian Otomatis&lt;/div&gt;
        &lt;div style="font-family:'Oswald',sans-serif; font-size:2.2rem; color:var(--amber);"&gt;&lt;span id="mc-score"&gt;0&lt;/span&gt;&lt;span style="font-size:1rem; color:var(--muted);"&gt; / 100&lt;/span&gt;&lt;/div&gt;
        &lt;div id="mc-grade" style="color:var(--off-white); font-weight:600;"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div style="height:10px; background:var(--blueprint-deep); border:1px solid var(--line); margin:10px 0 14px;"&gt;&lt;div id="mc-bar" style="height:100%; width:0%; background:var(--amber);"&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div style="overflow-x:auto;"&gt;
        &lt;table style="margin-top:0;"&gt;
          &lt;thead&gt;&lt;tr&gt;&lt;th style="width:34%;"&gt;Aspek&lt;/th&gt;&lt;th style="width:14%;"&gt;Skor&lt;/th&gt;&lt;th&gt;Umpan Balik&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
          &lt;tbody id="mc-live"&gt;&lt;/tbody&gt;
        &lt;/table&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div style="margin-top:22px; display:flex; align-items:center; gap:16px; flex-wrap:wrap;"&gt;
      &lt;button id="mc-kirim" type="button" style="background:var(--amber); color:var(--blueprint-deep); border:none; padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Kirim Nilai &amp;amp; Unduh PDF&lt;/button&gt;
      &lt;button id="mc-ulang" type="button" style="display:none; background:transparent; color:var(--amber); border:1px solid var(--amber); padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Unduh Ulang PDF&lt;/button&gt;
    &lt;/div&gt;
    &lt;p id="mc-st-send" class="mono" style="font-size:0.85rem; margin:12px 0 0;"&gt;&lt;/p&gt;
    &lt;p id="mc-st-pdf" class="mono" style="font-size:0.85rem; margin:4px 0 0;"&gt;&lt;/p&gt;

    &lt;div id="mc-final" style="display:none; margin-top:18px; padding:18px 20px; border:1px solid var(--amber); background:rgba(245,166,35,0.08);"&gt;
      &lt;p style="margin:0; font-size:1.05rem;"&gt;Nilai akhir: &lt;b id="mc-final-score" style="color:var(--amber); font-size:1.3rem;"&gt;&lt;/b&gt; / 100 — &lt;span id="mc-final-grade"&gt;&lt;/span&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/RESENSI-2 — Menganalisis Kaidah Kebahasaan Teks Resensi &amp;amp; Menulis Teks Resensi, 1× pertemuan (3 JP)&lt;/div&gt;
  &lt;p style="margin:0;"&gt;Referensi: Kurikulum Merdeka — Capaian Pembelajaran Bahasa Indonesia Fase F (Elemen Membaca dan Memirsa; Menulis); Buku Teks Bahasa Indonesia Kelas XI SMK/MAK Kemendikbudristek.&lt;/p&gt;
&lt;/footer&gt;
&lt;script&gt;
(function(){
  var ENDPOINT = "https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
  var MATERI = "Menganalisis Kaidah Kebahasaan dan Menulis Teks Resensi";
  var KKM = 75;
  var JSPDF_URL = "https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js";
  var LT = String.fromCharCode(60);
  var GT = String.fromCharCode(62);
  var AM = String.fromCharCode(38);

  function $(id){ return document.getElementById(id); }
  function esc(s){ return String(s).split(AM).join(AM + "amp;").split(LT).join(AM + "lt;").split(GT).join(AM + "gt;"); }
  function tag(n, a, inner){ return LT + n + (a ? " " + a : "") + GT + inner + LT + "/" + n + GT; }

  function wordsOf(s){ var t = s.trim(); return t === "" ? [] : t.split(/\s+/); }
  function tokens(s){ var m = s.toLowerCase().match(/[a-z\u00e0-\u00ff]+/g); return m ? m : []; }
  function sentencesOf(s){ return s.split(/[.!?]+/).map(function(x){ return x.trim(); }).filter(function(x){ return wordsOf(x).length &gt; 2; }); }
  function has(text, w){ return new RegExp("(^|[^a-z])" + w + "([^a-z]|$)").test(text); }
  function firstMatch(text, list){ var f = ""; list.forEach(function(w){ if(f === ""){ if(has(text, w)){ f = w; } } }); return f; }
  function allMatches(text, list){ return list.filter(function(w){ return has(text, w); }); }
  function countWord(text, w){ var m = text.match(new RegExp("(^|[^a-z])" + w + "(?=[^a-z]|$)", "g")); return m ? m.length : 0; }
  function item(label, max, got, note){ return {label: label, max: max, got: got, note: note}; }
  function sum(arr, key){ var t = 0; arr.forEach(function(x){ t += x[key]; }); return t; }

  var UNSUR = ["tema","alur","tokoh","penokohan","latar","sudut pandang","amanat","gaya bahasa","konflik","klimaks","watak"];
  var EVAL = ["menarik","memikat","apik","mengalir","realistis","menyentuh","inspiratif","segar","mendalam","hidup","kuat","menegangkan","sederhana","sarat makna","kaya","datar","lambat","bertele-tele","klise","mudah ditebak","membingungkan","dangkal","janggal","berlebihan","terburu-buru","membosankan","kurang","tidak jelas","rapi","koheren","unik","lugas","menghibur","menyentuh"];
  var KONJ_URUT = ["kemudian","selanjutnya","setelah itu","akhirnya","lalu","mula-mula","pada akhirnya"];
  var KONJ_SEBAB = ["karena","sebab","sehingga","oleh karena itu","akibatnya","maka"];
  var KONJ_LAWAN = ["namun","tetapi","akan tetapi","sayangnya","meskipun","walaupun","kendati","meski","di sisi lain"];
  var OPINI = ["menurut peresensi","menurut saya","terasa","tampak","terkesan","terlihat","dirasa","menurut penulis resensi","menurutku"];
  var REKOM = ["layak dibaca","direkomendasikan","rekomendasi","patut dibaca","disarankan","patut dicoba","dianjurkan","sangat cocok","cocok dibaca","layak dibaca"];
  var TAKBAKU = ["gue","lo","elo","banget","nggak","gak","enggak","udah","udahan","pokoknya","kayak","bikin","pengen","cuma","doang","ngga","sih","deh","dong","jelasin","bgt","sama2","wkwk"];
  var NARASI = ["menceritakan","mengisahkan","bercerita","kisah","tokoh","bernama","tentang","menghadirkan","mengangkat"];

  function evaluate(){
    var text = $("mc-teks").value;
    var low = text.toLowerCase();
    var wc = wordsOf(text).length;
    var sents = sentencesOf(text);
    var head = low.slice(0, 450);
    var groups = [];

    var f1 = $("mc-bjudul").value.trim();
    var f2 = $("mc-penulis").value.trim();
    var f3 = $("mc-penerbit").value.trim();
    var f4 = $("mc-tahun").value.trim();
    var jr = wordsOf($("mc-judul").value).length;
    var g1 = [];
    g1.push(item("Judul buku", 2, f1.length &gt; 1 ? 2 : 0, f1.length &gt; 1 ? "Terisi." : "Isi judul buku."));
    g1.push(item("Penulis", 2, f2.length &gt; 1 ? 2 : 0, f2.length &gt; 1 ? "Terisi." : "Isi nama penulis."));
    g1.push(item("Penerbit", 2, f3.length &gt; 1 ? 2 : 0, f3.length &gt; 1 ? "Terisi." : "Isi nama penerbit."));
    g1.push(item("Tahun terbit", 2, /^[0-9]{4}$/.test(f4) ? 2 : 0, /^[0-9]{4}$/.test(f4) ? "Terisi." : "Isi tahun terbit 4 angka."));
    g1.push(item("Judul resensi", 2, jr &gt; 1 ? 2 : 0, jr &gt; 1 ? "Terisi." : "Beri judul resensi minimal 2 kata."));
    groups.push({name: "Identitas karya dan judul", items: g1});

    var g2 = [];
    var f = firstMatch(head, ["novel","cerpen","kumpulan cerita","buku","karya","cerita"]);
    g2.push(item("Orientasi/pengantar", 4, f !== "" ? 4 : 0, f !== "" ? "Terdeteksi: " + f : "Awali dengan pengantar, mis. Novel ini mengisahkan..."));
    var nar = allMatches(low, NARASI);
    g2.push(item("Sinopsis (tokoh dan jalan cerita)", 5, nar.length &gt; 2 ? 5 : (nar.length &gt; 0 ? 3 : 0), nar.length &gt; 0 ? "Penanda sinopsis: " + nar.slice(0, 4).join(", ") + ". Target minimal 3." : "Ceritakan tokoh dan jalan cerita secara singkat."));
    var kA = firstMatch(low, ["kelebihan","keunggulan","unggul","kekuatan","kelebihannya","keunggulannya"]);
    g2.push(item("Evaluasi: kelebihan", 5, kA !== "" ? 5 : 0, kA !== "" ? "Terdeteksi: " + kA : "Tulis kelebihan dengan kata kelebihan/keunggulan."));
    var kB = firstMatch(low, ["kekurangan","kelemahan","kekurangannya","kelemahannya","sayangnya"]);
    g2.push(item("Evaluasi: kekurangan", 3, kB !== "" ? 3 : 0, kB !== "" ? "Terdeteksi: " + kB : "Tulis kekurangan dengan kata kekurangan/kelemahan/sayangnya."));
    var kC = allMatches(low.slice(Math.max(0, low.length - 450)), REKOM);
    g2.push(item("Rekomendasi", 3, kC.length &gt; 0 ? 3 : 0, kC.length &gt; 0 ? "Terdeteksi: " + kC[0] : "Tutup dengan rekomendasi, mis. Novel ini layak dibaca oleh..."));
    groups.push({name: "Struktur resensi", items: g2});

    var un = allMatches(low, UNSUR);
    var unGot = un.length &gt; 3 ? 10 : (un.length &gt; 2 ? 7 : (un.length &gt; 1 ? 4 : (un.length &gt; 0 ? 2 : 0)));
    groups.push({name: "Unsur intrinsik", items: [item("Unsur intrinsik yang dibahas", 10, unGot, un.length &gt; 0 ? "Terdeteksi: " + un.join(", ") + ". Target minimal 4 unsur." : "Bahas unsur seperti tema, alur, tokoh, latar, amanat.")]});

    var g4 = [];
    var ev = allMatches(low, EVAL);
    g4.push(item("Kata sifat evaluatif", 6, ev.length &gt; 3 ? 6 : (ev.length &gt; 1 ? 4 : (ev.length &gt; 0 ? 2 : 0)), ev.length &gt; 0 ? "Terdeteksi: " + ev.slice(0, 6).join(", ") + ". Target minimal 4." : "Pakai kata penilaian, mis. menarik, realistis, lambat."));
    var cl = allMatches(low, KONJ_LAWAN);
    g4.push(item("Konjungsi pertentangan/konsesi", 5, cl.length &gt; 1 ? 5 : (cl.length &gt; 0 ? 3 : 0), cl.length &gt; 0 ? "Terdeteksi: " + cl.join(", ") + ". Target minimal 2." : "Pakai namun, tetapi, meskipun, sayangnya."));
    var cs = allMatches(low, KONJ_URUT.concat(KONJ_SEBAB));
    g4.push(item("Konjungsi urutan/sebab-akibat", 4, cs.length &gt; 2 ? 4 : (cs.length &gt; 0 ? 2 : 0), cs.length &gt; 0 ? "Terdeteksi: " + cs.join(", ") + ". Target minimal 3." : "Pakai kemudian, akhirnya, karena, sehingga."));
    var op = allMatches(low, OPINI);
    g4.push(item("Kalimat opini (penanda opini)", 4, op.length &gt; 1 ? 4 : (op.length &gt; 0 ? 2 : 0), op.length &gt; 0 ? "Terdeteksi: " + op.join(", ") + ". Target minimal 2." : "Pakai penanda opini: terasa, tampak, terkesan, menurut peresensi."));
    var rk = allMatches(low, REKOM);
    g4.push(item("Kalimat rekomendasi", 3, rk.length &gt; 0 ? 3 : 0, rk.length &gt; 0 ? "Terdeteksi: " + rk[0] : "Tambahkan kalimat layak dibaca/direkomendasikan."));
    var third = allMatches(low, ["penulis","pengarang","peresensi","tokoh","novel ini","buku ini","cerita ini"]);
    var firstP = allMatches(low, ["gue","lo","elo","aku","kamu","kalian"]);
    g4.push(item("Sudut pandang orang ketiga", 3, (third.length &gt; 1 &amp;&amp; firstP.length === 0) ? 3 : (third.length &gt; 0 ? 1 : 0), firstP.length &gt; 0 ? "Hindari kata ganti " + firstP.join(", ") + "; pakai penulis/tokoh/peresensi." : (third.length &gt; 0 ? "Terdeteksi: " + third.slice(0, 4).join(", ") + "." : "Pakai penyebutan penulis, tokoh, atau peresensi.")));
    var tb = allMatches(low, TAKBAKU);
    g4.push(item("Ragam bahasa baku", 5, tb.length === 0 ? 5 : (tb.length === 1 ? 2 : 0), tb.length === 0 ? "Tidak ada kata tidak baku terdeteksi." : "Kata tidak baku: " + tb.join(", ") + ". Ganti dengan bentuk baku."));
    groups.push({name: "Kaidah kebahasaan", items: g4});

    var alas = allMatches(low, ["karena","sebab","misalnya","contohnya","sehingga","yaitu","seperti"]);
    var alasGot = (alas.length &gt; 1 &amp;&amp; ev.length &gt; 0) ? 10 : ((alas.length &gt; 0 &amp;&amp; ev.length &gt; 0) ? 5 : 0);
    groups.push({name: "Penilaian beralasan", items: [item("Penilaian disertai alasan/contoh", 10, alasGot, alasGot &gt; 9 ? "Alasan terdeteksi: " + alas.join(", ") + "." : "Setiap penilaian perlu alasan atau contoh (karena, sebab, misalnya).")]});

    var lenGot = wc &gt; 199 ? 10 : (wc &gt; 149 ? 8 : (wc &gt; 99 ? 4 : (wc &gt; 59 ? 2 : 0)));
    groups.push({name: "Panjang tulisan", items: [item("Jumlah kata", 10, lenGot, wc + " kata. Target 150 kata atau lebih (200 untuk nilai penuh).")]});

    var okSent = 0;
    var raw = text.replace(/([.!?])\s+/g, "$1\n").split("\n").map(function(x){ return x.trim(); }).filter(function(x){ return x.length &gt; 0; });
    raw.forEach(function(s){ if(/^[A-Z“"]/.test(s) &amp;&amp; /[.!?”"]$/.test(s)){ okSent++; } });
    var ratio = raw.length &gt; 0 ? okSent / raw.length : 0;
    var klGot = (raw.length &gt; 4 &amp;&amp; ratio &gt; 0.95) ? 10 : ((raw.length &gt; 2 &amp;&amp; ratio &gt; 0.75) ? 6 : ((raw.length &gt; 1 &amp;&amp; ratio &gt; 0.5) ? 3 : 0));
    groups.push({name: "Kalimat dan ejaan dasar", items: [item("Huruf kapital dan tanda baca akhir", 10, klGot, raw.length + " kalimat, " + okSent + " diawali kapital dan diakhiri tanda baca. Target semua kalimat benar.")]});

    var total = 0;
    groups.forEach(function(g){ g.max = sum(g.items, "max"); g.got = sum(g.items, "got"); total += g.got; });
    return {groups: groups, total: total, words: wc, sentences: sents.length};
  }

  function gradeOf(t){
    if(t &gt; 84){ return "Sangat Baik — tuntas KKM"; }
    if(t &gt; 74){ return "Baik — tuntas KKM"; }
    if(t &gt; 59){ return "Cukup — belum mencapai KKM " + KKM; }
    return "Perlu perbaikan — belum mencapai KKM " + KKM;
  }
  function col(got, max){ if(got &gt;= max){ return "#6EC6CA"; } if(got &gt; 0){ return "#F5A623"; } return "#F4936F"; }

  var TD = "padding:8px 10px; border:1px solid rgba(234,242,245,0.2); vertical-align:top; font-size:0.88rem; color:#EAF2F5 !important;";

  function rowsHtml(r){
    var out = "";
    r.groups.forEach(function(g){
      out += tag("tr", 'style="background:#0A1E2C;"',
        tag("td", 'style="' + TD + ' font-weight:700; color:#F5A623 !important;"', esc(g.name)) +
        tag("td", 'style="' + TD + ' font-weight:700; color:' + col(g.got, g.max) + ' !important; white-space:nowrap;"', g.got + " / " + g.max) +
        tag("td", 'style="' + TD + '"', ""));
      g.items.forEach(function(it){
        out += tag("tr", "",
          tag("td", 'style="' + TD + ' padding-left:22px;"', esc(it.label)) +
          tag("td", 'style="' + TD + ' color:' + col(it.got, it.max) + ' !important; white-space:nowrap;"', it.got + " / " + it.max) +
          tag("td", 'style="' + TD + ' color:#C9D8E0 !important;"', esc(it.note)));
      });
    });
    return out;
  }

  function render(r){
    $("mc-live").innerHTML = rowsHtml(r);
    $("mc-score").textContent = r.total;
    $("mc-bar").style.width = r.total + "%";
    $("mc-grade").textContent = gradeOf(r.total);
    $("mc-stats").textContent = r.words + " kata · " + r.sentences + " kalimat";
  }

  function setMsg(id, text, kind){
    var el = $(id);
    el.textContent = text;
    el.style.setProperty("color", kind === "ok" ? "#6EC6CA" : (kind === "warn" ? "#F5A623" : "#C9D8E0"), "important");
  }

  var timer = null;
  function schedule(){ if(timer){ clearTimeout(timer); } timer = setTimeout(function(){ render(evaluate()); }, 250); }
  ["mc-teks","mc-judul","mc-bjudul","mc-penulis","mc-penerbit","mc-tahun"].forEach(function(id){ $(id).addEventListener("input", schedule); });
  render(evaluate());

  function pdfSafe(s){
    return String(s).replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"').replace(/[\u2013\u2014]/g, "-").replace(/\u2026/g, "...").replace(/\u00b7/g, "-").replace(/[^\x09\x0A\x0D\x20-\x7E\u00A0-\u00FF]/g, "");
  }

  function loadJsPdf(cb){
    if(window.jspdf){ if(window.jspdf.jsPDF){ cb(true); return; } }
    var s = document.createElement("script");
    s.src = JSPDF_URL;
    s.onload = function(){ var ok = false; if(window.jspdf){ if(window.jspdf.jsPDF){ ok = true; } } cb(ok); };
    s.onerror = function(){ cb(false); };
    document.head.appendChild(s);
  }

  function makePdf(info, r, done){
    loadJsPdf(function(ok){
      if(!ok){ done(false); return; }
      try{
        var JS = window.jspdf.jsPDF;
        var doc = new JS({unit: "mm", format: "a4"});
        var M = 18;
        var CW = 174;
        var y = 0;
        function ensure(h){ if(y + h &gt; 278){ doc.addPage(); y = 20; } }

        doc.setFillColor(15, 42, 61);
        doc.rect(0, 0, 210, 34, "F");
        doc.setFillColor(245, 166, 35);
        doc.rect(0, 34, 210, 1.6, "F");
        doc.setTextColor(255, 255, 255);
        doc.setFont("helvetica", "bold");
        doc.setFontSize(17);
        doc.text("Resensi Buku Fiksi Karya Murid", M, 16);
        doc.setFont("helvetica", "normal");
        doc.setFontSize(9);
        doc.text("Bahasa Indonesia Kelas XI SMK - Menganalisis Kaidah Kebahasaan Teks Resensi dan Menulis Teks Resensi", M, 25);

        y = 46;
        doc.setTextColor(20, 32, 41);
        doc.setFontSize(10.5);
        var tgl = new Date().toLocaleDateString("id-ID", {day: "numeric", month: "long", year: "numeric"});
        [["Nama", info.nama], ["No. Absen", info.absen], ["Tanggal", tgl]].forEach(function(row){
          doc.setFont("helvetica", "bold"); doc.text(row[0], M, y);
          doc.setFont("helvetica", "normal"); doc.text(": " + pdfSafe(row[1]), M + 26, y);
          y += 6;
        });
        doc.setFont("helvetica", "bold");
        doc.text("Nilai otomatis", M, y);
        doc.setFont("helvetica", "normal");
        doc.text(": " + r.total + " / 100 (" + pdfSafe(gradeOf(r.total)) + ")", M + 26, y);
        y += 10;

        doc.setDrawColor(200, 200, 200);
        doc.line(M, y - 4, M + CW, y - 4);
        doc.setFont("helvetica", "bold");
        doc.setFontSize(14);
        var jl = doc.splitTextToSize(pdfSafe(info.judul !== "" ? info.judul : "(Tanpa judul)"), CW);
        jl.forEach(function(L){ ensure(7); doc.text(L, M, y); y += 7; });
        y += 2;

        doc.setFont("helvetica", "normal");
        doc.setFontSize(11);
        pdfSafe(info.teks).split("\n").forEach(function(p){
          if(p.trim() === ""){ y += 3; return; }
          doc.splitTextToSize(p, CW).forEach(function(L){ ensure(6); doc.text(L, M, y); y += 5.6; });
          y += 2;
        });

        y += 4;
        ensure(14);
        doc.setFont("helvetica", "bold"); doc.setFontSize(10.5);
        doc.text("Identitas buku: " + pdfSafe(info.bjudul + " - " + info.penulis + " - " + info.penerbit + " - " + info.tahun), M, y); y += 5.5;
        doc.setFont("helvetica", "normal");
        doc.splitTextToSize("Resensi dinilai pada identitas, struktur, unsur intrinsik, kaidah kebahasaan, penilaian beralasan, panjang, dan ejaan.", CW).forEach(function(L){ ensure(6); doc.text(L, M, y); y += 5.2; });

        y += 8;
        ensure(16);
        doc.setFont("helvetica", "bold"); doc.setFontSize(13);
        doc.text("Hasil Penilaian Otomatis", M, y); y += 7;
        r.groups.forEach(function(g){
          ensure(14);
          doc.setFillColor(238, 241, 244);
          doc.rect(M, y - 4.6, CW, 6.6, "F");
          doc.setFont("helvetica", "bold"); doc.setFontSize(10);
          doc.setTextColor(20, 32, 41);
          doc.text(pdfSafe(g.name), M + 2, y);
          doc.text(g.got + " / " + g.max, M + CW - 2, y, {align: "right"});
          y += 6.2;
          g.items.forEach(function(it){
            var nl = doc.splitTextToSize(pdfSafe(it.note), CW - 12);
            ensure(5 + nl.length * 4.2);
            doc.setFont("helvetica", "normal"); doc.setFontSize(9.5);
            doc.setTextColor(20, 32, 41);
            doc.text(pdfSafe(it.label), M + 4, y);
            doc.text(it.got + " / " + it.max, M + CW - 2, y, {align: "right"});
            y += 4.4;
            doc.setFontSize(8.5);
            doc.setTextColor(95, 105, 112);
            nl.forEach(function(L){ doc.text(L, M + 8, y); y += 4; });
            y += 1.4;
          });
          y += 2;
        });

        var n = doc.getNumberOfPages();
        for(var i = 1; n &gt;= i; i++){
          doc.setPage(i);
          doc.setFont("helvetica", "normal");
          doc.setFontSize(8);
          doc.setTextColor(120, 128, 135);
          doc.text("Penilaian otomatis berbasis aturan kebahasaan; mohon ditinjau oleh guru.", M, 290);
          doc.text("Halaman " + i + " / " + n, M + CW, 290, {align: "right"});
        }

        var fn = "Resensi_" + info.absen + "_" + info.nama.replace(/[^A-Za-z0-9]+/g, "_") + ".pdf";
        doc.save(fn);
        done(true);
      }catch(e){
        done(false);
      }
    });
  }

  function printFallback(info, r){
    var w = window.open("", "_blank");
    if(!w){ return false; }
    var d = w.document;
    d.open();
    d.write(tag("title", "", "Resensi " + esc(info.nama)));
    d.close();
    var b = d.body ? d.body : d.documentElement.appendChild(d.createElement("body"));
    b.style.fontFamily = "Arial, sans-serif";
    b.style.padding = "24px";
    b.style.background = "#fff";
    b.style.color = "#111";
    var paras = esc(info.teks).split("\n").map(function(p){ return tag("p", "", p); }).join("");
    b.innerHTML = tag("h2", "", esc(info.judul)) + tag("p", "", esc(info.nama) + " - No. Absen " + esc(info.absen) + " - Nilai otomatis " + r.total + " / 100") + tag("p", "", "Buku: " + esc(info.bjudul) + " - " + esc(info.penulis) + " - " + esc(info.penerbit) + " - " + esc(info.tahun)) + paras;
    w.focus();
    w.print();
    return true;
  }

  function sendScore(info, skor){
    var p = new URLSearchParams();
    p.append("noAbsen", info.absen);
    p.append("nama", info.nama);
    p.append("skor", skor);
    p.append("materi", MATERI);
    p.append("waktu", new Date().toISOString());
    return fetch(ENDPOINT, {method: "POST", mode: "no-cors", headers: {"Content-Type": "application/x-www-form-urlencoded"}, body: p.toString()});
  }

  var busy = false;
  var sentOnce = false;
  var lastInfo = null;
  var lastResult = null;

  function pdfFlow(info, r){
    makePdf(info, r, function(ok){
      if(ok){ setMsg("mc-st-pdf", "PDF berhasil diunduh.", "ok"); }
      else {
        var shown = printFallback(info, r);
        setMsg("mc-st-pdf", shown ? "PDF otomatis tidak tersedia; jendela cetak dibuka, pilih Simpan sebagai PDF." : "PDF gagal dibuat. Izinkan pop-up atau coba lagi.", "warn");
      }
    });
  }

  $("mc-kirim").addEventListener("click", function(){
    if(busy){ return; }
    var absen = $("mc-absen").value.trim();
    var nama = $("mc-nama").value.trim();
    if(absen === "" || nama === ""){ setMsg("mc-st-send", "Isi No. Absen dan Nama terlebih dahulu.", "warn"); return; }
    var r = evaluate();
    if(150 &gt; r.words){ setMsg("mc-st-send", "Resensi terlalu pendek (" + r.words + " kata). Tulis minimal 150 kata sebelum dikirim.", "warn"); return; }
    if(!window.confirm("Kirim nilai final dan unduh PDF? Pastikan resensimu sudah selesai.")){ return; }
    var resend = true;
    if(sentOnce){ resend = window.confirm("Nilai sudah pernah dikirim dari halaman ini. Kirim ulang nilai terbaru?"); }
    busy = true;
    var info = {absen: absen, nama: nama, judul: $("mc-judul").value.trim(), teks: $("mc-teks").value, bjudul: $("mc-bjudul").value.trim(), penulis: $("mc-penulis").value.trim(), penerbit: $("mc-penerbit").value.trim(), tahun: $("mc-tahun").value.trim()};
    lastInfo = info;
    lastResult = r;
    render(r);
    $("mc-final").style.display = "block";
    $("mc-final-score").textContent = r.total;
    $("mc-final-grade").textContent = gradeOf(r.total);
    setMsg("mc-st-pdf", "Membuat PDF...", "info");
    pdfFlow(info, r);
    if(resend){
      setMsg("mc-st-send", "Mengirim nilai...", "info");
      sendScore(info, r.total).then(function(){
        sentOnce = true;
        setMsg("mc-st-send", "Nilai berhasil dikirim ke guru.", "ok");
        busy = false;
      }).catch(function(){
        setMsg("mc-st-send", "Nilai gagal dikirim. Tunjukkan PDF dan skor ini kepada guru.", "warn");
        busy = false;
      });
    } else {
      setMsg("mc-st-send", "Nilai tidak dikirim ulang.", "info");
      busy = false;
    }
    $("mc-ulang").style.display = "inline-block";
  });

  $("mc-ulang").addEventListener("click", function(){
    if(lastInfo){ pdfFlow(lastInfo, lastResult); }
  });
})();

&lt;/script&gt;
&lt;/div&gt;</description><link>http://kitab-investasi.blogspot.com/2026/11/menganalisis-kaidah-kebahasaan-teks.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-1745507602303206946</guid><pubDate>Thu, 05 Nov 2026 08:05:19 +0000</pubDate><atom:updated>2026-11-05T15:05:19.389+07:00</atom:updated><title>Memahami Isi &amp; Mengevaluasi Struktur Teks Resensi</title><description>&lt;style&gt;
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&amp;family=Source+Sans+3:wght@400;500;600;700&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;display=swap');
.epb{
  --blueprint:#0F2A3D;
  --blueprint-deep:#0A1E2C;
  --panel:#4A2E12;
  --paper:#F3F0E6;
  --ink:#132029;
  --amber:#F5A623;
  --amber-deep:#C97F12;
  --mint:#6EC6CA;
  --line:rgba(234,242,245,0.16);
  --off-white:#EAF2F5;
  --muted:#9FB4C2;
}
.epb *{box-sizing:border-box;}
.epb{scroll-behavior:smooth; background:#0A1F2E;}
.epb{
  margin:0;
  position:relative;
  overflow:hidden;
  background:var(--blueprint);
  color:var(--off-white);
  font-family:'Source Sans 3',sans-serif;
  line-height:1.65;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 34px 34px;
}
.epb h1, .epb h2, .epb h3, .epb h4{font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.02em; margin:0 0 0.5em;}
.epb .mono{font-family:'IBM Plex Mono',monospace;}
.epb a{color:var(--amber);}
.epb .crop{position:absolute; width:22px; height:22px; z-index:5; opacity:.55; pointer-events:none;}
.epb .crop::before, .epb .crop::after{content:"";position:absolute;background:var(--mint);}
.epb .crop::before{width:100%;height:1px;top:50%;left:0;}
.epb .crop::after{width:1px;height:100%;left:50%;top:0;}
.epb .crop.tl{top:14px;left:14px;}
.epb .crop.tr{top:14px;right:14px;}
.epb .crop.bl{bottom:14px;left:14px;}
.epb .crop.br{bottom:14px;right:14px;}
.epb header.cover{
  position:relative;
  padding:40px 24px 36px;
  border-bottom:1px solid var(--line);
  overflow:hidden;
}
.epb .doc-code{font-family:'IBM Plex Mono',monospace; color:var(--amber); letter-spacing:0.15em; font-size:0.78rem; text-transform:uppercase; margin-bottom:18px; display:flex; gap:10px; align-items:center;}
.epb .doc-code::before{content:"◆"; color:var(--amber);}
.epb .cover h1{font-size:clamp(1.6rem,7vw,2.5rem); color:var(--off-white); max-width:16ch; line-height:1.05;}
.epb .cover .subtitle{color:var(--muted); font-size:1.05rem; max-width:60ch; margin-top:14px;}
.epb .meta-strip{
  margin-top:38px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px dashed var(--line); border-bottom:1px dashed var(--line);
}
.epb .meta-strip div{padding:14px 16px; border-right:1px dashed var(--line);}
.epb .meta-strip div:last-child{border-right:none;}
.epb .meta-strip .k{font-family:'IBM Plex Mono',monospace; font-size:0.68rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.1em;}
.epb .meta-strip .v{font-size:0.98rem; color:var(--off-white); margin-top:4px; font-weight:600;}
.epb .stamp{
  position:absolute; top:40px; right:20px; width:104px; height:104px;
  border:2.5px solid var(--amber); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  transform:rotate(-11deg);
  color:var(--amber); font-family:'IBM Plex Mono',monospace;
  text-align:center; font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase;
  opacity:0.9;
}
.epb .stamp .ring{position:absolute; inset:8px; border:1px dashed var(--amber); border-radius:50%;}
.epb .stamp b{display:block; font-family:'Oswald',sans-serif; font-size:1.05rem; letter-spacing:0.06em; margin-top:4px;}

@media (max-width:760px){.epb .stamp{display:none;}
.epb }


.epb main{max-width:920px; margin:0 auto; padding:0 20px 60px;}
.epb .section{padding-top:64px;}
.epb .eyebrow{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; margin-bottom:10px; display:flex; align-items:center; gap:10px;}
.epb .eyebrow::after{content:""; flex:1; height:1px; background:var(--line);}
.epb h2.section-title{font-size:1.7rem; color:var(--off-white);}
.epb p{color:#DDE7EC;}
.epb .lead{color:var(--muted); font-size:1.02rem; max-width:72ch;}
.epb .panel{
  background:var(--panel); border:1px solid var(--line); border-radius:2px;
  padding:26px 28px; margin:22px 0;
}
.epb .panel.tp{border-left:4px solid var(--mint);}
.epb .panel.warn{border-left:4px solid var(--amber);}
.epb .tp-box{display:grid; gap:14px; margin-top:18px;}
.epb .tp-item{display:flex; gap:14px; align-items:flex-start;}
.epb .tp-item .idx{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; min-width:34px;}
.epb .flow{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; margin-top:20px;}
.epb .flow .step{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px; position:relative;}
.epb .flow .step .tag{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase;}
.epb .flow .step h4{font-size:1rem; margin-top:8px; color:var(--off-white);}
.epb .flow .step .time{position:absolute; top:16px; right:16px; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; color:var(--amber);}
.epb .grid-3{display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:14px; margin-top:16px;}
.epb .card{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px;}
.epb .card h4{color:var(--amber); font-size:0.95rem;}
.epb .criteria{display:grid; gap:16px; margin-top:20px;}
.epb .crit{
  background:var(--blueprint-deep); border:1px solid var(--line);
  display:grid; grid-template-columns:64px 1fr; gap:0;
}
.epb .crit .num{
  background:var(--amber); color:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace;
  font-weight:700; display:flex; align-items:center; justify-content:center; font-size:1.05rem;
}
.epb .crit .body{padding:18px 20px;}
.epb .crit h4{color:var(--off-white); font-size:1.02rem; margin-bottom:6px;}
.epb .crit .q{margin-top:10px; padding-top:10px; border-top:1px dashed var(--line); font-family:'IBM Plex Mono',monospace; font-size:0.86rem; color:var(--amber);}
.epb .timeline{margin-top:24px; position:relative; padding-left:34px;}
.epb .timeline::before{content:""; position:absolute; left:11px; top:6px; bottom:6px; width:2px; background:var(--line);}
.epb .tl-item{position:relative; padding-bottom:26px;}
.epb .tl-item:last-child{padding-bottom:0;}
.epb .tl-item::before{content:""; position:absolute; left:-34px; top:2px; width:22px; height:22px; border-radius:50%; background:var(--blueprint); border:2px solid var(--amber);}
.epb .tl-item h4{color:var(--off-white); font-size:1rem; margin-bottom:4px;}
.epb .doc-sample{
  background:var(--paper); color:var(--ink); border-radius:2px; padding:0; margin-top:22px;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .doc-sample .doc-head{background:var(--ink); color:var(--paper); padding:12px 22px; font-family:'IBM Plex Mono',monospace; font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; display:flex; justify-content:space-between;}
.epb .doc-sample .doc-body{padding:26px 28px 30px;}
.epb .doc-sample h3{font-family:'Oswald',sans-serif; color:var(--ink); font-size:1.25rem; text-transform:none; letter-spacing:0;}
.epb .doc-sample ol{padding-left:22px;}
.epb .doc-sample li{margin-bottom:9px;}
.epb .flag{
  display:inline-block; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; font-weight:600;
  background:#D94E2A; color:#fff; padding:1px 7px; border-radius:2px; margin-left:6px; vertical-align:middle;
}
.epb .doc-sample .note-margin{margin-top:6px; font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:#8A3A1F; padding-left:8px; border-left:2px solid #D94E2A;}
.epb .legend{display:flex; flex-wrap:wrap; gap:10px 20px; margin-top:16px;}
.epb .legend span{font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:var(--muted); display:flex; align-items:center; gap:8px;}
.epb .legend .dot{width:10px; height:10px; border-radius:50%; display:inline-block;}
.epb table{width:100%; border-collapse:collapse; margin-top:18px; font-size:0.94rem;}
.epb th, .epb td{border:1px solid var(--line); padding:10px 12px; text-align:left; vertical-align:top;}
.epb th{background:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.06em; color:var(--amber);}
.epb .summary-box{
  border:1px solid var(--amber); background:linear-gradient(180deg, rgba(245,166,35,0.08), transparent);
  padding:24px 26px; margin-top:24px;
}
.epb .summary-box ul{margin:10px 0 0; padding-left:20px;}
.epb .summary-box li{margin-bottom:8px;}
.epb .task-header{
  background:var(--amber); color:var(--blueprint-deep); padding:22px 26px; margin-top:20px;
  display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;
}
.epb .task-header h2{color:var(--blueprint-deep); font-size:1.4rem; margin:0;}
.epb .task-header .badge{font-family:'IBM Plex Mono',monospace; font-size:0.8rem; background:var(--blueprint-deep); color:var(--amber); padding:6px 12px; border-radius:2px;}
.epb .steps-num{counter-reset:step; margin-top:18px; list-style:none; padding:0;}
.epb .steps-num li{counter-increment:step; position:relative; padding-left:44px; margin-bottom:16px;}
.epb .steps-num li::before{content:counter(step,decimal-leading-zero); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; font-size:0.95rem; border:1px solid var(--amber); width:30px; height:26px; display:flex; align-items:center; justify-content:center;}
.epb .rubric-scale{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:16px;}
.epb .rubric-scale div{background:var(--blueprint-deep); border:1px solid var(--line); padding:12px; text-align:center;}
.epb .rubric-scale .n{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:1.3rem; font-weight:700;}
.epb .rubric-scale .l{font-size:0.78rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.05em;}
.epb .notebook{
  background:repeating-linear-gradient(var(--paper), var(--paper) 33px, #D8D3C2 34px);
  color:var(--ink); border-radius:2px; padding:28px 30px; margin-top:20px; position:relative;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .notebook::before{content:""; position:absolute; left:46px; top:0; bottom:0; width:1.5px; background:#C0453A; opacity:0.5;}
.epb .notebook .content{padding-left:24px;}
.epb .notebook h3{color:var(--ink); font-family:'Oswald',sans-serif; letter-spacing:0.02em; margin-bottom:10px;}
.epb .notebook ul{padding-left:20px;}
.epb footer{border-top:1px solid var(--line); padding:24px 20px 40px; color:var(--muted); font-size:0.86rem;}
.epb footer .doc-code{margin-bottom:10px;}
.epb .divider-dash{border:none; border-top:1px dashed var(--line); margin:44px 0 0;}
.epb::selection{background:var(--amber); color:var(--blueprint-deep);}

&lt;/style&gt;
&lt;div class="epb"&gt;
&lt;div class="crop tl"&gt;&lt;/div&gt;&lt;div class="crop tr"&gt;&lt;/div&gt;&lt;div class="crop bl"&gt;&lt;/div&gt;&lt;div class="crop br"&gt;&lt;/div&gt;

&lt;header class="cover"&gt;
  &lt;div class="stamp"&gt;&lt;div class="ring"&gt;&lt;/div&gt;&lt;span&gt;Modul Ajar&lt;b&gt;Terverifikasi&lt;/b&gt;Fase F&lt;/span&gt;&lt;/div&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/RESENSI-1 — 3 JP · 1 Pertemuan&lt;/div&gt;
  &lt;h1&gt;Memahami Isi &amp;amp; Mengevaluasi Struktur Teks Resensi&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Materi Bahasa Indonesia Kelas XI SMK/MAK — melatih murid memahami isi teks resensi (identitas karya, sinopsis, penilaian, rekomendasi), mengevaluasi kelengkapan dan ketepatan strukturnya, serta membandingkannya dengan teks lain. Ditutup dengan tugas individu membuat rangkuman yang dinilai otomatis dan dapat langsung diunduh sebagai PDF.&lt;/p&gt;

  &lt;div class="meta-strip"&gt;
    &lt;div&gt;&lt;div class="k"&gt;Mata Pelajaran&lt;/div&gt;&lt;div class="v"&gt;Bahasa Indonesia&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK/MAK — Kelas XI&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Elemen&lt;/div&gt;&lt;div class="v"&gt;Membaca, Memirsa &amp;amp; Menulis&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;3 JP (3 × 45 menit)&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;

  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="eyebrow"&gt;01 — Identitas Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Capaian &amp;amp; Tujuan Pembelajaran&lt;/h2&gt;

    &lt;div class="panel tp"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Capaian Pembelajaran (Elemen Membaca–Memirsa &amp;amp; Menulis)&lt;/div&gt;
      &lt;p&gt;Murid mampu membaca dan memahami isi teks resensi, mengevaluasi struktur dan kebahasaannya secara kritis, serta membandingkannya dengan teks lain yang memiliki tujuan berbeda.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Tujuan Pembelajaran&lt;/div&gt;
      &lt;p style="font-size:1.05rem; color:var(--off-white); font-weight:600;"&gt;Murid mampu memahami isi teks resensi serta mengevaluasi struktur teks resensi dan membandingkannya dengan teks lain.&lt;/p&gt;

      &lt;div class="tp-box"&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.1&lt;/span&gt;&lt;p&gt;Murid dapat menjelaskan pengertian, tujuan, dan manfaat teks resensi.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.2&lt;/span&gt;&lt;p&gt;Murid dapat menemukan informasi tersurat dan tersirat serta membedakan fakta dan penilaian (opini) dalam teks resensi.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.3&lt;/span&gt;&lt;p&gt;Murid dapat mengidentifikasi struktur teks resensi: identitas karya, orientasi, tafsiran (sinopsis), evaluasi, dan rekomendasi.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.4&lt;/span&gt;&lt;p&gt;Murid dapat mengevaluasi kelengkapan, keseimbangan, dan ketepatan struktur sebuah resensi menggunakan kriteria yang jelas.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.5&lt;/span&gt;&lt;p&gt;Murid dapat membandingkan teks resensi dengan sinopsis, ulasan produk, berita, dan teks lain, lalu merangkum isi resensi dengan bahasa sendiri.&lt;/p&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="eyebrow" style="margin-top:26px; margin-bottom:6px;"&gt;8 Profil Lulusan yang Dikembangkan&lt;/div&gt;
    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Penalaran Kritis&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menilai apakah sebuah resensi lengkap, seimbang, dan beralasan.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Komunikasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Merangkum gagasan pokok dengan kalimat ringkas dan jelas.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kolaborasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Bekerja dalam kelompok membandingkan dua teks secara terstruktur.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kemandirian&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menyelesaikan rangkuman, memeriksa nilai, dan memperbaikinya sendiri.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-a"&gt;
    &lt;div class="eyebrow"&gt;02 — Materi Inti A&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Apa Itu Teks Resensi?&lt;/h2&gt;
    &lt;p class="lead"&gt;Resensi adalah ulasan atau penilaian terhadap suatu karya, misalnya buku, novel, film, pertunjukan, atau produk. Penulis resensi (peresensi) tidak sekadar menceritakan isi, tetapi juga menimbang kelebihan dan kekurangan karya itu, lalu menyarankan apakah karya tersebut layak dibaca atau ditonton.&lt;/p&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Tujuan&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Memberi informasi dan penilaian agar calon pembaca atau penonton dapat memutuskan apakah karya itu sesuai kebutuhannya.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Manfaat bagi Pembaca&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menghemat waktu memilih bacaan, memahami gambaran isi, dan memperoleh sudut pandang kritis.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Manfaat bagi Pengarang&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Memperoleh masukan membangun dan memperkenalkan karyanya kepada khalayak yang lebih luas.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Objek Resensi yang Dekat dengan Dunia SMK&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Objek&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;th&gt;Hal yang Biasanya Dinilai&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Buku nonfiksi/teknis&lt;/td&gt;&lt;td&gt;Buku panduan perawatan mesin, kewirausahaan, desain grafis&lt;/td&gt;&lt;td&gt;Ketepatan isi, kejelasan penyajian, kelengkapan gambar dan contoh&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Novel/cerpen&lt;/td&gt;&lt;td&gt;Novel remaja bertema dunia kerja&lt;/td&gt;&lt;td&gt;Alur, tokoh, latar, amanat, gaya bahasa&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Film/dokumenter&lt;/td&gt;&lt;td&gt;Film dokumenter tentang industri&lt;/td&gt;&lt;td&gt;Cerita, sinematografi, akting, pesan&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Produk/perangkat&lt;/td&gt;&lt;td&gt;Gawai, peralatan bengkel&lt;/td&gt;&lt;td&gt;Fungsi, kualitas, harga, kelebihan dan kekurangan&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;div class="panel warn" style="margin-top:22px;"&gt;
      &lt;p style="margin:0 0 6px;"&gt;&lt;b style="color:var(--off-white);"&gt;Ingat:&lt;/b&gt;&lt;/p&gt;
      &lt;p style="margin:0;"&gt;Resensi bukan ringkasan semata dan bukan promosi semata. Resensi yang baik memuat &lt;b style="color:var(--off-white);"&gt;fakta&lt;/b&gt; (data karya dan isi) sekaligus &lt;b style="color:var(--off-white);"&gt;penilaian&lt;/b&gt; yang disertai alasan.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-b"&gt;
    &lt;div class="eyebrow"&gt;03 — Materi Inti B&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Memahami Isi Teks Resensi&lt;/h2&gt;
    &lt;p class="lead"&gt;Memahami isi berarti mampu menjawab pertanyaan: apa karya yang diresensi, tentang apa, bagaimana penilaian peresensi, dan apa saran akhirnya. Gunakan langkah membaca berikut.&lt;/p&gt;

    &lt;div class="timeline"&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;01 · Baca Cepat Identitas&lt;/h4&gt;&lt;p&gt;Temukan judul, pengarang, penerbit, tahun terbit, tebal halaman, atau data film/produk. Bagian ini menjawab “karya apa yang dibahas?”.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;02 · Temukan Gagasan Pokok Isi&lt;/h4&gt;&lt;p&gt;Cari kalimat yang menyampaikan inti cerita atau isi karya: siapa tokoh/pokok bahasan, masalah apa, dan bagaimana perkembangannya.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;03 · Tandai Penilaian&lt;/h4&gt;&lt;p&gt;Tandai kata dan kalimat yang bernada menilai, seperti menarik, apik, kurang, lambat, mudah ditebak, patut diapresiasi.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;04 · Pisahkan Fakta dan Opini&lt;/h4&gt;&lt;p&gt;Fakta dapat dibuktikan (tahun terbit, jumlah halaman, nama tokoh). Opini bergantung pada penilaian peresensi (bahasanya indah, alurnya membosankan).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;05 · Simpulkan Rekomendasi&lt;/h4&gt;&lt;p&gt;Tentukan kepada siapa karya itu disarankan dan dengan alasan apa. Ini biasanya berada pada bagian akhir teks.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:28px;"&gt;Informasi Tersurat dan Tersirat&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:24%;"&gt;Jenis&lt;/th&gt;&lt;th&gt;Pengertian&lt;/th&gt;&lt;th&gt;Contoh pada Resensi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tersurat&lt;/td&gt;&lt;td&gt;Informasi yang tertulis jelas pada teks&lt;/td&gt;&lt;td&gt;“Novel setebal 216 halaman ini diterbitkan tahun 2022.”&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tersirat&lt;/td&gt;&lt;td&gt;Informasi yang harus disimpulkan dari petunjuk dalam teks&lt;/td&gt;&lt;td&gt;Peresensi menyebut “alur bagian tengah terasa lambat”, sehingga dapat disimpulkan bahwa bagian tersebut kurang memikat.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Fakta atau Opini?&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:55%;"&gt;Kalimat&lt;/th&gt;&lt;th&gt;Jenis&lt;/th&gt;&lt;th&gt;Alasan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Buku ini diterbitkan oleh Penerbit Cahaya Aksara pada tahun 2022.&lt;/td&gt;&lt;td&gt;Fakta&lt;/td&gt;&lt;td&gt;Dapat diperiksa pada halaman identitas buku.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bahasa yang digunakan penulis sangat mengalir dan menyenangkan.&lt;/td&gt;&lt;td&gt;Opini&lt;/td&gt;&lt;td&gt;Kata “mengalir” dan “menyenangkan” adalah penilaian peresensi.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tokoh Dimas adalah siswa SMK jurusan teknik sepeda motor.&lt;/td&gt;&lt;td&gt;Fakta&lt;/td&gt;&lt;td&gt;Informasi isi cerita yang dapat dibuktikan dari buku.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Akhir cerita terlalu mudah ditebak.&lt;/td&gt;&lt;td&gt;Opini&lt;/td&gt;&lt;td&gt;Menilai kualitas akhir cerita menurut peresensi.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-c"&gt;
    &lt;div class="eyebrow"&gt;04 — Materi Inti C&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Struktur Teks Resensi&lt;/h2&gt;
    &lt;p class="lead"&gt;Resensi yang utuh umumnya disusun dari lima bagian. Urutannya dapat sedikit berbeda, tetapi kelimanya sebaiknya hadir.&lt;/p&gt;

    &lt;div class="criteria"&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;1&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Identitas Karya&lt;/h4&gt;&lt;p&gt;Data singkat karya: judul, pengarang/pembuat, penerbit atau produksi, tahun, tebal atau durasi, dan harga bila perlu.&lt;/p&gt;&lt;div class="q"&gt;Penanda: Judul, Penulis, Penerbit, Tahun terbit, Tebal&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;2&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Orientasi&lt;/h4&gt;&lt;p&gt;Pengantar yang memperkenalkan karya: latar belakang pengarang, tema umum, atau alasan karya itu menarik dibahas.&lt;/p&gt;&lt;div class="q"&gt;Penanda: “Novel ini bercerita tentang…”, “Buku ini hadir sebagai…”&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;3&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Tafsiran (Sinopsis)&lt;/h4&gt;&lt;p&gt;Ringkasan isi: tokoh atau pokok bahasan, masalah, perkembangan, dan penyelesaian. Disajikan secukupnya tanpa membocorkan seluruh akhir cerita.&lt;/p&gt;&lt;div class="q"&gt;Penanda: kata kerja peristiwa dan konjungsi urutan (kemudian, akhirnya)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;4&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Evaluasi (Pertimbangan)&lt;/h4&gt;&lt;p&gt;Penilaian terhadap kelebihan dan kekurangan, masing-masing disertai alasan atau contoh dari karya.&lt;/p&gt;&lt;div class="q"&gt;Penanda: kata sifat evaluatif, konjungsi pertentangan (namun, meskipun, sayangnya)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;5&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Rekomendasi&lt;/h4&gt;&lt;p&gt;Simpulan dan saran: untuk siapa karya itu cocok dan apakah layak dibaca atau ditonton.&lt;/p&gt;&lt;div class="q"&gt;Penanda: “direkomendasikan bagi…”, “layak dibaca oleh…”&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:28px;"&gt;Kaidah Kebahasaan Teks Resensi&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:28%;"&gt;Ciri&lt;/th&gt;&lt;th&gt;Penjelasan&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata sifat evaluatif&lt;/td&gt;&lt;td&gt;Menyatakan penilaian terhadap karya&lt;/td&gt;&lt;td&gt;menarik, apik, realistis, membosankan, kurang mendalam&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi pertentangan&lt;/td&gt;&lt;td&gt;Menghubungkan kelebihan dengan kekurangan&lt;/td&gt;&lt;td&gt;namun, tetapi, meskipun demikian, sayangnya&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Konjungsi urutan dan sebab-akibat&lt;/td&gt;&lt;td&gt;Menyusun sinopsis secara runtut&lt;/td&gt;&lt;td&gt;kemudian, akhirnya, karena, sehingga&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Istilah khusus&lt;/td&gt;&lt;td&gt;Istilah bidang karya&lt;/td&gt;&lt;td&gt;alur, tokoh, latar, amanat, sinematografi, spesifikasi&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kalimat rekomendasi&lt;/td&gt;&lt;td&gt;Mengarahkan pilihan pembaca&lt;/td&gt;&lt;td&gt;Buku ini layak dibaca oleh murid SMK.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ragam bahasa baku&lt;/td&gt;&lt;td&gt;Formal namun komunikatif&lt;/td&gt;&lt;td&gt;peresensi menyatakan, penulis menghadirkan&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-d"&gt;
    &lt;div class="eyebrow"&gt;05 — Materi Inti D&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Mengevaluasi Struktur Teks Resensi&lt;/h2&gt;
    &lt;p class="lead"&gt;Mengevaluasi berarti menilai mutu resensi dengan kriteria yang jelas, bukan dengan selera pribadi. Gunakan enam pertanyaan berikut sebagai daftar periksa.&lt;/p&gt;

    &lt;div class="criteria"&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;K1&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Kelengkapan Identitas&lt;/h4&gt;&lt;p&gt;Apakah judul, pengarang, penerbit, tahun, dan data pendukung disebutkan?&lt;/p&gt;&lt;div class="q"&gt;Tidak lengkap bila hanya menyebut judul&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;K2&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Kelengkapan Struktur&lt;/h4&gt;&lt;p&gt;Apakah kelima bagian (identitas, orientasi, tafsiran, evaluasi, rekomendasi) tersedia dan berurutan logis?&lt;/p&gt;&lt;div class="q"&gt;Contoh kekurangan: resensi tanpa rekomendasi&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;K3&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Kecukupan dan Ketepatan Sinopsis&lt;/h4&gt;&lt;p&gt;Apakah ringkasan isi sesuai dengan karya, tidak terlalu panjang, dan tidak membocorkan seluruh akhir cerita?&lt;/p&gt;&lt;div class="q"&gt;Hindari: menceritakan ulang seluruh isi&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;K4&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Keseimbangan Evaluasi&lt;/h4&gt;&lt;p&gt;Apakah peresensi menyampaikan kelebihan sekaligus kekurangan?&lt;/p&gt;&lt;div class="q"&gt;Hindari: hanya memuji atau hanya mencela&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;K5&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Keberalasan Penilaian&lt;/h4&gt;&lt;p&gt;Apakah setiap penilaian didukung alasan atau contoh dari karya, bukan sekadar “bagus banget” atau “jelek”?&lt;/p&gt;&lt;div class="q"&gt;Uji: kalimat “mengapa?” dapat dijawab oleh teks&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div class="crit"&gt;&lt;div class="num"&gt;K6&lt;/div&gt;&lt;div class="body"&gt;&lt;h4&gt;Kejelasan Rekomendasi dan Bahasa&lt;/h4&gt;&lt;p&gt;Apakah saran akhir jelas ditujukan kepada siapa, dan apakah bahasanya baku, santun, serta tidak menyerang pengarang?&lt;/p&gt;&lt;div class="q"&gt;Hindari bahasa gaul dan hinaan&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="panel tp" style="margin-top:22px;"&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Cara memberi simpulan evaluasi:&lt;/b&gt; sebutkan kriteria yang sudah terpenuhi, kriteria yang belum, lalu beri saran perbaikan konkret. Contoh: “Resensi sudah memuat identitas dan sinopsis, tetapi belum menyebut kekurangan karya; tambahkan satu kekurangan beserta alasannya.”&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="materi-e"&gt;
    &lt;div class="eyebrow"&gt;06 — Materi Inti E&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Membandingkan Resensi dengan Teks Lain&lt;/h2&gt;
    &lt;p class="lead"&gt;Membandingkan membantu kita memahami ciri khas resensi. Perhatikan tujuan, isi, sifat penilaian, dan struktur masing-masing teks.&lt;/p&gt;

    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:15%;"&gt;Aspek&lt;/th&gt;&lt;th&gt;Resensi&lt;/th&gt;&lt;th&gt;Sinopsis&lt;/th&gt;&lt;th&gt;Ulasan Produk&lt;/th&gt;&lt;th&gt;Berita&lt;/th&gt;&lt;th&gt;Ceramah&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tujuan&lt;/td&gt;&lt;td&gt;Menilai dan merekomendasikan karya&lt;/td&gt;&lt;td&gt;Meringkas isi karya&lt;/td&gt;&lt;td&gt;Menilai fungsi dan kualitas produk&lt;/td&gt;&lt;td&gt;Menginformasikan peristiwa&lt;/td&gt;&lt;td&gt;Mengajak pendengar bertindak&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Sifat isi&lt;/td&gt;&lt;td&gt;Fakta dan penilaian&lt;/td&gt;&lt;td&gt;Dominan fakta&lt;/td&gt;&lt;td&gt;Fakta dan pengalaman pemakai&lt;/td&gt;&lt;td&gt;Dominan fakta objektif&lt;/td&gt;&lt;td&gt;Persuasif, subjektif&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Struktur&lt;/td&gt;&lt;td&gt;Identitas, orientasi, tafsiran, evaluasi, rekomendasi&lt;/td&gt;&lt;td&gt;Pengenalan, rangkaian peristiwa, penyelesaian&lt;/td&gt;&lt;td&gt;Spesifikasi, kelebihan, kekurangan, saran beli&lt;/td&gt;&lt;td&gt;Judul, teras berita, tubuh berita&lt;/td&gt;&lt;td&gt;Pembuka, isi, penutup&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ciri bahasa&lt;/td&gt;&lt;td&gt;Kata sifat evaluatif, konjungsi pertentangan&lt;/td&gt;&lt;td&gt;Kalimat ringkas, urutan waktu&lt;/td&gt;&lt;td&gt;Istilah teknis, perbandingan&lt;/td&gt;&lt;td&gt;Kalimat lugas, unsur 5W+1H&lt;/td&gt;&lt;td&gt;Kata ganti kita, ajakan, retoris&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekomendasi&lt;/td&gt;&lt;td&gt;Ada, berupa saran membaca/menonton&lt;/td&gt;&lt;td&gt;Tidak ada&lt;/td&gt;&lt;td&gt;Ada, berupa saran membeli&lt;/td&gt;&lt;td&gt;Tidak ada&lt;/td&gt;&lt;td&gt;Ada, berupa ajakan bertindak&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;div class="panel" style="margin-top:22px;"&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Persamaan resensi dan ulasan produk:&lt;/b&gt; keduanya memuat penilaian beralasan dan rekomendasi. &lt;b style="color:var(--off-white);"&gt;Perbedaannya:&lt;/b&gt; objek resensi biasanya karya (buku/film), sedangkan ulasan produk membahas barang atau layanan dan lebih menonjolkan spesifikasi serta harga.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="contoh"&gt;
    &lt;div class="eyebrow"&gt;07 — Contoh Teks &amp;amp; Analisis&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Contoh Teks Resensi Beranotasi&lt;/h2&gt;
    &lt;p class="lead"&gt;Perhatikan penanda setiap bagian struktur pada resensi berikut. Data buku bersifat rekaan untuk keperluan pembelajaran.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Contoh Resensi Buku&lt;/span&gt;&lt;span&gt;Dokumen-01&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Ketekunan di Balik Mesin Tua&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;&lt;b&gt;Judul buku:&lt;/b&gt; Mesin Tua Pak Ramli · &lt;b&gt;Penulis:&lt;/b&gt; Sinta Maharani · &lt;b&gt;Penerbit:&lt;/b&gt; Cahaya Aksara · &lt;b&gt;Tahun terbit:&lt;/b&gt; 2022 · &lt;b&gt;Tebal:&lt;/b&gt; 216 halaman&lt;span class="flag"&gt;IDENTITAS KARYA&lt;/span&gt;&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Novel remaja ini mengangkat kehidupan siswa SMK dengan latar bengkel yang akrab bagi banyak pembaca muda. Penulis menghadirkan cerita yang ringan, tetapi sarat nilai tentang kerja keras.&lt;span class="flag"&gt;ORIENTASI&lt;/span&gt;&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Dimas, siswa SMK jurusan teknik sepeda motor, mewarisi sepeda motor tua milik kakeknya, Pak Ramli, yang telah bertahun-tahun mogok. Bersama dua sahabatnya, ia memulihkan motor itu langkah demi langkah dengan berpedoman pada buku catatan perawatan peninggalan sang kakek. Di tengah perjalanan, Dimas diejek teman-temannya, gagal dalam sebuah lomba modifikasi, dan nyaris kehabisan biaya. Akhirnya, ia memahami bahwa ketelitian dan ketekunan lebih penting daripada kecepatan.&lt;span class="flag"&gt;TAFSIRAN / SINOPSIS&lt;/span&gt;&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Kelebihan buku ini terletak pada bahasanya yang mengalir dan istilah teknisnya yang dijelaskan dengan sederhana, misalnya cara kerja karburator yang dikisahkan lewat percakapan tokoh. Tokoh-tokohnya pun terasa nyata. Namun, alur pada bagian tengah terasa lambat karena terlalu banyak adegan pengulangan, dan konflik dengan teman-teman Dimas diselesaikan terlalu mudah.&lt;span class="flag"&gt;EVALUASI&lt;/span&gt;&lt;/p&gt;
        &lt;p style="margin:10px 0 0; color:#132029 !important;"&gt;Secara keseluruhan, Mesin Tua Pak Ramli layak dibaca oleh murid SMK, terutama yang menekuni bidang otomotif, serta oleh pembaca remaja yang ingin memperoleh semangat pantang menyerah.&lt;span class="flag"&gt;REKOMENDASI&lt;/span&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Analisis Isi dan Struktur&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:24%;"&gt;Bagian&lt;/th&gt;&lt;th&gt;Isi pada Teks&lt;/th&gt;&lt;th&gt;Evaluasi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Identitas karya&lt;/td&gt;&lt;td&gt;Judul, penulis, penerbit, tahun, tebal&lt;/td&gt;&lt;td&gt;Lengkap (K1 terpenuhi)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Orientasi&lt;/td&gt;&lt;td&gt;Novel remaja berlatar bengkel SMK, bernilai kerja keras&lt;/td&gt;&lt;td&gt;Singkat dan relevan&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tafsiran&lt;/td&gt;&lt;td&gt;Dimas, motor tua warisan, hambatan, dan pelajaran akhir&lt;/td&gt;&lt;td&gt;Cukup, tidak membocorkan seluruh akhir cerita (K3 terpenuhi)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Evaluasi&lt;/td&gt;&lt;td&gt;Kelebihan: bahasa dan istilah teknis; kekurangan: alur lambat dan konflik mudah selesai&lt;/td&gt;&lt;td&gt;Seimbang dan disertai alasan (K4 dan K5 terpenuhi)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekomendasi&lt;/td&gt;&lt;td&gt;Layak dibaca murid SMK dan pembaca remaja&lt;/td&gt;&lt;td&gt;Jelas sasaran dan alasannya (K6 terpenuhi)&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;div class="note-margin" style="margin-top:10px; font-family:'IBM Plex Mono',monospace; font-size:0.82rem; color:var(--amber);"&gt;Simpulan evaluasi: struktur resensi lengkap dan seimbang; tidak ada bagian yang perlu ditambahkan.&lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="contoh-rangkuman"&gt;
    &lt;div class="eyebrow"&gt;08 — Contoh Rangkuman&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Cara Merangkum Teks Resensi&lt;/h2&gt;
    &lt;p class="lead"&gt;Rangkuman adalah versi ringkas yang memuat gagasan pokok dengan kata-kata sendiri. Berikut langkahnya, lalu contoh hasilnya dari resensi “Ketekunan di Balik Mesin Tua”.&lt;/p&gt;

    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;b&gt;Baca utuh&lt;/b&gt; resensi sekali tanpa menandai apa pun untuk menangkap gambaran umum.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Tandai gagasan pokok&lt;/b&gt; pada tiap bagian: identitas, isi (tokoh, masalah, penyelesaian), kelebihan, kekurangan, dan rekomendasi.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Buang rincian&lt;/b&gt; yang tidak penting, seperti contoh panjang atau kalimat pengantar yang hanya memperindah.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Tulis ulang dengan kata sendiri&lt;/b&gt; dalam kalimat yang efektif. Jangan menyalin kalimat asli.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Hubungkan antargagasan&lt;/b&gt; dengan penanda seperti kemudian, selain itu, namun, dan dengan demikian.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Periksa&lt;/b&gt; panjang rangkuman (sekitar sepertiga teks asli) dan pastikan tidak ada gagasan pokok yang hilang.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;div class="notebook"&gt;
      &lt;div class="content"&gt;
        &lt;h3&gt;Contoh Rangkuman&lt;/h3&gt;
        &lt;p style="color:#132029 !important;"&gt;Resensi novel &lt;i&gt;Mesin Tua Pak Ramli&lt;/i&gt; karya Sinta Maharani (Cahaya Aksara, 2022) menceritakan Dimas, siswa SMK otomotif, yang memulihkan motor tua warisan kakeknya. Meskipun diejek, gagal dalam lomba, dan kekurangan biaya, ia akhirnya belajar bahwa ketekunan lebih penting daripada kecepatan. Peresensi menilai novel ini unggul pada bahasa yang mengalir, penjelasan istilah teknis yang sederhana, dan tokoh yang realistis. Namun, alurnya lambat di bagian tengah dan konfliknya terlalu mudah selesai. Novel ini direkomendasikan bagi murid SMK dan pembaca remaja.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;p class="lead" style="margin-top:14px;"&gt;Perhatikan: rangkuman memuat identitas karya, isi pokok, kelebihan, kekurangan, dan rekomendasi dalam sekitar 85 kata, menggunakan bahasa sendiri, dan disusun dengan penanda &lt;i&gt;meskipun&lt;/i&gt;, &lt;i&gt;akhirnya&lt;/i&gt;, dan &lt;i&gt;namun&lt;/i&gt;.&lt;/p&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="eyebrow"&gt;09 — Ringkasan Materi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Poin-Poin Penting&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;ul&gt;
        &lt;li&gt;Resensi adalah ulasan dan penilaian beralasan terhadap suatu karya yang diakhiri rekomendasi.&lt;/li&gt;
        &lt;li&gt;Memahami isi resensi berarti menemukan informasi tersurat dan tersirat serta membedakan fakta dan opini.&lt;/li&gt;
        &lt;li&gt;Struktur resensi: identitas karya, orientasi, tafsiran (sinopsis), evaluasi (kelebihan dan kekurangan), dan rekomendasi.&lt;/li&gt;
        &lt;li&gt;Mengevaluasi struktur memakai enam kriteria: kelengkapan identitas, kelengkapan struktur, ketepatan sinopsis, keseimbangan evaluasi, keberalasan penilaian, kejelasan rekomendasi dan bahasa.&lt;/li&gt;
        &lt;li&gt;Resensi berbeda dari sinopsis, ulasan produk, berita, dan ceramah dalam tujuan, sifat isi, struktur, dan ciri bahasa.&lt;/li&gt;
        &lt;li&gt;Rangkuman yang baik singkat, memuat gagasan pokok, memakai kata sendiri, dan runtut.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="tugas-kelompok"&gt;
    &lt;div class="eyebrow"&gt;10 — Lembar Kerja&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Kelompok: Mengevaluasi Struktur Resensi dan Membandingkannya&lt;/h2&gt;
      &lt;span class="badge"&gt;Durasi ± 60 menit&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Tujuan tugas:&lt;/b&gt; Murid secara berkelompok mengevaluasi struktur sebuah resensi, lalu membandingkannya dengan teks berita pada topik yang sama.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks 1&lt;/span&gt;&lt;span&gt;Dokumen-02&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Film Garasi Impian&lt;/h3&gt;
        &lt;p style="margin:10px 0 0; color:#132029 !important;"&gt;Film ini bagus banget dan semua orang wajib nonton. Ceritanya tentang anak bengkel yang punya mimpi besar. Pemainnya keren-keren dan musiknya juga mantap. Pokoknya film ini sempurna, tidak ada kekurangannya sama sekali.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks 2&lt;/span&gt;&lt;span&gt;Dokumen-03&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Film “Garasi Impian” Tayang Perdana Pekan Ini&lt;/h3&gt;
        &lt;p style="margin:10px 0 0; color:#132029 !important;"&gt;Film drama remaja “Garasi Impian” mulai tayang perdana di bioskop pada Kamis pekan ini. Film berdurasi 110 menit tersebut bercerita tentang seorang pemuda yang merintis bengkel di kampungnya. Pihak produser menyatakan bahwa proses syuting berlangsung selama tiga bulan di dua kota.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;p class="lead" style="margin-top:14px;"&gt;Data film pada teks ini rekaan untuk keperluan latihan.&lt;/p&gt;

    &lt;h4 style="color:var(--off-white); margin-top:24px;"&gt;Petunjuk Pengerjaan&lt;/h4&gt;
    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;b&gt;Bentuk kelompok&lt;/b&gt; beranggotakan 4–5 orang sesuai pembagian dari guru.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Baca Teks 1 dan Teks 2&lt;/b&gt; bersama-sama, lalu tentukan jenis masing-masing teks.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Evaluasi Teks 1&lt;/b&gt; memakai enam kriteria (K1–K6) pada Materi D. Tulis hasil pada tabel evaluasi.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Tulis saran perbaikan&lt;/b&gt; yang konkret untuk setiap kriteria yang belum terpenuhi.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Bandingkan Teks 1 dan Teks 2&lt;/b&gt; pada aspek tujuan, sifat isi, struktur, dan ciri bahasa.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Presentasikan&lt;/b&gt; hasil maksimal 5 menit per kelompok, lanjutkan dengan tanya jawab.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Tabel Evaluasi Struktur Teks 1&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:30%;"&gt;Kriteria&lt;/th&gt;&lt;th style="width:16%;"&gt;Terpenuhi?&lt;/th&gt;&lt;th&gt;Bukti dari Teks&lt;/th&gt;&lt;th&gt;Saran Perbaikan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K1 Kelengkapan identitas&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K2 Kelengkapan struktur&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K3 Ketepatan sinopsis&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K4 Keseimbangan evaluasi&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K5 Keberalasan penilaian&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K6 Rekomendasi dan bahasa&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Tabel Perbandingan Teks 1 dan Teks 2&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:24%;"&gt;Aspek&lt;/th&gt;&lt;th&gt;Teks 1&lt;/th&gt;&lt;th&gt;Teks 2&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Jenis teks&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tujuan&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Sifat isi (fakta/opini)&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ciri bahasa&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Rubrik Penilaian Kelompok&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Aspek Penilaian&lt;/th&gt;&lt;th&gt;Skor 4 (Sangat Baik)&lt;/th&gt;&lt;th&gt;Skor 3 (Baik)&lt;/th&gt;&lt;th&gt;Skor 2 (Cukup)&lt;/th&gt;&lt;th&gt;Skor 1 (Kurang)&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan evaluasi struktur&lt;/td&gt;&lt;td&gt;Seluruh kriteria dinilai tepat dengan bukti&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Sebagian keliru atau tanpa bukti&lt;/td&gt;&lt;td&gt;Tidak sesuai kriteria&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kualitas saran perbaikan&lt;/td&gt;&lt;td&gt;Konkret dan dapat diterapkan&lt;/td&gt;&lt;td&gt;Cukup konkret&lt;/td&gt;&lt;td&gt;Umum&lt;/td&gt;&lt;td&gt;Tidak ada&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan perbandingan teks&lt;/td&gt;&lt;td&gt;Seluruh aspek dibandingkan tepat&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Sebagian kecil tepat&lt;/td&gt;&lt;td&gt;Tidak tepat&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kerja sama dan presentasi&lt;/td&gt;&lt;td&gt;Semua aktif, presentasi jelas&lt;/td&gt;&lt;td&gt;Sebagian besar aktif&lt;/td&gt;&lt;td&gt;Hanya sebagian aktif&lt;/td&gt;&lt;td&gt;Didominasi satu orang&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="tugas-individu"&gt;
    &lt;div class="eyebrow"&gt;11 — Penutup Pembelajaran&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Individu: Membuat Rangkuman Teks Resensi&lt;/h2&gt;
      &lt;span class="badge"&gt;Dikerjakan Mandiri · Dinilai Otomatis · Unduh PDF&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Petunjuk:&lt;/b&gt; Baca resensi di bawah ini, lalu tulis rangkumannya dengan kata-katamu sendiri pada kolom yang tersedia (60 sampai 120 kata). Penilaian muncul langsung saat kamu menulis. Setelah yakin, isi No. Absen dan Nama, lalu tekan tombol kirim untuk mengirim nilai dan mengunduh PDF.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Teks Sumber · Resensi Novel&lt;/span&gt;&lt;span&gt;Dokumen-04&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Bengkel Kecil, Kejujuran Besar&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Judul buku: Bengkel di Ujung Gang. Penulis: Danu Wicaksana. Penerbit: Lentera Karya. Tahun terbit: 2023. Tebal: 248 halaman.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Novel ini mengangkat kisah anak muda yang kembali ke kampung halaman setelah lulus sekolah kejuruan. Penulis, yang pernah bekerja sebagai montir, menghadirkan latar bengkel dengan sangat meyakinkan.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Arga, lulusan SMK jurusan teknik otomotif, memutuskan membuka bengkel kecil di ujung gang kampungnya dengan modal terbatas. Ia segera berhadapan dengan persaingan bengkel besar di jalan raya. Ketika pelanggan menawarkan bayaran lebih untuk memasang suku cadang palsu, Arga tergoda. Pak Harjo, montir senior yang menjadi pembimbingnya, mengingatkan bahwa kepercayaan pelanggan jauh lebih mahal daripada keuntungan sesaat. Arga akhirnya memilih jujur. Setelah banjir besar melanda kampung, bengkelnya menjadi tempat pertama yang dicari warga karena reputasinya.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Keunggulan buku ini terletak pada bahasanya yang mengalir dan istilah teknis yang dijelaskan melalui percakapan tokoh sehingga mudah dipahami. Tokoh-tokohnya realistis dan nilai kejujuran serta ketekunan tersampaikan tanpa terkesan menggurui. Sayangnya, alur di bagian tengah terasa lambat karena banyak adegan perbaikan yang serupa, dan akhir ceritanya mudah ditebak.&lt;/p&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Meskipun demikian, novel ini patut dibaca oleh murid SMK, khususnya jurusan otomotif, dan pembaca remaja yang ingin memperoleh teladan dalam berwirausaha.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;p class="lead" style="margin-top:14px;"&gt;Data buku pada teks ini rekaan untuk keperluan latihan.&lt;/p&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div class="eyebrow" style="margin-bottom:8px;"&gt;Aspek yang Dinilai Otomatis (Total 100, KKM 75)&lt;/div&gt;
      &lt;p style="margin:0; font-size:0.92rem;"&gt;Identitas karya 10 · Cakupan isi pokok 30 · Kelebihan 10 · Kekurangan 10 · Rekomendasi 10 · Kepadatan (60–120 kata) 10 · Keaslian bahasa 10 · Penanda antargagasan 5 · Kalimat utuh dan ejaan dasar 5.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div style="display:grid; grid-template-columns:1fr 2fr; gap:14px; margin-bottom:18px;"&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;No. Absen&lt;/label&gt;
          &lt;input id="mc-absen" type="text" inputmode="numeric" placeholder="Contoh: 12" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Nama Lengkap&lt;/label&gt;
          &lt;input id="mc-nama" type="text" placeholder="Tulis nama lengkap" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Judul Rangkuman&lt;/label&gt;
      &lt;input id="mc-judul" type="text" placeholder="Contoh: Rangkuman Resensi Novel Bengkel Kecil, Kejujuran Besar" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem; margin-bottom:16px;"&gt;

      &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Rangkuman (60–120 kata, kata-kata sendiri)&lt;/label&gt;
      &lt;textarea id="mc-teks" rows="10" placeholder="Tulis rangkumanmu di sini..." style="width:100%; padding:12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:1rem; line-height:1.6; resize:vertical;"&gt;&lt;/textarea&gt;
      &lt;p id="mc-stats" class="mono" style="font-size:0.8rem; color:var(--muted); margin:6px 0 0;"&gt;0 kata · 0 kalimat&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div style="display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;"&gt;
        &lt;div class="eyebrow" style="margin:0;"&gt;Penilaian Otomatis&lt;/div&gt;
        &lt;div style="font-family:'Oswald',sans-serif; font-size:2.2rem; color:var(--amber);"&gt;&lt;span id="mc-score"&gt;0&lt;/span&gt;&lt;span style="font-size:1rem; color:var(--muted);"&gt; / 100&lt;/span&gt;&lt;/div&gt;
        &lt;div id="mc-grade" style="color:var(--off-white); font-weight:600;"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div style="height:10px; background:var(--blueprint-deep); border:1px solid var(--line); margin:10px 0 14px;"&gt;&lt;div id="mc-bar" style="height:100%; width:0%; background:var(--amber);"&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div style="overflow-x:auto;"&gt;
        &lt;table style="margin-top:0;"&gt;
          &lt;thead&gt;&lt;tr&gt;&lt;th style="width:34%;"&gt;Aspek&lt;/th&gt;&lt;th style="width:14%;"&gt;Skor&lt;/th&gt;&lt;th&gt;Umpan Balik&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
          &lt;tbody id="mc-live"&gt;&lt;/tbody&gt;
        &lt;/table&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div style="margin-top:22px; display:flex; align-items:center; gap:16px; flex-wrap:wrap;"&gt;
      &lt;button id="mc-kirim" type="button" style="background:var(--amber); color:var(--blueprint-deep); border:none; padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Kirim Nilai &amp;amp; Unduh PDF&lt;/button&gt;
      &lt;button id="mc-ulang" type="button" style="display:none; background:transparent; color:var(--amber); border:1px solid var(--amber); padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Unduh Ulang PDF&lt;/button&gt;
    &lt;/div&gt;
    &lt;p id="mc-st-send" class="mono" style="font-size:0.85rem; margin:12px 0 0;"&gt;&lt;/p&gt;
    &lt;p id="mc-st-pdf" class="mono" style="font-size:0.85rem; margin:4px 0 0;"&gt;&lt;/p&gt;

    &lt;div id="mc-final" style="display:none; margin-top:18px; padding:18px 20px; border:1px solid var(--amber); background:rgba(245,166,35,0.08);"&gt;
      &lt;p style="margin:0; font-size:1.05rem;"&gt;Nilai akhir: &lt;b id="mc-final-score" style="color:var(--amber); font-size:1.3rem;"&gt;&lt;/b&gt; / 100 — &lt;span id="mc-final-grade"&gt;&lt;/span&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/RESENSI-1 — Memahami Isi &amp;amp; Mengevaluasi Struktur Teks Resensi, 1× pertemuan (3 JP)&lt;/div&gt;
  &lt;p style="margin:0;"&gt;Referensi: Kurikulum Merdeka — Capaian Pembelajaran Bahasa Indonesia Fase F (Elemen Membaca dan Memirsa; Menulis); Buku Teks Bahasa Indonesia Kelas XI SMK/MAK Kemendikbudristek.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
(function(){
  var ENDPOINT = "https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
  var MATERI = "Memahami Isi dan Mengevaluasi Struktur Teks Resensi (Rangkuman)";
  var KKM = 75;
  var JSPDF_URL = "https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js";
  var LT = String.fromCharCode(60);
  var GT = String.fromCharCode(62);
  var AM = String.fromCharCode(38);

  function $(id){ return document.getElementById(id); }
  function esc(s){ return String(s).split(AM).join(AM + "amp;").split(LT).join(AM + "lt;").split(GT).join(AM + "gt;"); }
  function tag(n, a, inner){ return LT + n + (a ? " " + a : "") + GT + inner + LT + "/" + n + GT; }

  function wordsOf(s){ var t = s.trim(); return t === "" ? [] : t.split(/\s+/); }
  function tokens(s){ var m = s.toLowerCase().match(/[a-z\u00e0-\u00ff]+/g); return m ? m : []; }
  function sentencesOf(s){ return s.split(/[.!?]+/).map(function(x){ return x.trim(); }).filter(function(x){ return wordsOf(x).length &gt; 2; }); }
  function has(text, w){ return new RegExp("(^|[^a-z])" + w + "([^a-z]|$)").test(text); }
  function firstMatch(text, list){ var f = ""; list.forEach(function(w){ if(f === ""){ if(has(text, w)){ f = w; } } }); return f; }
  function allMatches(text, list){ return list.filter(function(w){ return has(text, w); }); }
  function countWord(text, w){ var m = text.match(new RegExp("(^|[^a-z])" + w + "(?=[^a-z]|$)", "g")); return m ? m.length : 0; }
  function item(label, max, got, note){ return {label: label, max: max, got: got, note: note}; }
  function sum(arr, key){ var t = 0; arr.forEach(function(x){ t += x[key]; }); return t; }

  var SUMBER = "Judul buku: Bengkel di Ujung Gang. Penulis: Danu Wicaksana. Penerbit: Lentera Karya. Tahun terbit: 2023. Tebal: 248 halaman. Novel ini mengangkat kisah anak muda yang kembali ke kampung halaman setelah lulus sekolah kejuruan. Penulis, yang pernah bekerja sebagai montir, menghadirkan latar bengkel dengan sangat meyakinkan. Arga, lulusan SMK jurusan teknik otomotif, memutuskan membuka bengkel kecil di ujung gang kampungnya dengan modal terbatas. Ia segera berhadapan dengan persaingan bengkel besar di jalan raya. Ketika pelanggan menawarkan bayaran lebih untuk memasang suku cadang palsu, Arga tergoda. Pak Harjo, montir senior yang menjadi pembimbingnya, mengingatkan bahwa kepercayaan pelanggan jauh lebih mahal daripada keuntungan sesaat. Arga akhirnya memilih jujur. Setelah banjir besar melanda kampung, bengkelnya menjadi tempat pertama yang dicari warga karena reputasinya. Keunggulan buku ini terletak pada bahasanya yang mengalir dan istilah teknis yang dijelaskan melalui percakapan tokoh sehingga mudah dipahami. Tokoh-tokohnya realistis dan nilai kejujuran serta ketekunan tersampaikan tanpa terkesan menggurui. Sayangnya, alur di bagian tengah terasa lambat karena banyak adegan perbaikan yang serupa, dan akhir ceritanya mudah ditebak. Meskipun demikian, novel ini patut dibaca oleh murid SMK, khususnya jurusan otomotif, dan pembaca remaja yang ingin memperoleh teladan dalam berwirausaha.";
  var SRC_TOK = tokens(SUMBER);
  function shingles(arr, n){ var o = {}; for(var i = 0; arr.length - n &gt;= i; i++){ o[arr.slice(i, i + n).join(" ")] = 1; } return o; }
  function anyOf(low, list){ return list.filter(function(w){ return has(low, w); }); }
  var KEYS = [
    {l: "Tokoh utama dan latar (Arga, lulusan SMK otomotif, kembali ke kampung)", a: ["arga"], b: ["smk","otomotif","montir","lulusan","kampung"]},
    {l: "Peristiwa pokok (membuka/merintis bengkel kecil)", a: ["bengkel"], b: ["membuka","mendirikan","membangun","merintis","memulai","buka"]},
    {l: "Masalah/konflik (persaingan, modal, suku cadang palsu)", a: ["persaingan","bersaing","modal","suku cadang palsu","palsu","tergoda","godaan"], b: []},
    {l: "Tokoh pendukung/pembimbing (Pak Harjo)", a: ["harjo","pembimbing","mentor","montir senior"], b: []},
    {l: "Penyelesaian (memilih jujur, kepercayaan pelanggan, banjir)", a: ["jujur","kejujuran","kepercayaan","reputasi","banjir"], b: []},
    {l: "Pesan/tema (kejujuran dan ketekunan)", a: ["kejujuran","jujur","ketekunan","integritas","amanat","pesan"], b: []}
  ];
  var CONJ2 = ["kemudian","selain itu","namun","meskipun","akhirnya","sedangkan","sehingga","karena","tetapi","sayangnya","dengan demikian","selanjutnya","setelah itu","di samping itu","oleh karena itu"];

  function evaluate(){
    var text = $("mc-teks").value;
    var low = text.toLowerCase();
    var wc = wordsOf(text).length;
    var toks = tokens(text);
    var sents = sentencesOf(text);
    var groups = [];

    var idHits = [];
    if(has(low, "bengkel di ujung gang")){ idHits.push("judul"); }
    if(has(low, "danu")){ idHits.push("penulis"); }
    if(has(low, "lentera")){ idHits.push("penerbit"); }
    if(has(low, "2023")){ idHits.push("tahun"); }
    var idGot = idHits.length &gt; 1 ? 10 : (idHits.length &gt; 0 ? 5 : 0);
    groups.push({name: "Identitas karya", items: [item("Judul, penulis, penerbit, tahun", 10, idGot, idHits.length &gt; 0 ? "Terdeteksi: " + idHits.join(", ") + ". Sebutkan minimal 2 unsur." : "Sebutkan identitas singkat, mis. judul novel dan nama penulisnya.")]});

    var g2 = [];
    KEYS.forEach(function(k){
      var ha = anyOf(low, k.a);
      var hb = k.b.length &gt; 0 ? anyOf(low, k.b) : [1];
      var ok = ha.length &gt; 0 &amp;&amp; hb.length &gt; 0;
      g2.push(item(k.l, 5, ok ? 5 : 0, ok ? "Gagasan pokok ini sudah tercakup." : "Belum tercakup. Cari bagian ini pada teks sumber."));
    });
    groups.push({name: "Cakupan isi pokok", items: g2});

    var kelA = anyOf(low, ["kelebihan","kelebihannya","keunggulan","keunggulannya","unggul","kekuatan","unggulan"]);
    var kelB = anyOf(low, ["bahasa","mengalir","istilah","tokoh","realistis","nilai","menggurui","dijelaskan"]);
    var kelGot = (kelA.length &gt; 0 &amp;&amp; kelB.length &gt; 0) ? 10 : ((kelA.length &gt; 0 || kelB.length &gt; 1) ? 5 : 0);
    groups.push({name: "Kelebihan", items: [item("Kelebihan buku beserta alasannya", 10, kelGot, kelGot &gt; 9 ? "Kelebihan dan alasannya terdeteksi." : "Sebutkan kelebihan (mis. bahasa, istilah teknis, tokoh) dengan kata unggul/kelebihan.")]});

    var kurA = anyOf(low, ["kekurangan","kekurangannya","kelemahan","kelemahannya","sayangnya","kurang"]);
    var kurB = anyOf(low, ["lambat","alur","tertebak","ditebak","serupa","berulang"]);
    var kurGot = (kurA.length &gt; 0 &amp;&amp; kurB.length &gt; 0) ? 10 : ((kurA.length &gt; 0 || kurB.length &gt; 0) ? 5 : 0);
    groups.push({name: "Kekurangan", items: [item("Kekurangan buku beserta alasannya", 10, kurGot, kurGot &gt; 9 ? "Kekurangan dan alasannya terdeteksi." : "Sebutkan kekurangan (mis. alur tengah lambat, akhir mudah ditebak) dengan kata kekurangan/kelemahan.")]});

    var recA = anyOf(low, ["direkomendasikan","rekomendasi","layak dibaca","patut dibaca","disarankan","cocok","dianjurkan"]);
    var recB = anyOf(low, ["murid","smk","remaja","pembaca","otomotif","pelajar"]);
    var recGot = (recA.length &gt; 0 &amp;&amp; recB.length &gt; 0) ? 10 : ((recA.length &gt; 0 || recB.length &gt; 0) ? 4 : 0);
    groups.push({name: "Rekomendasi", items: [item("Saran dan sasaran pembaca", 10, recGot, recGot &gt; 9 ? "Rekomendasi dan sasaran pembaca terdeteksi." : "Tutup dengan saran: buku layak dibaca oleh siapa.")]});

    var denGot = 0;
    var denNote = "Panjang " + wc + " kata. Target 60 sampai 120 kata.";
    if(wc &gt; 59 &amp;&amp; wc &lt; 121){ denGot = 10; }
    else { if((wc &gt; 39 &amp;&amp; wc &lt; 60) || (wc &gt; 120 &amp;&amp; wc &lt; 151)){ denGot = 5; } }
    groups.push({name: "Kepadatan", items: [item("Rangkuman ringkas (60–120 kata)", 10, denGot, denNote)]});

    var sh = shingles(toks, 5);
    var shSrc = shingles(SRC_TOK, 5);
    var tot = 0, cop = 0;
    Object.keys(sh).forEach(function(k){ tot++; if(shSrc[k]){ cop++; } });
    var ratio = tot &gt; 0 ? cop / tot : 0;
    var orGot = 0;
    var orNote = "Tulis minimal 40 kata agar keaslian dapat dinilai.";
    if(wc &gt; 39){
      orGot = ratio &lt; 0.2 ? 10 : (ratio &lt; 0.4 ? 6 : (ratio &lt; 0.6 ? 3 : 0));
      orNote = Math.round(ratio * 100) + "% rangkaian kata sama dengan teks sumber. Target kurang dari 20%; tulis ulang dengan kata sendiri.";
    }
    groups.push({name: "Keaslian bahasa", items: [item("Memakai kata-kata sendiri", 10, orGot, orNote)]});

    var cj = anyOf(low, CONJ2);
    var cjGot = cj.length &gt; 1 ? 5 : (cj.length &gt; 0 ? 2 : 0);
    groups.push({name: "Keterpaduan", items: [item("Penanda antargagasan", 5, cjGot, cj.length &gt; 0 ? "Terdeteksi: " + cj.join(", ") + ". Target minimal 2." : "Pakai penanda seperti kemudian, namun, selain itu, akhirnya.")]});

    var okSent = 0;
    var raw = text.replace(/([.!?])\s+/g, "$1\n").split("\n").map(function(x){ return x.trim(); }).filter(function(x){ return x.length &gt; 0; });
    raw.forEach(function(s){ if(/^[A-Z“"]/.test(s) &amp;&amp; /[.!?”"]$/.test(s)){ okSent++; } });
    var klGot = (raw.length &gt; 2 &amp;&amp; okSent === raw.length) ? 5 : ((raw.length &gt; 1 &amp;&amp; okSent * 2 &gt; raw.length) ? 3 : 0);
    groups.push({name: "Kalimat dan ejaan dasar", items: [item("Huruf kapital dan tanda baca akhir", 5, klGot, raw.length + " kalimat, " + okSent + " diawali kapital dan diakhiri tanda baca. Target minimal 3 kalimat yang semuanya benar.")]});

    var total = 0;
    groups.forEach(function(g){ g.max = sum(g.items, "max"); g.got = sum(g.items, "got"); total += g.got; });
    return {groups: groups, total: total, words: wc, sentences: sents.length};
  }

  function gradeOf(t){
    if(t &gt; 84){ return "Sangat Baik — tuntas KKM"; }
    if(t &gt; 74){ return "Baik — tuntas KKM"; }
    if(t &gt; 59){ return "Cukup — belum mencapai KKM " + KKM; }
    return "Perlu perbaikan — belum mencapai KKM " + KKM;
  }
  function col(got, max){ if(got &gt;= max){ return "#6EC6CA"; } if(got &gt; 0){ return "#F5A623"; } return "#F4936F"; }

  var TD = "padding:8px 10px; border:1px solid rgba(234,242,245,0.2); vertical-align:top; font-size:0.88rem; color:#EAF2F5 !important;";

  function rowsHtml(r){
    var out = "";
    r.groups.forEach(function(g){
      out += tag("tr", 'style="background:#0A1E2C;"',
        tag("td", 'style="' + TD + ' font-weight:700; color:#F5A623 !important;"', esc(g.name)) +
        tag("td", 'style="' + TD + ' font-weight:700; color:' + col(g.got, g.max) + ' !important; white-space:nowrap;"', g.got + " / " + g.max) +
        tag("td", 'style="' + TD + '"', ""));
      g.items.forEach(function(it){
        out += tag("tr", "",
          tag("td", 'style="' + TD + ' padding-left:22px;"', esc(it.label)) +
          tag("td", 'style="' + TD + ' color:' + col(it.got, it.max) + ' !important; white-space:nowrap;"', it.got + " / " + it.max) +
          tag("td", 'style="' + TD + ' color:#C9D8E0 !important;"', esc(it.note)));
      });
    });
    return out;
  }

  function render(r){
    $("mc-live").innerHTML = rowsHtml(r);
    $("mc-score").textContent = r.total;
    $("mc-bar").style.width = r.total + "%";
    $("mc-grade").textContent = gradeOf(r.total);
    $("mc-stats").textContent = r.words + " kata · " + r.sentences + " kalimat";
  }

  function setMsg(id, text, kind){
    var el = $(id);
    el.textContent = text;
    el.style.setProperty("color", kind === "ok" ? "#6EC6CA" : (kind === "warn" ? "#F5A623" : "#C9D8E0"), "important");
  }

  var timer = null;
  function schedule(){ if(timer){ clearTimeout(timer); } timer = setTimeout(function(){ render(evaluate()); }, 250); }
  ["mc-teks","mc-judul"].forEach(function(id){ $(id).addEventListener("input", schedule); });
  render(evaluate());

  function pdfSafe(s){
    return String(s).replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"').replace(/[\u2013\u2014]/g, "-").replace(/\u2026/g, "...").replace(/\u00b7/g, "-").replace(/[^\x09\x0A\x0D\x20-\x7E\u00A0-\u00FF]/g, "");
  }

  function loadJsPdf(cb){
    if(window.jspdf){ if(window.jspdf.jsPDF){ cb(true); return; } }
    var s = document.createElement("script");
    s.src = JSPDF_URL;
    s.onload = function(){ var ok = false; if(window.jspdf){ if(window.jspdf.jsPDF){ ok = true; } } cb(ok); };
    s.onerror = function(){ cb(false); };
    document.head.appendChild(s);
  }

  function makePdf(info, r, done){
    loadJsPdf(function(ok){
      if(!ok){ done(false); return; }
      try{
        var JS = window.jspdf.jsPDF;
        var doc = new JS({unit: "mm", format: "a4"});
        var M = 18;
        var CW = 174;
        var y = 0;
        function ensure(h){ if(y + h &gt; 278){ doc.addPage(); y = 20; } }

        doc.setFillColor(15, 42, 61);
        doc.rect(0, 0, 210, 34, "F");
        doc.setFillColor(245, 166, 35);
        doc.rect(0, 34, 210, 1.6, "F");
        doc.setTextColor(255, 255, 255);
        doc.setFont("helvetica", "bold");
        doc.setFontSize(17);
        doc.text("Rangkuman Teks Resensi Karya Murid", M, 16);
        doc.setFont("helvetica", "normal");
        doc.setFontSize(9);
        doc.text("Bahasa Indonesia Kelas XI SMK - Memahami Isi dan Mengevaluasi Struktur Teks Resensi", M, 25);

        y = 46;
        doc.setTextColor(20, 32, 41);
        doc.setFontSize(10.5);
        var tgl = new Date().toLocaleDateString("id-ID", {day: "numeric", month: "long", year: "numeric"});
        [["Nama", info.nama], ["No. Absen", info.absen], ["Tanggal", tgl]].forEach(function(row){
          doc.setFont("helvetica", "bold"); doc.text(row[0], M, y);
          doc.setFont("helvetica", "normal"); doc.text(": " + pdfSafe(row[1]), M + 26, y);
          y += 6;
        });
        doc.setFont("helvetica", "bold");
        doc.text("Nilai otomatis", M, y);
        doc.setFont("helvetica", "normal");
        doc.text(": " + r.total + " / 100 (" + pdfSafe(gradeOf(r.total)) + ")", M + 26, y);
        y += 10;

        doc.setDrawColor(200, 200, 200);
        doc.line(M, y - 4, M + CW, y - 4);
        doc.setFont("helvetica", "bold");
        doc.setFontSize(14);
        var jl = doc.splitTextToSize(pdfSafe(info.judul !== "" ? info.judul : "(Tanpa judul)"), CW);
        jl.forEach(function(L){ ensure(7); doc.text(L, M, y); y += 7; });
        y += 2;

        doc.setFont("helvetica", "normal");
        doc.setFontSize(11);
        pdfSafe(info.teks).split("\n").forEach(function(p){
          if(p.trim() === ""){ y += 3; return; }
          doc.splitTextToSize(p, CW).forEach(function(L){ ensure(6); doc.text(L, M, y); y += 5.6; });
          y += 2;
        });

        y += 4;
        ensure(14);
        doc.setFont("helvetica", "bold"); doc.setFontSize(10.5);
        doc.text("Teks sumber: resensi novel Bengkel di Ujung Gang (Danu Wicaksana, Lentera Karya, 2023)", M, y); y += 5.5;
        doc.setFont("helvetica", "normal");
        doc.splitTextToSize("Rangkuman dinilai pada identitas, isi pokok, kelebihan, kekurangan, rekomendasi, kepadatan, keaslian, keterpaduan, dan kalimat.", CW).forEach(function(L){ ensure(6); doc.text(L, M, y); y += 5.2; });

        y += 8;
        ensure(16);
        doc.setFont("helvetica", "bold"); doc.setFontSize(13);
        doc.text("Hasil Penilaian Otomatis", M, y); y += 7;
        r.groups.forEach(function(g){
          ensure(14);
          doc.setFillColor(238, 241, 244);
          doc.rect(M, y - 4.6, CW, 6.6, "F");
          doc.setFont("helvetica", "bold"); doc.setFontSize(10);
          doc.setTextColor(20, 32, 41);
          doc.text(pdfSafe(g.name), M + 2, y);
          doc.text(g.got + " / " + g.max, M + CW - 2, y, {align: "right"});
          y += 6.2;
          g.items.forEach(function(it){
            var nl = doc.splitTextToSize(pdfSafe(it.note), CW - 12);
            ensure(5 + nl.length * 4.2);
            doc.setFont("helvetica", "normal"); doc.setFontSize(9.5);
            doc.setTextColor(20, 32, 41);
            doc.text(pdfSafe(it.label), M + 4, y);
            doc.text(it.got + " / " + it.max, M + CW - 2, y, {align: "right"});
            y += 4.4;
            doc.setFontSize(8.5);
            doc.setTextColor(95, 105, 112);
            nl.forEach(function(L){ doc.text(L, M + 8, y); y += 4; });
            y += 1.4;
          });
          y += 2;
        });

        var n = doc.getNumberOfPages();
        for(var i = 1; n &gt;= i; i++){
          doc.setPage(i);
          doc.setFont("helvetica", "normal");
          doc.setFontSize(8);
          doc.setTextColor(120, 128, 135);
          doc.text("Penilaian otomatis berbasis aturan kebahasaan; mohon ditinjau oleh guru.", M, 290);
          doc.text("Halaman " + i + " / " + n, M + CW, 290, {align: "right"});
        }

        var fn = "Rangkuman_" + info.absen + "_" + info.nama.replace(/[^A-Za-z0-9]+/g, "_") + ".pdf";
        doc.save(fn);
        done(true);
      }catch(e){
        done(false);
      }
    });
  }

  function printFallback(info, r){
    var w = window.open("", "_blank");
    if(!w){ return false; }
    var d = w.document;
    d.open();
    d.write(tag("title", "", "Rangkuman Resensi " + esc(info.nama)));
    d.close();
    var b = d.body ? d.body : d.documentElement.appendChild(d.createElement("body"));
    b.style.fontFamily = "Arial, sans-serif";
    b.style.padding = "24px";
    b.style.background = "#fff";
    b.style.color = "#111";
    var paras = esc(info.teks).split("\n").map(function(p){ return tag("p", "", p); }).join("");
    b.innerHTML = tag("h2", "", esc(info.judul)) + tag("p", "", esc(info.nama) + " - No. Absen " + esc(info.absen) + " - Nilai otomatis " + r.total + " / 100") + paras;
    w.focus();
    w.print();
    return true;
  }

  function sendScore(info, skor){
    var p = new URLSearchParams();
    p.append("noAbsen", info.absen);
    p.append("nama", info.nama);
    p.append("skor", skor);
    p.append("materi", MATERI);
    p.append("waktu", new Date().toISOString());
    return fetch(ENDPOINT, {method: "POST", mode: "no-cors", headers: {"Content-Type": "application/x-www-form-urlencoded"}, body: p.toString()});
  }

  var busy = false;
  var sentOnce = false;
  var lastInfo = null;
  var lastResult = null;

  function pdfFlow(info, r){
    makePdf(info, r, function(ok){
      if(ok){ setMsg("mc-st-pdf", "PDF berhasil diunduh.", "ok"); }
      else {
        var shown = printFallback(info, r);
        setMsg("mc-st-pdf", shown ? "PDF otomatis tidak tersedia; jendela cetak dibuka, pilih Simpan sebagai PDF." : "PDF gagal dibuat. Izinkan pop-up atau coba lagi.", "warn");
      }
    });
  }

  $("mc-kirim").addEventListener("click", function(){
    if(busy){ return; }
    var absen = $("mc-absen").value.trim();
    var nama = $("mc-nama").value.trim();
    if(absen === "" || nama === ""){ setMsg("mc-st-send", "Isi No. Absen dan Nama terlebih dahulu.", "warn"); return; }
    var r = evaluate();
    if(40 &gt; r.words){ setMsg("mc-st-send", "Rangkuman terlalu pendek (" + r.words + " kata). Tulis minimal 40 kata sebelum dikirim.", "warn"); return; }
    if(!window.confirm("Kirim nilai final dan unduh PDF? Pastikan rangkumanmu sudah selesai.")){ return; }
    var resend = true;
    if(sentOnce){ resend = window.confirm("Nilai sudah pernah dikirim dari halaman ini. Kirim ulang nilai terbaru?"); }
    busy = true;
    var info = {absen: absen, nama: nama, judul: $("mc-judul").value.trim(), teks: $("mc-teks").value};
    lastInfo = info;
    lastResult = r;
    render(r);
    $("mc-final").style.display = "block";
    $("mc-final-score").textContent = r.total;
    $("mc-final-grade").textContent = gradeOf(r.total);
    setMsg("mc-st-pdf", "Membuat PDF...", "info");
    pdfFlow(info, r);
    if(resend){
      setMsg("mc-st-send", "Mengirim nilai...", "info");
      sendScore(info, r.total).then(function(){
        sentOnce = true;
        setMsg("mc-st-send", "Nilai berhasil dikirim ke guru.", "ok");
        busy = false;
      }).catch(function(){
        setMsg("mc-st-send", "Nilai gagal dikirim. Tunjukkan PDF dan skor ini kepada guru.", "warn");
        busy = false;
      });
    } else {
      setMsg("mc-st-send", "Nilai tidak dikirim ulang.", "info");
      busy = false;
    }
    $("mc-ulang").style.display = "inline-block";
  });

  $("mc-ulang").addEventListener("click", function(){
    if(lastInfo){ pdfFlow(lastInfo, lastResult); }
  });
})();

&lt;/script&gt;
&lt;/div&gt;</description><link>http://kitab-investasi.blogspot.com/2026/11/memahami-isi-mengevaluasi-struktur-teks.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-1841806522291150847</guid><pubDate>Wed, 28 Oct 2026 02:57:03 +0000</pubDate><atom:updated>2026-11-10T11:10:46.323+07:00</atom:updated><title>UJIAN ONLINE </title><description>&lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/jspdf-autotable@3.8.2/dist/jspdf.plugin.autotable.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.sheetjs.com/xlsx-0.20.2/package/dist/xlsx.full.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"&gt;&lt;/script&gt;
&lt;style&gt;
:root{--navy:#0f2742;--blue:#1d4ed8;--bs:#dbeafe;--amber:#d97706;--as:#fef3c7;--ad:#92400e;--teal:#0f766e;--ts:#ccfbf1;--ok:#15803d;--okb:#dcfce7;--bad:#b91c1c;--badb:#fee2e2;--ink:#0f172a;--sub:#475569;--line:#dbe3ee;--bg:#eef2f8;--card:#fff}
#examHotsRoot *{box-sizing:border-box}
#examHotsRoot{margin:0;font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.55}
.wrap{max-width:920px;margin:0 auto;padding:18px 16px 60px}
.hidden{display:none!important}
h1,h2,h3{color:var(--navy);margin:0 0 8px}
.card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 2px 10px rgba(15,39,66,.05)}
.topbar{background:var(--navy);color:#fff;padding:14px 16px}
.topbar .in{max-width:920px;margin:0 auto;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px}
.topbar b{font-size:15px}.topbar span{font-size:12.5px;opacity:.85}
label.fl{display:block;font-weight:700;font-size:13px;color:var(--navy);margin-bottom:4px}
input[type=text],input[type=number],select,textarea{width:100%;padding:10px 12px;border:1.5px solid var(--line);border-radius:10px;font-size:14.5px;font-family:inherit;background:#fff;color:var(--ink)}
textarea{resize:vertical;min-height:110px}
.field-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media(max-width:600px){.field-grid{grid-template-columns:1fr}}
.btn{display:inline-block;border:none;border-radius:10px;padding:11px 20px;font-weight:700;font-size:14.5px;cursor:pointer;font-family:inherit}
.btn-primary{background:var(--blue);color:#fff}
.btn-primary:disabled{background:#93a7c9;cursor:not-allowed}
.btn-outline{background:#fff;color:var(--navy);border:1.5px solid var(--line)}
.btn-amber{background:var(--amber);color:#fff}
.btn-teal{background:var(--teal);color:#fff}
.btn-bad{background:var(--bad);color:#fff}
.btn-sm{font-size:12px;padding:6px 12px}
.msg{padding:10px 12px;border-radius:10px;font-size:13.5px;margin-top:10px;display:none}
.msg.show{display:block}
.msg.err{background:var(--badb);color:var(--bad)}
.msg.info{background:var(--bs);color:var(--blue)}
.msg.ok{background:var(--okb);color:var(--ok)}
.notice ul{margin:6px 0 0;padding-left:20px}
.notice li{margin-bottom:6px;font-size:13.5px;color:var(--sub)}
table.ref{border-collapse:collapse;width:100%;margin:10px 0;font-size:13.5px}
table.ref th,table.ref td{border:1px solid var(--line);padding:6px 9px;text-align:left}
table.ref th{background:var(--bs);color:var(--navy)}
table.ref td.num{text-align:right}
.exam-top{position:sticky;top:0;z-index:40;background:var(--navy);color:#fff;padding:10px 14px}
.exam-top .in{max-width:920px;margin:0 auto;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.exam-top .who{font-size:12.5px;font-weight:700}
.chip{font-size:12px;font-weight:700;padding:4px 10px;border-radius:999px}
.chip.timer{background:var(--as);color:var(--ad)}
.chip.timer.low{background:var(--badb);color:var(--bad)}
.chip.sec{background:var(--okb);color:var(--ok)}
.chip.sec.warn{background:var(--badb);color:var(--bad)}
.chip.prog{background:rgba(255,255,255,.15);color:#fff;margin-left:auto}
.numgrid{display:flex;flex-wrap:wrap;gap:6px;margin:12px 0}
.numgrid button{width:34px;height:34px;border-radius:8px;border:1.5px solid var(--line);background:#fff;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--sub)}
.numgrid button.answered{background:var(--okb);border-color:var(--ok);color:var(--ok)}
.numgrid button.marked{background:var(--as);border-color:var(--amber);color:var(--ad)}
.numgrid button.current{outline:2.5px solid var(--blue);outline-offset:1px}
.sec-label{font-size:12px;font-weight:800;color:var(--sub);margin:14px 0 2px}
.q-box{border:1.5px solid var(--line);border-radius:14px;padding:16px}
.q-head{font-size:12.5px;font-weight:800;color:var(--blue);text-transform:uppercase;letter-spacing:.03em;margin-bottom:6px}
.q-stem{font-size:15px;margin-bottom:12px}
.opt-row{display:flex;gap:10px;align-items:flex-start;padding:9px 10px;border:1.5px solid var(--line);border-radius:10px;margin-bottom:8px;cursor:pointer}
.opt-row:hover{border-color:var(--blue)}.opt-row input{margin-top:3px}
.navbtns{display:flex;justify-content:space-between;gap:10px;margin-top:16px;flex-wrap:wrap}
.navbtns .mid{display:flex;gap:10px}
.cnt-small{font-size:12px;color:var(--sub);text-align:right;margin-top:4px}
.overlay{position:fixed;inset:0;background:rgba(15,23,42,.88);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px}
.overlay .box{background:#fff;border-radius:16px;padding:24px;max-width:460px;width:100%;max-height:92vh;overflow:auto;color:var(--ink)}
.overlay.danger .box{border-top:5px solid var(--bad)}
.overlay h2{margin-top:0}
.overlay .row{display:flex;gap:10px;justify-content:flex-end;margin-top:16px;flex-wrap:wrap}
.status-pill{display:inline-block;padding:6px 16px;border-radius:999px;font-weight:800;font-size:14px;margin-bottom:8px}
.status-pill.ok{background:var(--okb);color:var(--ok)}
.status-pill.no{background:var(--badb);color:var(--bad)}
.ring{width:120px;height:120px;border-radius:50%;margin:0 auto 10px;display:flex;align-items:center;justify-content:center;font-size:30px;font-weight:800;color:var(--navy);background:conic-gradient(var(--ringc,#1d4ed8) calc(var(--p,0)*1%),#e5ebf5 0)}
.ring .inner{width:92px;height:92px;border-radius:50%;background:#fff;display:flex;align-items:center;justify-content:center}
.bar-row{margin-bottom:10px}
.bar-row .lbl{display:flex;justify-content:space-between;font-size:13px;font-weight:700;color:var(--navy);margin-bottom:3px}
.bar-track{height:9px;border-radius:6px;background:#e5ebf5;overflow:hidden}
.bar-fill{height:100%;border-radius:6px}
.btn-row{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
footer{text-align:center;font-size:12px;color:var(--sub);padding:20px}
@media print{.no-print{display:none!important}body{background:#fff}}
.guru-tabs{display:flex;gap:4px;border-bottom:2px solid var(--line);margin:0 0 14px;flex-wrap:wrap}
.guru-tab{padding:9px 14px;font-weight:700;font-size:12.5px;cursor:pointer;border:none;background:transparent;color:var(--sub);border-bottom:2.5px solid transparent;margin-bottom:-2px;font-family:inherit}
.guru-tab.active{color:var(--blue);border-bottom-color:var(--blue)}
.guru-panel{display:none}.guru-panel.active{display:block}
.bank-item{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px;background:#fafbfe;display:flex;gap:10px;align-items:flex-start;}
.bank-item .meta{font-size:11px;color:var(--sub);margin-bottom:4px;display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.bank-item .q{font-size:13px;font-weight:600;margin-bottom:4px;color:var(--navy)}
.bank-item .opt{font-size:12px;color:var(--sub);margin-left:12px}
.bank-item .kunci{font-size:11.5px;color:var(--ok);font-weight:700;margin-top:4px}
.bank-item .actions{display:flex;gap:6px;margin-top:6px}
.bank-item .actions button{font-size:11px;padding:4px 10px;border-radius:6px;border:1px solid var(--line);background:#fff;cursor:pointer;font-family:inherit}
.bank-item .actions .edit{color:var(--blue);border-color:var(--bs)}
.bank-item .actions .del{color:var(--bad);border-color:var(--badb)}
.bank-cb{margin-top:4px;width:auto;cursor:pointer;}
.upload-zone{border:2px dashed var(--line);border-radius:12px;padding:22px;text-align:center;color:var(--sub);font-size:13px;cursor:pointer;transition:.15s;background:#fafbfe;margin-top:6px}
.upload-zone:hover,.upload-zone.dragover{border-color:var(--blue);background:var(--bs);color:var(--blue)}
.badge{display:inline-block;font-size:10px;font-weight:800;padding:2px 7px;border-radius:999px}
.badge-pg{background:var(--bs);color:var(--blue)}
.badge-isian{background:var(--as);color:var(--ad)}
.badge-bs{background:var(--ts);color:var(--teal)}
.badge-uraian{background:var(--okb);color:var(--ok)}
.badge-lembar-kerja{background:#ede9fe;color:#5b21b6}
.warn-box{background:var(--as);border-left:4px solid var(--amber);padding:10px 12px;border-radius:6px;font-size:12.5px;margin-bottom:12px;color:var(--ad);line-height:1.5}
.mono{font-family:'Courier New',monospace!important;font-size:12px!important}
.exam-card{border:1px solid var(--line);border-radius:12px;padding:12px 14px;margin-bottom:10px;background:#fafbfe}
.exam-head{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px}
.exam-head b{font-size:14px;color:var(--navy)}
.token-pill{font-family:monospace;font-size:11.5px;background:var(--bs);color:var(--blue);padding:3px 8px;border-radius:6px;font-weight:700}
.exam-meta{font-size:12px;color:var(--sub);margin-bottom:8px;line-height:1.5}
.exam-actions{display:flex;gap:6px;flex-wrap:wrap}
.exam-actions button{font-size:11.5px;padding:5px 10px;border-radius:6px;border:1px solid var(--line);background:#fff;cursor:pointer;font-family:inherit}
.exam-actions .edit{color:var(--blue);border-color:var(--bs)}
.exam-actions .del{color:var(--bad);border-color:var(--badb)}
.exam-actions .dup{color:var(--teal);border-color:var(--ts)}
.exam-edit-form{background:#fff;border:1px dashed var(--blue);border-radius:10px;padding:12px;margin-top:10px}
.exam-edit-form label{font-size:11.5px}
.exam-edit-form input,.exam-edit-form select{padding:7px 10px;font-size:13px}
.action-bar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:12px;padding:10px;background:var(--bs);border-radius:8px;}
.code-out{font-family:monospace;font-size:20px;font-weight:800;letter-spacing:.05em;color:var(--navy);background:#fff;border:2px dashed var(--blue);border-radius:10px;padding:12px;text-align:center;margin-top:10px;user-select:all}
.q-imgwrap{margin:0 0 12px;text-align:center}
.q-img{max-width:100%;max-height:340px;border:1px solid var(--line);border-radius:10px;cursor:zoom-in;background:#fff}
.sheet-wrap{overflow:auto;border:1.5px solid var(--line);border-radius:10px;margin:10px 0;max-height:480px;background:#fff}
table.sheet{border-collapse:collapse;font-size:13px}
table.sheet th{background:#e8eef7;color:var(--navy);font-weight:700;border:1px solid var(--line);padding:4px 6px;position:sticky;top:0;z-index:1;min-width:34px;text-align:center}
table.sheet th.rh{position:sticky;left:0;top:auto;z-index:1}
table.sheet th.cn{left:0;z-index:2}
table.sheet td{border:1px solid var(--line);padding:0}
table.sheet input.sc{display:block;width:104px;min-width:104px;border:none;border-radius:0;padding:7px 8px;font-size:13px;background:#fff;color:var(--ink);font-family:inherit}
table.sheet input.sc:focus{outline:2px solid var(--blue);outline-offset:-2px;background:#f0f6ff}
table.sheet input.sc.lk{background:#f1f5f9;color:#1e293b;font-weight:600}
table.sheet input.sc.er{color:var(--bad);font-weight:700}
&lt;/style&gt;

&lt;div id="examHotsRoot"&gt;
&lt;div class="topbar no-print"&gt;&lt;div class="in"&gt;&lt;div&gt;&lt;b id="hdrJudul"&gt;Ujian Online Sekolah&lt;/b&gt;&lt;br&gt;&lt;span id="hdrSub"&gt;Cerdas Menguji, Adil Menilai&lt;/span&gt;&lt;/div&gt;&lt;span id="hdrDoc"&gt;"Jujur Itu Keren, Berintegritas Itu Juara"&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="wrap"&gt;

&lt;div id="screenStart"&gt;
&lt;div class="card"&gt;
&lt;h1 id="startTitle"&gt;Ujian Online Sekolah&lt;/h1&gt;
&lt;p style="color:var(--sub);font-size:14px;"&gt;Pilih mata pelajaran, kelas, dan masukkan token ujian dari guru untuk memulai.&lt;/p&gt;
&lt;div class="notice"&gt;&lt;b&gt;Petunjuk:&lt;/b&gt;&lt;ul id="rulesList"&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h2&gt;Identitas Murid&lt;/h2&gt;
&lt;div class="field-grid"&gt;
&lt;div&gt;&lt;label class="fl" for="fMapel"&gt;Mata Pelajaran&lt;/label&gt;&lt;select id="fMapel"&gt;&lt;option value="Pendidikan Agama dan Budi Pekerti"&gt;Pendidikan Agama dan Budi Pekerti&lt;/option&gt;&lt;option value="Pendidikan Pancasila"&gt;Pendidikan Pancasila&lt;/option&gt;&lt;option value="Bahasa Indonesia"&gt;Bahasa Indonesia&lt;/option&gt;&lt;option value="Matematika"&gt;Matematika&lt;/option&gt;&lt;option value="Bahasa Inggris"&gt;Bahasa Inggris&lt;/option&gt;&lt;option value="Pendidikan Jasmani, Olahraga, dan Kesehatan (PJOK)"&gt;Pendidikan Jasmani, Olahraga, dan Kesehatan (PJOK)&lt;/option&gt;&lt;option value="Sejarah"&gt;Sejarah&lt;/option&gt;&lt;option value="Seni dan Budaya"&gt;Seni dan Budaya&lt;/option&gt;&lt;option value="Informatika" selected&gt;Informatika&lt;/option&gt;&lt;option value="Projek Ilmu Pengetahuan Alam dan Sosial (IPAS)"&gt;Projek Ilmu Pengetahuan Alam dan Sosial (IPAS)&lt;/option&gt;&lt;option value="Dasar-dasar Kejuruan Mata Pelajaran Konsentrasi Keahlian"&gt;Dasar-dasar Kejuruan Mata Pelajaran Konsentrasi Keahlian&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl" for="fKelas"&gt;Kelas&lt;/label&gt;&lt;select id="fKelas"&gt;&lt;option value=""&gt;- Pilih Kelas -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl" for="fAbsen"&gt;No. Absen&lt;/label&gt;&lt;select id="fAbsen"&gt;&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl" for="fNama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="fNama" placeholder="Terisi otomatis"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div style="margin-top:12px;"&gt;&lt;label class="fl" for="fToken"&gt;Token Ujian&lt;/label&gt;&lt;input type="text" id="fToken" placeholder="Diberikan guru (contoh: UJI-DEFAULT-2026)" style="font-family:monospace;text-transform:uppercase;"&gt;&lt;/div&gt;
&lt;div style="margin-top:14px;"&gt;&lt;button class="btn btn-primary" id="btnStart"&gt;Mulai Ujian&lt;/button&gt;&lt;/div&gt;
&lt;div class="msg err" id="startErr"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p style="text-align:center;"&gt;&lt;button type="button" class="btn btn-outline" id="btnOpenGuru" style="padding:7px 16px;font-size:12.5px;"&gt;Pengaturan Guru&lt;/button&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div id="guruCard" class="card hidden"&gt;
&lt;div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:12px;"&gt;
&lt;h2 style="margin:0;"&gt;Panel Guru&lt;/h2&gt;
&lt;button type="button" class="btn btn-outline" id="btnGuruClose"&gt;&amp;larr; Kembali&lt;/button&gt;
&lt;/div&gt;
&lt;div id="guruLockBox"&gt;
&lt;label class="fl" for="guruCode"&gt;Kode Akses Guru&lt;/label&gt;
&lt;input type="text" id="guruCode" placeholder="Masukkan kode akses"&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnGuruUnlock"&gt;Buka Panel&lt;/button&gt;&lt;/div&gt;
&lt;div class="msg err" id="guruLockErr"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id="guruForm" class="hidden"&gt;
&lt;div class="guru-tabs"&gt;
&lt;button class="guru-tab active" data-tab="exams"&gt;Daftar Ujian&lt;/button&gt;
&lt;button class="guru-tab" data-tab="bank"&gt;Bank Soal&lt;/button&gt;
&lt;button class="guru-tab" data-tab="ai"&gt;AI Generator&lt;/button&gt;
&lt;button class="guru-tab" data-tab="import"&gt;Impor Soal&lt;/button&gt;
&lt;button class="guru-tab" data-tab="apply"&gt;Terapkan ke Ujian&lt;/button&gt;
&lt;button class="guru-tab" data-tab="token"&gt;Token Ujian&lt;/button&gt;
&lt;button class="guru-tab" data-tab="retake"&gt;Ujian Ulang&lt;/button&gt;
&lt;/div&gt;

&lt;div class="guru-panel active" data-panel="exams"&gt;
&lt;div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;flex-wrap:wrap;gap:8px;"&gt;
&lt;b style="font-size:13px;color:var(--navy);"&gt;Kelola Ujian&lt;/b&gt;
&lt;button class="btn btn-primary btn-sm" id="btnCreateExam"&gt;+ Buat Ujian Baru&lt;/button&gt;
&lt;/div&gt;
&lt;div id="examList"&gt;&lt;/div&gt;
&lt;div class="msg info" id="examMsg"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class="guru-panel" data-panel="bank"&gt;
&lt;div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;flex-wrap:wrap;gap:8px;"&gt;
&lt;b id="bankCount" style="font-size:13px;color:var(--navy);"&gt;0 soal tersimpan&lt;/b&gt;
&lt;div style="display:flex;gap:6px;"&gt;
&lt;button class="btn btn-outline btn-sm" id="btnBankAdd"&gt;+ Tambah Soal&lt;/button&gt;
&lt;button class="btn btn-outline btn-sm" id="btnBankSeed"&gt;Muat Contoh&lt;/button&gt;
&lt;button class="btn btn-bad btn-sm" id="btnBankClear"&gt;Hapus Semua&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="warn-box"&gt;Bank soal = area kerja. Klik "+ Tambah Soal" untuk membuat soal (bisa dengan gambar atau lembar kerja rumus). Centang soal yang ingin diterapkan, pilih Ujian Target, lalu klik "Terapkan yang Dipilih".&lt;/div&gt;
&lt;div class="action-bar" style="background:var(--bs); border:1px solid var(--line); padding:10px; border-radius:8px; margin-bottom:12px; display:flex; flex-wrap:wrap; gap:10px; align-items:center;"&gt;
  &lt;div style="display:flex; align-items:center; gap:6px;"&gt;
    &lt;label style="font-size:13px; font-weight:700; white-space:nowrap;"&gt;Mapel Bank:&lt;/label&gt;
    &lt;select id="bankMapelFilter" style="padding:8px 10px; font-size:13px; border-radius:6px; border:1px solid var(--line); min-width:150px; width:auto;"&gt;&lt;option value=""&gt;Semua Mapel&lt;/option&gt;&lt;/select&gt;
  &lt;/div&gt;
  &lt;div style="display:flex; align-items:center; gap:6px;"&gt;
    &lt;label style="font-size:13px; font-weight:700; white-space:nowrap;"&gt;Target Ujian:&lt;/label&gt;
    &lt;select id="bankTargetExam" style="padding:8px 10px; font-size:13px; border-radius:6px; border:1px solid var(--line); min-width:150px; width:auto;"&gt;&lt;option value=""&gt;- Pilih Ujian -&lt;/option&gt;&lt;/select&gt;
  &lt;/div&gt;
  &lt;label style="font-size:13px; font-weight:700; display:flex; align-items:center; gap:4px; cursor:pointer; margin-left:auto;"&gt;
    &lt;input type="checkbox" id="bankSelectAll"&gt; Pilih Semua
  &lt;/label&gt;
  &lt;button class="btn btn-primary btn-sm" id="btnApplySelected" style="padding:8px 16px;"&gt;Terapkan yang Dipilih&lt;/button&gt;
&lt;/div&gt;
&lt;div id="bankList"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class="guru-panel" data-panel="ai"&gt;
&lt;div class="warn-box"&gt;&lt;b&gt;Keamanan:&lt;/b&gt; API key AI jangan disimpan di perangkat bersama. Gunakan template prompt atau API key pribadi Anda.&lt;/div&gt;
&lt;div class="field-grid"&gt;
&lt;div&gt;&lt;label class="fl"&gt;Topik / Materi&lt;/label&gt;&lt;input type="text" id="aiTopik" placeholder="Contoh: Rumus VLOOKUP"&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl"&gt;Jumlah Soal&lt;/label&gt;&lt;input type="number" id="aiJumlah" value="5" min="1" max="30"&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl"&gt;Tipe Soal&lt;/label&gt;&lt;select id="aiTipe"&gt;&lt;option value="pg"&gt;Pilihan Ganda&lt;/option&gt;&lt;option value="isian"&gt;Isian&lt;/option&gt;&lt;option value="benar-salah"&gt;Benar/Salah&lt;/option&gt;&lt;option value="uraian"&gt;Uraian&lt;/option&gt;&lt;option value="campur"&gt;Campuran&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl"&gt;Kesulitan&lt;/label&gt;&lt;select id="aiLevel"&gt;&lt;option value="mudah"&gt;Mudah&lt;/option&gt;&lt;option value="sedang" selected&gt;Sedang&lt;/option&gt;&lt;option value="sulit"&gt;Sulit&lt;/option&gt;&lt;option value="hots"&gt;HOTS&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnAIBuildPrompt"&gt;Buat Prompt AI&lt;/button&gt;&lt;/div&gt;
&lt;div style="margin-top:12px;"&gt;&lt;label class="fl"&gt;Prompt (salin ke ChatGPT/Gemini)&lt;/label&gt;&lt;textarea id="aiPromptOut" rows="9" readonly class="mono"&gt;&lt;/textarea&gt;&lt;/div&gt;
&lt;div style="margin-top:12px;"&gt;
&lt;label class="fl"&gt;Response AI (paste di sini)&lt;/label&gt;
&lt;textarea id="aiResponse" rows="9" class="mono" placeholder="Tempel respons AI..."&gt;&lt;/textarea&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnAIParseResponse"&gt;Parse &amp; Simpan ke Bank&lt;/button&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div style="margin-top:14px;padding-top:14px;border-top:1px solid var(--line);"&gt;
&lt;label class="fl"&gt;Gemini API Key (opsional)&lt;/label&gt;
&lt;input type="text" id="aiApiKey" placeholder="AIza... (dapatkan di aistudio.google.com)"&gt;
&lt;div class="btn-row"&gt;
&lt;button class="btn btn-amber" id="btnAIGenerate"&gt;Generate Otomatis&lt;/button&gt;
&lt;button class="btn btn-outline btn-sm" id="btnAISaveKey"&gt;Simpan Key&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="msg info" id="aiMsg"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class="guru-panel" data-panel="import"&gt;
&lt;div class="warn-box"&gt;&lt;b&gt;Format didukung:&lt;/b&gt; .txt, .docx, .xlsx, .xls, .csv. Soal bergambar dan lembar kerja dibuat lewat tombol "+ Tambah Soal" di tab Bank Soal.&lt;/div&gt;
&lt;label class="fl"&gt;1. Copy-Paste Soal&lt;/label&gt;
&lt;textarea id="pasteInput" rows="9" class="mono" placeholder="Format pipe:&amp;#10;tipe | pertanyaan | opsiA | opsiB | opsiC | opsiD | opsiE | kunci | poin&amp;#10;pg | Apa itu spreadsheet? | Teks | Angka | Gambar | Video | | B | 1"&gt;&lt;/textarea&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnPasteParse"&gt;Parse &amp; Simpan ke Bank&lt;/button&gt;&lt;/div&gt;
&lt;label class="fl" style="margin-top:16px;"&gt;2. Unggah Dokumen (.txt, .docx)&lt;/label&gt;
&lt;div class="upload-zone" id="uploadDocZone"&gt;&lt;div&gt;Klik atau seret file .txt / .docx ke sini&lt;/div&gt;&lt;input type="file" id="fileDocInput" accept=".txt,.docx" style="display:none;"&gt;&lt;/div&gt;
&lt;label class="fl" style="margin-top:16px;"&gt;3. Unggah Spreadsheet (.xlsx, .xls, .csv)&lt;/label&gt;
&lt;div class="upload-zone" id="uploadXlsZone"&gt;&lt;div&gt;Klik atau seret file .xlsx / .xls / .csv ke sini&lt;/div&gt;&lt;input type="file" id="fileXlsInput" accept=".xlsx,.xls,.csv" style="display:none;"&gt;&lt;/div&gt;
&lt;div class="msg info" id="importMsg"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class="guru-panel" data-panel="apply"&gt;
&lt;p style="font-size:13px;color:var(--sub);"&gt;Pindahkan seluruh soal dari Bank Soal ke salah satu Ujian. Soal ujian akan diganti seluruhnya.&lt;/p&gt;
&lt;label class="fl"&gt;Pilih Ujian Target&lt;/label&gt;
&lt;select id="applyTargetExam"&gt;&lt;option value=""&gt;- Pilih Ujian -&lt;/option&gt;&lt;/select&gt;
&lt;div style="margin-top:12px;padding:10px;background:#fafbfe;border:1px solid var(--line);border-radius:8px;"&gt;
&lt;div style="font-size:12.5px;color:var(--sub);"&gt;Info ujian:&lt;/div&gt;
&lt;div id="applyExamInfo" style="font-size:13px;color:var(--navy);margin-top:4px;"&gt;-&lt;/div&gt;
&lt;/div&gt;
&lt;div style="margin-top:12px;padding:10px;background:#fafbfe;border:1px solid var(--line);border-radius:8px;"&gt;
&lt;div style="font-size:12.5px;color:var(--sub);"&gt;Bank soal saat ini:&lt;/div&gt;
&lt;div id="applyBankInfo" style="font-size:13px;color:var(--navy);margin-top:4px;"&gt;0 soal&lt;/div&gt;
&lt;/div&gt;
&lt;div class="btn-row"&gt;
&lt;button class="btn btn-primary" id="btnApplyBank"&gt;Terapkan Semua Bank → Ujian&lt;/button&gt;
&lt;button class="btn btn-outline" id="btnEmptyExam"&gt;Kosongkan Soal Ujian&lt;/button&gt;
&lt;/div&gt;
&lt;div class="msg info" id="applyMsg"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class="guru-panel" data-panel="token"&gt;
&lt;div class="warn-box"&gt;Token = pintu masuk ujian. Setiap ujian punya token unik. Murid mengetik token untuk masuk ke ujian tertentu.&lt;/div&gt;
&lt;div id="tokenList"&gt;&lt;/div&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnSaveTokens"&gt;Simpan Semua Token&lt;/button&gt;&lt;/div&gt;
&lt;div class="msg info" id="tokenMsg"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class="guru-panel" data-panel="retake"&gt;
&lt;div class="warn-box"&gt;&lt;b&gt;Ujian ulang:&lt;/b&gt; pakai bila murid sudah menghabiskan semua kesempatan dan nilainya belum KKM. Setiap pemberian menambah &lt;b&gt;1 kesempatan&lt;/b&gt;. Ada dua cara: (1) buat &lt;b&gt;kode ujian ulang&lt;/b&gt; lalu berikan ke murid, atau (2) bila murid mengerjakan di perangkat ini, klik tombol di daftar bawah.&lt;/div&gt;
&lt;b style="font-size:13px;color:var(--navy);"&gt;1. Buat Kode Ujian Ulang&lt;/b&gt;
&lt;div class="field-grid" style="margin-top:8px;"&gt;
&lt;div&gt;&lt;label class="fl" for="rtKelas"&gt;Kelas&lt;/label&gt;&lt;select id="rtKelas"&gt;&lt;option value=""&gt;- Pilih Kelas -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl" for="rtAbsen"&gt;Murid (No. Absen)&lt;/label&gt;&lt;select id="rtAbsen"&gt;&lt;option value=""&gt;- Pilih Murid -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl" for="rtExam"&gt;Ujian&lt;/label&gt;&lt;select id="rtExam"&gt;&lt;option value=""&gt;- Pilih Ujian -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
&lt;div&gt;&lt;label class="fl" for="rtNo"&gt;Pemberian ke-&lt;/label&gt;&lt;input type="number" id="rtNo" value="1" min="1" max="20"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cnt-small" style="text-align:left;"&gt;Isi 1 untuk pemberian pertama. Isi 2 bila murid ini sudah pernah diberi kode sebelumnya, dan seterusnya. Kode hanya berlaku untuk murid dan ujian yang dipilih, dan hanya sekali pakai.&lt;/div&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnRtGen"&gt;Buat Kode&lt;/button&gt;&lt;/div&gt;
&lt;div id="rtCode" class="code-out hidden"&gt;&lt;/div&gt;
&lt;div style="margin-top:20px;padding-top:14px;border-top:1px solid var(--line);"&gt;
&lt;b style="font-size:13px;color:var(--navy);"&gt;2. Murid di Perangkat Ini yang Belum KKM&lt;/b&gt;
&lt;div id="rtLocalList" style="margin-top:8px;"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="msg info" id="rtMsg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="screenExam" class="hidden"&gt;
&lt;div class="exam-top no-print"&gt;&lt;div class="in"&gt;&lt;span class="who" id="whoLbl"&gt;&lt;/span&gt;&lt;span class="chip timer" id="timerChip"&gt;60:00&lt;/span&gt;&lt;span class="chip sec" id="secChip"&gt;Keamanan: Aman&lt;/span&gt;&lt;span class="chip prog" id="progChip"&gt;Soal 1 dari 50&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="numgrid" id="numGrid"&gt;&lt;/div&gt;
&lt;div class="sec-label" id="qSection"&gt;&lt;/div&gt;
&lt;div class="q-box" id="qBox"&gt;&lt;/div&gt;
&lt;div class="navbtns"&gt;
&lt;button class="btn btn-outline" id="btnPrev"&gt;&amp;larr; Sebelumnya&lt;/button&gt;
&lt;div class="mid"&gt;&lt;button class="btn btn-outline" id="btnMark"&gt;Tandai&lt;/button&gt;&lt;button class="btn btn-teal" id="btnFinish"&gt;Selesai &amp;amp; Kirim&lt;/button&gt;&lt;/div&gt;
&lt;button class="btn btn-outline" id="btnNext"&gt;Berikutnya &amp;rarr;&lt;/button&gt;
&lt;/div&gt;
&lt;div class="msg err" id="examErr"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="screenResult" class="hidden"&gt;
&lt;div class="card" id="resultPrintArea"&gt;
&lt;div style="text-align:center;"&gt;
&lt;h2 id="rTitle"&gt;-&lt;/h2&gt;
&lt;p id="rDesc" style="color:var(--sub);font-size:13.5px;"&gt;&lt;/p&gt;
&lt;div class="ring" id="ring"&gt;&lt;div class="inner"&gt;&lt;span id="rVal"&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div id="rPill" class="status-pill no"&gt;-&lt;/div&gt;
&lt;/div&gt;
&lt;table class="ref"&gt;
&lt;tr&gt;&lt;th&gt;Nama&lt;/th&gt;&lt;td id="riNama"&gt;&lt;/td&gt;&lt;th&gt;Kelas&lt;/th&gt;&lt;td id="riKelas"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;No. Absen&lt;/th&gt;&lt;td id="riAbsen"&gt;&lt;/td&gt;&lt;th&gt;KKM&lt;/th&gt;&lt;td id="riKKM"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;Ujian&lt;/th&gt;&lt;td id="riUjian" colspan="3"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;Mulai&lt;/th&gt;&lt;td id="riMulai"&gt;&lt;/td&gt;&lt;th&gt;Selesai&lt;/th&gt;&lt;td id="riSelesai"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;Durasi&lt;/th&gt;&lt;td id="riDurasi"&gt;&lt;/td&gt;&lt;th&gt;Pelanggaran&lt;/th&gt;&lt;td id="riPelanggaran"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;div id="rBars" style="margin-top:10px;"&gt;&lt;/div&gt;
&lt;div class="msg info show" id="rSend"&gt;&lt;/div&gt;
&lt;div id="rBenchmark" style="font-size:13px;color:var(--sub);"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="btn-row no-print"&gt;
&lt;button class="btn btn-teal" id="btnRetry" style="display:none;"&gt;Ulangi Ujian&lt;/button&gt;
&lt;button class="btn btn-primary" id="btnPdf"&gt;Unduh Bukti (PDF)&lt;/button&gt;
&lt;button class="btn btn-outline" id="btnPrint"&gt;Cetak&lt;/button&gt;
&lt;button class="btn btn-outline" id="btnCsv"&gt;Ekspor CSV&lt;/button&gt;
&lt;button class="btn btn-outline" id="btnResend" style="display:none; background:#fef3c7; border-color:#d97706; color:#92400e;"&gt;Kirim Ulang Nilai&lt;/button&gt;
&lt;button class="btn btn-outline" id="btnHome"&gt;Kembali&lt;/button&gt;
&lt;/div&gt;
&lt;div id="retryBox" class="card no-print hidden" style="margin-top:16px;"&gt;
&lt;h3&gt;Kesempatan mengulang sudah habis&lt;/h3&gt;
&lt;p style="font-size:13.5px;color:var(--sub);"&gt;Nilaimu belum mencapai KKM. Minta guru memberikan &lt;b&gt;kode ujian ulang&lt;/b&gt;, lalu masukkan di bawah ini.&lt;/p&gt;
&lt;input type="text" id="fRetryCode" placeholder="ULANG-XXXXXXXX" style="font-family:monospace;text-transform:uppercase;"&gt;
&lt;div class="btn-row"&gt;&lt;button class="btn btn-amber" id="btnRedeem"&gt;Pakai Kode&lt;/button&gt;&lt;/div&gt;
&lt;div class="msg err" id="retryErr"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;footer&gt;Ujian online berjalan sepenuhnya di peramban murid.&lt;/footer&gt;
&lt;/div&gt;

&lt;form id="gform" action="" method="POST" target="hidden_iframe" style="display:none;"&gt;
&lt;input type="text" name="timestamp" id="gTimestamp"&gt;&lt;input type="text" name="nama" id="gNama"&gt;&lt;input type="text" name="kelas" id="gKelas"&gt;&lt;input type="text" name="absen" id="gAbsen"&gt;&lt;input type="text" name="mapel" id="gMapel2"&gt;&lt;input type="text" name="ujian" id="gUjian"&gt;&lt;input type="text" name="judul" id="gJudul"&gt;&lt;input type="text" name="nilaiPG" id="gNilaiPG"&gt;&lt;input type="text" name="nilaiPraktik" id="gNilaiPraktik"&gt;&lt;input type="text" name="nilaiEsai" id="gNilaiEsai"&gt;&lt;input type="text" name="nilaiAkhir" id="gNilai"&gt;&lt;input type="text" name="nilaiSTS" id="gNilaiSTS"&gt;&lt;input type="text" name="status" id="gStatus"&gt;&lt;input type="text" name="durasi" id="gDurasi"&gt;&lt;input type="text" name="pelanggaran" id="gPelanggaran"&gt;&lt;input type="text" name="skor" id="gSkor"&gt;
&lt;/form&gt;
&lt;iframe name="hidden_iframe" style="display:none;"&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;script&gt;//&lt;![CDATA[
window.addEventListener('error',function(e){
  try{
    // Abaikan error dari skrip pihak lain (template Blogger, widget, CDN):
    // pesan "Script error." tanpa nomor baris/kolom
    if(!e.lineno&amp;&amp;!e.colno){try{console.warn("Error skrip pihak lain diabaikan:",e.message)}catch(x){}return}
    if(/^Script error\.?$/i.test(e.message||""))return;
    // Hanya tampilkan bila error berasal dari halaman ujian ini
    var f=String(e.filename||"").split("#")[0].split("?")[0];
    var here=String(location.href).split("#")[0].split("?")[0];
    if(f&amp;&amp;f!==here)return;
    if(document.getElementById('examHotsErrBanner'))return;
    var b=document.createElement('div');
    b.id='examHotsErrBanner';
    b.style.cssText='position:fixed;top:0;left:0;right:0;z-index:99999;background:#b91c1c;color:#fff;padding:10px 40px 10px 14px;font:13px/1.4 sans-serif;';
    b.textContent='Skrip ujian gagal (baris '+(e.lineno||'?')+', kolom '+(e.colno||'?')+'): '+(e.message||'tidak diketahui');
    var x=document.createElement('span');
    x.textContent='\u2715';
    x.style.cssText='position:absolute;right:14px;top:8px;cursor:pointer;font-weight:700';
    x.onclick=function(){b.remove()};
    b.appendChild(x);
    document.body.insertBefore(b,document.body.firstChild);
  }catch(x2){}
});
//]]&gt;&lt;/script&gt;

&lt;script&gt;//&lt;![CDATA[
!function(){"use strict";
function a(i){return document.getElementById(i)}
function n(s){return String(null==s?"":s).replace(/[&amp;&lt;&gt;"']/g,function(c){return{"&amp;":"&amp;amp;","&lt;":"&amp;lt;","&gt;":"&amp;gt;",'"':"&amp;quot;","'":"&amp;#39;"}[c]})}
function shuf(arr){for(var r=arr.slice(),i=r.length-1;i&gt;0;i--){var j=Math.floor(Math.random()*(i+1)),t=r[i];r[i]=r[j];r[j]=t}return r}
function idxs(len){var r=[];for(var i=0;i&lt;len;i++)r.push(i);return r}
function setMsg(el,msg,type){if(!el)return;el.textContent=msg;el.className="msg "+(type||"err")+(msg?" show":"")}
function low(s){return String(null==s?"":s).trim().toLowerCase().replace(/\s+/g," ")}
function wc(s){var t=String(s||"").trim();return t?t.split(/\s+/).length:0}
function mkTable(head,rows,numCols){numCols=numCols||[];var h='&lt;table class="ref"&gt;&lt;tr&gt;'+head.map(function(x){return"&lt;th&gt;"+n(x)+"&lt;/th&gt;"}).join("")+"&lt;/tr&gt;";rows.forEach(function(r){h+="&lt;tr&gt;"+r.map(function(c,idx){return"&lt;td"+(numCols.indexOf(idx)&gt;=0?' class="num"':"")+"&gt;"+n(c)+"&lt;/td&gt;"}).join("")+"&lt;/tr&gt;"});return h+"&lt;/table&gt;"}
function safeImg(s){return typeof s==="string"&amp;&amp;/^(https?:\/\/|data:image\/(png|jpe?g|gif|webp);base64,)/i.test(s)}
function imgHtml(s){return safeImg(s)?'&lt;div class="q-imgwrap"&gt;&lt;img class="q-img" src="'+n(s)+'" alt="Gambar soal" data-zoom="1"&gt;&lt;/div&gt;':""}

/* ===== Mesin lembar kerja spreadsheet (rumus dihitung sungguhan) ===== */
var SheetKit=(function(){
"use strict";
function XErr(code){this.code=code}
function fail(code){throw new XErr(code)}
function colNum(s){var r=0;for(var i=0;i&lt;s.length;i++)r=r*26+(s.charCodeAt(i)-64);return r}
function colName(k){var s="";while(k&gt;0){var m=(k-1)%26;s=String.fromCharCode(65+m)+s;k=Math.floor((k-1)/26)}return s}
function splitRef(s){var m=/^([A-Z]+)(\d+)$/.exec(String(s).toUpperCase());return m?{c:colNum(m[1]),r:parseInt(m[2],10)}:{c:0,r:0}}
var NUMRE=/^[-+]?\d+(\.\d+)?$/;
function fmtNum(v){return isFinite(v)?String(parseFloat(v.toPrecision(12))):"#NUM!"}
function toStr(v){return typeof v==="boolean"?(v?"TRUE":"FALSE"):typeof v==="number"?fmtNum(v):(v==null?"":String(v))}
function toNum(v){if(typeof v==="number")return v;if(typeof v==="boolean")return v?1:0;if(v===""||v==null)return 0;var s=String(v).trim();if(NUMRE.test(s))return parseFloat(s);return fail("#VALUE!")}
function toBool(v){if(typeof v==="boolean")return v;if(typeof v==="number")return v!==0;var s=String(v).trim().toUpperCase();if(s==="TRUE")return true;if(s==="FALSE"||s==="")return false;return fail("#VALUE!")}
function sc(v){if(v&amp;&amp;typeof v==="object")fail("#VALUE!");return v}
function cap(rg){if((rg.r2-rg.r1+1)*(rg.c2-rg.c1+1)&gt;5000)fail("#VALUE!")}
function flat(v,x){if(v&amp;&amp;v.rg){cap(v);var o=[];for(var r=v.r1;r&lt;=v.r2;r++)for(var c=v.c1;c&lt;=v.c2;c++)o.push(x.get(colName(c)+r));return o}return[v]}
function shifted(sr,rg){return{rg:true,c1:sr.c1,r1:sr.r1,c2:sr.c1+(rg.c2-rg.c1),r2:sr.r1+(rg.r2-rg.r1)}}
function cmpVals(x,y){var tx=typeof x,ty=typeof y;if(tx!==ty){var od={number:0,string:1,"boolean":2};return od[tx]-od[ty]}if(tx==="string"){x=x.toLowerCase();y=y.toLowerCase()}return x&lt;y?-1:x&gt;y?1:0}
function doCmp(x,y,op){
  if(x===""&amp;&amp;typeof y==="number")x=0;
  if(y===""&amp;&amp;typeof x==="number")y=0;
  var c=cmpVals(x,y);
  switch(op){case"=":return c===0;case"&lt;&gt;":return c!==0;case"&lt;":return c&lt;0;case"&gt;":return c&gt;0;case"&lt;=":return c&lt;=0;default:return c&gt;=0}
}
function wild(s,pat){var re=new RegExp("^"+pat.replace(/[.+^${}()|[\]\\]/g,"\\$&amp;").replace(/\*/g,".*").replace(/\?/g,".")+"$","i");return re.test(s)}
function mkCrit(c){
  if(typeof c==="boolean")return function(v){return v===c};
  var s=String(c),op="=";
  if(typeof c!=="number"){var m=/^(&gt;=|&lt;=|&lt;&gt;|&gt;|&lt;|=)([\s\S]*)$/.exec(s);if(m){op=m[1];s=m[2]}}
  var isN=s.trim()!==""&amp;&amp;NUMRE.test(s.trim()),num=isN?parseFloat(s):null;
  return function(v){
    var r;
    if(op==="="||op==="&lt;&gt;"){
      if(isN)r=(typeof v==="number"&amp;&amp;v===num);
      else if(s==="")r=(v===""||v==null);
      else r=(typeof v==="string"&amp;&amp;wild(v,s));
      return op==="="?r:!r;
    }
    var p1,p2;
    if(isN){if(typeof v!=="number")return false;p1=v;p2=num}
    else{if(typeof v!=="string"||v==="")return false;p1=v.toLowerCase();p2=s.toLowerCase()}
    return op==="&gt;"?p1&gt;p2:op==="&lt;"?p1&lt;p2:op==="&gt;="?p1&gt;=p2:p1&lt;=p2;
  };
}
function nums(args,x){
  var out=[];
  args.forEach(function(t){
    var v=t();
    if(v&amp;&amp;v.rg){flat(v,x).forEach(function(c){if(typeof c==="number")out.push(c)})}
    else if(typeof v==="number")out.push(v);
    else if(typeof v==="boolean")out.push(v?1:0);
    else if(typeof v==="string"&amp;&amp;NUMRE.test(v.trim()))out.push(parseFloat(v));
  });
  return out;
}
function rnd(v,d,mode){var m=Math.pow(10,d),s=v&lt;0?-1:1,ab=Math.abs(v)*m;ab=mode==="up"?Math.ceil(ab-1e-9):mode==="down"?Math.floor(ab+1e-9):Math.round(ab+1e-9);return s*ab/m}
var FN={
  SUM:function(args,x){return nums(args,x).reduce(function(s,v){return s+v},0)},
  AVERAGE:function(args,x){var v=nums(args,x);if(!v.length)fail("#DIV/0!");return v.reduce(function(s,k){return s+k},0)/v.length},
  COUNT:function(args,x){return nums(args,x).length},
  COUNTA:function(args,x){var c=0;args.forEach(function(t){flat(t(),x).forEach(function(v){if(v!==""&amp;&amp;v!=null)c++})});return c},
  MAX:function(args,x){var v=nums(args,x);return v.length?Math.max.apply(null,v):0},
  MIN:function(args,x){var v=nums(args,x);return v.length?Math.min.apply(null,v):0},
  IF:function(args){if(args.length&lt;2)fail("#VALUE!");return toBool(sc(args[0]()))?args[1]():(args.length&gt;2?args[2]():false)},
  IFERROR:function(args){try{var v=sc(args[0]());if(typeof v==="string"&amp;&amp;/^#[A-Z\/0!?]+$/.test(v))return args[1]();return v}catch(e){if(e instanceof XErr)return args[1]();throw e}},
  AND:function(args,x){var r=true;args.forEach(function(t){flat(t(),x).forEach(function(v){if(v!==""&amp;&amp;!toBool(v))r=false})});return r},
  OR:function(args,x){var r=false;args.forEach(function(t){flat(t(),x).forEach(function(v){if(v!==""&amp;&amp;toBool(v))r=true})});return r},
  NOT:function(args){return !toBool(sc(args[0]()))},
  ABS:function(args){return Math.abs(toNum(sc(args[0]())))},
  INT:function(args){return Math.floor(toNum(sc(args[0]())))},
  ROUND:function(args){return rnd(toNum(sc(args[0]())),args.length&gt;1?toNum(sc(args[1]())):0,"n")},
  ROUNDUP:function(args){return rnd(toNum(sc(args[0]())),args.length&gt;1?toNum(sc(args[1]())):0,"up")},
  ROUNDDOWN:function(args){return rnd(toNum(sc(args[0]())),args.length&gt;1?toNum(sc(args[1]())):0,"down")},
  LEFT:function(args){var s=toStr(sc(args[0]())),k=args.length&gt;1?toNum(sc(args[1]())):1;if(k&lt;0)fail("#VALUE!");return s.substr(0,k)},
  RIGHT:function(args){var s=toStr(sc(args[0]())),k=args.length&gt;1?toNum(sc(args[1]())):1;if(k&lt;0)fail("#VALUE!");return s.slice(Math.max(0,s.length-k))},
  MID:function(args){var s=toStr(sc(args[0]())),st=toNum(sc(args[1]())),k=toNum(sc(args[2]()));if(st&lt;1||k&lt;0)fail("#VALUE!");return s.substr(st-1,k)},
  LEN:function(args){return toStr(sc(args[0]())).length},
  UPPER:function(args){return toStr(sc(args[0]())).toUpperCase()},
  LOWER:function(args){return toStr(sc(args[0]())).toLowerCase()},
  TRIM:function(args){return toStr(sc(args[0]())).trim().replace(/\s+/g," ")},
  CONCATENATE:function(args){return args.map(function(t){return toStr(sc(t()))}).join("")},
  COUNTIF:function(args,x){var rg=args[0]();if(!rg||!rg.rg)fail("#VALUE!");var f=mkCrit(sc(args[1]()));return flat(rg,x).filter(f).length},
  SUMIF:function(args,x){
    var rg=args[0]();if(!rg||!rg.rg)fail("#VALUE!");
    var f=mkCrit(sc(args[1]())),sr=args.length&gt;2?args[2]():rg;if(!sr||!sr.rg)fail("#VALUE!");
    var cv=flat(rg,x),sv=flat(shifted(sr,rg),x),s=0;
    cv.forEach(function(v,i){if(f(v)&amp;&amp;typeof sv[i]==="number")s+=sv[i]});return s;
  },
  AVERAGEIF:function(args,x){
    var rg=args[0]();if(!rg||!rg.rg)fail("#VALUE!");
    var f=mkCrit(sc(args[1]())),sr=args.length&gt;2?args[2]():rg;if(!sr||!sr.rg)fail("#VALUE!");
    var cv=flat(rg,x),sv=flat(shifted(sr,rg),x),s=0,k=0;
    cv.forEach(function(v,i){if(f(v)&amp;&amp;typeof sv[i]==="number"){s+=sv[i];k++}});
    if(!k)fail("#DIV/0!");return s/k;
  },
  SUMIFS:function(args,x){
    if(args.length&lt;3||args.length%2===0)fail("#VALUE!");
    var sr=args[0]();if(!sr||!sr.rg)fail("#VALUE!");
    var sv=flat(sr,x),keep=sv.map(function(){return true});
    for(var i=1;i&lt;args.length;i+=2){
      var rg=args[i]();if(!rg||!rg.rg)fail("#VALUE!");
      var cv=flat(shifted(rg,sr),x),f=mkCrit(sc(args[i+1]()));
      cv.forEach(function(v,j){if(!f(v))keep[j]=false});
    }
    var s=0;sv.forEach(function(v,j){if(keep[j]&amp;&amp;typeof v==="number")s+=v});return s;
  },
  COUNTIFS:function(args,x){
    if(args.length&lt;2||args.length%2)fail("#VALUE!");
    var base=args[0]();if(!base||!base.rg)fail("#VALUE!");
    var keep=flat(base,x).map(function(){return true});
    for(var i=0;i&lt;args.length;i+=2){
      var rg=args[i]();if(!rg||!rg.rg)fail("#VALUE!");
      var cv=flat(shifted(rg,base),x),f=mkCrit(sc(args[i+1]()));
      cv.forEach(function(v,j){if(!f(v))keep[j]=false});
    }
    return keep.filter(Boolean).length;
  },
  VLOOKUP:function(args,x){
    var look=sc(args[0]()),rg=args[1]();if(!rg||!rg.rg)fail("#VALUE!");
    var ci=toNum(sc(args[2]()));if(ci&lt;1||rg.c1+ci-1&gt;rg.c2)fail("#REF!");
    var exact=args.length&gt;3&amp;&amp;!toBool(sc(args[3]())),found=-1;
    for(var r=rg.r1;r&lt;=rg.r2;r++){
      var v=x.get(colName(rg.c1)+r);
      if(exact){if(v!==""&amp;&amp;cmpVals(v,look)===0){found=r;break}}
      else{if(v===""||typeof v!==typeof look)continue;if(cmpVals(v,look)&lt;=0)found=r;else break}
    }
    if(found&lt;0)fail("#N/A");
    return x.get(colName(rg.c1+ci-1)+found);
  },
  HLOOKUP:function(args,x){
    var look=sc(args[0]()),rg=args[1]();if(!rg||!rg.rg)fail("#VALUE!");
    var ri=toNum(sc(args[2]()));if(ri&lt;1||rg.r1+ri-1&gt;rg.r2)fail("#REF!");
    var exact=args.length&gt;3&amp;&amp;!toBool(sc(args[3]())),found=-1;
    for(var c=rg.c1;c&lt;=rg.c2;c++){
      var v=x.get(colName(c)+rg.r1);
      if(exact){if(v!==""&amp;&amp;cmpVals(v,look)===0){found=c;break}}
      else{if(v===""||typeof v!==typeof look)continue;if(cmpVals(v,look)&lt;=0)found=c;else break}
    }
    if(found&lt;0)fail("#N/A");
    return x.get(colName(found)+(rg.r1+ri-1));
  }
};
function tokenize(src){
  var t=[],i=0,m;
  while(i&lt;src.length){
    var ch=src.charAt(i);
    if(/\s/.test(ch)){i++;continue}
    if(ch==='"'){
      var j=i+1,s="";
      while(j&lt;src.length){if(src.charAt(j)==='"'){if(src.charAt(j+1)==='"'){s+='"';j+=2;continue}break}s+=src.charAt(j);j++}
      if(j&gt;=src.length)fail("#VALUE!");
      t.push({k:"str",v:s});i=j+1;continue;
    }
    var rest=src.slice(i);
    if((m=/^\d+(\.\d+)?/.exec(rest))||(m=/^\.\d+/.exec(rest))){t.push({k:"num",v:parseFloat(m[0])});i+=m[0].length;continue}
    if((m=/^\$?[A-Za-z]{1,3}\$?\d+(?![A-Za-z0-9_(])/.exec(rest))){t.push({k:"ref",v:m[0].replace(/\$/g,"").toUpperCase()});i+=m[0].length;continue}
    if((m=/^[A-Za-z_][A-Za-z0-9_.]*/.exec(rest))){
      var w=m[0],up=w.toUpperCase();i+=w.length;
      if(src.charAt(i)==="(")t.push({k:"fn",v:up});
      else if(up==="TRUE"||up==="FALSE")t.push({k:"bool",v:up==="TRUE"});
      else fail("#NAME?");
      continue;
    }
    if((m=/^(&lt;=|&gt;=|&lt;&gt;)/.exec(rest))){t.push({k:"op",v:m[0]});i+=2;continue}
    if("+-*/^&amp;=&lt;&gt;(),;:%".indexOf(ch)&gt;=0){t.push({k:"op",v:ch===";"?",":ch});i++;continue}
    fail("#NAME?");
  }
  return t;
}
function compile(src,ctx){
  var tk=tokenize(src),p=0;
  function isOp(v){var q=tk[p];return !!q&amp;&amp;q.k==="op"&amp;&amp;q.v===v}
  function eat(v){if(!isOp(v))fail("#VALUE!");p++}
  function expr(){return cmp()}
  function cmp(){
    var l=cat();
    while(tk[p]&amp;&amp;tk[p].k==="op"&amp;&amp;/^(=|&lt;&gt;|&lt;=|&gt;=|&lt;|&gt;)$/.test(tk[p].v)){
      var op=tk[p++].v,r=cat();
      l=(function(l,r,op){return function(){return doCmp(sc(l()),sc(r()),op)}})(l,r,op);
    }
    return l;
  }
  function cat(){
    var l=add();
    while(isOp("&amp;")){p++;var r=add();l=(function(l,r){return function(){return toStr(sc(l()))+toStr(sc(r()))}})(l,r)}
    return l;
  }
  function add(){
    var l=mul();
    while(isOp("+")||isOp("-")){
      var op=tk[p++].v,r=mul();
      l=(function(l,r,op){return function(){var u=toNum(sc(l())),w=toNum(sc(r()));return op==="+"?u+w:u-w}})(l,r,op);
    }
    return l;
  }
  function mul(){
    var l=pow();
    while(isOp("*")||isOp("/")){
      var op=tk[p++].v,r=pow();
      l=(function(l,r,op){return function(){var u=toNum(sc(l())),w=toNum(sc(r()));if(op==="/"){if(w===0)fail("#DIV/0!");return u/w}return u*w}})(l,r,op);
    }
    return l;
  }
  function pow(){
    var l=unary();
    while(isOp("^")){p++;var r=unary();l=(function(l,r){return function(){return Math.pow(toNum(sc(l())),toNum(sc(r())))}})(l,r)}
    return l;
  }
  function unary(){
    if(isOp("-")){p++;var u=unary();return function(){return -toNum(sc(u()))}}
    if(isOp("+")){p++;return unary()}
    return post();
  }
  function post(){
    var v=prim();
    while(isOp("%")){p++;v=(function(v){return function(){return toNum(sc(v()))/100}})(v)}
    return v;
  }
  function prim(){
    var x=tk[p];if(!x)fail("#VALUE!");
    if(x.k==="num"||x.k==="str"||x.k==="bool"){p++;return function(){return x.v}}
    if(x.k==="ref"){
      p++;
      if(isOp(":")){
        p++;var y=tk[p];if(!y||y.k!=="ref")fail("#REF!");p++;
        var s1=splitRef(x.v),s2=splitRef(y.v);
        var rg={rg:true,c1:Math.min(s1.c,s2.c),r1:Math.min(s1.r,s2.r),c2:Math.max(s1.c,s2.c),r2:Math.max(s1.r,s2.r)};
        return function(){return rg};
      }
      return function(){return ctx.get(x.v)};
    }
    if(x.k==="fn"){
      p++;eat("(");
      var args=[];
      if(!isOp(")")){args.push(expr());while(isOp(",")){p++;args.push(expr())}}
      eat(")");
      if(!FN.hasOwnProperty(x.v))fail("#NAME?");
      var f=FN[x.v];
      return function(){return f(args,ctx)};
    }
    if(isOp("(")){p++;var e=expr();eat(")");return e}
    return fail("#VALUE!");
  }
  var root=expr();
  if(p&lt;tk.length)fail("#VALUE!");
  return root;
}
function calc(raws){
  var cache={},busy={},ctx={get:get};
  function get(ref){
    ref=String(ref).toUpperCase();
    if(cache.hasOwnProperty(ref))return cache[ref];
    var raw=raws[ref];
    if(raw==null||String(raw).trim()==="")return(cache[ref]="");
    raw=String(raw);
    var v;
    if(raw.charAt(0)==="="){
      if(busy[ref])return "#REF!";
      busy[ref]=1;
      try{var f=compile(raw.slice(1),ctx);v=f();if(v&amp;&amp;typeof v==="object")v="#VALUE!"}
      catch(e){v=(e instanceof XErr)?e.code:"#ERROR!"}
      delete busy[ref];
    }else{
      var s=raw.trim();v=NUMRE.test(s)?parseFloat(s):raw;
    }
    cache[ref]=v;return v;
  }
  return{get:get};
}
function sameVal(x,y){
  if(typeof x==="number"&amp;&amp;typeof y==="number")return Math.abs(x-y)&lt;=1e-6*Math.max(1,Math.abs(y));
  if(typeof x==="string"&amp;&amp;typeof y==="string")return x.trim().toLowerCase()===y.trim().toLowerCase();
  return x===y;
}
function merge(o1,o2){var r={};Object.keys(o1||{}).forEach(function(k){r[k]=o1[k]});Object.keys(o2||{}).forEach(function(k){r[k]=o2[k]});return r}
function sortRef(x,y){var u=splitRef(x),w=splitRef(y);return u.r-w.r||u.c-w.c}
function derive(q){
  var awal=q.awal||{},kunci=q.kunci||{},locked={},targets=[];
  Object.keys(awal).forEach(function(k){if(kunci[k]===undefined||kunci[k]===awal[k])locked[k]=awal[k]});
  var ck=calc(merge(awal,kunci));
  Object.keys(kunci).filter(function(k){return !locked.hasOwnProperty(k)}).sort(sortRef).forEach(function(k){
    targets.push({ref:k,exp:ck.get(k),raw:kunci[k],needF:String(kunci[k]).charAt(0)==="="});
  });
  return{locked:locked,targets:targets};
}
function score(q,cells){
  var d=derive(q),raws={};cells=cells||{};
  Object.keys(d.locked).forEach(function(k){raws[k]=d.locked[k]});
  Object.keys(cells).forEach(function(k){if(!d.locked.hasOwnProperty(k))raws[k]=cells[k]});
  var cs=calc(raws),ok=0;
  d.targets.forEach(function(t){
    var raw=String(cells[t.ref]==null?"":cells[t.ref]).trim(),got=cs.get(t.ref);
    if(raw!==""&amp;&amp;sameVal(got,t.exp)&amp;&amp;(!t.needF||raw.charAt(0)==="="))ok++;
  });
  var total=d.targets.length,pts=q.points||5;
  return{score:total?Math.round(pts*ok/total*100)/100:0,ok:ok,total:total};
}
function parseCells(text,start){
  var out={},lines=String(text||"").replace(/\r/g,"").split("\n");
  var ne=lines.filter(function(l){return l.trim()!==""});
  if(!ne.length)return out;
  var listMode=ne.every(function(l){return /^\s*[A-Za-z]{1,2}\d{1,3}\s*:/.test(l)});
  if(listMode){
    ne.forEach(function(l){var m=/^\s*([A-Za-z]{1,2}\d{1,3})\s*:\s?([\s\S]*)$/.exec(l);var v=m[2].replace(/\s+$/,"");if(v!=="")out[m[1].toUpperCase()]=v});
    return out;
  }
  var st=/^([A-Za-z]{1,2})(\d{1,3})$/.exec(String(start||"A1").trim());
  var c0=st?colNum(st[1].toUpperCase()):1,r0=st?parseInt(st[2],10):1;
  lines.forEach(function(l,ri){l.split("\t").forEach(function(v,ci){v=v.trim();if(v!=="")out[colName(c0+ci)+(r0+ri)]=v})});
  return out;
}
return{calc:calc,derive:derive,score:score,parseCells:parseCells,show:toStr,colName:colName,colNum:colNum,splitRef:splitRef};
})();

var K_EXAMS="examHotsExams_v2",K_STATE="examHotsState_v2_",K_DRAFT="examHotsDraft_v2_";
var DEF_MAXATT=3;

var l={"X TB1":[[1,"ADHA MIRAHAYU NINGSIH"],[2,"ANGELINA AGNES NOVIOLA"],[3,"ANISSA VIRA FIRMANDA"],[4,"Awinda Rizqi Amru Anjani"],[5,"CITRA AULIA BAHARIZQI"],[6,"DHIRA CAHYA OKTAVIANINGRUM"],[7,"Ema Deny Setyaningrum"],[8,"EVITA APRILIANI"],[9,"FAIR ROCHMATUL ISHMAH"],[10,"FATMA NABILA"],[11,"FATMAWATI"],[12,"Fika Setya Wulandari"],[13,"Gelsi Zalika Thufaila"],[14,"HIDAYATUL HUSNA MAULIDA"],[15,"IZZATUL LUTHFA IMANDA"],[16,"MEISHA AYU BERLIA"],[17,"MEYLISA PUTRI MURDIANI"],[18,"MITA TRI OKTAVIA"],[19,"MUTIARA SYABILLA"],[20,"NABILA SASKIA"],[21,"NADYA VEGA RAHMADANI"],[22,"Nafisa Nailal Husna"],[23,"NAWA SYIFFA MARYAM"],[24,"NIRIN FADILLAH"],[25,"RENI RESTIA PRAMESTI"],[26,"RIZKIYA MAULIDA"],[27,"SHINTA AULIA MUKTI"],[28,"SHINTIA AMELIA PUTRI"],[29,"SUCI NUR AZAHRA"],[30,"SYAH UITTA JIHAN MAKAELA"],[31,"VARISCA INDRI ARIANI"],[32,"VILSA SAFA AZZAHRA"],[33,"YUANITA ROBBITOTUN NAJA"],[34,"ZUNI ARIFAH"]],"X TB2":[[1,"ADINDA EKA NUR AINI"],[2,"Adinda Permata Sari"],[3,"AMANDA PUSPA MAHARANI"],[4,"ANA AMELLIYA"],[5,"AULIYA NATASYA DEWI"],[6,"AYUB MAULANA"],[7,"BILQIS NGIZZATUN NISA'"],[8,"CEYSA ELVINA AMANDA"],[9,"CHAUDLI MAULIDINA AZRA"],[10,"DESTA AURA AZALIA SANI"],[11,"DINDA NURUL AMALIA"],[12,"Dwi Mayangsari"],[13,"EFRIDA DEWI ANANDA"],[14,"Emi Rara Pramudita"],[15,"FIRMAN ARYA DINAIA"],[16,"KEISYA ALISYA PUTRI ALI"],[17,"Kusnul Kusuma Wardhani"],[18,"LIBY ARUMBIYA"],[19,"Lutfia Putri"],[20,"MARISKA NUR AIRA"],[21,"NADIA NUR AZIZAH IRYSATI"],[22,"NAHLA AULIA SALSABILLA"],[23,"NAURA IZA AZAHRA"],[24,"NAZWA AKAYLA SAPUTRI"],[25,"NOVITA ADHA RIZKI ANGGRAINI"],[26,"PEMBAYUN ANGGER WIDJAYA"],[27,"REVALIA NOVIANTI TAMARA"],[28,"SAHAL HANIF YUSUF"],[29,"SEKAR WANGI"],[30,"Silvi Dwi Noviyanti"],[31,"SIRVI KHUNAIFIYAH LINA AINI NISA"],[32,"VITO ADI CANDRA"],[33,"ZAHWA"],[34,"ZIDANNAJ MAROSAT"]],"X TO1":[[1,"ACHMAT SAPTA ARYA"],[2,"ADE FASIYAN NANDO"],[3,"ALBERTUS ADELIO RONALD FAGUNDEZ"],[4,"ALLAN FADLI MUHAMMAD JIBRANNI"],[5,"ARGA SYARUL SETIYAWAN"],[6,"DEDDY SETIYAWAN"],[7,"DENNIZ PRADIVTA ALVIANDRA"],[8,"DIMAS PUTRA ERLANGGA"],[9,"DIYAS PRASETYO"],[10,"DZAKIE AZ ZAAHII"],[11,"FADHIL DIANTORO"],[12,"FAQIH UBAIDILLAH"],[13,"FAQIYUDDIN ASYRAF"],[14,"HANIF TAUFIQULHAQIM"],[15,"IQBAL HAIDAR ARIQ"],[16,"IRFAN BAGAS VEBYANTINO"],[17,"MARCELINO ADI SANTOSO"],[18,"MUHAMAD AFTAH FARKHAN NAKA"],[19,"MUHAMAD AMAR MA'RUF"],[20,"MUHAMAD ILHAM PRASETYO"],[21,"MUHAMMAD AFWAN MAULANA AZIDAN"],[22,"MUHAMMAD KAFA BIHI FADLURROZAG"],[23,"MUHAMMAD SONY TAFRIKHAN"],[24,"MUHAMMAD SYAHID SYAHPUTRA"],[25,"MUHAMMAD ZAHRUL ULUM AL BUSTHOMI"],[26,"NANDO KURNIA EKA PUTRA"],[27,"NAUFAL ZAKY MUTTAQIN"],[28,"Nizham Zafir Hariz Saputra"],[29,"NUR FATHUR RAHMATULLAH"],[30,"RADITYA PUTRA PERDANA"],[31,"RAFKA MAULANA PUTRA"],[32,"RAKA RAFIF AZMI"],[33,"REYHAN YUNAZTA PUTRA HANY"],[34,"RIZKI IBADALLAH ALBAROYA"],[35,"SEPTIAN DANANG PRASETYO"],[36,"Yehuda Bayu Seno Abimanyu"]],"X TO2":[[1,"ADHAM DZAKI IBRAHIM MUFID"],[2,"AFLAH RAPHAEL WINDRA RADITYA"],[3,"AHMAD DAVI PRIA UTAMA"],[4,"AHMAD MAULANA ABDUL MALIK"],[5,"Ahmad Shovi Maola"],[6,"ALBARASYA HERMAN"],[7,"ANDIK FEBRI FIRMANSYAH"],[8,"DIMAS SAPUTRA"],[9,"DISKA INDRA FATA WIDAKDO"],[10,"FAREL ANDRIAN PRATAMA"],[11,"Ferdita Fatma Kusuma"],[12,"GALIH GINANJAR SAPUTRO"],[13,"LEVI FASTABIKHUL KHOIR"],[14,"LIONEL NATHAN KURNIAWAN"],[15,"MUHAMAD FIRDIANSYAH"],[16,"MUHAMAD KHOIRUL ANWAR"],[17,"MUHAMAD RIJAL ALIF IBRAHIM"],[18,"MUHAMAD TANFEBRO PERMANA"],[19,"MUHAMMAD AKMAL FARIZKY"],[20,"MUHAMMAD ALWI SIHAB"],[21,"MUHAMMAD 'AQILA AZKA"],[22,"MUHAMMAD FATIH"],[23,"MUHAMMAD FAUZI NOVANTYO"],[24,"Muhammad Rafka Adi Pratama"],[25,"MUHAMMAD TONI SUPRASETYO"],[26,"NOVAN ANANDA RIZKY"],[27,"PRAKASA TEGAR ANDRIYANTO"],[28,"RAFAEL BAYU PRATAMA"],[29,"RANDI KHOIRUL AZZAM"],[30,"RIDHO AMRU BAIN"],[31,"SABRI RADHITYA GANESWARA"],[32,"SATRIA FIRMAN WIBOWO"],[33,"SYARIF HIDAYAD"],[34,"WILDAN RIZQI ARRUMMAN"],[35,"VICKY ADIKA CHANDRA"]],"X TJKT1":[[1,"ADLY SURYA HERNAWAN"],[2,"ADRIANUS FERDINAND BARRA ARDIONA"],[3,"AISYAH KHOIRUN NISA"],[4,"ALIF EVAN SANJAYA"],[5,"Alif Maulana Ashidiqie"],[6,"Alif Rizqullah Ramadhan"],[7,"ANDIKA BILLIE FAUSTINO"],[8,"ARVIN ARYASETA ATMAJA"],[9,"BRYLIANA JIHAN AMIR"],[10,"DANDRY KORNELIUS ARISANDI"],[11,"DANIEL REVELINO FEIVEL WIDJANARKO"],[12,"DWI APRIELLA NUR RAHMA"],[13,"HANAR FAIRUS NADHIR AMRULLAH"],[14,"HANI DWI RAHMAWATI"],[15,"IKHSAN ABDUL ROZAK"],[16,"JAVIEN PUTRA MAHENDRA"],[17,"Linda Putri Darmawan"],[18,"LINGGA MAHARDIKA SUSATYO"],[19,"MUAZARAH FARRAS AMELIA"],[20,"MUHAMMAD FA'DLIL ASYHARI"],[21,"MUHAMMAD FAQIH ANWARI"],[22,"MUHAMMAD SIDIQ FATHUL MU'IZ"],[23,"NAILATUSSAKINAH"],[24,"NAJMA AULYA"],[25,"Nazila Karrolina Aishi Azzahra"],[26,"RAKHUL SURYA PERMANA"],[27,"RAYA GHANIA PUTRI"],[28,"REVALINA AULIA ZAHRA"],[29,"Salma Anggun Aurellia"],[30,"SANREZ REKSI KUSUMA"],[31,"SETIYA RIZKY MAULANA"],[32,"SHARA FATIKA ADDAHLANI"],[33,"SINTA PUSPA SARI"],[34,"ZULFA IBNUL KARIM"]],"X TJKT2":[[1,"AKHMAD FAKHRI HIDAYAT"],[2,"ALIA NUR AISYAH"],[3,"ANDIKA MAULANA MUHAIMIN"],[4,"ANDRIANSYAH MAULANA"],[5,"ANUM MANURAGA"],[6,"BRIANDA OCTA PRATAMA"],[7,"DAVID LUQMAN"],[8,"DEVANDRI KUSUMA"],[9,"DIMAS SALMAN KHAFI"],[10,"DIVANI AULIA NAJMI"],[11,"DWI AYU SASKIYA"],[12,"DYLLAN DEEP YOSHAN"],[13,"DZIKRI MAULANA"],[14,"FAREL RADHITYA PRADANA"],[15,"GHIFARI SAHILA DZAKI"],[16,"INDAH NAZIFA"],[17,"JATI WEKO ADI"],[18,"JERIKO PRANATA"],[19,"KHOIRUL MUNA"],[20,"MAYLINDA QHONITA PUTRI"],[21,"MIFTAKHUL ADIYANSYAH"],[22,"MUHAMAD RESTU FAUZI"],[23,"MUHAMMAD ABADI"],[24,"MUHAMMAD FAIZ FIRMANSYAH"],[25,"MUHAMMAD IRFAN SAPUTRA"],[26,"MUHAMMAD REVA IBRAHIMMUVIQ"],[27,"NAJA ADKAR KHAIR"],[28,"NIZAM JATI WARDOYO"],[29,"NUR FIDYAH"],[30,"OKFAL NOVA PUJI SATRIAWAN"],[31,"QUINSHA VIROSYA PRICILLIA"],[32,"REZA HAFID ARIYANTO"],[33,"RICKY ARDIANSYAH"],[34,"SITI ERNAWATI"],[35,"SYAHRINA LAILA FARHANA"],[36,"WILDAN SETIO FIRMANSYAH"]],"XI TO2":[[1,"ADAM RAGA MAULIYANSYAH"],[2,"AHMAD BIMANTARA"],[3,"AMELIA NAOMI BUNGA CANTIKA"],[4,"ANGGA WAHYU ROMANDONI"],[5,"BIMA SETYAWAN"],[6,"CATUR FIRMAN ROMADHON"],[7,"DAFFA HAFIDZ IBNI"],[8,"DAFFA KHAIRAN"],[9,"DEDY ALMUNAWAR"],[10,"Dheo Vano Ananda Wijaya"],[11,"EKA AKHMAD PRASETYA"],[12,"FAROS WINDIANISA"],[13,"FERRY HAFID ANANDA"],[14,"GHALIF FAJAR ISMAIL"],[15,"HADAYA QABUS LIL ABRAR"],[16,"JOHAN DWI SAPUTRA"],[17,"MAULANA FERI WIJAYANTO"],[18,"MU'AMAR HAFIZ AZKA"],[19,"Muhamad Andika Dwi Cahyono"],[20,"MUHAMAD DAMAR DUWI SAPUTRA"],[21,"MUHAMAD DIVA LESTARI"],[22,"MUHAMMAD LATIF BRILIAN"],[23,"MUHAMMAD TASTAFTIYAN"],[24,"NAJMI FAUZAN"],[25,"PANDU AHMAD SIDDIQ"],[26,"RAFA ARDIE SURYATAMA"],[27,"Retno Anjani"],[28,"RIFKI RASENDRIYA"],[29,"RISTIAN PUJA BASKARA"],[30,"Sandya Aidil Gastia Dirrijal"],[31,"Tri Angga Khoirul Muna"],[32,"WAFIQ NAWANA ARFAN"]]};

var DEF_A=[{stem:'Rujuk sel A1 pada worksheet "Rekap" dari worksheet lain. Penulisan alamat yang benar?',opts:["Rekap!A1","Rekap#A1","Rekap-&gt;A1","A1(Rekap)","=A1@Rekap"],ans:0},{stem:"Rumus =100+50*2-10/2, hasil prioritas operasi?",opts:["290","145","195","100","75"],ans:2},{stem:"Kolom diskon 0,1 ditampilkan 10%. Format sel?",opts:["Persentase (%)","Mata Uang","Tanggal","Teks","Pecahan"],ans:0},{stem:"Fungsi untuk hitung banyak transaksi bernominal angka?",opts:["COUNT","COUNTA","SUM","AVERAGE","MAX"],ans:0},{stem:"Fungsi VERTIKAL untuk cari harga berdasarkan kode?",opts:["VLOOKUP","HLOOKUP","LEFT","SUMIF","COUNTIF"],ans:0},{stem:"Fungsi HORIZONTAL untuk cari predikat berdasarkan nilai?",opts:["HLOOKUP","VLOOKUP","SUMIF","LEFT","MID"],ans:0},{stem:"Jumlah transaksi berkategori 'Elektronik' dari ratusan baris?",opts:["COUNTIF","SUMIF","AVERAGEIF","COUNT","SUM"],ans:0},{stem:"Merujuk tarif tetap B1 saat disalin ke bawah, pakai referensi?",opts:["Absolut ($B$1)","Relatif (B1)","Campuran (B$1)","Luar workbook","Nama range"],ans:0},{stem:"Diagram untuk proporsi kontribusi tiap kategori?",opts:["Pie Chart","Line Chart","Scatter Plot","Area 3D","Tabel"],ans:0},{stem:"Fitur paling efisien untuk 1000 baris total per kategori per bulan?",opts:["PivotTable","Conditional Formatting","Freeze Panes","Data Validation","Print Area"],ans:0}];
var DEF_D=[{judul:"Total Transaksi",skenario:"Tiga transaksi harian.",table:mkTable(["Barang","Jumlah","Harga"],[["Pensil",40,2500],["Buku",25,3000],["Penghapus",30,1500]],[1,2]),instruksi:"Hitung total nilai seluruh transaksi (Jumlah × Harga, semua baris). Ketik angka saja.",hint:"Angka, misal 220000",type:"number",accept:[220000],points:3},{judul:"Rata-rata",skenario:"Nilai: 78, 85, 90, 66, 72.",table:"",instruksi:"Hitung rata-rata, bulatkan ke bilangan bulat terdekat.",hint:"Angka bulat",type:"number",accept:[78],points:3},{judul:"Status KKM",skenario:"KKM 75, nilai murid 74.",table:"",instruksi:'Gunakan =IF(nilai&gt;=KKM,"Tuntas","Belum Tuntas"). Ketik hasil.',hint:"Tuntas / Belum Tuntas",type:"text",accept:["belum tuntas"],points:3}];
var DEF_K=[{q:"Jelaskan mengapa referensi absolut ($B$1) penting ketika rumus disalin ke banyak baris. Berikan contoh kasus nyata.",groups:[{label:"definisi absolut",words:["absolut","$","acuan tetap","tidak berubah"]},{label:"masalah relatif",words:["relatif","bergeser","salah acuan"]},{label:"contoh",words:["pajak","diskon","komisi","tarif","bunga"]}],minWords:20},{q:"Bandingkan SUM vs SUMIF dan jelaskan kapan masing-masing lebih tepat untuk data penjualan ribuan baris.",groups:[{label:"SUM dasar",words:["sum","total semua","fungsi dasar"]},{label:"SUMIF kriteria",words:["sumif","kriteria","kategori","bersyarat"]},{label:"efisiensi",words:["efisien","cepat","otomatis","data besar"]}],minWords:20}];

var examsCache={},activeExam=null,engine={A:[],d:[],k:[],s:[],m:{}};
var g=null,p=null,b=null,h=!1,c=null,I=null;

function loadExams(){try{var x=localStorage.getItem(K_EXAMS);return x?JSON.parse(x):null}catch(e){return null}}
function saveExams(x){try{localStorage.setItem(K_EXAMS,JSON.stringify(x));return true}catch(e){alert("Gagal simpan (penyimpanan browser penuh?): "+e.message);return false}}

function ensureExams(){
  var ex=loadExams();
  if(ex&amp;&amp;Object.keys(ex).length&gt;0){examsCache=ex;return ex}
  var old=null;try{old=JSON.parse(localStorage.getItem("examHotsBankApplied_v1")||"null")}catch(e){}
  var A=DEF_A.slice(),D=DEF_D.slice(),K=DEF_K.slice();
  if(old){if(old.A&amp;&amp;old.A.length)A=old.A;if(old.d&amp;&amp;old.d.length)D=old.d;if(old.k&amp;&amp;old.k.length)K=old.k}
  ex={"DEFAULT":{id:"DEFAULT",judul:"Ujian Default",sekolah:"",mapel:"Informatika",kkm:75,durasiMenit:60,batasPelanggaran:3,maxAtt:DEF_MAXATT,pembahasan:false,endpoint:"",token:"UJI-DEFAULT-2026",jadwalMulai:"",jadwalSelesai:"",A:A,d:D,k:K,s:[],createdAt:Date.now(),updatedAt:Date.now()}};
  saveExams(ex);examsCache=ex;return ex;
}

function getExamById(id){var ex=loadExams();return ex?ex[id]:null}
function findExamByToken(tok){var ex=loadExams();if(!ex)return null;var T=String(tok||"").trim().toUpperCase();for(var id in ex){if(String(ex[id].token||"").toUpperCase()===T)return ex[id]}return null}

function loadExamIntoEngine(exam){
  if(!exam)return;
  activeExam=exam;
  engine.A=exam.A||[];engine.d=exam.d||[];engine.k=exam.k||[];engine.s=exam.s||[];
  engine.m={judul:exam.judul||"Ujian",sekolah:exam.sekolah||"",mapel:exam.mapel||"Informatika",kkm:exam.kkm||75,durasiMenit:exam.durasiMenit||60,batasPelanggaran:exam.batasPelanggaran||3,maxAtt:exam.maxAtt||DEF_MAXATT,pembahasan:!!exam.pembahasan,endpoint:exam.endpoint||""};
}

// Kode ujian ulang: dibuat guru, dipakai murid (satu kali pakai, menambah 1 kesempatan)
function grantCode(kelas,absen,examId,no){
  var s=String(kelas)+"|"+String(parseInt(absen,10))+"|"+String(examId)+"|"+String(no)+"|ULANG-HOTS-2026";
  var h1=5381,h2=52711;
  for(var i=0;i&lt;s.length;i++){var ch=s.charCodeAt(i);h1=(((h1&lt;&lt;5)+h1)+ch)&gt;&gt;&gt;0;h2=(((h2&lt;&lt;5)^h2)+ch*31)&gt;&gt;&gt;0}
  return "ULANG-"+(h1.toString(36)+h2.toString(36)).toUpperCase().replace(/[^A-Z0-9]/g,"").slice(0,8);
}
function maxAllowed(){return (engine.m.maxAtt||DEF_MAXATT)+((p&amp;&amp;p.grants)||0)}

function maxes(){
  var pg=0,pr=0,es=0,sh=0;
  engine.A.forEach(function(q){pg+=q.points||1});
  engine.d.forEach(function(q){pr+=q.points||1});
  engine.k.forEach(function(q){if(q.groups&amp;&amp;q.groups.length)es+=5});
  engine.s.forEach(function(q){sh+=q.points||5});
  return{pg:pg,pr:pr,es:es,sh:sh,total:pg+pr+es+sh};
}

function loadStudentState(key){try{var s=localStorage.getItem(K_STATE+key);if(s){var o=JSON.parse(s);if(!o.grants)o.grants=0;return o}}catch(e){}return{attempts:[],sent:false,sentValue:0,best:null,grants:0}}
function saveStudentState(key,v){try{localStorage.setItem(K_STATE+key,JSON.stringify(v))}catch(e){}}
function draftKey(key,no){return K_DRAFT+key+"_"+no}
function saveDraft(){if(g&amp;&amp;b)try{localStorage.setItem(draftKey(g.key,b.attemptNo),JSON.stringify(b))}catch(e){}}

function refresh(){
  a("hdrJudul").textContent=engine.m.judul||"Ujian Online";
  a("hdrSub").textContent="Cerdas Menguji, Adil Menilai";
  a("startTitle").textContent=engine.m.judul||"Ujian Online Sekolah";
  var rules=["Pilih &lt;b&gt;Mata Pelajaran&lt;/b&gt;, &lt;b&gt;Kelas&lt;/b&gt;, &lt;b&gt;No. Absen&lt;/b&gt; - nama terisi otomatis.","Masukkan &lt;b&gt;Token Ujian&lt;/b&gt; dari guru. Token menentukan ujian yang dikerjakan.","Soal &amp; pilihan jawaban &lt;b&gt;diacak&lt;/b&gt;. Soal bisa memuat gambar (klik gambar untuk memperbesar).","Soal &lt;b&gt;Lembar Kerja&lt;/b&gt;: ketik data dan rumus sendiri pada sel putih (diawali tanda =). Hasil dihitung langsung, tempel (paste) dinonaktifkan.","Waktu mundur sejak mulai. Saat habis, otomatis dikumpulkan.","Jika nilai belum mencapai KKM, kamu dapat &lt;b&gt;mengulang hingga "+(engine.m.maxAtt||DEF_MAXATT)+" kali percobaan&lt;/b&gt;. Bila kesempatan habis, minta guru memberikan kode ujian ulang.","&lt;b&gt;Keamanan:&lt;/b&gt; jangan pindah tab/keluar fullscreen/buka F12. Pelanggaran dicatat.","Klik kanan &amp; paste dinonaktifkan.","Status tersimpan otomatis.","Unduh bukti PDF atau ekspor CSV setelah selesai."];
  a("rulesList").innerHTML=rules.map(function(x){return"&lt;li&gt;"+x+"&lt;/li&gt;"}).join("");
}

function populateClasses(){
  var sel=a("fKelas");sel.innerHTML='&lt;option value=""&gt;- Pilih Kelas -&lt;/option&gt;';
  Object.keys(l).forEach(function(cls){var o=document.createElement("option");o.value=cls;o.textContent=cls;sel.appendChild(o)});
}

function updateSecurityChip(){
  var el=a("secChip");if(!el)return;
  if(!b||b.violations&lt;=0){el.textContent="Keamanan: Aman";el.className="chip sec"}
  else{el.textContent="Keamanan: "+b.violations+"x pelanggaran";el.className="chip sec warn"}
}

function logViolation(reason){
  if(!h||!b||b.finishing)return;
  b.violations=(b.violations||0)+1;
  updateSecurityChip();saveDraft();
  if(!a("secOverlay")){
    var ov=document.createElement("div");ov.id="secOverlay";ov.className="overlay danger";
    ov.innerHTML='&lt;div class="box"&gt;&lt;h2&gt;Pelanggaran Keamanan&lt;/h2&gt;&lt;p&gt;'+n(reason)+'&lt;/p&gt;&lt;p&gt;Pelanggaran ke-&lt;b&gt;'+b.violations+'&lt;/b&gt; dari batas &lt;b&gt;'+engine.m.batasPelanggaran+'&lt;/b&gt;. Jika batas tercapai, ujian otomatis diakhiri.&lt;/p&gt;&lt;div class="row"&gt;&lt;button class="btn btn-bad" id="secOverlayClose"&gt;Kembali ke Ujian&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;';
    document.body.appendChild(ov);
    a("secOverlayClose").addEventListener("click",function(){var x=a("secOverlay");if(x)x.remove();reqFullscreen()});
  }
  if(b.violations&gt;=engine.m.batasPelanggaran) finishExam(true,true);
}

function reqFullscreen(){try{var el=document.documentElement;if(el.requestFullscreen&amp;&amp;!document.fullscreenElement)el.requestFullscreen().catch(function(){})}catch(e){}}
function exitFullscreen(){try{if(document.fullscreenElement&amp;&amp;document.exitFullscreen)document.exitFullscreen().catch(function(){})}catch(e){}}
function totalQ(){return engine.A.length+engine.d.length+engine.k.length+engine.s.length}
function qTypeAt(i){
  var A=engine.A.length,D=engine.d.length,K=engine.k.length;
  if(i&lt;A)return{type:"pg",pos:i};
  if(i&lt;A+D)return{type:"praktik",pos:i-A};
  if(i&lt;A+D+K)return{type:"esai",pos:i-A-D};
  return{type:"sheet",pos:i-A-D-K};
}
function getRealIdx(t){return t.type==="pg"?b.pgOrder[t.pos]:t.type==="praktik"?b.praktikOrder[t.pos]:t.type==="esai"?b.esaiOrder[t.pos]:b.sheetOrder[t.pos]}

function startExam(){
  var attemptNo=p.attempts.length+1;
  var existing=null;try{var dr=localStorage.getItem(draftKey(g.key,attemptNo));if(dr)existing=JSON.parse(dr)}catch(e){}
  if(existing){
    if(!existing.sheetOrder){existing.sheetOrder=[];existing.sheets=[]}
    var okD=existing.answers&amp;&amp;existing.answers.length===totalQ()&amp;&amp;existing.pgOrder&amp;&amp;existing.pgOrder.length===engine.A.length&amp;&amp;existing.praktikOrder&amp;&amp;existing.praktikOrder.length===engine.d.length&amp;&amp;existing.esaiOrder&amp;&amp;existing.esaiOrder.length===engine.k.length&amp;&amp;existing.sheetOrder.length===engine.s.length;
    if(!okD)existing=null;
  }
  if(existing){b=existing}
  else{
    b={attemptNo:attemptNo,pgOrder:shuf(idxs(engine.A.length)),praktikOrder:shuf(idxs(engine.d.length)),esaiOrder:shuf(idxs(engine.k.length)),sheetOrder:shuf(idxs(engine.s.length)),pgOptOrder:[],sheets:engine.s.map(function(){return{}}),answers:new Array(totalQ()).fill(null),marked:new Array(totalQ()).fill(false),idx:0,violations:0,finishing:false,timeLeft:60*engine.m.durasiMenit,startedAt:Date.now()};
    b.pgOrder.forEach(function(ri){var cnt=(engine.A[ri].opts||[]).length||5;b.pgOptOrder.push(shuf(idxs(cnt)))});
  }
  h=true;
  a("whoLbl").textContent=g.nama+" - "+g.kelas+" / No. "+g.absen+(g.mapel?" - "+g.mapel:"")+" - Percobaan "+attemptNo+"/"+maxAllowed();
  updateSecurityChip();setMsg(a("examErr"),"");
  renderNumGrid();renderQuestion();updateProgress();
  showScreen("exam");reqFullscreen();startTimer();
}

function startTimer(){
  if(I)clearInterval(I);
  I=setInterval(function(){
    if(!b)return;
    b.timeLeft--;
    var tm=a("timerChip");
    if(tm){tm.textContent=formatTime(b.timeLeft);tm.className="chip timer"+(b.timeLeft&lt;=120?" low":"")}
    if(b.timeLeft%5===0)saveDraft();
    if(b.timeLeft&lt;=0){stopTimer();finishExam(true,false)}
  },1000);
}

function stopTimer(){if(I){clearInterval(I);I=null}}
function formatTime(s){s=Math.max(0,0|s);var m=Math.floor(s/60),sec=s%60;return(m&lt;10?"0":"")+m+":"+(sec&lt;10?"0":"")+sec}

function updateProgress(){
  var answered=0;b.answers.forEach(function(v){if(v!==null&amp;&amp;v!=="")answered++});
  a("progChip").textContent="Soal "+(b.idx+1)+" dari "+totalQ()+" - "+answered+" terjawab";
}

function renderNumGrid(){
  var html="";
  for(var i=0;i&lt;totalQ();i++){
    var cls=[""];var v=b.answers[i];
    if(v!==null&amp;&amp;v!=="")cls.push("answered");
    if(b.marked[i])cls.push("marked");
    if(i===b.idx)cls.push("current");
    html+='&lt;button type="button" class="'+cls.join(" ").trim()+'" data-i="'+i+'"&gt;'+(i+1)+'&lt;/button&gt;';
  }
  a("numGrid").innerHTML=html;
  Array.prototype.forEach.call(a("numGrid").querySelectorAll("button"),function(btn){
    btn.addEventListener("click",function(){gotoQ(parseInt(btn.getAttribute("data-i"),10))});
  });
}

function anyCell(cells){return Object.keys(cells||{}).some(function(k){return String(cells[k]).trim()!==""})}

function captureAnswer(){
  var t=qTypeAt(b.idx),el;
  if(t.type==="pg"){el=document.querySelector('input[name="curPg"]:checked');b.answers[b.idx]=el?el.value:null}
  else if(t.type==="praktik"){el=a("praktikInput");b.answers[b.idx]=el?el.value:""}
  else if(t.type==="esai"){el=a("esaiInput");b.answers[b.idx]=el?el.value:""}
  else{b.answers[b.idx]=anyCell(b.sheets[t.pos])?"1":null}
}

function sheetHtml(Q){
  var rows=Math.min(60,Math.max(1,(Q.rows|0)||10)),cols=Math.min(26,Math.max(1,(Q.cols|0)||6));
  var s='&lt;div class="sheet-wrap"&gt;&lt;table class="sheet"&gt;&lt;tr&gt;&lt;th class="cn"&gt;&lt;/th&gt;';
  var cc,r;
  for(cc=1;cc&lt;=cols;cc++)s+='&lt;th&gt;'+SheetKit.colName(cc)+'&lt;/th&gt;';
  s+='&lt;/tr&gt;';
  for(r=1;r&lt;=rows;r++){
    s+='&lt;tr&gt;&lt;th class="rh"&gt;'+r+'&lt;/th&gt;';
    for(cc=1;cc&lt;=cols;cc++){var ref=SheetKit.colName(cc)+r;s+='&lt;td&gt;&lt;input class="sc" type="text" data-ref="'+ref+'" autocomplete="off" spellcheck="false"&gt;&lt;/td&gt;'}
    s+='&lt;/tr&gt;';
  }
  return s+'&lt;/table&gt;&lt;/div&gt;';
}

function refreshSheet(pos,d){
  var raws={},cells=b.sheets[pos]||{};
  Object.keys(d.locked).forEach(function(k){raws[k]=d.locked[k]});
  Object.keys(cells).forEach(function(k){if(!d.locked.hasOwnProperty(k))raws[k]=cells[k]});
  var cs=SheetKit.calc(raws);
  Array.prototype.forEach.call(document.querySelectorAll("#qBox input.sc"),function(inp){
    if(inp===document.activeElement)return;
    var ref=inp.getAttribute("data-ref"),raw=raws[ref],show="",isF=false;
    if(raw!=null&amp;&amp;String(raw)!==""){isF=String(raw).charAt(0)==="=";show=isF?SheetKit.show(cs.get(ref)):String(raw)}
    inp.value=show;
    inp.className="sc"+(d.locked.hasOwnProperty(ref)?" lk":"")+(isF&amp;&amp;show.charAt(0)==="#"?" er":"");
  });
}

function renderQuestion(){
  var t=qTypeAt(b.idx),realIdx=getRealIdx(t),html="",Q;
  if(t.type==="pg"){
    a("qSection").textContent="BAGIAN A - PILIHAN GANDA";
    Q=engine.A[realIdx];var optOrder=b.pgOptOrder[t.pos],L="ABCDEFGH";
    html='&lt;div class="q-head"&gt;Soal '+(b.idx+1)+'&lt;/div&gt;&lt;div class="q-stem"&gt;'+Q.stem+'&lt;/div&gt;'+imgHtml(Q.gambar);
    optOrder.forEach(function(oi,ii){
      var checked=b.answers[b.idx]!==null&amp;&amp;parseInt(b.answers[b.idx],10)===ii?"checked":"";
      html+='&lt;label class="opt-row"&gt;&lt;input type="radio" name="curPg" value="'+ii+'" '+checked+'&gt;&lt;span&gt;&lt;b&gt;'+L.charAt(ii)+'.&lt;/b&gt; '+Q.opts[oi]+'&lt;/span&gt;&lt;/label&gt;';
    });
  }
  else if(t.type==="praktik"){
    a("qSection").textContent="BAGIAN B - PRAKTIK / ISIAN";
    Q=engine.d[realIdx];
    html='&lt;div class="q-head"&gt;Soal '+(b.idx+1)+'&lt;/div&gt;&lt;div class="q-stem"&gt;&lt;b&gt;'+Q.judul+'&lt;/b&gt;&lt;br&gt;'+(Q.skenario||"")+'&lt;/div&gt;'+imgHtml(Q.gambar)+(Q.table||"")+'&lt;p style="font-size:14px;"&gt;'+Q.instruksi+'&lt;/p&gt;&lt;label class="fl"&gt;Jawaban&lt;/label&gt;&lt;input type="text" id="praktikInput" autocomplete="off" value="'+n(b.answers[b.idx]||"")+'"&gt;&lt;div class="cnt-small"&gt;'+n(Q.hint||"")+'&lt;/div&gt;';
  }
  else if(t.type==="esai"){
    a("qSection").textContent="BAGIAN C - ESAI";
    Q=engine.k[realIdx];
    html='&lt;div class="q-head"&gt;Soal '+(b.idx+1)+'&lt;/div&gt;&lt;div class="q-stem"&gt;'+Q.q+'&lt;/div&gt;'+imgHtml(Q.gambar)+'&lt;label class="fl"&gt;Jawaban (minimal '+(Q.minWords||20)+' kata)&lt;/label&gt;&lt;textarea id="esaiInput"&gt;'+n(b.answers[b.idx]||"")+'&lt;/textarea&gt;&lt;div class="cnt-small"&gt;&lt;span id="esaiWc"&gt;0&lt;/span&gt; kata&lt;/div&gt;';
  }
  else{
    a("qSection").textContent="BAGIAN D - LEMBAR KERJA SPREADSHEET";
    Q=engine.s[realIdx];
    html='&lt;div class="q-head"&gt;Soal '+(b.idx+1)+' - Lembar Kerja&lt;/div&gt;&lt;div class="q-stem"&gt;'+(Q.instruksi||"")+'&lt;/div&gt;'+imgHtml(Q.gambar)+'&lt;div class="cnt-small" style="text-align:left;margin:0 0 4px;"&gt;Ketik data dan rumus sendiri pada sel putih (rumus diawali tanda =). Sel abu-abu terkunci. Tempel (paste) dinonaktifkan.&lt;/div&gt;'+sheetHtml(Q);
  }
  a("qBox").innerHTML=html;
  attachInputEvents(t);
  a("btnMark").textContent=b.marked[b.idx]?"Batalkan Tanda":"Tandai";
  a("btnPrev").disabled=(b.idx===0);
  a("btnNext").disabled=(b.idx===totalQ()-1);
}

function attachInputEvents(t){
  var el;
  if(t.type==="pg"){
    Array.prototype.forEach.call(document.querySelectorAll('input[name="curPg"]'),function(r){
      r.addEventListener("change",function(){captureAnswer();renderNumGrid();updateProgress();saveDraft()});
    });
  }
  else if(t.type==="praktik"){
    el=a("praktikInput");
    if(el){el.addEventListener("paste",function(e){e.preventDefault()});el.addEventListener("input",function(){captureAnswer();renderNumGrid();updateProgress();saveDraft()})}
  }
  else if(t.type==="esai"){
    el=a("esaiInput");
    if(el){
      el.addEventListener("paste",function(e){e.preventDefault()});
      el.addEventListener("input",function(){captureAnswer();var w=a("esaiWc");if(w)w.textContent=wc(el.value);renderNumGrid();updateProgress();saveDraft()});
      var w2=a("esaiWc");if(w2)w2.textContent=wc(el.value);
    }
  }
  else{
    var Q=engine.s[getRealIdx(t)],d=SheetKit.derive(Q),cells=b.sheets[t.pos];
    Array.prototype.forEach.call(document.querySelectorAll("#qBox input.sc"),function(inp){
      var ref=inp.getAttribute("data-ref");
      if(d.locked.hasOwnProperty(ref)){inp.readOnly=true;inp.tabIndex=-1;return}
      inp.addEventListener("focus",function(){inp.value=cells[ref]==null?"":cells[ref]});
      inp.addEventListener("input",function(){cells[ref]=inp.value;b.answers[b.idx]=anyCell(cells)?"1":null;renderNumGrid();updateProgress();saveDraft()});
      inp.addEventListener("blur",function(){refreshSheet(t.pos,d)});
      inp.addEventListener("keydown",function(e){
        if(e.key==="Enter"){
          e.preventDefault();
          var m=/^([A-Z]+)(\d+)$/.exec(ref),nx=document.querySelector('#qBox input.sc[data-ref="'+m[1]+(parseInt(m[2],10)+1)+'"]');
          if(nx&amp;&amp;!nx.readOnly)nx.focus();else inp.blur();
        }
      });
      inp.addEventListener("paste",function(e){e.preventDefault();setMsg(a("examErr"),"Tempel (paste) dinonaktifkan. Ketik sendiri.","err");setTimeout(function(){setMsg(a("examErr"),"")},2500)});
      inp.addEventListener("drop",function(e){e.preventDefault()});
    });
    refreshSheet(t.pos,d);
  }
}

function gotoQ(i){captureAnswer();b.idx=Math.max(0,Math.min(totalQ()-1,i));renderQuestion();renderNumGrid();updateProgress();saveDraft()}

function scorePraktik(q,ans){
  if(ans==null||String(ans).trim()==="")return{score:0,ok:false};
  if(q.type==="number"){var num=parseFloat(String(ans).replace(/[^\d\-]/g,""));if(isNaN(num))return{score:0,ok:false};var tgt=q.accept[0];if(num===tgt)return{score:q.points,ok:true};if(Math.abs(num-tgt)&lt;=Math.abs(tgt)*0.1)return{score:Math.round(q.points*0.5*10)/10,ok:false};return{score:0,ok:false}}
  if(q.type==="text"){var v=low(ans);var ok=q.accept.indexOf(v)&gt;=0;return{score:ok?q.points:0,ok:ok}}
  if(q.type==="formula"){var v2=String(ans).replace(/\s+/g,"").toUpperCase();var ok2=q.accept.some(function(re){return re.test(v2)});return{score:ok2?q.points:0,ok:ok2}}
  if(q.type==="set"){var v3=String(ans).split(/[,\n]/).map(low).filter(Boolean);var tg=q.accept.slice().sort();var vv=v3.slice().sort();if(tg.length===vv.length&amp;&amp;tg.every(function(x,i){return x===vv[i]}))return{score:q.points,ok:true};var hits=v3.filter(function(x){return q.accept.indexOf(x)&gt;=0}).length;if(hits&gt;0)return{score:Math.round(q.points*(hits/tg.length)*10)/10,ok:false};return{score:0,ok:false}}
  return{score:0,ok:false};
}

function scoreEsai(q,ans){
  var txt=String(ans||"");
  if(wc(txt)&lt;(q.minWords||20))return{score:0,matched:0,total:q.groups.length,feedback:"Jawaban terlalu singkat (minimal "+(q.minWords||20)+" kata)"};
  var t=low(txt),matched=[],missing=[];
  q.groups.forEach(function(gp){if(gp.words.some(function(w){return t.indexOf(w)&gt;=0}))matched.push(gp.label);else missing.push(gp.label)});
  var pct=matched.length/q.groups.length,score=Math.round(100*pct);
  return{score:score,matched:matched.length,total:q.groups.length,feedback:"Aspek terdeteksi: "+(matched.length?matched.join("; "):"-")+". Kurang: "+(missing.length?missing.join("; "):"-")};
}

function showScreen(name){
  ["start","exam","result"].forEach(function(s){a("screen"+s.charAt(0).toUpperCase()+s.slice(1)).classList.toggle("hidden",s!==name)});
}

function handleStartClick(){
  var kelas=a("fKelas").value,absen=a("fAbsen").value,nama=a("fNama").value.trim(),token=a("fToken").value.trim(),mapel=a("fMapel")?a("fMapel").value:"";
  setMsg(a("startErr"),"");
  if(!kelas){setMsg(a("startErr"),"Pilih Kelas.");return}
  if(!absen){setMsg(a("startErr"),"Pilih No. Absen.");return}
  if(!nama){setMsg(a("startErr"),"Nama tidak ditemukan.");return}
  if(!token){setMsg(a("startErr"),"Masukkan Token Ujian.");return}
  var exam=findExamByToken(token);
  if(!exam){setMsg(a("startErr"),"Token Ujian tidak valid.");return}
  g={kelas:kelas,absen:String(parseInt(absen,10)),nama:nama,mapel:mapel,examId:exam.id,examJudul:exam.judul||"",examToken:exam.token||""};
  g.key=kelas+"|"+absen+"|"+exam.id;
  loadExamIntoEngine(exam);
  refresh();
  if(totalQ()===0){setMsg(a("startErr"),"Ujian ini belum memiliki soal. Hubungi guru.");return}
  p=loadStudentState(g.key);
  if(p.sent&amp;&amp;p.best&amp;&amp;p.best.total&lt;engine.m.kkm&amp;&amp;p.attempts.length&lt;maxAllowed()){p.sent=false;saveStudentState(g.key,p)}
  if(p.sent||p.attempts.length&gt;=maxAllowed()){showResult(false);showScreen("result");return}
  saveStudentState(g.key,p);
  startExam();
}

function closeStrayOverlays(){
  ["finishOverlay","secOverlay","zoomOverlay"].forEach(function(id){
    var x=document.getElementById(id);if(x)x.remove();
  });
}

function finishExam(auto,flagged){
  if(!b||b.finishing)return;
  b.finishing=true;
  try{captureAnswer()}catch(e){}
  stopTimer();h=false;exitFullscreen();closeStrayOverlays();

  showScreen("result");
  a("rTitle").textContent="Sedang menghitung nilai...";
  a("rDesc").textContent="Mohon tunggu sebentar.";

  setTimeout(function(){
    try{
      var pgScore=0,praktikScore=0,esaiScore=0,sheetScore=0;
      var pgDetail=[],praktikDetail=[],esaiDetail=[],sheetDetail=[];
      var i,realIdx,Q,r;

      for(i=0;i&lt;engine.A.length;i++){
        realIdx=getRealIdx({type:"pg",pos:i});
        Q=engine.A[realIdx];
        var ans=b.answers[i],optOrder=b.pgOptOrder[i];
        var userOrig=ans!==null&amp;&amp;ans!==""?optOrder[parseInt(ans,10)]:null;
        var correct=(userOrig===Q.ans);
        if(correct)pgScore+=(Q.points||1);
        pgDetail.push({n:i+1,ok:correct,answered:ans!==null&amp;&amp;ans!==""});
      }

      for(i=0;i&lt;engine.d.length;i++){
        var idxP=engine.A.length+i;
        Q=engine.d[getRealIdx({type:"praktik",pos:i})];
        r=scorePraktik(Q,b.answers[idxP]);
        praktikScore+=r.score;
        praktikDetail.push({n:idxP+1,judul:Q.judul,score:r.score,max:Q.points,ok:r.ok});
      }

      for(i=0;i&lt;engine.k.length;i++){
        var idxE=engine.A.length+engine.d.length+i;
        Q=engine.k[getRealIdx({type:"esai",pos:i})];
        if(!Q.groups||!Q.groups.length){esaiDetail.push({n:idxE+1,score:0,max:0,manual:true});continue}
        r=scoreEsai(Q,b.answers[idxE]);
        var sc5=Math.round(r.score/100*5*10)/10;
        esaiScore+=sc5;
        esaiDetail.push({n:idxE+1,score:sc5,max:5,feedback:r.feedback});
      }

      for(i=0;i&lt;engine.s.length;i++){
        var idxS=engine.A.length+engine.d.length+engine.k.length+i;
        Q=engine.s[getRealIdx({type:"sheet",pos:i})];
        r=SheetKit.score(Q,b.sheets[i]||{});
        sheetScore+=r.score;
        sheetDetail.push({n:idxS+1,score:r.score,max:Q.points||5,ok:r.ok,total:r.total});
      }

      pgScore=Math.round(10*pgScore)/10;
      praktikScore=Math.round(10*praktikScore)/10;
      esaiScore=Math.round(10*esaiScore)/10;
      sheetScore=Math.round(10*sheetScore)/10;

      var M=maxes();
      var totalPossible=M.total||1;
      var rawScore=pgScore+praktikScore+esaiScore+sheetScore;
      var total=Math.round((rawScore/totalPossible)*100);
      if(total&gt;100)total=100;
      if(total&lt;0)total=0;

      var result={pg:pgScore,praktik:praktikScore,esai:esaiScore,sheet:sheetScore,M:M,total:total,pgDetail:pgDetail,praktikDetail:praktikDetail,esaiDetail:esaiDetail,sheetDetail:sheetDetail,violations:b.violations||0,startedAt:b.startedAt,finishedAt:Date.now(),auto:!!auto,flagged:!!flagged,no:p.attempts.length+1};

      p.attempts.push(result);
      if(!p.best||result.total&gt;p.best.total)p.best=result;

      // Nilai dikirim bila KKM tercapai, atau kesempatan terakhir sudah dipakai
      var shouldSend=false;
      if(result.total&gt;=engine.m.kkm){p.sent=true;p.sentValue=result.total;shouldSend=true}
      else if(p.attempts.length&gt;=maxAllowed()){p.sent=true;p.sentValue=p.best.total;shouldSend=true}

      saveStudentState(g.key,p);
      try{localStorage.removeItem(draftKey(g.key,b.attemptNo))}catch(e){}

      if(shouldSend)sendResult(p.sentValue,result);

      c=result;
      renderResult(shouldSend);
    }catch(err){
      console.error(err);
      a("rTitle").textContent="Gagal Menghitung";
      a("rDesc").textContent="Terjadi kesalahan: "+err.message+". Silakan hubungi guru.";
    }
  },100);
}

function sendResult(nilai,res){
  if(!engine.m.endpoint)return;
  try{
    a("gTimestamp").value=(new Date).toISOString()+" - "+Date.now();
    a("gNama").value=g.nama||"";
    a("gKelas").value=g.kelas||"";
    a("gAbsen").value=g.absen||"";
    a("gMapel2").value=g.mapel||"";
    a("gUjian").value=g.examJudul||"";
    a("gJudul").value=engine.m.judul||"";
    a("gNilaiPG").value=res.pg||0;
    a("gNilaiPraktik").value=Math.round(10*((res.praktik||0)+(res.sheet||0)))/10;
    a("gNilaiEsai").value=res.esai||0;
    a("gNilai").value=nilai||0;
    a("gNilaiSTS").value=nilai||0;
    a("gSkor").value=nilai||0;
    a("gStatus").value=nilai&gt;=engine.m.kkm?"Tuntas":"Belum Tuntas";
    a("gDurasi").value=Math.round((res.finishedAt-res.startedAt)/6e4)+" menit";
    a("gPelanggaran").value=res.violations+(res.flagged?" (flagged)":"");
    var form=a("gform");
    if(form){form.action=engine.m.endpoint;form.submit()}
  }catch(e){
    console.error("Gagal kirim nilai: ",e);
  }
}

function makeBar(label,val,max,color){
  var pct=max?Math.round(val/max*100):0;
  return '&lt;div class="bar-row"&gt;&lt;div class="lbl"&gt;&lt;span&gt;'+n(label)+'&lt;/span&gt;&lt;span&gt;'+val+' / '+max+'&lt;/span&gt;&lt;/div&gt;&lt;div class="bar-track"&gt;&lt;div class="bar-fill" style="width:'+pct+'%;background:'+color+'"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
}

function renderResult(sent){
  var best=p.best,attempts=p.attempts.length,total=best?best.total:0,pass=!!(best&amp;&amp;best.total&gt;=engine.m.kkm);
  c=best||c;
  a("rVal").textContent=total;
  a("ring").style.setProperty("--p",total);
  a("ring").style.setProperty("--ringc",pass?"#15803d":"#d97706");
  var pill=a("rPill");
  if(p.sent){pill.textContent=pass?"TUNTAS - KKM TERCAPAI":"BELUM TUNTAS - NILAI DIKIRIM";pill.className="status-pill "+(pass?"ok":"no")}
  else{pill.textContent="BELUM MENCAPAI KKM ("+engine.m.kkm+")";pill.className="status-pill no"}
  if(attempts&gt;0&amp;&amp;best){
    if(p.sent&amp;&amp;!sent){a("rTitle").textContent="Nilai kamu sudah tercatat";a("rDesc").textContent="Kesempatan selesai. Nilai tercatat: "+p.sentValue}
    else if(p.sent){a("rTitle").textContent=pass?"Selamat, kamu mencapai KKM!":"Kesempatan terakhir selesai";a("rDesc").textContent=pass?"Nilai akhirmu "+best.total+" sudah dikirim.":"KKM belum tercapai. Nilai tertinggi dari "+attempts+" percobaan ("+best.total+") dikirim."}
    else{var left=maxAllowed()-attempts,last=p.attempts[attempts-1];a("rTitle").textContent="Nilaimu belum mencapai KKM";a("rDesc").textContent="Percobaan ini: "+last.total+" (tertinggi sementara "+best.total+"). Sisa: "+left+" kesempatan. Klik \"Ulangi Ujian\" untuk mencoba lagi."}
  }else{a("rTitle").textContent="Belum ada data";a("rDesc").textContent=""}
  if(best&amp;&amp;best.violations&gt;0)a("rDesc").textContent+=" Tercatat "+best.violations+" pelanggaran"+(best.flagged?" (flagged)":"")+".";
  a("riNama").textContent=g.nama;a("riKelas").textContent=g.kelas;a("riAbsen").textContent=g.absen;a("riKKM").textContent=engine.m.kkm;
  a("riUjian").textContent=(g.examJudul||"")+" ["+(g.examToken||"")+"]";
  if(best){
    a("riMulai").textContent=new Date(best.startedAt).toLocaleString("id-ID");
    a("riSelesai").textContent=new Date(best.finishedAt).toLocaleString("id-ID");
    a("riDurasi").textContent=Math.round((best.finishedAt-best.startedAt)/6e4)+" menit";
    a("riPelanggaran").textContent=best.violations+(best.flagged?" (flagged)":"");
  }
  var bars="",M=(best&amp;&amp;best.M)||maxes();
  if(best){
    if(M.pg)bars+=makeBar("A. Pilihan Ganda",best.pg,M.pg,"#1d4ed8");
    if(M.pr)bars+=makeBar("B. Praktik/Isian",best.praktik,M.pr,"#d97706");
    if(M.es)bars+=makeBar("C. Esai",best.esai,M.es,"#0f766e");
    if(M.sh||best.sheet)bars+=makeBar("D. Lembar Kerja",best.sheet||0,M.sh,"#7c3aed");
  }
  a("rBars").innerHTML=bars;
  var send=a("rSend");
  if(engine.m.endpoint){
    if(p.sent){send.textContent="Nilai "+p.sentValue+" tercatat dan dikirim ke guru.";send.className="msg info show"}
    else{send.textContent="Nilai belum dikirim. Akan dikirim saat KKM tercapai atau percobaan terakhir.";send.className="msg info show"}
    var btnResend=a("btnResend");
    if(btnResend)btnResend.style.display="inline-block";
  }
  else{
    send.textContent="Endpoint belum dikonfigurasi guru - nilai tersimpan lokal.";send.className="msg info show"
  }
  var note="";
  if(M.es)note+="Nilai esai estimasi otomatis berbasis kata kunci, perlu verifikasi guru. ";
  if(engine.k.some(function(q){return !q.groups||!q.groups.length}))note+="Soal uraian tanpa kata kunci dinilai guru secara manual. ";
  if(M.sh)note+="Lembar kerja dinilai otomatis dari hasil hitung rumus yang kamu ketik. ";
  a("rBenchmark").textContent=note?"Catatan: "+note.trim():"";

  var canRetry=!p.sent&amp;&amp;!pass&amp;&amp;attempts&gt;0&amp;&amp;attempts&lt;maxAllowed();
  var exhausted=!pass&amp;&amp;attempts&gt;0&amp;&amp;attempts&gt;=maxAllowed();
  a("btnRetry").style.display=canRetry?"inline-block":"none";
  a("retryBox").classList.toggle("hidden",!exhausted);
  if(!exhausted)setMsg(a("retryErr"),"");
}

function showResult(sent){renderResult(sent)}

window.__examMulti={
  loadExams:loadExams,saveExams:saveExams,ensureExams:ensureExams,
  getExamById:getExamById,findExamByToken:findExamByToken,loadExamIntoEngine:loadExamIntoEngine,
  grantCode:grantCode,sheet:SheetKit,
  getExamCounts:function(ex){return{pg:(ex.A||[]).length,pr:(ex.d||[]).length,es:(ex.k||[]).length,sh:(ex.s||[]).length}}
};

document.addEventListener("visibilitychange",function(){if(document.hidden)logViolation("Pindah tab/aplikasi")});
window.addEventListener("blur",function(){if(!document.hidden)logViolation("Jendela kehilangan fokus")});
document.addEventListener("fullscreenchange",function(){if(h&amp;&amp;!document.fullscreenElement)logViolation("Keluar mode layar penuh")});
document.addEventListener("keydown",function(e){if(!h)return;var k=(e.key||"").toLowerCase(),isDev=(e.ctrlKey||e.metaKey)&amp;&amp;e.shiftKey&amp;&amp;(k==="i"||k==="j"||k==="c"||k==="k");if(k==="f12"||isDev||((e.ctrlKey||e.metaKey)&amp;&amp;k==="u")){e.preventDefault();logViolation("Mencoba buka dev tools")}});
document.addEventListener("contextmenu",function(e){if(h)e.preventDefault()});
document.addEventListener("paste",function(e){if(h)e.preventDefault()});

// Tombol dialog "Kumpulkan Jawaban?" - fase capture agar tidak terblokir skrip lain
document.addEventListener("click",function(e){
  var t=e.target;
  while(t&amp;&amp;t!==document){
    if(t.id==="finishCancel"||t.id==="finishOk"){
      e.preventDefault();e.stopPropagation();
      var ov=document.getElementById("finishOverlay");
      if(ov)ov.remove();
      if(t.id==="finishOk"&amp;&amp;b&amp;&amp;!b.finishing)finishExam(false,false);
      return;
    }
    t=t.parentNode;
  }
},true);

// Klik gambar soal = perbesar
document.addEventListener("click",function(e){
  var t=e.target;
  if(t&amp;&amp;t.getAttribute&amp;&amp;t.getAttribute("data-zoom")){
    var old=document.getElementById("zoomOverlay");if(old)old.remove();
    var ov=document.createElement("div");ov.id="zoomOverlay";ov.className="overlay";ov.style.zIndex="2147483646";ov.style.cursor="zoom-out";
    ov.innerHTML='&lt;img src="'+n(t.getAttribute("src"))+'" alt="Gambar soal" style="max-width:96vw;max-height:92vh;border-radius:10px;background:#fff"&gt;';
    ov.addEventListener("click",function(){ov.remove()});
    document.body.appendChild(ov);
  }
});

var HANDLERS={
  btnStart:handleStartClick,
  btnPrev:function(){gotoQ(b.idx-1)},
  btnNext:function(){gotoQ(b.idx+1)},
  btnMark:function(){b.marked[b.idx]=!b.marked[b.idx];renderQuestion();renderNumGrid();saveDraft()},
  btnFinish:function(){
    if(!b||b.finishing)return;
    try{captureAnswer()}catch(e){}
    var unanswered=0,marked=0;
    b.answers.forEach(function(x){if(x===null||x==="")unanswered++});
    b.marked.forEach(function(x){if(x)marked++});

    var old=document.getElementById("finishOverlay");
    if(old)old.remove();

    var ov=document.createElement("div");
    ov.id="finishOverlay";
    ov.className="overlay";
    ov.style.zIndex="2147483647";
    ov.style.pointerEvents="auto";
    ov.innerHTML='&lt;div class="box"&gt;&lt;h2&gt;Kumpulkan Jawaban?&lt;/h2&gt;&lt;p&gt;Belum dijawab: &lt;b&gt;'+unanswered+'&lt;/b&gt;. Ditandai: &lt;b&gt;'+marked+'&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;Jawaban tidak dapat diubah setelah dikumpulkan.&lt;/p&gt;&lt;div class="row"&gt;&lt;button type="button" class="btn btn-outline" id="finishCancel"&gt;Batal&lt;/button&gt;&lt;button type="button" class="btn btn-primary" id="finishOk"&gt;Ya, Kumpulkan&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;';
    document.body.appendChild(ov);
  },
  btnRetry:function(){
    if(!g||!p)return;
    if(p.sent||p.attempts.length&gt;=maxAllowed())return;
    startExam();
  },
  btnRedeem:function(){
    if(!g||!p)return;
    var code=String(a("fRetryCode").value||"").trim().toUpperCase();
    if(!code){setMsg(a("retryErr"),"Masukkan kode ujian ulang dari guru.");return}
    var expected=grantCode(g.kelas,g.absen,g.examId,(p.grants||0)+1);
    if(code!==expected){setMsg(a("retryErr"),"Kode tidak valid, bukan untuk kamu, atau sudah dipakai.");return}
    p.grants=(p.grants||0)+1;
    p.sent=false;
    saveStudentState(g.key,p);
    a("fRetryCode").value="";
    setMsg(a("retryErr"),"");
    renderResult(false);
  },
  btnResend:function(){
    if(!c)return alert("Belum ada data nilai untuk dikirim.");
    if(!engine.m.endpoint)return alert("Endpoint (URL Apps Script) belum diatur di Panel Guru.");
    var btn=a("btnResend");
    if(btn){btn.textContent="Mengirim...";btn.disabled=true}
    sendResult(p.sentValue||c.total,c);
    setTimeout(function(){
      if(btn){btn.textContent="Kirim Ulang Nilai";btn.disabled=false}
      alert("Sinyal pengiriman ulang telah dikirim ke Spreadsheet. Silakan cek Google Sheets Anda beberapa saat lagi.");
    },2000);
  },
  btnPdf:function(){
    if(!c)return;
    var pdf=new(window.jspdf?window.jspdf.jsPDF:window.jsPDF)({unit:"mm",format:"a4"}),r=c,M=r.M||maxes();
    pdf.setFontSize(13);pdf.text(engine.m.judul,14,16);
    pdf.setFontSize(9);pdf.text("Bukti Pengerjaan - Cerdas Menguji, Adil Menilai"+(engine.m.sekolah?" - "+engine.m.sekolah:""),14,22);
    pdf.autoTable({startY:28,theme:"grid",styles:{fontSize:9},head:[["Nama","Kelas","No.","Ujian","Tanggal"]],body:[[g.nama,g.kelas,g.absen,g.examJudul||"",new Date(r.finishedAt).toLocaleString("id-ID")]]});
    var y=pdf.lastAutoTable.finalY+6;
    var body=[];
    if(M.pg)body.push(["A. Pilihan Ganda",r.pg,M.pg]);
    if(M.pr)body.push(["B. Praktik/Isian",r.praktik,M.pr]);
    if(M.es)body.push(["C. Esai",r.esai,M.es]);
    if(M.sh||r.sheet)body.push(["D. Lembar Kerja",r.sheet||0,M.sh]);
    body.push(["Nilai Akhir",r.total,100]);
    pdf.autoTable({startY:y,theme:"grid",styles:{fontSize:9},head:[["Bagian","Skor","Maks"]],body:body});
    y=pdf.lastAutoTable.finalY+6;
    pdf.autoTable({startY:y,theme:"grid",styles:{fontSize:9},head:[["KKM","Status","Durasi","Pelanggaran"]],body:[[engine.m.kkm,r.total&gt;=engine.m.kkm?"Tuntas":"Belum",Math.round((r.finishedAt-r.startedAt)/6e4)+" menit",r.violations]]});
    pdf.save("Bukti_"+g.nama.replace(/\s+/g,"_")+".pdf");
  },
  btnPrint:function(){window.print()},
  btnCsv:function(){
    if(!c)return;
    var data=[["Nama",g.nama],["Kelas",g.kelas],["Absen",g.absen],["Mapel",g.mapel||""],["Ujian",g.examJudul||""],["Token",g.examToken||""],["Nilai PG",c.pg],["Nilai Praktik",c.praktik],["Nilai Esai",c.esai],["Nilai Lembar Kerja",c.sheet||0],["Nilai Akhir",c.total],["KKM",engine.m.kkm],["Status",c.total&gt;=engine.m.kkm?"Tuntas":"Belum"],["Durasi",Math.round((c.finishedAt-c.startedAt)/6e4)+" mnt"],["Pelanggaran",c.violations]];
    var csv=data.map(function(r){return r.map(function(x){return'"'+String(x).replace(/"/g,'""')+'"'}).join(",")}).join("\n");
    var blob=new Blob([csv],{type:"text/csv;charset=utf-8"});
    var url=URL.createObjectURL(blob),link=document.createElement("a");
    link.href=url;link.download="Nilai_"+g.nama.replace(/\s+/g,"_")+".csv";document.body.appendChild(link);link.click();link.remove();
  },
  btnHome:function(){h=false;exitFullscreen();try{window.location.hash=""}catch(e){}showScreen("start")},
  btnOpenGuru:function(){a("guruCard").classList.remove("hidden");a("screenStart").classList.add("hidden");a("screenExam").classList.add("hidden");a("screenResult").classList.add("hidden");try{window.location.hash="guru"}catch(e){}},
  btnGuruClose:function(){a("guruCard").classList.add("hidden");a("guruLockBox").classList.remove("hidden");a("guruForm").classList.add("hidden");a("guruCode").value="";setMsg(a("guruLockErr"),"");try{if(window.location.hash==="#guru")window.location.hash=""}catch(e){}showScreen("start")},
  btnGuruClose2:function(){a("guruCard").classList.add("hidden");a("guruLockBox").classList.remove("hidden");a("guruForm").classList.add("hidden");a("guruCode").value="";try{if(window.location.hash==="#guru")window.location.hash=""}catch(e){}showScreen("start")},
  btnGuruUnlock:function(){
    if(a("guruCode").value.trim().toUpperCase()==="GURU-HOTS-2026"){
      setMsg(a("guruLockErr"),"");
      a("guruLockBox").classList.add("hidden");
      a("guruForm").classList.remove("hidden");
      if(window.__guruPanelInit)window.__guruPanelInit();
    }else setMsg(a("guruLockErr"),"Kode akses salah.");
  }
};

document.addEventListener("click",function(e){
  var t=e.target;while(t&amp;&amp;t!==document){if(t.id&amp;&amp;HANDLERS.hasOwnProperty(t.id)){e.preventDefault();HANDLERS[t.id]();return}t=t.parentNode}
});

document.addEventListener("change",function(e){
  var id=e.target&amp;&amp;e.target.id;
  if(id==="fKelas"){
    var cls=a("fKelas").value,abs=a("fAbsen");
    abs.innerHTML='&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;';
    a("fNama").value="";
    (l[cls]||[]).forEach(function(row){var o=document.createElement("option");o.value=row[0];o.textContent=row[0];abs.appendChild(o)});
  }else if(id==="fAbsen"){
    var cls2=a("fKelas").value,abs2=a("fAbsen").value;
    var row=(l[cls2]||[]).filter(function(r){return String(r[0])===String(abs2)})[0];
    a("fNama").value=row?row[1]:"";
  }
});

ensureExams();
populateClasses();
refresh();
a("hdrSub").textContent="Cerdas Menguji, Adil Menilai";

window.__examHots={
  get PG(){return engine.A},get PRAKTIK(){return engine.d},get ESAI(){return engine.k},get SHEET(){return engine.s},
  get settings(){return engine.m},ROSTER:l,
  getActiveExam:function(){return activeExam}
};
}();
//]]&gt;&lt;/script&gt;

&lt;script&gt;//&lt;![CDATA[
(function(){
"use strict";
var BK="examHotsBank_v2",AK="examHotsAiKey_v1",KS="examHotsState_v2_";
var ALL_MAPEL=["Pendidikan Agama dan Budi Pekerti","Pendidikan Pancasila","Bahasa Indonesia","Matematika","Bahasa Inggris","Pendidikan Jasmani, Olahraga, dan Kesehatan (PJOK)","Sejarah","Seni dan Budaya","Informatika","Projek Ilmu Pengetahuan Alam dan Sosial (IPAS)","Dasar-dasar Kejuruan Mata Pelajaran Konsentrasi Keahlian"];

function $(i){return document.getElementById(i)}
function esc(s){return String(null==s?"":s).replace(/[&amp;&lt;&gt;"']/g,function(c){return{"&amp;":"&amp;amp;","&lt;":"&amp;lt;","&gt;":"&amp;gt;",'"':"&amp;quot;","'":"&amp;#39;"}[c]})}
function escHtml(s){return esc(s).replace(/\n/g,"&lt;br&gt;")}
function notify(el,msg,type){if(!el)return;el.textContent=msg;el.className="msg "+(type||"info")+(msg?" show":"")}
function safeImg(s){return typeof s==="string"&amp;&amp;/^(https?:\/\/|data:image\/(png|jpe?g|gif|webp);base64,)/i.test(s)}
function loadBank(){try{var b=JSON.parse(localStorage.getItem(BK))||[];b.forEach(function(q){if(!q.mapel)q.mapel='Informatika'});return b}catch(e){return[]}}
function saveBank(a){try{localStorage.setItem(BK,JSON.stringify(a));return true}catch(e){alert("Gagal menyimpan (penyimpanan browser penuh? coba kecilkan/kurangi gambar): "+e.message);return false}}
function clearBank(){try{localStorage.removeItem(BK)}catch(e){}}

function addQ(qs){
  if(!qs||!qs.length)return 0;
  var activeM=$("bankMapelFilter")?$("bankMapelFilter").value:"Informatika";
  if(!activeM)activeM="Informatika";
  qs.forEach(function(q){if(!q.mapel)q.mapel=activeM});
  var b=loadBank();b=b.concat(qs);saveBank(b);renderBank();return qs.length
}

function normQ(o){
  if(!o)return null;
  var q={tipe:String(o.tipe||o.type||"pg").toLowerCase(),pertanyaan:String(o.pertanyaan||o.question||o.stem||"").trim(),opsi:[],kunci:o.kunci!=null?o.kunci:(o.answer!=null?o.answer:(o.ans!=null?o.ans:"")),poin:parseFloat(o.poin||o.points||1)||1,mapel:o.mapel||""};
  var raw=o.opsi||o.opts||o.options||[];
  if(Array.isArray(raw))q.opsi=raw.map(function(x){return String(x||"").trim()}).filter(Boolean);
  if(q.tipe==="bs"||q.tipe==="benar-salah"||q.tipe==="true-false")q.tipe="benar-salah";
  if(q.tipe==="esai"||q.tipe==="essay"||q.tipe==="uraian")q.tipe="uraian";
  if(q.tipe==="isian"||q.tipe==="fill")q.tipe="isian";
  if(!q.pertanyaan)return null;
  if(q.tipe==="pg"&amp;&amp;typeof q.kunci==="string"&amp;&amp;/^[A-Ea-e]$/.test(q.kunci.trim()))q.kunci=String("ABCDE".indexOf(q.kunci.trim().toUpperCase()));
  return q;
}

function parseBlock(tipe,body){
  var lines=body.split(/\r?\n/).map(function(l){return l.trim()}).filter(Boolean);
  var q={tipe:"pg",pertanyaan:"",opsi:[],kunci:"",poin:1,mapel:""};
  tipe=tipe.toUpperCase().replace(/-/g,"");
  if(tipe==="PG")q.tipe="pg";else if(tipe==="ISIAN")q.tipe="isian";else if(tipe==="BS"||tipe==="BENARSALAH")q.tipe="benar-salah";else if(tipe==="URAIAN"||tipe==="ESAI")q.tipe="uraian";
  lines.forEach(function(line){
    if(/^soal\s*:/i.test(line)||/^pertanyaan\s*:/i.test(line))q.pertanyaan=line.substring(line.indexOf(":")+1).trim();
    else if(/^jawaban\s*:/i.test(line)||/^kunci\s*:/i.test(line))q.kunci=line.substring(line.indexOf(":")+1).trim();
    else if(/^poin\s*:/i.test(line)||/^skor\s*:/i.test(line))q.poin=parseFloat(line.substring(line.indexOf(":")+1))||1;
    else if(/^[A-Ea-e][.)\]]\s+/.test(line))q.opsi.push(line.substring(2).trim());
    else if(!q.pertanyaan)q.pertanyaan=line;
  });
  if(q.tipe==="pg"&amp;&amp;/^[A-Ea-e]$/.test(q.kunci))q.kunci=String("ABCDE".indexOf(q.kunci.toUpperCase()));
  return q.pertanyaan?q:null;
}

function rowToQ(parts){
  if(!parts||parts.length&lt;2)return null;
  var t=String(parts[0]||"").trim().toLowerCase();
  if(/^(tipe|type|kategori)$/.test(t))return null;
  var tipe="pg";
  if(/^(pg|pilihan ganda|multiple choice)$/.test(t))tipe="pg";
  else if(/^(isian|fill|short)$/.test(t))tipe="isian";
  else if(/^(benar-salah|bs|true\/false)$/.test(t))tipe="benar-salah";
  else if(/^(uraian|esai|essay)$/.test(t))tipe="uraian";
  else return null;
  var q={tipe:tipe,pertanyaan:String(parts[1]||"").trim(),opsi:[],kunci:String(parts[7]||"").trim(),poin:parseFloat(parts[8])||1,mapel:""};
  for(var i=2;i&lt;=6;i++)if(parts[i]&amp;&amp;String(parts[i]).trim())q.opsi.push(String(parts[i]).trim());
  if(tipe==="pg"&amp;&amp;/^[A-Ea-e]$/.test(q.kunci))q.kunci=String("ABCDE".indexOf(q.kunci.toUpperCase()));
  return q.pertanyaan?q:null;
}

function parseText(text){
  if(!text||!text.trim())return[];
  text=text.trim();
  if(text.charAt(0)==="["||text.charAt(0)==="{"){try{var j=JSON.parse(text);var arr=Array.isArray(j)?j:(j.questions||j.soal||[]);if(Array.isArray(arr)&amp;&amp;arr.length)return arr.map(normQ).filter(Boolean)}catch(e){}}
  var re=/\[(PG|ISIAN|BENAR-?SALAH|BS|URAIAN|ESAI)\]([\s\S]*?)(?=\[(?:PG|ISIAN|BENAR-?SALAH|BS|URAIAN|ESAI)\]|$)/gi,m,out=[],found=false;
  while((m=re.exec(text))!==null){found=true;var q=parseBlock(m[1],m[2]);if(q)out.push(q)}
  if(found&amp;&amp;out.length)return out;
  var lines=text.split(/\r?\n/).filter(function(l){return l.trim()});
  if(lines.length){
    var delim=lines[0].indexOf("|")&gt;-1?"|":(lines[0].indexOf("\t")&gt;-1?"\t":null);
    if(delim){
      var start=/^(tipe|type|kategori)/i.test(lines[0])?1:0;
      for(var i=start;i&lt;lines.length;i++){var q2=rowToQ(lines[i].split(delim));if(q2)out.push(q2)}
      return out;
    }
  }
  return out;
}

/* ===== Konversi soal bank -&gt; soal ujian ===== */
function parseGroups(text){
  var out=[];
  String(text||"").split(/\r?\n/).forEach(function(line){
    line=line.trim();if(!line)return;
    var label,words;
    var ix=line.indexOf(":");
    if(ix&gt;0){label=line.slice(0,ix).trim();words=line.slice(ix+1)}
    else{words=line;label=line.split(",")[0].trim()}
    words=words.split(",").map(function(w){return w.trim().toLowerCase()}).filter(Boolean);
    if(words.length)out.push({label:label||words[0],words:words});
  });
  return out;
}
function convertBank(qs){
  var A=[],D=[],K=[],S=[];
  qs.forEach(function(q){
    var gm=safeImg(q.gambar)?q.gambar:"";
    if(q.tipe==="pg"){
      var opts=(q.opsi&amp;&amp;q.opsi.length?q.opsi:["-","-","-","-","-"]).map(esc);
      A.push({stem:escHtml(q.pertanyaan),opts:opts,ans:parseInt(q.kunci,10)||0,points:q.poin||1,gambar:gm});
    }else if(q.tipe==="isian"||q.tipe==="benar-salah"){
      D.push({judul:escHtml(q.pertanyaan),skenario:"",table:"",instruksi:q.tipe==="benar-salah"?"Ketik Benar/Salah.":"Ketik jawaban singkat.",hint:q.tipe==="benar-salah"?"Benar / Salah":"Jawaban isian",type:"text",accept:[String(q.kunci||"").trim().toLowerCase().replace(/\s+/g," ")],points:q.poin||1,gambar:gm});
    }else if(q.tipe==="uraian"){
      K.push({q:escHtml(q.pertanyaan),groups:parseGroups(q.kunci),minWords:10,gambar:gm});
    }else if(q.tipe==="lembar-kerja"&amp;&amp;q.sheet){
      S.push({instruksi:escHtml(q.pertanyaan),rows:q.sheet.rows||10,cols:q.sheet.cols||6,awal:q.sheet.awal||{},kunci:q.sheet.kunci||{},points:q.poin||5,gambar:gm});
    }
  });
  return{A:A,D:D,K:K,S:S};
}

function renderBank(){
  var selM=$("bankMapelFilter");
  if(selM&amp;&amp;selM.options.length&lt;=1){
    selM.innerHTML='&lt;option value=""&gt;Semua Mapel&lt;/option&gt;';
    ALL_MAPEL.forEach(function(m){var o=document.createElement("option");o.value=m;o.textContent=m;selM.appendChild(o)});
    var ex0=window.__examMulti.loadExams()||{};var keys=Object.keys(ex0);
    if(keys.length&gt;0)selM.value=ex0[keys[0]].mapel||"Informatika";
    else selM.value="Informatika";
  }

  var filterMapel=selM?selM.value:"";
  var allBank=loadBank();
  var bankWithIndices=allBank.map(function(q,idx){return{q:q,realIdx:idx}});
  if(filterMapel)bankWithIndices=bankWithIndices.filter(function(item){return(item.q.mapel||'Informatika')===filterMapel});

  var list=$("bankList"),cnt=$("bankCount");
  if(cnt)cnt.textContent=bankWithIndices.length+" soal"+(filterMapel?" ("+filterMapel+")":" tersimpan");

  var ex=window.__examMulti.loadExams()||{};
  var selT=$("bankTargetExam");
  if(selT){
    var cur=selT.value;
    selT.innerHTML='&lt;option value=""&gt;- Pilih Ujian -&lt;/option&gt;';
    Object.keys(ex).forEach(function(id){
      var o=document.createElement("option");
      o.value=id;
      o.textContent=(ex[id].judul||id)+" ["+(ex[id].mapel||"-")+"]";
      if(id===cur)o.selected=true;
      selT.appendChild(o);
    });
    if(!selT.value&amp;&amp;Object.keys(ex).length&gt;0)selT.value=Object.keys(ex)[0];
  }

  if(!bankWithIndices.length){list.innerHTML='&lt;p style="font-size:13px;color:var(--sub);text-align:center;padding:20px;"&gt;Belum ada soal untuk filter ini.&lt;/p&gt;';return}

  var lbl={pg:"PG",isian:"ISIAN","benar-salah":"B/S",uraian:"URAIAN","lembar-kerja":"LEMBAR KERJA"};
  var html="";
  bankWithIndices.forEach(function(item){
    var q=item.q,realIdx=item.realIdx,k=q.kunci;
    if(q.tipe==="pg"&amp;&amp;/^\d$/.test(String(q.kunci)))k="ABCDE".charAt(parseInt(q.kunci,10));
    html+='&lt;div class="bank-item"&gt;';
    html+='&lt;input type="checkbox" class="bank-cb" data-idx="'+realIdx+'"&gt;';
    html+='&lt;div style="flex:1;"&gt;&lt;div class="meta"&gt;&lt;span class="badge badge-'+(q.tipe==="benar-salah"?"bs":q.tipe)+'"&gt;'+(lbl[q.tipe]||esc(q.tipe))+'&lt;/span&gt;&lt;span style="background:#e2e8f0;color:#475569;padding:2px 6px;border-radius:4px;font-weight:700;font-size:10px;"&gt;'+esc(q.mapel||'Informatika')+'&lt;/span&gt;&lt;span&gt;Poin: '+(q.poin||1)+'&lt;/span&gt;&lt;span&gt;#'+(realIdx+1)+'&lt;/span&gt;&lt;/div&gt;&lt;div class="q"&gt;'+esc(q.pertanyaan)+'&lt;/div&gt;';
    if(safeImg(q.gambar))html+='&lt;img src="'+esc(q.gambar)+'" alt="" style="max-height:56px;max-width:160px;margin:4px 0;border:1px solid var(--line);border-radius:6px;background:#fff"&gt;';
    if(q.tipe==="pg"&amp;&amp;q.opsi&amp;&amp;q.opsi.length){q.opsi.forEach(function(o,idx){html+='&lt;div class="opt"&gt;'+String.fromCharCode(65+idx)+'. '+esc(o)+'&lt;/div&gt;'})}
    if(q.tipe==="lembar-kerja"&amp;&amp;q.sheet){
      var dd=window.__examMulti.sheet.derive(q.sheet);
      html+='&lt;div class="kunci"&gt;Lembar kerja '+q.sheet.rows+' baris × '+q.sheet.cols+' kolom, '+dd.targets.length+' sel dinilai&lt;/div&gt;';
    }else{
      html+='&lt;div class="kunci"&gt;Kunci: '+esc(k)+'&lt;/div&gt;';
    }
    html+='&lt;div class="actions"&gt;&lt;button class="edit" data-edit="'+realIdx+'"&gt;Edit&lt;/button&gt;&lt;button class="del" data-del="'+realIdx+'"&gt;Hapus&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
  });
  list.innerHTML=html;
}

/* ===== Editor soal (gambar + lembar kerja) ===== */
var qeImg="";

function readImage(file,cb){
  if(!file)return;
  if(!/^image\//.test(file.type)){cb(null,"File bukan gambar.");return}
  var fr=new FileReader();
  fr.onload=function(){
    var img=new Image();
    img.onload=function(){
      var max=900,w=img.width,hh=img.height,k=Math.min(1,max/Math.max(w,hh));
      var cv=document.createElement("canvas");
      cv.width=Math.max(1,Math.round(w*k));cv.height=Math.max(1,Math.round(hh*k));
      var cx=cv.getContext("2d");cx.fillStyle="#fff";cx.fillRect(0,0,cv.width,cv.height);cx.drawImage(img,0,0,cv.width,cv.height);
      cb(cv.toDataURL("image/jpeg",0.78));
    };
    img.onerror=function(){cb(null,"Gambar tidak dapat dibaca.")};
    img.src=fr.result;
  };
  fr.readAsDataURL(file);
}

function clamp(v,lo,hi){return Math.max(lo,Math.min(hi,v))}

function sheetCheck(){
  var K=window.__examMulti.sheet;
  var rows=clamp(parseInt($("qeShRows").value,10)||10,1,60),cols=clamp(parseInt($("qeShCols").value,10)||6,1,26);
  var cut=0;
  function clip(o){var r={};Object.keys(o).forEach(function(k){var s=K.splitRef(k);if(s.c&gt;=1&amp;&amp;s.c&lt;=cols&amp;&amp;s.r&gt;=1&amp;&amp;s.r&lt;=rows)r[k]=o[k];else cut++});return r}
  var awal=clip(K.parseCells($("qeShAwal").value,$("qeShAwalStart").value));
  var kunci=clip(K.parseCells($("qeShKunci").value,$("qeShKunciStart").value));
  var d=K.derive({awal:awal,kunci:kunci});
  return{rows:rows,cols:cols,awal:awal,kunci:kunci,d:d,cut:cut};
}

function showSheetCheck(){
  var ck=sheetCheck(),K=window.__examMulti.sheet,out=$("qeShOut");
  if(!ck.d.targets.length){out.innerHTML='&lt;div class="msg err show"&gt;Belum ada sel yang dinilai. Isi kunci jawaban (sel yang ada di kunci tetapi tidak ada di data awal akan dinilai).&lt;/div&gt;';return}
  var errs=0;
  var h2='&lt;div class="msg ok show"&gt;'+ck.d.targets.length+' sel dinilai, '+Object.keys(ck.d.locked).length+' sel terkunci.'+(ck.cut?' '+ck.cut+' sel di luar ukuran lembar diabaikan.':'')+'&lt;/div&gt;';
  h2+='&lt;table class="ref"&gt;&lt;tr&gt;&lt;th&gt;Sel&lt;/th&gt;&lt;th&gt;Isi kunci&lt;/th&gt;&lt;th&gt;Nilai diharapkan&lt;/th&gt;&lt;th&gt;Wajib rumus&lt;/th&gt;&lt;/tr&gt;';
  ck.d.targets.slice(0,80).forEach(function(t){
    var sh=K.show(t.exp);if(sh.charAt(0)==="#")errs++;
    h2+='&lt;tr&gt;&lt;td&gt;'+esc(t.ref)+'&lt;/td&gt;&lt;td class="mono"&gt;'+esc(t.raw)+'&lt;/td&gt;&lt;td&gt;'+esc(sh)+'&lt;/td&gt;&lt;td&gt;'+(t.needF?"Ya":"-")+'&lt;/td&gt;&lt;/tr&gt;';
  });
  h2+='&lt;/table&gt;';
  if(errs)h2+='&lt;div class="msg err show"&gt;'+errs+' kunci menghasilkan error (#...). Periksa rumusnya.&lt;/div&gt;';
  out.innerHTML=h2;
}

function openQuestionEditor(idx){
  var bank=loadBank();
  var q=idx!=null?bank[idx]:null;
  q=q||{tipe:"pg",pertanyaan:"",opsi:[],kunci:"",poin:1,mapel:"",gambar:"",sheet:null};
  var activeM=$("bankMapelFilter")?$("bankMapelFilter").value:"";
  var mapel=q.mapel||activeM||"Informatika";
  var sh=q.sheet||{rows:12,cols:6,awalText:"",awalStart:"A1",kunciText:"",kunciStart:"A1"};
  var kIdx=(q.tipe==="pg"&amp;&amp;/^\d$/.test(String(q.kunci)))?parseInt(q.kunci,10):0;
  qeImg=safeImg(q.gambar)?q.gambar:"";

  var tipeOpts=[["pg","Pilihan Ganda"],["isian","Isian Singkat"],["benar-salah","Benar / Salah"],["uraian","Uraian (Esai)"],["lembar-kerja","Lembar Kerja Spreadsheet"]];
  var P=[];
  P.push('&lt;div class="box" style="max-width:780px"&gt;&lt;h2&gt;'+(idx!=null?"Edit":"Tambah")+' Soal&lt;/h2&gt;&lt;div class="exam-edit-form"&gt;');
  P.push('&lt;div class="field-grid"&gt;&lt;div&gt;&lt;label class="fl"&gt;Mata Pelajaran&lt;/label&gt;&lt;select id="qeMapel"&gt;'+ALL_MAPEL.map(function(m){return'&lt;option value="'+esc(m)+'"'+(m===mapel?" selected":"")+'&gt;'+esc(m)+'&lt;/option&gt;'}).join("")+'&lt;/select&gt;&lt;/div&gt;');
  P.push('&lt;div&gt;&lt;label class="fl"&gt;Tipe Soal&lt;/label&gt;&lt;select id="qeTipe"&gt;'+tipeOpts.map(function(o){return'&lt;option value="'+o[0]+'"'+(o[0]===q.tipe?" selected":"")+'&gt;'+o[1]+'&lt;/option&gt;'}).join("")+'&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;');
  P.push('&lt;div style="margin-top:10px"&gt;&lt;label class="fl" id="qePertLbl"&gt;Pertanyaan&lt;/label&gt;&lt;textarea id="qePert" rows="4" style="min-height:80px"&gt;'+esc(q.pertanyaan||"")+'&lt;/textarea&gt;&lt;/div&gt;');
  P.push('&lt;div style="margin-top:10px"&gt;&lt;label class="fl"&gt;Gambar (opsional)&lt;/label&gt;&lt;input type="file" id="qeFile" accept="image/*"&gt;&lt;input type="text" id="qeUrl" placeholder="atau tempel alamat gambar langsung (https://...jpg / .png)" style="margin-top:6px"'+(qeImg&amp;&amp;!/^data:/.test(qeImg)?' value="'+esc(qeImg)+'"':'')+'&gt;&lt;div id="qeImgPrev" style="margin-top:6px"&gt;&lt;/div&gt;&lt;/div&gt;');
  P.push('&lt;div id="qeSecPg" style="margin-top:10px"&gt;');
  for(var i=0;i&lt;5;i++){P.push('&lt;div style="display:flex;gap:6px;align-items:center;margin-bottom:6px"&gt;&lt;b style="width:18px"&gt;'+"ABCDE".charAt(i)+'&lt;/b&gt;&lt;input type="text" id="qeOp'+i+'" placeholder="Opsi '+"ABCDE".charAt(i)+'" value="'+esc((q.opsi||[])[i]||"")+'"&gt;&lt;/div&gt;')}
  P.push('&lt;label class="fl"&gt;Kunci jawaban&lt;/label&gt;&lt;select id="qeKunciPg"&gt;'+"ABCDE".split("").map(function(L,ix){return'&lt;option value="'+ix+'"'+(ix===kIdx?" selected":"")+'&gt;'+L+'&lt;/option&gt;'}).join("")+'&lt;/select&gt;&lt;/div&gt;');
  P.push('&lt;div id="qeSecIs" style="margin-top:10px"&gt;&lt;label class="fl"&gt;Kunci jawaban singkat&lt;/label&gt;&lt;input type="text" id="qeKunciIs" value="'+esc(q.tipe==="isian"?q.kunci:"")+'"&gt;&lt;/div&gt;');
  P.push('&lt;div id="qeSecBs" style="margin-top:10px"&gt;&lt;label class="fl"&gt;Kunci&lt;/label&gt;&lt;select id="qeKunciBs"&gt;&lt;option value="Benar"'+(q.kunci==="Benar"?" selected":"")+'&gt;Benar&lt;/option&gt;&lt;option value="Salah"'+(q.kunci==="Salah"?" selected":"")+'&gt;Salah&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;');
  P.push('&lt;div id="qeSecEs" style="margin-top:10px"&gt;&lt;label class="fl"&gt;Kata kunci penilaian (opsional, satu aspek per baris)&lt;/label&gt;&lt;textarea id="qeKunciEs" rows="4" class="mono" placeholder="definisi: absolut, $, acuan tetap&amp;#10;contoh: pajak, diskon, tarif" style="min-height:70px"&gt;'+esc(q.tipe==="uraian"?q.kunci:"")+'&lt;/textarea&gt;&lt;div class="cnt-small" style="text-align:left"&gt;Format: nama aspek: kata1, kata2. Jika dikosongkan, soal uraian dinilai guru secara manual.&lt;/div&gt;&lt;/div&gt;');
  P.push('&lt;div id="qeSecSh" style="margin-top:10px"&gt;&lt;div class="warn-box"&gt;&lt;b&gt;Cara membuat lembar kerja:&lt;/b&gt; (1) isi &lt;b&gt;Data awal&lt;/b&gt; = sel yang sudah terisi dan terkunci (judul kolom, tabel referensi). (2) isi &lt;b&gt;Kunci jawaban&lt;/b&gt; = keadaan akhir yang benar, termasuk rumus. Sel yang ada di kunci tetapi tidak ada di data awal otomatis menjadi sel yang dinilai. Kunci berisi rumus (diawali =) mewajibkan murid mengetik rumus. Salin blok sel dari Excel/Google Sheets lalu tempel (tab terbaca otomatis), atau tulis per baris seperti &lt;span class="mono"&gt;D2: =B2*C2&lt;/span&gt;.&lt;/div&gt;');
  P.push('&lt;div class="field-grid"&gt;&lt;div&gt;&lt;label class="fl"&gt;Jumlah baris (maks 60)&lt;/label&gt;&lt;input type="number" id="qeShRows" min="1" max="60" value="'+sh.rows+'"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Jumlah kolom (maks 26)&lt;/label&gt;&lt;input type="number" id="qeShCols" min="1" max="26" value="'+sh.cols+'"&gt;&lt;/div&gt;&lt;/div&gt;');
  P.push('&lt;div style="margin-top:10px;display:flex;gap:8px;align-items:center;flex-wrap:wrap"&gt;&lt;label class="fl" style="margin:0"&gt;1. Data awal (terkunci), mulai dari sel&lt;/label&gt;&lt;input type="text" id="qeShAwalStart" value="'+esc(sh.awalStart||"A1")+'" style="width:80px"&gt;&lt;/div&gt;&lt;textarea id="qeShAwal" rows="5" class="mono" style="min-height:90px;margin-top:4px" placeholder="Tempel dari Excel/Sheets, atau tulis per baris:&amp;#10;A1: Barang&amp;#10;B1: Jumlah"&gt;'+esc(sh.awalText||"")+'&lt;/textarea&gt;');
  P.push('&lt;div style="margin-top:10px;display:flex;gap:8px;align-items:center;flex-wrap:wrap"&gt;&lt;label class="fl" style="margin:0"&gt;2. Kunci jawaban (keadaan akhir), mulai dari sel&lt;/label&gt;&lt;input type="text" id="qeShKunciStart" value="'+esc(sh.kunciStart||"A1")+'" style="width:80px"&gt;&lt;/div&gt;&lt;textarea id="qeShKunci" rows="6" class="mono" style="min-height:100px;margin-top:4px" placeholder="Tempel dari Excel/Sheets, atau tulis per baris:&amp;#10;D2: =B2*C2&amp;#10;D5: =SUM(D2:D4)"&gt;'+esc(sh.kunciText||"")+'&lt;/textarea&gt;');
  P.push('&lt;div class="btn-row"&gt;&lt;button type="button" class="btn btn-outline btn-sm" id="qeShPrev"&gt;Periksa Kunci&lt;/button&gt;&lt;/div&gt;&lt;div id="qeShOut"&gt;&lt;/div&gt;&lt;/div&gt;');
  P.push('&lt;div style="margin-top:10px"&gt;&lt;label class="fl"&gt;Poin&lt;/label&gt;&lt;input type="number" id="qePoin" min="0.5" step="0.5" value="'+(q.poin||1)+'" style="width:120px"&gt;&lt;/div&gt;');
  P.push('&lt;div class="msg err" id="qeErr"&gt;&lt;/div&gt;');
  P.push('&lt;div class="row"&gt;&lt;button class="btn btn-outline" id="qeCancel" type="button"&gt;Batal&lt;/button&gt;&lt;button class="btn btn-primary" id="qeSave" type="button"&gt;Simpan Soal&lt;/button&gt;&lt;/div&gt;');
  P.push('&lt;/div&gt;&lt;/div&gt;');

  var old=$("qeOverlay");if(old)old.remove();
  var ov=document.createElement("div");ov.id="qeOverlay";ov.className="overlay";
  ov.innerHTML=P.join("");
  document.body.appendChild(ov);

  function toggle(){
    var t=$("qeTipe").value;
    $("qeSecPg").style.display=t==="pg"?"block":"none";
    $("qeSecIs").style.display=t==="isian"?"block":"none";
    $("qeSecBs").style.display=t==="benar-salah"?"block":"none";
    $("qeSecEs").style.display=t==="uraian"?"block":"none";
    $("qeSecSh").style.display=t==="lembar-kerja"?"block":"none";
    $("qePertLbl").textContent=t==="lembar-kerja"?"Petunjuk / instruksi untuk murid":"Pertanyaan";
  }
  function renderPrev(){
    var el=$("qeImgPrev");
    if(safeImg(qeImg)){
      var kb=/^data:/.test(qeImg)?" (kira-kira "+Math.round(qeImg.length*0.75/1024)+" KB)":"";
      el.innerHTML='&lt;img src="'+esc(qeImg)+'" alt="" style="max-width:220px;max-height:140px;border:1px solid var(--line);border-radius:8px;background:#fff"&gt; &lt;button type="button" class="btn btn-outline btn-sm" id="qeClearImg"&gt;Hapus gambar&lt;/button&gt;&lt;span style="font-size:11.5px;color:var(--sub)"&gt;'+kb+'&lt;/span&gt;';
      $("qeClearImg").addEventListener("click",function(){qeImg="";$("qeUrl").value="";$("qeFile").value="";renderPrev()});
    }else el.innerHTML="";
  }
  var prevTipe=$("qeTipe").value;
  $("qeTipe").addEventListener("change",function(){
    var t=$("qeTipe").value,pn=$("qePoin");
    if(t==="lembar-kerja"&amp;&amp;parseFloat(pn.value)===1)pn.value=5;
    if(prevTipe==="lembar-kerja"&amp;&amp;t!=="lembar-kerja"&amp;&amp;parseFloat(pn.value)===5)pn.value=1;
    prevTipe=t;toggle();
  });
  $("qeFile").addEventListener("change",function(){
    var f=this.files&amp;&amp;this.files[0];if(!f)return;
    readImage(f,function(url,err){
      if(!url){notify($("qeErr"),err||"Gagal memproses gambar.","err");return}
      qeImg=url;$("qeUrl").value="";notify($("qeErr"),"","err");renderPrev();
    });
  });
  $("qeUrl").addEventListener("input",function(){
    var v=this.value.trim();
    if(!v){qeImg="";renderPrev();return}
    if(/^https?:\/\//i.test(v)){qeImg=v;renderPrev()}
  });
  $("qeShPrev").addEventListener("click",showSheetCheck);
  $("qeCancel").addEventListener("click",function(){ov.remove()});
  $("qeSave").addEventListener("click",function(){
    var err=$("qeErr"),tipe=$("qeTipe").value,pert=$("qePert").value.trim();
    if(!pert){notify(err,"Isi pertanyaan / petunjuk terlebih dahulu.","err");return}
    var nq={tipe:tipe,pertanyaan:pert,opsi:[],kunci:"",poin:Math.max(0.5,parseFloat($("qePoin").value)||1),mapel:$("qeMapel").value,gambar:qeImg||""};
    if(tipe==="pg"){
      var sel=parseInt($("qeKunciPg").value,10),pos=-1;
      for(var i2=0;i2&lt;5;i2++){var v=$("qeOp"+i2).value.trim();if(v){nq.opsi.push(v);if(i2===sel)pos=nq.opsi.length-1}}
      if(nq.opsi.length&lt;2){notify(err,"Isi minimal 2 opsi jawaban.","err");return}
      if(pos&lt;0){notify(err,"Kunci jawaban menunjuk opsi yang masih kosong.","err");return}
      nq.kunci=String(pos);
    }else if(tipe==="isian"){
      nq.kunci=$("qeKunciIs").value.trim();
      if(!nq.kunci){notify(err,"Isi kunci jawaban singkat.","err");return}
    }else if(tipe==="benar-salah"){
      nq.opsi=["Benar","Salah"];nq.kunci=$("qeKunciBs").value;
    }else if(tipe==="uraian"){
      nq.kunci=$("qeKunciEs").value.trim();
    }else{
      var ck=sheetCheck();
      if(!ck.d.targets.length){notify(err,"Kunci jawaban belum berisi sel yang dinilai. Klik \"Periksa Kunci\".","err");return}
      nq.sheet={rows:ck.rows,cols:ck.cols,awalText:$("qeShAwal").value,awalStart:$("qeShAwalStart").value.trim()||"A1",kunciText:$("qeShKunci").value,kunciStart:$("qeShKunciStart").value.trim()||"A1",awal:ck.awal,kunci:ck.kunci};
    }
    var bk=loadBank();
    if(idx!=null)bk[idx]=nq;else bk.push(nq);
    if(!saveBank(bk))return;
    ov.remove();renderBank();
  });
  toggle();renderPrev();
}

function handleDoc(file){
  if(!file)return;
  var ext=file.name.split(".").pop().toLowerCase();
  if(ext==="txt"){var r=new FileReader();r.onload=function(e){var qs=parseText(e.target.result);if(qs.length){addQ(qs);notify($("importMsg"),"Impor "+qs.length+" soal dari .txt","ok")}else notify($("importMsg"),"Tidak ada soal.","err")};r.readAsText(file)}
  else if(ext==="docx"){
    if(typeof mammoth==="undefined"){alert("Mammoth belum termuat");return}
    var r2=new FileReader();r2.onload=function(e){mammoth.extractRawText({arrayBuffer:e.target.result}).then(function(res){var qs=parseText(res.value);if(qs.length){addQ(qs);notify($("importMsg"),"Impor "+qs.length+" soal dari .docx","ok")}else notify($("importMsg"),"Tidak ada soal.","err")}).catch(function(err){notify($("importMsg"),"Gagal: "+err.message,"err")})};r2.readAsArrayBuffer(file);
  }else if(ext==="doc"){alert("Format .doc tidak didukung. Simpan ulang sebagai .docx")}
  else notify($("importMsg"),"Format tidak didukung: ."+ext,"err");
}

function handleXls(file){
  if(!file)return;
  if(typeof XLSX==="undefined"){alert("SheetJS belum termuat");return}
  var r=new FileReader();r.onload=function(e){
    try{
      var wb=XLSX.read(e.target.result,{type:"array"});
      var ws=wb.Sheets[wb.SheetNames[0]];
      var rows=XLSX.utils.sheet_to_json(ws,{header:1});
      var start=0;if(rows.length&amp;&amp;rows[0]&amp;&amp;/^(tipe|type|kategori)/i.test(String(rows[0][0]||"")))start=1;
      var qs=[];
      for(var i=start;i&lt;rows.length;i++){var q=rowToQ((rows[i]||[]).map(String));if(q)qs.push(q)}
      if(qs.length){addQ(qs);notify($("importMsg"),"Impor "+qs.length+" soal","ok")}else notify($("importMsg"),"Tidak ada soal.","err");
    }catch(err){notify($("importMsg"),"Gagal: "+err.message,"err")}
  };r.readAsArrayBuffer(file);
}

function buildPrompt(){
  var topik=$("aiTopik").value.trim(),jumlah=parseInt($("aiJumlah").value,10)||5,tipe=$("aiTipe").value,level=$("aiLevel").value;
  var activeM=$("bankMapelFilter")?$("bankMapelFilter").value:"Informatika";
  if(!activeM)activeM="Informatika";

  if(!topik){notify($("aiMsg"),"Isi topik dulu","err");return}
  var desc={pg:"Pilihan Ganda",isian:"Isian Singkat","benar-salah":"Benar/Salah",uraian:"Uraian",campur:"Campuran"}[tipe]||"PG";
  var p="Kamu adalah guru "+activeM+" SMK yang ahli menyusun soal. Buatkan "+jumlah+" soal "+desc+" tingkat "+level+" tentang: "+topik+".\n\nFORMAT (WAJIB pipe-delimited, tanpa penjelasan):\n"+
    "tipe | pertanyaan | opsiA | opsiB | opsiC | opsiD | opsiE | kunci | poin\n\n"+
    "Keterangan:\n- tipe: pg / isian / benar-salah / uraian\n- opsiA-E untuk PG saja\n- kunci: A/B/C/D/E untuk PG; jawaban singkat untuk isian/BS\n- poin: angka default 1\n\n"+
    "Contoh:\npg | Fungsi mencari nilai berdasarkan kode? | VLOOKUP | HLOOKUP | SUM | AVERAGE | | A | 1";
  $("aiPromptOut").value=p;
  notify($("aiMsg"),"Prompt siap. Salin ke ChatGPT/Gemini.","ok");
}

async function gemini(prompt,apiKey){
  var url="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key="+encodeURIComponent(apiKey);
  var res=await fetch(url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contents:[{parts:[{text:prompt}]}],generationConfig:{temperature:0.7,maxOutputTokens:4096}})});
  if(!res.ok){var err=await res.text();throw new Error("API "+res.status+": "+err.slice(0,200))}
  var data=await res.json();
  if(!data.candidates||!data.candidates[0]||!data.candidates[0].content)throw new Error("Respons kosong");
  return data.candidates[0].content.parts.map(function(p){return p.text}).join("");
}

function renderExamList(){
  var ex=window.__examMulti.loadExams()||{};
  var list=$("examList");if(!list)return;
  var ids=Object.keys(ex);
  if(!ids.length){list.innerHTML='&lt;p style="text-align:center;padding:20px;color:var(--sub);"&gt;Belum ada ujian. Klik "+ Buat Ujian Baru".&lt;/p&gt;';return}
  var html="";
  ids.forEach(function(id){
    var e=ex[id],counts=window.__examMulti.getExamCounts(e);
    html+='&lt;div class="exam-card"&gt;&lt;div class="exam-head"&gt;&lt;b&gt;'+esc(e.judul||id)+'&lt;/b&gt;&lt;span class="token-pill"&gt;'+esc(e.token||"-")+'&lt;/span&gt;&lt;/div&gt;';
    html+='&lt;div class="exam-meta"&gt;Mapel: '+esc(e.mapel||"-")+' | KKM: '+(e.kkm||75)+' | Durasi: '+(e.durasiMenit||60)+' mnt | Percobaan: '+(e.maxAtt||3)+'x | Soal: '+counts.pg+' PG, '+counts.pr+' isian, '+counts.es+' uraian, '+counts.sh+' lembar kerja&lt;/div&gt;';
    html+='&lt;div class="exam-actions"&gt;&lt;button class="edit" data-edit-exam="'+esc(id)+'"&gt;Edit&lt;/button&gt;&lt;button class="dup" data-dup-exam="'+esc(id)+'"&gt;Duplikat&lt;/button&gt;&lt;button class="del" data-del-exam="'+esc(id)+'"&gt;Hapus&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;';
  });
  list.innerHTML=html;
}

function openExamEditor(id){
  var ex=window.__examMulti.loadExams()||{},e=id?ex[id]:{judul:"",sekolah:"",mapel:"Informatika",kkm:75,durasiMenit:60,batasPelanggaran:3,maxAtt:3,pembahasan:false,endpoint:"",token:"UJI-"+Date.now().toString(36).toUpperCase(),jadwalMulai:"",jadwalSelesai:""};
  var ov=document.createElement("div");ov.id="examEditOverlay";ov.className="overlay";
  ov.innerHTML='&lt;div class="box" style="max-width:640px"&gt;&lt;h2&gt;'+(id?"Edit":"Buat")+' Ujian&lt;/h2&gt;&lt;div class="exam-edit-form"&gt;'+
    '&lt;div class="field-grid"&gt;&lt;div&gt;&lt;label class="fl"&gt;Judul Ujian&lt;/label&gt;&lt;input type="text" id="eeJudul" value="'+esc(e.judul||"")+'"&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;Token Ujian&lt;/label&gt;&lt;input type="text" id="eeToken" value="'+esc(e.token||"")+'" style="font-family:monospace"&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;Nama Sekolah&lt;/label&gt;&lt;input type="text" id="eeSekolah" value="'+esc(e.sekolah||"")+'"&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;Mata Pelajaran&lt;/label&gt;&lt;select id="eeMapel"&gt;'+ALL_MAPEL.map(function(m){return'&lt;option value="'+esc(m)+'"'+(e.mapel===m||(!e.mapel&amp;&amp;m==="Informatika")?" selected":"")+'&gt;'+esc(m)+'&lt;/option&gt;'}).join('')+'&lt;/select&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;KKM&lt;/label&gt;&lt;input type="number" id="eeKKM" value="'+(e.kkm||75)+'" min="0" max="100"&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;Durasi (menit)&lt;/label&gt;&lt;input type="number" id="eeDurasi" value="'+(e.durasiMenit||60)+'" min="5" max="300"&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;Batas Pelanggaran&lt;/label&gt;&lt;input type="number" id="eeBatas" value="'+(e.batasPelanggaran||3)+'" min="1" max="20"&gt;&lt;/div&gt;'+
    '&lt;div&gt;&lt;label class="fl"&gt;Jumlah Percobaan (jika belum KKM)&lt;/label&gt;&lt;input type="number" id="eeMaxAtt" value="'+(e.maxAtt||3)+'" min="1" max="5"&gt;&lt;/div&gt;'+
    '&lt;/div&gt;&lt;div style="margin-top:12px;"&gt;&lt;label class="fl"&gt;Endpoint Google Apps Script&lt;/label&gt;&lt;input type="text" id="eeEndpoint" value="'+esc(e.endpoint||"")+'"&gt;&lt;/div&gt;'+
    '&lt;div class="row"&gt;&lt;button class="btn btn-outline" id="eeCancel"&gt;Batal&lt;/button&gt;&lt;button class="btn btn-primary" id="eeSave"&gt;Simpan&lt;/button&gt;&lt;/div&gt;'+
    '&lt;/div&gt;&lt;/div&gt;';
  document.body.appendChild(ov);
  $("eeCancel").addEventListener("click",function(){ov.remove()});
  $("eeSave").addEventListener("click",function(){
    var ex2=window.__examMulti.loadExams()||{};
    var newId=id||("EX-"+Date.now().toString(36).toUpperCase());
    var old=id?ex2[id]:null;
    ex2[newId]={
      id:newId,
      judul:$("eeJudul").value.trim()||"Ujian Baru",
      sekolah:$("eeSekolah").value.trim(),
      mapel:$("eeMapel").value,
      kkm:Math.max(0,Math.min(100,parseInt($("eeKKM").value,10)||75)),
      durasiMenit:Math.max(5,parseInt($("eeDurasi").value,10)||60),
      batasPelanggaran:Math.max(1,parseInt($("eeBatas").value,10)||3),
      maxAtt:Math.max(1,parseInt($("eeMaxAtt").value,10)||3),
      pembahasan:false,
      endpoint:$("eeEndpoint").value.trim(),
      token:$("eeToken").value.trim().toUpperCase()||("UJI-"+Date.now().toString(36).toUpperCase()),
      jadwalMulai:old?old.jadwalMulai:"",
      jadwalSelesai:old?old.jadwalSelesai:"",
      A:old?old.A:[],
      d:old?old.d:[],
      k:old?old.k:[],
      s:old?(old.s||[]):[],
      createdAt:old?old.createdAt:Date.now(),
      updatedAt:Date.now()
    };
    window.__examMulti.saveExams(ex2);
    ov.remove();renderExamList();renderTokenList();populateApplySelect();
    notify($("examMsg"),"Ujian tersimpan.","ok");
  });
}

function renderTokenList(){
  var ex=window.__examMulti.loadExams()||{};
  var list=$("tokenList");if(!list)return;
  var html='&lt;table class="ref"&gt;&lt;tr&gt;&lt;th&gt;Ujian&lt;/th&gt;&lt;th&gt;Mapel&lt;/th&gt;&lt;th&gt;Token&lt;/th&gt;&lt;/tr&gt;';
  Object.keys(ex).forEach(function(id){
    var e=ex[id];
    html+='&lt;tr&gt;&lt;td&gt;&lt;b&gt;'+esc(e.judul||id)+'&lt;/b&gt;&lt;br&gt;&lt;span style="font-size:11px;color:var(--sub);"&gt;'+esc(id)+'&lt;/span&gt;&lt;/td&gt;&lt;td&gt;'+esc(e.mapel||"-")+'&lt;/td&gt;&lt;td&gt;&lt;input type="text" data-token-id="'+esc(id)+'" value="'+esc(e.token||"")+'" style="font-family:monospace;font-size:12.5px;padding:6px 8px"&gt;&lt;/td&gt;&lt;/tr&gt;';
  });
  html+="&lt;/table&gt;";
  list.innerHTML=html;
}

function populateApplySelect(){
  var ex=window.__examMulti.loadExams()||{};
  var sel=$("applyTargetExam");if(!sel)return;
  var cur=sel.value;
  sel.innerHTML='&lt;option value=""&gt;- Pilih Ujian -&lt;/option&gt;';
  var keys=Object.keys(ex);
  keys.forEach(function(id){
    var o=document.createElement("option");
    o.value=id;
    o.textContent=(ex[id].judul||id)+" ["+(ex[id].mapel||"-")+"]";
    if(id===cur||(!cur&amp;&amp;id===keys[0]))o.selected=true;
    sel.appendChild(o);
  });
  updateApplyInfo();
}

function updateApplyInfo(){
  var exId=$("applyTargetExam")?$("applyTargetExam").value:"";
  var ex=window.__examMulti.loadExams()||{};
  var info=$("applyExamInfo");
  if(exId&amp;&amp;ex[exId]){
    var e=ex[exId],cn=window.__examMulti.getExamCounts(e);
    info.innerHTML="&lt;b&gt;"+esc(e.judul)+"&lt;/b&gt;&lt;br&gt;Mapel: "+esc(e.mapel||"-")+" | Token: &lt;code&gt;"+esc(e.token)+"&lt;/code&gt;&lt;br&gt;Soal saat ini: "+cn.pg+" PG, "+cn.pr+" isian, "+cn.es+" uraian, "+cn.sh+" lembar kerja";
    var targetMapel=e.mapel||'Informatika';
    var allBank=loadBank();
    var matchCount=allBank.filter(function(q){return(q.mapel||'Informatika')===targetMapel}).length;
    $("applyBankInfo").innerHTML="&lt;b&gt;"+matchCount+"&lt;/b&gt; soal cocok dengan Mapel &lt;b&gt;"+esc(targetMapel)+"&lt;/b&gt; (dari total "+allBank.length+" soal di bank).";
  }else{
    info.textContent="-";
    $("applyBankInfo").textContent="-";
  }
}

/* ===== Panel Ujian Ulang ===== */
function initRetake(){
  var roster=(window.__examHots&amp;&amp;window.__examHots.ROSTER)||{};
  var sk=$("rtKelas");
  if(sk&amp;&amp;sk.options.length&lt;=1){
    Object.keys(roster).forEach(function(cls){var o=document.createElement("option");o.value=cls;o.textContent=cls;sk.appendChild(o)});
  }
  var ex=window.__examMulti.loadExams()||{};
  var se=$("rtExam");
  if(se){
    var cur=se.value;
    se.innerHTML='&lt;option value=""&gt;- Pilih Ujian -&lt;/option&gt;';
    Object.keys(ex).forEach(function(id){var o=document.createElement("option");o.value=id;o.textContent=(ex[id].judul||id)+" ["+(ex[id].mapel||"-")+"]";if(id===cur)o.selected=true;se.appendChild(o)});
  }
  renderRetakeLocal();
}

function renderRetakeLocal(){
  var el=$("rtLocalList");if(!el)return;
  var ex=window.__examMulti.loadExams()||{};
  var roster=(window.__examHots&amp;&amp;window.__examHots.ROSTER)||{};
  var rows=[];
  for(var i=0;i&lt;localStorage.length;i++){
    var k=localStorage.key(i);
    if(!k||k.indexOf(KS)!==0)continue;
    var key=k.slice(KS.length),parts=key.split("|");
    if(parts.length&lt;3)continue;
    var st=null;try{st=JSON.parse(localStorage.getItem(k))}catch(e){}
    if(!st||!st.best||!st.attempts||!st.attempts.length)continue;
    var e2=ex[parts[2]],kkm=e2?(e2.kkm||75):75;
    if(st.best.total&gt;=kkm)continue;
    var maxA=(e2?(e2.maxAtt||3):3)+(st.grants||0);
    var nama="-";
    (roster[parts[0]]||[]).forEach(function(r){if(String(r[0])===String(parts[1]))nama=r[1]});
    rows.push({key:key,nama:nama,kelas:parts[0],absen:parts[1],ujian:e2?(e2.judul||parts[2]):parts[2],best:st.best.total,att:st.attempts.length,maxA:maxA,habis:st.attempts.length&gt;=maxA});
  }
  if(!rows.length){el.innerHTML='&lt;p style="font-size:13px;color:var(--sub);"&gt;Tidak ada murid yang belum KKM pada perangkat ini.&lt;/p&gt;';return}
  var html='&lt;table class="ref"&gt;&lt;tr&gt;&lt;th&gt;Nama&lt;/th&gt;&lt;th&gt;Kelas&lt;/th&gt;&lt;th&gt;Ujian&lt;/th&gt;&lt;th&gt;Terbaik&lt;/th&gt;&lt;th&gt;Percobaan&lt;/th&gt;&lt;th&gt;&lt;/th&gt;&lt;/tr&gt;';
  rows.forEach(function(r){
    html+='&lt;tr&gt;&lt;td&gt;'+esc(r.nama)+' (No. '+esc(r.absen)+')&lt;/td&gt;&lt;td&gt;'+esc(r.kelas)+'&lt;/td&gt;&lt;td&gt;'+esc(r.ujian)+'&lt;/td&gt;&lt;td class="num"&gt;'+r.best+'&lt;/td&gt;&lt;td class="num"&gt;'+r.att+' / '+r.maxA+'&lt;/td&gt;&lt;td&gt;'+(r.habis?'&lt;button class="btn btn-amber btn-sm" data-grant-local="'+esc(r.key)+'"&gt;Beri 1 kesempatan&lt;/button&gt;':'&lt;span style="font-size:12px;color:var(--sub);"&gt;Masih ada kesempatan&lt;/span&gt;')+'&lt;/td&gt;&lt;/tr&gt;';
  });
  html+='&lt;/table&gt;';
  el.innerHTML=html;
}

function applyQs(exId,selectedQs,mode,msgEl){
  var ex=window.__examMulti.loadExams()||{};
  if(!ex[exId]){notify(msgEl,"Ujian tidak ditemukan","err");return false}
  var r=convertBank(selectedQs);
  if(mode==="replace"){ex[exId].A=r.A;ex[exId].d=r.D;ex[exId].k=r.K;ex[exId].s=r.S}
  else{
    ex[exId].A=(ex[exId].A||[]).concat(r.A);
    ex[exId].d=(ex[exId].d||[]).concat(r.D);
    ex[exId].k=(ex[exId].k||[]).concat(r.K);
    ex[exId].s=(ex[exId].s||[]).concat(r.S);
  }
  ex[exId].updatedAt=Date.now();
  if(!window.__examMulti.saveExams(ex))return false;
  notify(msgEl,"Diterapkan ke ujian: "+r.A.length+" PG, "+r.D.length+" isian, "+r.K.length+" uraian, "+r.S.length+" lembar kerja.","ok");
  return true;
}

function bind(){
  if(window.__guruBound)return;
  window.__guruBound=true;

  document.querySelectorAll(".guru-tab").forEach(function(t){
    t.addEventListener("click",function(){
      var tab=t.getAttribute("data-tab");
      document.querySelectorAll(".guru-tab").forEach(function(x){x.classList.remove("active")});
      document.querySelectorAll(".guru-panel").forEach(function(x){x.classList.remove("active")});
      t.classList.add("active");
      var pn=document.querySelector('.guru-panel[data-panel="'+tab+'"]');if(pn)pn.classList.add("active");
      if(tab==="exams")renderExamList();
      if(tab==="bank")renderBank();
      if(tab==="apply"){populateApplySelect();updateApplyInfo()}
      if(tab==="token")renderTokenList();
      if(tab==="retake")initRetake();
    });
  });

  document.addEventListener("click",function(e){
    var t=e.target;if(!t||!t.getAttribute)return;
    var ed=t.getAttribute("data-edit"),dl=t.getAttribute("data-del"),eid=t.getAttribute("data-edit-exam"),did=t.getAttribute("data-dup-exam"),xid=t.getAttribute("data-del-exam"),gl=t.getAttribute("data-grant-local");
    if(ed!=null){openQuestionEditor(parseInt(ed,10));e.preventDefault()}
    if(dl!=null){if(confirm("Hapus soal ini?")){var b=loadBank();b.splice(parseInt(dl,10),1);saveBank(b);renderBank()}e.preventDefault()}
    if(eid){openExamEditor(eid);e.preventDefault()}
    if(xid){if(confirm("Hapus ujian ini? Tindakan tidak bisa dibatalkan.")){var ex=window.__examMulti.loadExams()||{};delete ex[xid];window.__examMulti.saveExams(ex);renderExamList();renderTokenList();populateApplySelect();notify($("examMsg"),"Ujian dihapus.","ok")}e.preventDefault()}
    if(did){var ex3=window.__examMulti.loadExams()||{},orig=ex3[did];if(!orig)return;var newId="EX-"+Date.now().toString(36).toUpperCase();var copy=JSON.parse(JSON.stringify(orig));copy.id=newId;copy.judul=orig.judul+" (Copy)";copy.token="UJI-"+Date.now().toString(36).toUpperCase();copy.createdAt=Date.now();copy.updatedAt=Date.now();ex3[newId]=copy;window.__examMulti.saveExams(ex3);renderExamList();renderTokenList();populateApplySelect();notify($("examMsg"),"Ujian diduplikasi.","ok");e.preventDefault()}
    if(gl){
      var raw=localStorage.getItem(KS+gl);
      if(raw){
        try{
          var st=JSON.parse(raw);
          st.grants=(st.grants||0)+1;st.sent=false;
          localStorage.setItem(KS+gl,JSON.stringify(st));
          notify($("rtMsg"),"1 kesempatan ujian ulang diberikan. Murid bisa masuk lagi dengan token ujian.","ok");
          renderRetakeLocal();
        }catch(err){notify($("rtMsg"),"Gagal: "+err.message,"err")}
      }
      e.preventDefault();
    }
  });

  var bCreate=$("btnCreateExam");if(bCreate)bCreate.addEventListener("click",function(){openExamEditor(null)});

  var bAdd=$("btnBankAdd");if(bAdd)bAdd.addEventListener("click",function(){openQuestionEditor(null)});
  var bClear=$("btnBankClear");if(bClear)bClear.addEventListener("click",function(){if(confirm("Hapus SEMUA soal bank?")){clearBank();renderBank()}});
  var bSeed=$("btnBankSeed");if(bSeed)bSeed.addEventListener("click",function(){
    var K=window.__examMulti.sheet;
    var awalText="Barang\tJumlah\tHarga\tTotal\n\n\n\nTotal Keseluruhan";
    var kunciText="Pensil\t40\t2500\t=B2*C2\nBuku\t25\t3000\t=B3*C3\nPenghapus\t30\t1500\t=B4*C4\n\t\t\t=SUM(D2:D4)";
    var seed=[
      {tipe:"pg",pertanyaan:"Contoh PG: Fungsi VLOOKUP digunakan untuk?",opsi:["Cari vertikal","Jumlah","Rata-rata","Hitung"],kunci:"0",poin:1,gambar:""},
      {tipe:"isian",pertanyaan:"Rumus SUM untuk A1:A10?",opsi:[],kunci:"=SUM(A1:A10)",poin:1,gambar:""},
      {tipe:"uraian",pertanyaan:"Jelaskan perbedaan SUM dan SUMIF.",opsi:[],kunci:"sum: sum, total semua, semua data\nsumif: sumif, kriteria, syarat, bersyarat",poin:2,gambar:""},
      {tipe:"lembar-kerja",pertanyaan:"Kamu staf koperasi sekolah. Ketik data barang pada A2:C4 (Pensil 40 x 2500, Buku 25 x 3000, Penghapus 30 x 1500), hitung Total tiap barang pada D2:D4 dengan rumus, lalu jumlahkan seluruh Total pada D5 memakai SUM.",opsi:[],kunci:"",poin:5,gambar:"",sheet:{rows:6,cols:4,awalText:awalText,awalStart:"A1",kunciText:kunciText,kunciStart:"A2",awal:K.parseCells(awalText,"A1"),kunci:K.parseCells(kunciText,"A2")}}
    ];
    addQ(seed);notify($("importMsg"),"4 soal contoh dimuat (termasuk 1 lembar kerja)","ok");
  });

  var pBtn=$("btnPasteParse");if(pBtn)pBtn.addEventListener("click",function(){
    var qs=parseText($("pasteInput").value);
    if(qs.length){addQ(qs);notify($("importMsg"),"Parse "+qs.length+" soal","ok");$("pasteInput").value=""}
    else notify($("importMsg"),"Tidak ada soal terdeteksi.","err");
  });

  var dz=$("uploadDocZone"),df=$("fileDocInput");
  if(dz&amp;&amp;df){dz.addEventListener("click",function(){df.click()});dz.addEventListener("dragover",function(e){e.preventDefault();dz.classList.add("dragover")});dz.addEventListener("dragleave",function(){dz.classList.remove("dragover")});dz.addEventListener("drop",function(e){e.preventDefault();dz.classList.remove("dragover");if(e.dataTransfer.files[0])handleDoc(e.dataTransfer.files[0])});df.addEventListener("change",function(){if(df.files[0])handleDoc(df.files[0])})}
  var xz=$("uploadXlsZone"),xf=$("fileXlsInput");
  if(xz&amp;&amp;xf){xz.addEventListener("click",function(){xf.click()});xz.addEventListener("dragover",function(e){e.preventDefault();xz.classList.add("dragover")});xz.addEventListener("dragleave",function(){xz.classList.remove("dragover")});xz.addEventListener("drop",function(e){e.preventDefault();xz.classList.remove("dragover");if(e.dataTransfer.files[0])handleXls(e.dataTransfer.files[0])});xf.addEventListener("change",function(){if(xf.files[0])handleXls(xf.files[0])})}

  var aib=$("btnAIBuildPrompt");if(aib)aib.addEventListener("click",buildPrompt);
  var aip=$("btnAIParseResponse");if(aip)aip.addEventListener("click",function(){
    var qs=parseText($("aiResponse").value);
    if(qs.length){addQ(qs);notify($("aiMsg"),qs.length+" soal dari AI ke bank","ok");$("aiResponse").value=""}
    else notify($("aiMsg"),"Tidak ada soal terdeteksi.","err");
  });
  var aik=$("btnAISaveKey");if(aik)aik.addEventListener("click",function(){try{localStorage.setItem(AK,$("aiApiKey").value.trim());notify($("aiMsg"),"API key disimpan","ok")}catch(e){}});
  var aig=$("btnAIGenerate");if(aig)aig.addEventListener("click",async function(){
    var key=$("aiApiKey").value.trim()||localStorage.getItem(AK)||"";
    if(!key){notify($("aiMsg"),"Masukkan API key dulu","err");return}
    if(!$("aiPromptOut").value){buildPrompt();if(!$("aiPromptOut").value)return}
    notify($("aiMsg"),"Menghubungi Gemini...","info");
    try{var txt=await gemini($("aiPromptOut").value,key);$("aiResponse").value=txt;var qs=parseText(txt);if(qs.length){addQ(qs);notify($("aiMsg"),qs.length+" soal digenerate","ok")}else notify($("aiMsg"),"Respons tidak bisa diparse","err")}catch(e){notify($("aiMsg"),e.message,"err")}
  });
  try{$("aiApiKey").value=localStorage.getItem(AK)||""}catch(e){}

  var selApply=$("applyTargetExam");if(selApply)selApply.addEventListener("change",updateApplyInfo);

  var ab=$("btnApplyBank");if(ab)ab.addEventListener("click",function(){
    var sel=$("applyTargetExam");
    var exId=sel.options[sel.selectedIndex]?sel.options[sel.selectedIndex].value:"";
    if(!exId){notify($("applyMsg"),"Pilih ujian target.","err");return}

    var ex=window.__examMulti.loadExams()||{};
    var targetExam=ex[exId];
    if(!targetExam){notify($("applyMsg"),"Ujian tidak ditemukan","err");return}

    var targetMapel=targetExam.mapel||"Informatika";
    var allBank=loadBank();
    var matchBank=allBank.filter(function(q){return(q.mapel||'Informatika')===targetMapel});

    if(matchBank.length===0){
      if(!confirm("Tidak ada soal di bank yang cocok dengan Mapel Ujian ("+targetMapel+"). Terapkan SEMUA soal di bank?"))return;
      matchBank=allBank;
    }else{
      if(!confirm("Ditemukan "+matchBank.length+" soal dengan Mapel "+targetMapel+" di bank. Terapkan hanya soal tersebut?")){
        if(!confirm("Lanjutkan menerapkan SEMUA soal ("+allBank.length+") di bank?"))return;
        matchBank=allBank;
      }
    }

    if(!matchBank.length){notify($("applyMsg"),"Bank kosong.","err");return}
    if(applyQs(exId,matchBank,"replace",$("applyMsg"))){renderExamList();updateApplyInfo()}
  });

  var eb=$("btnEmptyExam");if(eb)eb.addEventListener("click",function(){
    var sel=$("applyTargetExam");
    var exId=sel.options[sel.selectedIndex]?sel.options[sel.selectedIndex].value:"";
    if(!exId){notify($("applyMsg"),"Pilih ujian target.","err");return}
    if(!confirm("Kosongkan semua soal ujian ini?"))return;
    var ex=window.__examMulti.loadExams()||{};
    if(ex[exId]){ex[exId].A=[];ex[exId].d=[];ex[exId].k=[];ex[exId].s=[];ex[exId].updatedAt=Date.now();window.__examMulti.saveExams(ex);renderExamList();updateApplyInfo();notify($("applyMsg"),"Ujian dikosongkan.","ok")}
  });

  var st=$("btnSaveTokens");if(st)st.addEventListener("click",function(){
    var ex=window.__examMulti.loadExams()||{};
    var inputs=document.querySelectorAll('#tokenList input[data-token-id]');
    var count=0;
    Array.prototype.forEach.call(inputs,function(inp){
      var id=inp.getAttribute("data-token-id"),v=inp.value.trim().toUpperCase();
      if(ex[id]&amp;&amp;v){ex[id].token=v;ex[id].updatedAt=Date.now();count++}
    });
    window.__examMulti.saveExams(ex);renderTokenList();notify($("tokenMsg"),count+" token tersimpan.","ok");
  });

  var bas=$("bankSelectAll");if(bas)bas.addEventListener("change",function(){
    var cbs=document.querySelectorAll(".bank-cb");
    Array.prototype.forEach.call(cbs,function(cb){cb.checked=bas.checked});
  });

  var selMFilter=$("bankMapelFilter");
  if(selMFilter){selMFilter.addEventListener("change",function(){renderBank()})}

  var selTarget=$("bankTargetExam");
  if(selTarget){
    selTarget.addEventListener("change",function(){
      var exId=selTarget.value;
      if(exId){
        var ex=window.__examMulti.loadExams();
        if(ex&amp;&amp;ex[exId]){
          var mapel=ex[exId].mapel||"Informatika";
          var selM=$("bankMapelFilter");
          if(selM){selM.value=mapel;renderBank()}
        }
      }
    });
  }

  var btnApplySel=$("btnApplySelected");if(btnApplySel)btnApplySel.addEventListener("click",function(){
    var sel=$("bankTargetExam");
    var exId=sel.options[sel.selectedIndex]?sel.options[sel.selectedIndex].value:"";
    if(!exId){notify($("importMsg"),"Pilih ujian target dari dropdown.","err");return}
    var cbs=document.querySelectorAll(".bank-cb:checked");
    if(!cbs.length){notify($("importMsg"),"Centang minimal 1 soal.","err");return}

    var bank=loadBank();
    var selectedQs=[];
    Array.prototype.forEach.call(cbs,function(cb){
      var idx=parseInt(cb.getAttribute("data-idx"),10);
      if(bank[idx])selectedQs.push(bank[idx]);
    });

    var ex=window.__examMulti.loadExams()||{};
    var targetExam=ex[exId];
    if(!targetExam){notify($("importMsg"),"Ujian tidak ditemukan","err");return}

    var targetMapel=targetExam.mapel||"Informatika";
    var mismatch=false;
    selectedQs.forEach(function(q){if((q.mapel||'Informatika')!==targetMapel)mismatch=true});
    if(mismatch){
      if(!confirm("Peringatan: Soal yang dipilih ada yang berbeda Mapel dengan Ujian Target ("+targetMapel+"). Tetap terapkan?"))return;
    }

    var replace=confirm("Klik OK untuk MENGGANTI seluruh soal ujian ini dengan "+selectedQs.length+" soal terpilih.\nKlik Batal untuk MENAMBAHKAN ke soal yang sudah ada.");
    if(applyQs(exId,selectedQs,replace?"replace":"append",$("importMsg"))){renderBank();renderExamList()}
  });

  /* Panel Ujian Ulang: pilih kelas -&gt; isi daftar murid; buat kode */
  var rtK=$("rtKelas");if(rtK)rtK.addEventListener("change",function(){
    var roster=(window.__examHots&amp;&amp;window.__examHots.ROSTER)||{};
    var sa=$("rtAbsen");
    sa.innerHTML='&lt;option value=""&gt;- Pilih Murid -&lt;/option&gt;';
    (roster[rtK.value]||[]).forEach(function(r){var o=document.createElement("option");o.value=r[0];o.textContent=r[0]+". "+r[1];sa.appendChild(o)});
    $("rtCode").classList.add("hidden");
  });
  var rtG=$("btnRtGen");if(rtG)rtG.addEventListener("click",function(){
    var kelas=$("rtKelas").value,absen=$("rtAbsen").value,exId=$("rtExam").value,no=parseInt($("rtNo").value,10)||1;
    if(!kelas||!absen||!exId){notify($("rtMsg"),"Pilih kelas, murid, dan ujian terlebih dahulu.","err");return}
    var code=window.__examMulti.grantCode(kelas,absen,exId,no);
    var out=$("rtCode");out.textContent=code;out.classList.remove("hidden");
    notify($("rtMsg"),"Kode untuk "+kelas+" No. "+absen+" (pemberian ke-"+no+"). Berikan ke murid; murid memasukkannya di halaman hasil.","ok");
  });

  renderExamList();renderBank();
}

window.__guruPanelInit=function(){if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",bind);else bind()};
})();
//]]&gt;&lt;/script&gt; &lt;!-- ADD-ON TOKEN UJIAN v4 (token per kelas / beberapa kelas / semua kelas + kategori bank soal)
     Ganti add-on v1/v2 dengan blok ini. Tempel SETELAH dua blok &lt;script&gt; yang sudah ada.
     WAJIB: isi SERVER_URL dengan URL Web App (berakhiran /exec) dari Apps Script (Code.gs v3). --&gt;
&lt;script&gt;//&lt;![CDATA[
(function(){
"use strict";
var SERVER_URL="https://script.google.com/macros/s/AKfycbyzOq_SP4ZVH8h66YQ1LlgphpBE1BoBKeKA18GDujlyS0snCW-BvoMZ98IMn5dQEc4X6w/exec";   // URL Web App Apps Script (token + nilai)

var SK="examHotsServer_v1",GK="examHotsGuruKode_v1";
function $(i){return document.getElementById(i)}
function esc(s){return String(null==s?"":s).replace(/[&amp;&lt;&gt;"']/g,function(c){return{"&amp;":"&amp;amp;","&lt;":"&amp;lt;","&gt;":"&amp;gt;",'"':"&amp;quot;","'":"&amp;#39;"}[c]})}
function notify(el,msg,type){if(!el)return;el.textContent=msg;el.className="msg "+(type||"info")+(msg?" show":"")}
function M(){return window.__examMulti}
function clean(v){return String(v||"").trim().toUpperCase().replace(/\s+/g,"")}
function genToken(){var c="ABCDEFGHJKLMNPQRSTUVWXYZ23456789",s="";for(var i=0;i&lt;6;i++)s+=c.charAt(Math.floor(Math.random()*c.length));return "UJI-"+s}
function kelasList(){return Object.keys((window.__examHots&amp;&amp;window.__examHots.ROSTER)||{})}
function kelasCode(k){return String(k).replace(/^(XII|XI|X)\s+/i,"").replace(/\s+/g,"").toUpperCase()}
function genKelasToken(k){return kelasCode(k)+"-"+(1000+Math.floor(Math.random()*9000))}

/* Token aktif sebuah ujian: token per kelas bila dipakai, jika tidak token umum */
function perKelas(e){var m=e.tokenKelas||{};return Object.keys(m).some(function(k){return clean(m[k]).length&gt;0})}
function allowed(e,kelas){var a=e.kelasBoleh;return !a||!a.length||a.indexOf(kelas)&gt;=0}
function activeTokens(e){
  if(perKelas(e)){var m=e.tokenKelas,o=[];Object.keys(m).forEach(function(k){if(clean(m[k]))o.push(clean(m[k]))});return o}
  return [clean(e.token)];
}
function dupe(ex,id,tok){
  return Object.keys(ex).some(function(k){return k!==id&amp;&amp;activeTokens(ex[k]).indexOf(tok)&gt;=0});
}

function serverUrl(){
  if(SERVER_URL)return SERVER_URL;
  try{var s=localStorage.getItem(SK);if(s)return s}catch(e){}
  var ex=(M()&amp;&amp;M().loadExams())||{},u="";
  Object.keys(ex).forEach(function(id){if(!u&amp;&amp;ex[id].endpoint)u=ex[id].endpoint});
  return u;
}
function withQuery(url,q){return url+(url.indexOf("?")&gt;-1?"&amp;":"?")+q}
function fetchJson(url,opt,ms){
  var ctl=window.AbortController?new AbortController():null,to=null;
  if(ctl){opt=opt||{};opt.signal=ctl.signal;to=setTimeout(function(){ctl.abort()},ms||10000)}
  return fetch(url,opt).then(function(r){if(to)clearTimeout(to);return r.json()},function(e){if(to)clearTimeout(to);throw e});
}

/* ===== Tab Token Ujian ===== */
function kbHtml(id,e,on,kl){
  var kb=e.kelasBoleh||[],r=kb.length&gt;0;
  var h='&lt;div data-kbbox="'+esc(id)+'" style="display:'+(on?"none":"block")+';margin:8px 0"&gt;'+
    '&lt;b style="font-size:13px"&gt;Token umum berlaku untuk:&lt;/b&gt; '+
    '&lt;label style="font-size:13px;margin-left:8px;cursor:pointer"&gt;&lt;input type="radio" name="kb-'+esc(id)+'" value="semua" data-kbmode="'+esc(id)+'"'+(r?"":" checked")+'&gt; Semua kelas&lt;/label&gt; '+
    '&lt;label style="font-size:13px;margin-left:8px;cursor:pointer"&gt;&lt;input type="radio" name="kb-'+esc(id)+'" value="tertentu" data-kbmode="'+esc(id)+'"'+(r?" checked":"")+'&gt; Kelas tertentu&lt;/label&gt;'+
    '&lt;div data-kbclasses="'+esc(id)+'" style="display:'+(r?"flex":"none")+';flex-wrap:wrap;gap:6px 14px;margin-top:6px"&gt;';
  kl.forEach(function(k){h+='&lt;label style="font-size:12.5px;cursor:pointer"&gt;&lt;input type="checkbox" data-kbk="'+esc(id)+'" value="'+esc(k)+'"'+(kb.indexOf(k)&gt;=0?" checked":"")+'&gt; '+esc(k)+'&lt;/label&gt;'});
  return h+'&lt;/div&gt;&lt;/div&gt;';
}
var rendering=false;
function renderTokens(){
  var list=$("tokenList");if(!list||!M())return;
  rendering=true;
  var ex=M().loadExams()||{},url=serverUrl(),kl=kelasList();
  var h='&lt;div class="warn-box"&gt;&lt;b&gt;Token lintas perangkat.&lt;/b&gt; Atur token (umum atau berbeda per kelas) dan jadwal, lalu klik &lt;b&gt;Publikasikan ke Server&lt;/b&gt;. Murid di perangkat mana pun mengambil ujian memakai kelas + token. URL server untuk murid ditentukan oleh konstanta &lt;span class="mono"&gt;SERVER_URL&lt;/span&gt; di kode halaman.&lt;/div&gt;'+
    '&lt;label class="fl"&gt;URL Server (Apps Script /exec)&lt;/label&gt;'+
    '&lt;input type="text" id="srvUrl" value="'+esc(url)+'" placeholder="https://script.google.com/macros/s/.../exec" class="mono"&gt;'+
    '&lt;div class="btn-row"&gt;&lt;button type="button" class="btn btn-outline btn-sm" id="srvSave"&gt;Simpan URL&lt;/button&gt;'+
    '&lt;button type="button" class="btn btn-outline btn-sm" id="srvTest"&gt;Tes Koneksi&lt;/button&gt;'+
    '&lt;button type="button" class="btn btn-primary btn-sm" id="srvPub"&gt;Publikasikan ke Server&lt;/button&gt;&lt;/div&gt;'+
    '&lt;div style="height:10px"&gt;&lt;/div&gt;';
  h+='&lt;table class="ref"&gt;&lt;tr&gt;&lt;th&gt;Ujian&lt;/th&gt;&lt;th&gt;Token umum&lt;/th&gt;&lt;th&gt;Aktif mulai&lt;/th&gt;&lt;th&gt;Aktif sampai&lt;/th&gt;&lt;/tr&gt;';
  Object.keys(ex).forEach(function(id){
    var e=ex[id],on=perKelas(e),map=e.tokenKelas||{};
    h+='&lt;tr&gt;&lt;td&gt;&lt;b&gt;'+esc(e.judul||id)+'&lt;/b&gt;&lt;br&gt;&lt;span style="font-size:11px;color:var(--sub);"&gt;'+esc(e.mapel||"-")+'&lt;/span&gt;&lt;/td&gt;'+
      '&lt;td&gt;&lt;div style="display:flex;gap:4px;align-items:center;flex-wrap:wrap"&gt;'+
      '&lt;input type="text" data-token-id="'+esc(id)+'" value="'+esc(e.token||"")+'" style="font-family:monospace;font-size:12.5px;padding:6px 8px;width:140px"&gt;'+
      '&lt;button type="button" class="btn btn-outline btn-sm" data-gen-token="'+esc(id)+'"&gt;Acak&lt;/button&gt;'+
      '&lt;button type="button" class="btn btn-outline btn-sm" data-copy-token="'+esc(id)+'"&gt;Salin&lt;/button&gt;&lt;/div&gt;&lt;/td&gt;'+
      '&lt;td&gt;&lt;input type="datetime-local" data-start-id="'+esc(id)+'" value="'+esc(e.jadwalMulai||"")+'" style="padding:6px 8px;font-size:12px"&gt;&lt;/td&gt;'+
      '&lt;td&gt;&lt;input type="datetime-local" data-end-id="'+esc(id)+'" value="'+esc(e.jadwalSelesai||"")+'" style="padding:6px 8px;font-size:12px"&gt;&lt;/td&gt;&lt;/tr&gt;';
    h+='&lt;tr&gt;&lt;td colspan="4" style="background:#fafbfe"&gt;'+
      '&lt;label style="font-size:13px;font-weight:700;cursor:pointer"&gt;&lt;input type="checkbox" data-ktmode="'+esc(id)+'"'+(on?" checked":"")+'&gt; Token berbeda untuk setiap kelas&lt;/label&gt; '+
      '&lt;span style="font-size:11.5px;color:var(--sub)"&gt;(bila aktif, token umum di atas tidak dipakai)&lt;/span&gt;'+
      kbHtml(id,e,on,kl)+
      '&lt;div data-ktbox="'+esc(id)+'" style="display:'+(on?"block":"none")+';margin-top:8px"&gt;'+
      '&lt;div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:8px"&gt;';
    kl.forEach(function(k){
      h+='&lt;div&gt;&lt;label class="fl" style="font-size:12px"&gt;'+esc(k)+'&lt;/label&gt;'+
        '&lt;input type="text" data-kt-id="'+esc(id)+'" data-kt-kelas="'+esc(k)+'" value="'+esc(map[k]||"")+'" style="font-family:monospace;font-size:12.5px;padding:6px 8px"&gt;&lt;/div&gt;';
    });
    h+='&lt;/div&gt;&lt;div class="btn-row"&gt;&lt;button type="button" class="btn btn-outline btn-sm" data-kt-gen="'+esc(id)+'"&gt;Acak Semua Kelas&lt;/button&gt;'+
      '&lt;button type="button" class="btn btn-outline btn-sm" data-kt-copy="'+esc(id)+'"&gt;Salin Daftar Token&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;';
  });
  h+='&lt;/table&gt;&lt;div class="cnt-small" style="text-align:left"&gt;Jadwal boleh dikosongkan (token aktif terus). Jam jadwal mengikuti jam server. Kelas yang tokennya dikosongkan tidak bisa masuk saat mode per kelas aktif.&lt;/div&gt;';
  list.innerHTML=h;
  setTimeout(function(){rendering=false},0);
}

function readKelasInputs(id){
  var map={};
  Array.prototype.forEach.call(document.querySelectorAll('#tokenList input[data-kt-id="'+id+'"]'),function(inp){
    var v=clean(inp.value);if(v)map[inp.getAttribute("data-kt-kelas")]=v;
  });
  return map;
}

function saveTokens(){
  var ex=M().loadExams()||{},msg=$("tokenMsg");
  var inputs=document.querySelectorAll("#tokenList input[data-token-id]");
  var seen={},bad="",rows=[];
  function claim(v,label){
    if(v.length&lt;4){bad="Token "+label+" minimal 4 karakter.";return false}
    if(seen[v]){bad="Token "+v+" dipakai lebih dari sekali ("+label+").";return false}
    seen[v]=1;return true;
  }
  Array.prototype.forEach.call(inputs,function(inp){
    if(bad)return;
    var id=inp.getAttribute("data-token-id"),v=clean(inp.value);
    if(!ex[id])return;
    var title=ex[id].judul||id;
    var cb=document.querySelector('#tokenList input[data-ktmode="'+id+'"]');
    var useK=!!(cb&amp;&amp;cb.checked),map={};
    if(useK){
      map=readKelasInputs(id);
      var ks=Object.keys(map);
      if(!ks.length){bad="Mode per kelas aktif pada \""+title+"\" tetapi belum ada token kelas yang diisi.";return}
      for(var i=0;i&lt;ks.length;i++){if(!claim(map[ks[i]],"\""+title+"\" kelas "+ks[i]))return}
    }else{
      if(!claim(v,"\""+title+"\""))return;
    }
    var kb=[];
    if(!useK){
      var rd=document.querySelector('#tokenList input[data-kbmode="'+id+'"]:checked');
      if(rd&amp;&amp;rd.value==="tertentu"){
        Array.prototype.forEach.call(document.querySelectorAll('#tokenList input[data-kbk="'+id+'"]:checked'),function(c){kb.push(c.value)});
        if(!kb.length){bad="Pilih minimal satu kelas untuk \""+title+"\" atau ubah ke Semua kelas.";return}
      }
    }
    var s=document.querySelector('#tokenList input[data-start-id="'+id+'"]'),f=document.querySelector('#tokenList input[data-end-id="'+id+'"]');
    var a=s?s.value:"",b=f?f.value:"";
    if(a&amp;&amp;b&amp;&amp;new Date(b)&lt;=new Date(a)){bad="Jadwal "+title+": waktu selesai harus setelah waktu mulai.";return}
    rows.push({id:id,token:v,map:useK?map:{},kb:kb,a:a,b:b});
  });
  if(bad){notify(msg,bad,"err");return}
  rows.forEach(function(r){
    if(r.token.length&gt;=4)ex[r.id].token=r.token;
    ex[r.id].tokenKelas=r.map;ex[r.id].kelasBoleh=r.kb;ex[r.id].jadwalMulai=r.a;ex[r.id].jadwalSelesai=r.b;ex[r.id].updatedAt=Date.now();
  });
  if(!M().saveExams(ex))return;
  renderTokens();
  notify($("tokenMsg"),rows.length+" ujian tersimpan di perangkat ini. Klik \"Publikasikan ke Server\" agar berlaku di semua perangkat.","ok");
}

function publish(){
  var msg=$("tokenMsg"),url=serverUrl();
  if(!url){notify(msg,"Isi URL Server dulu.","err");return}
  var kode=null;try{kode=sessionStorage.getItem(GK)}catch(e){}
  if(!kode){kode=prompt("Masukkan kode akses guru (sama dengan GURU_KODE di Apps Script):");if(!kode)return}
  var ex0=M().loadExams()||{},ex={};
  Object.keys(ex0).forEach(function(id){var cp=JSON.parse(JSON.stringify(ex0[id]));cp.endpoint=url;ex[id]=cp});
  notify(msg,"Mempublikasikan ke server...","info");
  fetchJson(url,{method:"POST",headers:{"Content-Type":"text/plain;charset=utf-8"},body:JSON.stringify({action:"publish",kode:kode,exams:ex})},60000)
  .then(function(r){
    if(r&amp;&amp;r.ok){try{sessionStorage.setItem(GK,kode)}catch(e){}notify(msg,r.count+" ujian dipublikasikan. Token kini berlaku di semua perangkat.","ok")}
    else{
      if(r&amp;&amp;r.error==="kode"){try{sessionStorage.removeItem(GK)}catch(e){}notify(msg,"Kode akses guru salah.","err")}
      else notify(msg,"Gagal: "+((r&amp;&amp;r.error)||"respons server tidak dikenali"),"err");
    }
  },function(err){notify(msg,"Tidak bisa terhubung ke server: "+err.message+". Periksa URL dan pengaturan akses (Anyone).","err")});
}

function testServer(){
  var msg=$("tokenMsg"),url=($("srvUrl")&amp;&amp;$("srvUrl").value.trim())||serverUrl();
  if(!url){notify(msg,"Isi URL Server dulu.","err");return}
  notify(msg,"Menguji koneksi...","info");
  fetchJson(withQuery(url,"action=ping"),{},10000).then(function(r){
    notify(msg,r&amp;&amp;r.ok?"Server terhubung.":"Server merespons tetapi tidak sesuai.",r&amp;&amp;r.ok?"ok":"err");
  },function(err){notify(msg,"Gagal terhubung: "+err.message,"err")});
}

/* ===== Klik biasa (bubble) ===== */
document.addEventListener("click",function(e){
  var t=e.target;if(!t||!t.closest)return;
  var g=t.closest("[data-gen-token]"),c=t.closest("[data-copy-token]"),kg=t.closest("[data-kt-gen]"),kc=t.closest("[data-kt-copy]"),km=t.closest("[data-ktmode]"),kr=t.closest("[data-kbmode]");
  if(g){var i1=document.querySelector('#tokenList input[data-token-id="'+g.getAttribute("data-gen-token")+'"]');if(i1)i1.value=genToken();return}
  if(c){
    var i2=document.querySelector('#tokenList input[data-token-id="'+c.getAttribute("data-copy-token")+'"]');
    if(i2){try{navigator.clipboard.writeText(i2.value).then(function(){notify($("tokenMsg"),"Token disalin: "+i2.value,"ok")},function(){i2.select()})}catch(err){i2.select()}}
    return;
  }
  if(kg){
    Array.prototype.forEach.call(document.querySelectorAll('#tokenList input[data-kt-id="'+kg.getAttribute("data-kt-gen")+'"]'),function(inp){inp.value=genKelasToken(inp.getAttribute("data-kt-kelas"))});
    return;
  }
  if(kc){
    var id=kc.getAttribute("data-kt-copy"),ex=M().loadExams()||{},lines=[ (ex[id]&amp;&amp;ex[id].judul||id)+" - token per kelas:" ];
    Array.prototype.forEach.call(document.querySelectorAll('#tokenList input[data-kt-id="'+id+'"]'),function(inp){
      if(clean(inp.value))lines.push(inp.getAttribute("data-kt-kelas")+" : "+clean(inp.value));
    });
    var txt=lines.join("\n");
    try{navigator.clipboard.writeText(txt).then(function(){notify($("tokenMsg"),"Daftar token kelas disalin.","ok")},function(){prompt("Salin daftar ini:",txt)})}catch(err){prompt("Salin daftar ini:",txt)}
    return;
  }
  if(kr){
    var kcl=document.querySelector('#tokenList [data-kbclasses="'+kr.getAttribute("data-kbmode")+'"]');
    if(kcl)kcl.style.display=kr.value==="tertentu"?"flex":"none";
    return;
  }
  if(km){
    var box=document.querySelector('#tokenList [data-ktbox="'+km.getAttribute("data-ktmode")+'"]');
    if(box)box.style.display=km.checked?"block":"none";
    var kbb=document.querySelector('#tokenList [data-kbbox="'+km.getAttribute("data-ktmode")+'"]');
    if(kbb)kbb.style.display=km.checked?"none":"block";
    if(km.checked){
      var ins=document.querySelectorAll('#tokenList input[data-kt-id="'+km.getAttribute("data-ktmode")+'"]'),any=false;
      Array.prototype.forEach.call(ins,function(inp){if(clean(inp.value))any=true});
      if(!any)Array.prototype.forEach.call(ins,function(inp){inp.value=genKelasToken(inp.getAttribute("data-kt-kelas"))});
    }
    return;
  }
  if(t.closest("#srvSave")){try{localStorage.setItem(SK,$("srvUrl").value.trim());notify($("tokenMsg"),"URL server disimpan.","ok")}catch(err){}return}
  if(t.closest("#srvTest")){testServer();return}
  if(t.closest("#srvPub")){publish();return}
  var tab=t.closest(".guru-tab");
  if(tab&amp;&amp;tab.getAttribute("data-tab")==="token")renderTokens();
},false);

/* ===== Sisi murid: kelas + token ===== */
var editingId=null,bypass=false;

/* cari ujian lokal yang cocok dengan (kelas, token) */
var blocked=false;
function matchLocal(kelas,tk){
  var all=M().loadExams()||{},f=null,T=clean(tk);blocked=false;
  Object.keys(all).forEach(function(id){
    if(f)return;
    var e=all[id],hit=perKelas(e)?clean((e.tokenKelas||{})[kelas])===T:clean(e.token)===T;
    if(!hit)return;
    if(allowed(e,kelas))f=e;else blocked=true;
  });
  return f;
}
function scheduleMsg(exam,now){
  var t1=exam.jadwalMulai?new Date(exam.jadwalMulai).getTime():0,t2=exam.jadwalSelesai?new Date(exam.jadwalSelesai).getTime():0;
  if(t1&amp;&amp;now&lt;t1)return "Ujian belum dibuka. Dibuka: "+new Date(t1).toLocaleString("id-ID");
  if(t2&amp;&amp;now&gt;t2)return "Ujian sudah ditutup ("+new Date(t2).toLocaleString("id-ID")+").";
  return "";
}
function startErr(m,type){notify($("startErr"),m,type||"err")}

/* teruskan ke mesin ujian bawaan: token diganti sementara dengan token internal ujian */
function runOriginal(exam){
  var tokIn=$("fToken"),orig=tokIn.value,url=serverUrl();
  /* arahkan pengiriman nilai ke server (sheet per kelas) */
  if(url){
    var all=M().loadExams()||{};
    if(all[exam.id]&amp;&amp;all[exam.id].endpoint!==url){all[exam.id].endpoint=url;M().saveExams(all)}
  }
  bypass=true;
  try{tokIn.value=exam.token;$("btnStart").click()}
  finally{tokIn.value=orig;bypass=false}
}

function handleStart(e){
  if(bypass)return;
  var tokIn=$("fToken"),kelasSel=$("fKelas");if(!tokIn||!kelasSel||!M())return;
  var tk=String(tokIn.value||"").trim().toUpperCase(),kelas=kelasSel.value;
  if(!tk||!kelas)return; /* biarkan validasi bawaan menampilkan pesan */
  e.stopImmediatePropagation();e.preventDefault();
  var url=serverUrl(),invalid="Token tidak valid untuk kelas "+kelas+".",kelasMsg="Token ini tidak berlaku untuk kelas "+kelas+".";

  if(!url){
    var loc=matchLocal(kelas,tk);
    if(!loc){startErr(blocked?kelasMsg:invalid);return}
    var m0=scheduleMsg(loc,Date.now());
    if(m0){startErr(m0);return}
    startErr("");runOriginal(loc);return;
  }
  startErr("Memeriksa token...","info");
  fetchJson(withQuery(url,"action=exam&amp;kelas="+encodeURIComponent(kelas)+"&amp;token="+encodeURIComponent(tk)),{},12000)
  .then(function(res){return res},function(){return null})
  .then(function(res){
    var all=M().loadExams()||{},local=matchLocal(kelas,tk),now=Date.now(),exam=local;
    if(res&amp;&amp;res.ok===false&amp;&amp;res.error==="kelas"){startErr(kelasMsg);return}
    if(res&amp;&amp;res.ok&amp;&amp;res.exam){
      var rem=res.exam;rem._remote=true;
      var keep=all[rem.id]&amp;&amp;!all[rem.id]._remote&amp;&amp;(all[rem.id].updatedAt||0)&gt;(rem.updatedAt||0);
      if(!keep){all[rem.id]=rem;M().saveExams(all);exam=rem}else exam=all[rem.id];
      if(res.now)now=res.now;
    }else if(res&amp;&amp;res.ok===false){
      if(local&amp;&amp;local._remote){delete all[local.id];M().saveExams(all);startErr(invalid);return}
      if(!local){startErr(invalid);return}
    }else if(!local){
      startErr("Tidak dapat terhubung ke server. Periksa koneksi internet lalu coba lagi.");return;
    }
    var m=scheduleMsg(exam,now);
    if(m){startErr(m);return}
    startErr("");
    runOriginal(exam);
  });
}

/* Fase capture: dijalankan lebih dulu daripada skrip lama */
document.addEventListener("click",function(e){
  var t=e.target;if(!t||!t.closest)return;
  var ed=t.closest("[data-edit-exam]");
  if(ed)editingId=ed.getAttribute("data-edit-exam");
  if(t.closest("#btnCreateExam"))editingId=null;

  /* Duplikat ujian: jangan ikut menyalin token kelas (agar tidak kembar) */
  var dp=t.closest("[data-dup-exam]");
  if(dp&amp;&amp;M()){
    var before=Object.keys(M().loadExams()||{});
    setTimeout(function(){
      var ex=M().loadExams()||{},ch=false;
      Object.keys(ex).forEach(function(id){if(before.indexOf(id)&lt;0){ex[id].tokenKelas={};ch=true}});
      if(ch){M().saveExams(ex);renderTokens()}
    },0);
    return;
  }

  if(t.closest("#btnSaveTokens")){e.stopImmediatePropagation();e.preventDefault();saveTokens();return}

  if(t.closest("#eeSave")){
    var f=$("eeToken");if(!f||!M())return;
    var tok=clean(f.value),ex0=M().loadExams()||{};
    if(tok){
      if(dupe(ex0,editingId||"",tok)){e.stopImmediatePropagation();e.preventDefault();alert("Token "+tok+" sudah dipakai ujian lain. Gunakan token berbeda.");return}
      f.value=tok;
    }else f.value=genToken();
    /* form edit bawaan membuat ulang objek ujian: kembalikan token per kelas setelah disimpan */
    var oldE=editingId&amp;&amp;ex0[editingId]?ex0[editingId]:null,pid=editingId;
    if(oldE&amp;&amp;(Object.keys(oldE.tokenKelas||{}).length||(oldE.kelasBoleh||[]).length)){
      var prev=oldE.tokenKelas||{},prevKb=oldE.kelasBoleh||[];
      setTimeout(function(){
        var ex=M().loadExams()||{};
        if(ex[pid]){ex[pid].tokenKelas=prev;ex[pid].kelasBoleh=prevKb;M().saveExams(ex);renderTokens()}
      },0);
    }
    return;
  }
  if(t.closest("#btnStart"))handleStart(e);
},true);

/* ===== Tombol Acak di form Edit Ujian + jaga tab token versi baru ===== */
function onDom(){
  var ov=$("examEditOverlay"),f=$("eeToken");
  if(ov&amp;&amp;f&amp;&amp;!$("eeGen")){
    var b=document.createElement("button");
    b.type="button";b.id="eeGen";b.className="btn btn-outline btn-sm";b.textContent="Acak";b.style.marginTop="6px";
    b.addEventListener("click",function(){f.value=genToken()});
    f.parentNode.appendChild(b);
  }
  var tl=$("tokenList");
  if(tl&amp;&amp;!rendering&amp;&amp;tl.children.length&amp;&amp;!tl.querySelector("[data-gen-token]"))renderTokens();
}
new MutationObserver(onDom).observe(document.body,{childList:true,subtree:true});
})();

/* ===== KATEGORI SOAL DI BANK SOAL ===== */
(function(){
"use strict";
var BK="examHotsBank_v2";
var BASE=["Tes Formatif","Tes Sumatif","STS / UTS","SAS / UAS","Latihan Soal","Remedial","Pengayaan"];
var editIdx=null;
function $(i){return document.getElementById(i)}
function esc(s){return String(null==s?"":s).replace(/[&amp;&lt;&gt;"']/g,function(c){return{"&amp;":"&amp;amp;","&lt;":"&amp;lt;","&gt;":"&amp;gt;",'"':"&amp;quot;","'":"&amp;#39;"}[c]})}
function loadBank(){try{return JSON.parse(localStorage.getItem(BK))||[]}catch(e){return[]}}
function saveBank(b){try{localStorage.setItem(BK,JSON.stringify(b));return true}catch(e){alert("Gagal menyimpan: "+e.message);return false}}
function say(msg,type){var el=$("bankKatMsg");if(!el)return;el.textContent=msg;el.className="msg "+(type||"info")+(msg?" show":"")}
function katList(){var s={};BASE.forEach(function(k){s[k]=1});loadBank().forEach(function(q){if(q&amp;&amp;q.kategori)s[q.kategori]=1});return Object.keys(s)}
function rerender(){var m=$("bankMapelFilter");if(m)m.dispatchEvent(new Event("change"))}

function fillKat(){
  var sel=$("bankKatFilter"),dl=$("bankKatList"),ks=katList();
  if(sel){
    var cur=sel.value;
    sel.innerHTML='&lt;option value=""&gt;Semua Kategori&lt;/option&gt;'+ks.map(function(k){return '&lt;option value="'+esc(k)+'"&gt;'+esc(k)+'&lt;/option&gt;'}).join("")+'&lt;option value="__none"&gt;(Tanpa kategori)&lt;/option&gt;';
    sel.value=cur;if(sel.value!==cur)sel.value="";
  }
  if(dl)dl.innerHTML=ks.map(function(k){return '&lt;option value="'+esc(k)+'"&gt;'}).join("");
}

function applyFilter(){
  var fs=$("bankKatFilter");if(!fs)return;
  var f=fs.value,bank=loadBank(),vis=0;
  fillKat();
  Array.prototype.forEach.call(document.querySelectorAll("#bankList .bank-item"),function(it){
    var cb=it.querySelector(".bank-cb");if(!cb)return;
    var q=bank[parseInt(cb.getAttribute("data-idx"),10)]||{},k=q.kategori||"";
    var meta=it.querySelector(".meta");
    if(meta&amp;&amp;!meta.querySelector(".kat-badge")){
      var sp=document.createElement("span");sp.className="kat-badge";
      sp.style.cssText="background:#ccfbf1;color:#0f766e;padding:2px 6px;border-radius:4px;font-weight:700;font-size:10px";
      sp.textContent=k||"Tanpa kategori";meta.appendChild(sp);
    }
    var show=!f||(f==="__none"?!k:k===f);
    it.style.display=show?"":"none";
    if(!show)cb.checked=false;else vis++;
  });
  var cnt=$("bankCount");
  if(cnt&amp;&amp;f)cnt.textContent=vis+" soal - kategori: "+(f==="__none"?"tanpa kategori":f);
}

function inject(){
  var bar=document.querySelector('.guru-panel[data-panel="bank"] .action-bar');
  if(!bar||$("bankKatFilter"))return;
  var g=document.createElement("div");g.style.cssText="display:flex;align-items:center;gap:6px";
  g.innerHTML='&lt;label style="font-size:13px;font-weight:700;white-space:nowrap"&gt;Kategori:&lt;/label&gt;&lt;select id="bankKatFilter" style="padding:8px 10px;font-size:13px;border-radius:6px;border:1px solid var(--line);min-width:150px;width:auto"&gt;&lt;/select&gt;';
  var all=$("bankSelectAll");
  bar.insertBefore(g,all?all.parentNode:null);
  var bar2=document.createElement("div");bar2.className="action-bar";
  bar2.innerHTML='&lt;b style="font-size:13px"&gt;Kategori soal:&lt;/b&gt;'+
    '&lt;input type="text" id="bankKatSet" list="bankKatList" placeholder="mis. Tes Formatif, STS, SAS" style="max-width:240px"&gt;'+
    '&lt;datalist id="bankKatList"&gt;&lt;/datalist&gt;'+
    '&lt;button type="button" class="btn btn-outline btn-sm" id="btnKatSet"&gt;Beri Kategori ke Soal yang Dicentang&lt;/button&gt;'+
    '&lt;div class="msg" id="bankKatMsg" style="flex-basis:100%;margin-top:0"&gt;&lt;/div&gt;';
  bar.parentNode.insertBefore(bar2,bar.nextSibling);
  fillKat();
  var bl=$("bankList");
  if(bl)new MutationObserver(applyFilter).observe(bl,{childList:true});
  applyFilter();
}

function injectEditor(){
  var ov=$("qeOverlay"),pl=$("qePertLbl");
  if(!ov||!pl||$("qeKat"))return;
  var cur="";
  if(editIdx!=null)cur=(loadBank()[editIdx]||{}).kategori||"";
  else{var f=$("bankKatFilter");if(f&amp;&amp;f.value&amp;&amp;f.value!=="__none")cur=f.value}
  var d=document.createElement("div");d.style.marginTop="10px";
  d.innerHTML='&lt;label class="fl"&gt;Kategori soal (opsional)&lt;/label&gt;&lt;input type="text" id="qeKat" list="bankKatList" placeholder="mis. Tes Formatif, STS, SAS" value="'+esc(cur)+'"&gt;';
  pl.parentNode.parentNode.insertBefore(d,pl.parentNode);
}

function setKat(){
  var v=(($("bankKatSet")||{}).value||"").trim();
  var cbs=document.querySelectorAll("#bankList .bank-cb:checked");
  if(!cbs.length){say("Centang minimal 1 soal terlebih dahulu.","err");return}
  var b=loadBank(),n=0;
  Array.prototype.forEach.call(cbs,function(cb){var i=parseInt(cb.getAttribute("data-idx"),10);if(b[i]){b[i].kategori=v;n++}});
  if(!saveBank(b))return;
  rerender();
  say(n+" soal diberi kategori "+(v?"\""+v+"\"":"(dikosongkan)")+".","ok");
}

document.addEventListener("click",function(e){
  var t=e.target;if(!t||!t.closest)return;
  var ed=t.closest("[data-edit]");
  if(ed)editIdx=parseInt(ed.getAttribute("data-edit"),10);
  if(t.closest("#btnBankAdd"))editIdx=null;
  if(t.closest("#btnKatSet")){setKat();return}
  if(t.closest("#qeSave")){
    var kf=$("qeKat"),kat=kf?kf.value.trim():"",ei=editIdx,ol=loadBank().length;
    setTimeout(function(){
      if($("qeOverlay"))return; /* gagal validasi: form masih terbuka */
      var b=loadBank(),tg=ei!=null?b[ei]:(b.length&gt;ol?b[b.length-1]:null);
      if(tg){tg.kategori=kat;saveBank(b);rerender()}
    },0);
  }
},true);

document.addEventListener("change",function(e){
  var t=e.target;if(!t)return;
  if(t.id==="bankKatFilter"){rerender();return}
  if(t.id==="bankSelectAll"){
    e.stopImmediatePropagation();
    Array.prototype.forEach.call(document.querySelectorAll("#bankList .bank-item"),function(it){
      var cb=it.querySelector(".bank-cb");if(cb)cb.checked=t.checked&amp;&amp;it.style.display!=="none";
    });
  }
},true);

new MutationObserver(function(){injectEditor();inject()}).observe(document.body,{childList:true});
inject();
})();
//]]&gt;&lt;/script&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/test-panel-guru-pro.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-1889824469183696581</guid><pubDate>Tue, 27 Oct 2026 08:12:17 +0000</pubDate><atom:updated>2026-10-27T15:12:17.350+07:00</atom:updated><title>SERTIFIKAT KOMBEL 23 OKTOBER</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Generator Sertifikat&lt;/title&gt;

&lt;link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@700&amp;display=swap" rel="stylesheet"&gt;

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;

&lt;style&gt;

body{
    font-family:Arial,sans-serif;
    background:#eef2f7;
    padding:20px;
    max-width:700px;
    margin:auto;
}

.card{
    background:white;
    padding:24px;
    border-radius:20px;
    box-shadow:0 10px 20px rgba(0,0,0,.1);
}

h2{
    text-align:center;
    color:#1e4668;
}

.confirm-box{
    background:#f0f6ff;
    border-left:5px solid #2c7da0;
    padding:16px;
    border-radius:16px;
    margin-bottom:20px;
    text-align:center;
}

.confirm-box p{
    margin-top:0;
    font-weight:bold;
    color:#1e4668;
}

.confirm-buttons{
    display:flex;
    gap:10px;
    margin-top:10px;
}

.confirm-buttons button{
    flex:1;
}

.yes-btn{
    background:#1f8a4c;
}

.yes-btn:hover{
    background:#166638;
}

.no-btn{
    background:#c0392b;
}

.no-btn:hover{
    background:#962d22;
}

input,select,button{
    width:100%;
    padding:12px;
    margin-top:10px;
    border-radius:12px;
    border:1px solid #ccc;
    font-size:16px;
}

button{
    background:#2c7da0;
    color:white;
    border:none;
    cursor:pointer;
    font-weight:bold;
}

button:hover{
    background:#1f5e7a;
}

button:disabled{
    background:#94a3b8;
    cursor:not-allowed;
}

input:disabled,
select:disabled{
    background:#f1f5f9;
}

#preview{
    width:100%;
    border-radius:12px;
    margin-top:10px;
}

.loading{
    display:none;
    margin-top:15px;
    color:#1e4668;
    font-weight:bold;
    text-align:center;
}

.status{
    margin-top:12px;
    font-weight:bold;
}

.success{
    color:#1f8a4c;
}

.error{
    color:#c0392b;
}

&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="card"&gt;

&lt;h2&gt;&#128220; Generator Sertifikat&lt;/h2&gt;

&lt;!-- ================= KONFIRMASI ================= --&gt;

&lt;div class="confirm-box"&gt;

&lt;p&gt;&#128269; Apakah Anda mengikuti pelatihan?&lt;/p&gt;

&lt;div class="confirm-buttons"&gt;

&lt;button class="yes-btn" onclick="confirmIkut()"&gt;
✅ YA, IKUT
&lt;/button&gt;

&lt;button class="no-btn" onclick="confirmTidakIkut()"&gt;
❌ TIDAK
&lt;/button&gt;

&lt;/div&gt;

&lt;div id="statusKonfirmasi" class="status"&gt;&lt;/div&gt;

&lt;/div&gt;

&lt;!-- ================= FORM ================= --&gt;

&lt;label&gt;Nama Lengkap&lt;/label&gt;

&lt;input
type="text"
id="nama"
placeholder="Contoh: Ahmad Fauzi"
disabled&gt;

&lt;label&gt;Peran&lt;/label&gt;

&lt;select id="peran" disabled&gt;
    &lt;option&gt;Peserta&lt;/option&gt;
    &lt;option&gt;Narasumber&lt;/option&gt;
&lt;/select&gt;

&lt;img
id="preview"
src="https://lh3.googleusercontent.com/d/1od7VHGPdspwjI9tuBmq7HzKtK529ygyc"
alt="Template"&gt;

&lt;button
id="downloadBtn"
onclick="generatePDF()"
disabled&gt;

⬇️ UNDUH PDF

&lt;/button&gt;

&lt;div class="loading" id="loading"&gt;
Memproses PDF...
&lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;

// ================= TEMPLATE (DIUBAH) =================

const FILE_ID =
'1od7VHGPdspwjI9tuBmq7HzKtK529ygyc';  // &lt;-- ID baru

const TEMPLATE_URL =
`https://lh3.googleusercontent.com/d/${FILE_ID}`;

// ================= UTIL =================

function showLoading(show){

    document.getElementById('loading').style.display =
        show ? 'block' : 'none';

}

function getRomanMonth(monthIndex){

    const romawi = [
        "I","II","III","IV","V","VI",
        "VII","VIII","IX","X","XI","XII"
    ];

    return romawi[monthIndex];

}

function capitalizeEachWord(text){

    return text
        .toLowerCase()
        .replace(/\b\w/g,c=&gt;c.toUpperCase());

}

// ================= KONFIRMASI =================

function confirmIkut(){

    document.getElementById('nama').disabled = false;

    document.getElementById('peran').disabled = false;

    document.getElementById('downloadBtn').disabled = false;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="success"&gt;✅ Konfirmasi diterima. Anda dapat mengunduh sertifikat.&lt;/span&gt;';

}

function confirmTidakIkut(){

    document.getElementById('nama').disabled = true;

    document.getElementById('peran').disabled = true;

    document.getElementById('downloadBtn').disabled = true;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="error"&gt;❌ Anda tidak dapat mengunduh sertifikat.&lt;/span&gt;';

}

// ================= LOAD IMAGE =================

function loadImage(url){

    return new Promise((resolve,reject)=&gt;{

        const img = new Image();

        img.crossOrigin = "anonymous";

        img.onload = ()=&gt;resolve(img);

        img.onerror = ()=&gt;{

            reject(
                new Error(
                    'Template Google Drive gagal dimuat'
                )
            );

        };

        img.src = url + '?t=' + Date.now();

    });

}

// ================= NOMOR =================

async function generateNomor(){

    const now = new Date();

    const bulan =
        getRomanMonth(now.getMonth());

    const tahun =
        now.getFullYear();

    const nomor =
        Math.floor(Math.random()*900)+100;

    return `${nomor}/K/${bulan}/${tahun}`;

}

// ================= PDF =================

async function generatePDF(){

    const namaInput =
        document.getElementById('nama');

    const peranSelect =
        document.getElementById('peran');

    let nama =
        namaInput.value.trim();

    const peran =
        peranSelect.value;

    if(!nama){

        alert('Nama wajib diisi');

        return;

    }

    nama =
        capitalizeEachWord(nama);

    showLoading(true);

    try{

        await document.fonts.ready;

        const img =
            await loadImage(TEMPLATE_URL);

        const canvas =
            document.createElement('canvas');

        canvas.width = img.width;
        canvas.height = img.height;

        const ctx =
            canvas.getContext('2d');

        ctx.drawImage(
            img,
            0,
            0,
            canvas.width,
            canvas.height
        );

        // ================= POSISI =================

        const nomorY =
            canvas.height * 0.235;

        const namaY =
            canvas.height * 0.3990;

        const peranY =
            canvas.height * 0.5420;

        // ================= STYLE =================

        ctx.textAlign = 'center';

        ctx.textBaseline = 'middle';

        ctx.fillStyle = '#000';

        // nomor
        const nomorSertifikat =
            await generateNomor();

        ctx.font =
            `bold ${canvas.width*0.0125}px Montserrat`;

        ctx.fillText(
            nomorSertifikat,
            canvas.width/2,
            nomorY
        );

        // nama auto shrink
        let fontSize =
            canvas.width * 0.031;

        ctx.font =
            `700 ${fontSize}px Montserrat`;

        let textWidth =
            ctx.measureText(nama).width;

        const maxWidth =
            canvas.width - 180;

        while(
            textWidth &gt; maxWidth &amp;&amp;
            fontSize &gt; 20
        ){

            fontSize -= 2;

            ctx.font =
                `700 ${fontSize}px Montserrat`;

            textWidth =
                ctx.measureText(nama).width;

        }

        ctx.fillText(
            nama,
            canvas.width/2,
            namaY
        );

        // peran
        ctx.font =
            `bold ${canvas.width*0.024}px Montserrat`;

        ctx.fillText(
            peran,
            canvas.width/2,
            peranY
        );

        // ================= PDF =================

        const imageData =
            canvas.toDataURL(
                'image/jpeg',
                1.0
            );

        const { jsPDF } =
            window.jspdf;

        const pdf =
            new jsPDF({

                orientation:
                    canvas.width &gt; canvas.height
                    ? 'landscape'
                    : 'portrait',

                unit:'px',

                format:[
                    canvas.width,
                    canvas.height
                ]

            });

        pdf.addImage(
            imageData,
            'JPEG',
            0,
            0,
            canvas.width,
            canvas.height
        );

        const safeName =
            nama.replace(/[^a-z0-9]/gi,'_');

        pdf.save(
            `Sertifikat_${safeName}.pdf`
        );

    }catch(error){

        console.error(error);

        alert(
            '❌ Gagal membuat PDF\n\n' +
            error.message +
            '\n\nPastikan file Google Drive bersifat PUBLIC.'
        );

    }finally{

        showLoading(false);

    }

}

&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/sertifikat-kombel-23-oktober.html</link><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://lh3.googleusercontent.com/d/1od7VHGPdspwjI9tuBmq7HzKtK529ygyc=s72-c" width="72"/><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-4101479288934891844</guid><pubDate>Tue, 27 Oct 2026 07:22:13 +0000</pubDate><atom:updated>2026-10-27T14:22:13.821+07:00</atom:updated><title>SERTIFIKAT KOMBEL</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Generator Sertifikat&lt;/title&gt;

&lt;link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@700&amp;display=swap" rel="stylesheet"&gt;

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;

&lt;style&gt;

body{
    font-family:Arial,sans-serif;
    background:#eef2f7;
    padding:20px;
    max-width:700px;
    margin:auto;
}

.card{
    background:white;
    padding:24px;
    border-radius:20px;
    box-shadow:0 10px 20px rgba(0,0,0,.1);
}

h2{
    text-align:center;
    color:#1e4668;
}

.confirm-box{
    background:#f0f6ff;
    border-left:5px solid #2c7da0;
    padding:16px;
    border-radius:16px;
    margin-bottom:20px;
    text-align:center;
}

.confirm-box p{
    margin-top:0;
    font-weight:bold;
    color:#1e4668;
}

.confirm-buttons{
    display:flex;
    gap:10px;
    margin-top:10px;
}

.confirm-buttons button{
    flex:1;
}

.yes-btn{
    background:#1f8a4c;
}

.yes-btn:hover{
    background:#166638;
}

.no-btn{
    background:#c0392b;
}

.no-btn:hover{
    background:#962d22;
}

input,select,button{
    width:100%;
    padding:12px;
    margin-top:10px;
    border-radius:12px;
    border:1px solid #ccc;
    font-size:16px;
}

button{
    background:#2c7da0;
    color:white;
    border:none;
    cursor:pointer;
    font-weight:bold;
}

button:hover{
    background:#1f5e7a;
}

button:disabled{
    background:#94a3b8;
    cursor:not-allowed;
}

input:disabled,
select:disabled{
    background:#f1f5f9;
}

#preview{
    width:100%;
    border-radius:12px;
    margin-top:10px;
}

.loading{
    display:none;
    margin-top:15px;
    color:#1e4668;
    font-weight:bold;
    text-align:center;
}

.status{
    margin-top:12px;
    font-weight:bold;
}

.success{
    color:#1f8a4c;
}

.error{
    color:#c0392b;
}

&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="card"&gt;

&lt;h2&gt;&#128220; Generator Sertifikat&lt;/h2&gt;

&lt;!-- ================= KONFIRMASI ================= --&gt;

&lt;div class="confirm-box"&gt;

&lt;p&gt;&#128269; Apakah Anda mengikuti pelatihan?&lt;/p&gt;

&lt;div class="confirm-buttons"&gt;

&lt;button class="yes-btn" onclick="confirmIkut()"&gt;
✅ YA, IKUT
&lt;/button&gt;

&lt;button class="no-btn" onclick="confirmTidakIkut()"&gt;
❌ TIDAK
&lt;/button&gt;

&lt;/div&gt;

&lt;div id="statusKonfirmasi" class="status"&gt;&lt;/div&gt;

&lt;/div&gt;

&lt;!-- ================= FORM ================= --&gt;

&lt;label&gt;Nama Lengkap&lt;/label&gt;

&lt;input
type="text"
id="nama"
placeholder="Contoh: Ahmad Fauzi"
disabled&gt;

&lt;label&gt;Peran&lt;/label&gt;

&lt;select id="peran" disabled&gt;
    &lt;option&gt;Peserta&lt;/option&gt;
    &lt;option&gt;Narasumber&lt;/option&gt;
&lt;/select&gt;

&lt;img
id="preview"
src="https://lh3.googleusercontent.com/d/17Xjr9jNkV3LkZAENBKA_SkFUI-v-obFM"
alt="Template"&gt;

&lt;button
id="downloadBtn"
onclick="generatePDF()"
disabled&gt;

⬇️ UNDUH PDF

&lt;/button&gt;

&lt;div class="loading" id="loading"&gt;
Memproses PDF...
&lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;

// ================= TEMPLATE (DIUBAH) =================

const FILE_ID =
'17Xjr9jNkV3LkZAENBKA_SkFUI-v-obFM';  // &lt;-- ID baru

const TEMPLATE_URL =
`https://lh3.googleusercontent.com/d/${FILE_ID}`;

// ================= UTIL =================

function showLoading(show){

    document.getElementById('loading').style.display =
        show ? 'block' : 'none';

}

function getRomanMonth(monthIndex){

    const romawi = [
        "I","II","III","IV","V","VI",
        "VII","VIII","IX","X","XI","XII"
    ];

    return romawi[monthIndex];

}

function capitalizeEachWord(text){

    return text
        .toLowerCase()
        .replace(/\b\w/g,c=&gt;c.toUpperCase());

}

// ================= KONFIRMASI =================

function confirmIkut(){

    document.getElementById('nama').disabled = false;

    document.getElementById('peran').disabled = false;

    document.getElementById('downloadBtn').disabled = false;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="success"&gt;✅ Konfirmasi diterima. Anda dapat mengunduh sertifikat.&lt;/span&gt;';

}

function confirmTidakIkut(){

    document.getElementById('nama').disabled = true;

    document.getElementById('peran').disabled = true;

    document.getElementById('downloadBtn').disabled = true;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="error"&gt;❌ Anda tidak dapat mengunduh sertifikat.&lt;/span&gt;';

}

// ================= LOAD IMAGE =================

function loadImage(url){

    return new Promise((resolve,reject)=&gt;{

        const img = new Image();

        img.crossOrigin = "anonymous";

        img.onload = ()=&gt;resolve(img);

        img.onerror = ()=&gt;{

            reject(
                new Error(
                    'Template Google Drive gagal dimuat'
                )
            );

        };

        img.src = url + '?t=' + Date.now();

    });

}

// ================= NOMOR =================

async function generateNomor(){

    const now = new Date();

    const bulan =
        getRomanMonth(now.getMonth());

    const tahun =
        now.getFullYear();

    const nomor =
        Math.floor(Math.random()*900)+100;

    return `${nomor}/K/${bulan}/${tahun}`;

}

// ================= PDF =================

async function generatePDF(){

    const namaInput =
        document.getElementById('nama');

    const peranSelect =
        document.getElementById('peran');

    let nama =
        namaInput.value.trim();

    const peran =
        peranSelect.value;

    if(!nama){

        alert('Nama wajib diisi');

        return;

    }

    nama =
        capitalizeEachWord(nama);

    showLoading(true);

    try{

        await document.fonts.ready;

        const img =
            await loadImage(TEMPLATE_URL);

        const canvas =
            document.createElement('canvas');

        canvas.width = img.width;
        canvas.height = img.height;

        const ctx =
            canvas.getContext('2d');

        ctx.drawImage(
            img,
            0,
            0,
            canvas.width,
            canvas.height
        );

        // ================= POSISI =================

        const nomorY =
            canvas.height * 0.235;

        const namaY =
            canvas.height * 0.3990;

        const peranY =
            canvas.height * 0.5420;

        // ================= STYLE =================

        ctx.textAlign = 'center';

        ctx.textBaseline = 'middle';

        ctx.fillStyle = '#000';

        // nomor
        const nomorSertifikat =
            await generateNomor();

        ctx.font =
            `bold ${canvas.width*0.0125}px Montserrat`;

        ctx.fillText(
            nomorSertifikat,
            canvas.width/2,
            nomorY
        );

        // nama auto shrink
        let fontSize =
            canvas.width * 0.031;

        ctx.font =
            `700 ${fontSize}px Montserrat`;

        let textWidth =
            ctx.measureText(nama).width;

        const maxWidth =
            canvas.width - 180;

        while(
            textWidth &gt; maxWidth &amp;&amp;
            fontSize &gt; 20
        ){

            fontSize -= 2;

            ctx.font =
                `700 ${fontSize}px Montserrat`;

            textWidth =
                ctx.measureText(nama).width;

        }

        ctx.fillText(
            nama,
            canvas.width/2,
            namaY
        );

        // peran
        ctx.font =
            `bold ${canvas.width*0.024}px Montserrat`;

        ctx.fillText(
            peran,
            canvas.width/2,
            peranY
        );

        // ================= PDF =================

        const imageData =
            canvas.toDataURL(
                'image/jpeg',
                1.0
            );

        const { jsPDF } =
            window.jspdf;

        const pdf =
            new jsPDF({

                orientation:
                    canvas.width &gt; canvas.height
                    ? 'landscape'
                    : 'portrait',

                unit:'px',

                format:[
                    canvas.width,
                    canvas.height
                ]

            });

        pdf.addImage(
            imageData,
            'JPEG',
            0,
            0,
            canvas.width,
            canvas.height
        );

        const safeName =
            nama.replace(/[^a-z0-9]/gi,'_');

        pdf.save(
            `Sertifikat_${safeName}.pdf`
        );

    }catch(error){

        console.error(error);

        alert(
            '❌ Gagal membuat PDF\n\n' +
            error.message +
            '\n\nPastikan file Google Drive bersifat PUBLIC.'
        );

    }finally{

        showLoading(false);

    }

}

&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/sertifikat-kombel.html</link><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://lh3.googleusercontent.com/d/17Xjr9jNkV3LkZAENBKA_SkFUI-v-obFM=s72-c" width="72"/><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-6963411026500157864</guid><pubDate>Thu, 22 Oct 2026 04:50:01 +0000</pubDate><atom:updated>2026-10-22T11:50:01.339+07:00</atom:updated><title>Menyajikan Teks Ceramah secara Lisan dan Rangkuman</title><description>&lt;style&gt;
.kbl{--ink:#14213d;--mut:#475569;--pri:#0f766e;--pri2:#134e4a;--acc:#b45309;--line:#cbd5e1;--card:#ffffff;font-family:Segoe UI,Roboto,Arial,sans-serif;color:var(--ink);line-height:1.7;max-width:900px;margin:0 auto}
.kbl *{box-sizing:border-box}
.kbl .hero{background:linear-gradient(135deg,#134e4a,#0f766e 55%,#0891b2);color:#fff;border-radius:18px;padding:26px 22px;margin-bottom:14px}
.kbl .hero h2{margin:0 0 6px;font-size:1.55em;color:#fff;border:0}
.kbl .hero p{margin:3px 0;color:#ccfbf1}
.kbl .nav{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}
.kbl .nav a{background:#fff;border:2px solid var(--pri);color:var(--pri2);border-radius:999px;padding:5px 14px;font-weight:700;font-size:.9em;text-decoration:none}
.kbl .nav a:hover{background:var(--pri);color:#fff}
.kbl section{background:var(--card);border:2px solid var(--line);border-radius:16px;padding:18px 20px;margin-bottom:16px}
.kbl h3{margin:0 0 10px;color:var(--pri2);font-size:1.25em;border-bottom:3px solid #99f6e4;padding-bottom:6px}
.kbl h4{margin:16px 0 6px;color:var(--acc);font-size:1.05em}
.kbl p{margin:6px 0}
.kbl ul,.kbl ol{margin:6px 0;padding-left:22px}
.kbl li{margin:3px 0}
.kbl .box{background:#f0fdfa;border-left:6px solid var(--pri);border-radius:10px;padding:10px 14px;margin:10px 0}
.kbl .tip{background:#fffbeb;border-left:6px solid #f59e0b;border-radius:10px;padding:10px 14px;margin:10px 0}
.kbl .naskah{background:#f8fafc;border:2px dashed #94a3b8;border-radius:12px;padding:12px 16px;font-family:Georgia,serif;margin:10px 0}
.kbl table{border-collapse:collapse;width:100%;margin:10px 0;font-size:.95em}
.kbl th{background:var(--pri2);color:#fff;text-align:left;padding:8px 10px}
.kbl td{border:1px solid var(--line);padding:7px 10px;vertical-align:top}
.kbl tr:nth-child(even) td{background:#f8fafc}
.kbl .tw{overflow-x:auto}
.kbl label.f{display:block;font-weight:700;margin:10px 0 4px}
.kbl input.t,.kbl textarea.t{width:100%;padding:11px 12px;border:2px solid #94a3b8;border-radius:10px;font-size:1em;color:var(--ink);background:#fff;font-family:inherit}
.kbl input.t:focus,.kbl textarea.t:focus{outline:3px solid #5eead4;border-color:var(--pri)}
.kbl textarea.t{min-height:300px;line-height:1.6}
.kbl .row{display:grid;grid-template-columns:2fr 1fr 1fr;gap:10px}
.kbl button.b{background:var(--pri);color:#fff;border:0;border-radius:12px;padding:12px 20px;font-size:1em;font-weight:700;cursor:pointer;margin:10px 8px 0 0}
.kbl button.b:hover{background:var(--pri2)}
.kbl button.b:disabled{background:#94a3b8;cursor:not-allowed}
.kbl button.g{background:#fff;color:var(--pri2);border:2px solid var(--pri)}
.kbl .clock{font-size:3em;font-weight:800;text-align:center;font-variant-numeric:tabular-nums;padding:8px;border-radius:14px;background:#f1f5f9;border:3px solid #94a3b8}
.kbl .clock.ok{background:#dcfce7;border-color:#16a34a;color:#166534}
.kbl .clock.hi{background:#fee2e2;border-color:#dc2626;color:#991b1b}
.kbl .qq{border:2px solid var(--line);border-radius:12px;padding:10px 14px;margin:10px 0}
.kbl .qq label{display:block;padding:6px 8px;border-radius:8px;cursor:pointer}
.kbl .qq label:hover{background:#ecfeff}
.kbl .good{color:#166534;font-weight:700}.kbl .bad{color:#b91c1c;font-weight:700}
.kbl .pasteWarn{color:#b91c1c;font-weight:700;display:none;margin-top:6px}
.kbl .msg{margin-top:10px;font-weight:700}.kbl .msg.e{color:#b91c1c}
.kbl .score{font-size:2.6em;font-weight:800;color:var(--pri2)}
.kbl .meta{color:var(--mut);font-size:.92em}
@media(max-width:640px){.kbl .row{grid-template-columns:1fr}.kbl .clock{font-size:2.4em}}

#kblPrint{display:none;font-family:Arial,sans-serif;color:#000;font-size:10.5pt}
#kblPrint h1{font-size:14pt;text-align:center;margin:0 0 2px}
#kblPrint h2{font-size:11pt;margin:10px 0 4px}
#kblPrint table{border-collapse:collapse;width:100%;margin-bottom:6px}
#kblPrint td,#kblPrint th{border:1px solid #000;padding:3px 6px;font-size:10pt;text-align:left;vertical-align:top}
#kblPrint .nas{border:1px solid #000;padding:8px;white-space:pre-wrap;font-size:10pt;line-height:1.45}
#kblPrint .sig{display:flex;justify-content:space-between;margin-top:16px;page-break-inside:avoid}
#kblPrint .pb{page-break-before:always}
@media print{@page{size:215mm 330mm;margin:12mm}body.kbl-printing&gt;*:not(#kblPrint){display:none!important}body.kbl-printing #kblPrint{display:block!important}}

.kh{--ink:#14213d;--mut:#475569;--pri:#6d28d9;--pri2:#4c1d95;--line:#cbd5e1;font-family:Segoe UI,Roboto,Arial,sans-serif;color:var(--ink);line-height:1.7;max-width:900px;margin:0 auto}
.kh *{box-sizing:border-box}
.kh .hero{background:linear-gradient(135deg,#4c1d95,#6d28d9 60%,#db2777);color:#fff;border-radius:18px;padding:24px 22px;margin-bottom:14px}
.kh .hero h2{margin:0 0 6px;font-size:1.5em;color:#fff;border:0}
.kh .hero p{margin:3px 0;color:#ede9fe}
.kh .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px}
.kh .stat{background:#fff;border:2px solid var(--line);border-radius:14px;padding:10px;text-align:center}
.kh .stat b{display:block;font-size:1.5em;color:var(--pri2)}
.kh .stat span{font-size:.85em;color:var(--mut)}
.kh .card{background:#fff;border:2px solid var(--line);border-radius:14px;padding:14px 18px;margin-bottom:14px}
.kh .card h3{margin:0 0 8px;color:var(--pri2);font-size:1.12em}
.kh .card ul{margin:0;padding-left:20px}
.kh .stim{background:#faf5ff;border:2px solid #c084fc;border-radius:14px;padding:12px 16px;margin:18px 0 8px}
.kh .stim h4{margin:0 0 6px;color:var(--pri2)}
.kh .stim p{margin:5px 0}
.kh .q{background:#fff;border:2px solid var(--line);border-radius:14px;padding:12px 16px;margin:10px 0}
.kh .qt{font-weight:700;margin-bottom:6px}
.kh .lv{display:inline-block;background:#ede9fe;color:var(--pri2);border-radius:999px;padding:1px 10px;font-size:.78em;font-weight:700;margin-left:6px}
.kh .o{display:flex;gap:10px;align-items:flex-start;padding:8px 10px;border:2px solid #e2e8f0;border-radius:10px;margin:6px 0;cursor:pointer;background:#f8fafc}
.kh .o:hover{border-color:#a78bfa;background:#f5f3ff}
.kh .o input{margin-top:6px;flex:none}
.kh .o.sel{border-color:var(--pri);background:#ede9fe}
.kh label.f{display:block;font-weight:700;margin:10px 0 4px}
.kh input.t,.kh textarea.t{width:100%;padding:11px 12px;border:2px solid #94a3b8;border-radius:10px;font-size:1em;color:var(--ink);background:#fff;font-family:inherit}
.kh input.t:focus,.kh textarea.t:focus{outline:3px solid #c4b5fd;border-color:var(--pri)}
.kh textarea.t{min-height:150px}
.kh .row{display:grid;grid-template-columns:2fr 1fr 1fr;gap:10px}
.kh button.b{background:var(--pri);color:#fff;border:0;border-radius:12px;padding:13px 22px;font-size:1em;font-weight:700;cursor:pointer;margin:12px 8px 0 0}
.kh button.b:hover{background:var(--pri2)}
.kh button.b:disabled{background:#94a3b8;cursor:not-allowed}
.kh .pw{color:#b91c1c;font-weight:700;display:none;margin-top:4px}
.kh .meta{color:var(--mut);font-size:.9em}
.kh .msg{margin-top:10px;font-weight:700}.kh .msg.e{color:#b91c1c}
.kh .score{font-size:2.8em;font-weight:800;color:var(--pri2)}
.kh .res{background:#f5f3ff;border:2px solid #a78bfa;border-radius:16px;padding:18px;margin-top:16px}
.kh table{border-collapse:collapse;width:100%;font-size:.93em;margin:8px 0}
.kh th{background:var(--pri2);color:#fff;text-align:left;padding:6px 8px}
.kh td{border:1px solid var(--line);padding:6px 8px;vertical-align:top}
.kh .good{color:#166534;font-weight:700}.kh .bad{color:#b91c1c;font-weight:700}
@media(max-width:640px){.kh .grid{grid-template-columns:repeat(2,1fr)}.kh .row{grid-template-columns:1fr}}
#khPrint{display:none;font-family:Arial,sans-serif;color:#000;font-size:10.5pt}
#khPrint h1{font-size:14pt;text-align:center;margin:0 0 2px}
#khPrint h2{font-size:11pt;margin:10px 0 4px}
#khPrint table{border-collapse:collapse;width:100%;margin-bottom:6px}
#khPrint td,#khPrint th{border:1px solid #000;padding:3px 6px;font-size:10pt;text-align:left;vertical-align:top}
#khPrint .nas{border:1px solid #000;padding:6px;white-space:pre-wrap;margin-bottom:6px;font-size:10pt}
#khPrint .sig{display:flex;justify-content:space-between;margin-top:16px;page-break-inside:avoid}
@media print{@page{size:215mm 330mm;margin:12mm}body.kh-printing&gt;*:not(#khPrint){display:none!important}body.kh-printing #khPrint{display:block!important}}
&lt;/style&gt;

&lt;div class="kbl" id="kbl"&gt;
&lt;div class="hero"&gt;
  &lt;h2&gt;Menyajikan Teks Ceramah secara Lisan dan Rangkuman&lt;/h2&gt;
  &lt;p&gt;Bahasa Indonesia Kelas XI SMK/MAK | Elemen Berbicara dan Mempresentasikan | 3 JP&lt;/p&gt;
  &lt;p&gt;&lt;b&gt;Tujuan:&lt;/b&gt; murid mampu menyajikan teks ceramah secara lisan dengan teknik berbicara, intonasi, dan sikap santun, serta mereview seluruh materi ceramah.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="nav"&gt;
  &lt;a href="#kbl-a"&gt;Konsep&lt;/a&gt;&lt;a href="#kbl-b"&gt;Teknik Berbicara&lt;/a&gt;&lt;a href="#kbl-c"&gt;Intonasi&lt;/a&gt;&lt;a href="#kbl-d"&gt;Sikap Santun&lt;/a&gt;&lt;a href="#kbl-e"&gt;Contoh Naskah&lt;/a&gt;&lt;a href="#kbl-f"&gt;Rangkuman&lt;/a&gt;&lt;a href="#kbl-g"&gt;Tugas Individu&lt;/a&gt;
&lt;/div&gt;

&lt;section id="kbl-a"&gt;
&lt;h3&gt;A. Hakikat Menyajikan Ceramah secara Lisan&lt;/h3&gt;
&lt;p&gt;Ceramah adalah teks yang &lt;b&gt;ditulis untuk didengar&lt;/b&gt;. Naskah yang bagus belum tentu menjadi ceramah yang bagus jika penyampaiannya datar, terlalu cepat, atau tidak menyentuh pendengar. Karena itu, penyajian lisan adalah gabungan tiga hal: &lt;b&gt;isi&lt;/b&gt; (gagasan, data, struktur), &lt;b&gt;bahasa&lt;/b&gt; (kaidah kebahasaan ceramah), dan &lt;b&gt;penampilan&lt;/b&gt; (suara, ekspresi, sikap).&lt;/p&gt;
&lt;div class="box"&gt;&lt;b&gt;Perbedaan membaca dan berceramah.&lt;/b&gt; Membaca naskah berarti mata terpaku pada kertas. Berceramah berarti penceramah memahami gagasannya, memakai naskah hanya sebagai pegangan, dan terus menjalin kontak dengan hadirin.&lt;/div&gt;
&lt;h4&gt;Tiga cara menyampaikan ceramah&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Cara&lt;/th&gt;&lt;th&gt;Ciri&lt;/th&gt;&lt;th&gt;Kelebihan&lt;/th&gt;&lt;th&gt;Kekurangan&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Membaca naskah&lt;/td&gt;&lt;td&gt;Seluruh teks dibaca&lt;/td&gt;&lt;td&gt;Isi tidak terlewat, aman bagi pemula&lt;/td&gt;&lt;td&gt;Kontak mata kurang, suara cenderung datar&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Menghafal&lt;/td&gt;&lt;td&gt;Naskah diingat kata demi kata&lt;/td&gt;&lt;td&gt;Mata bebas menatap hadirin&lt;/td&gt;&lt;td&gt;Mudah lupa, terdengar kaku, panik jika tersendat&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Garis besar (poin)&lt;/td&gt;&lt;td&gt;Hanya kerangka dan kata kunci yang dipegang&lt;/td&gt;&lt;td&gt;Alami, luwes, interaktif&lt;/td&gt;&lt;td&gt;Perlu banyak latihan dan penguasaan materi&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;b&gt;Anjuran untuk siswa SMK:&lt;/b&gt; tulis naskah lengkap, latih berulang, lalu pegang catatan poin berisi salam, tiga gagasan pokok, data penting, ajakan, dan penutup.&lt;/p&gt;

&lt;h4&gt;Tahapan persiapan&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;Kenali pendengar dan tujuan.&lt;/b&gt; Siapa hadirin (teman sekelas, guru, calon dunia usaha)? Apa tujuannya: menginformasikan, menyadarkan, atau mengajak?&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Susun naskah&lt;/b&gt; dengan struktur pembukaan, isi, penutup, lengkap dengan kaidah kebahasaan.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Beri penanda lisan&lt;/b&gt; pada naskah: jeda, tekanan, dan bagian yang dibaca pelan atau cepat.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Berlatih&lt;/b&gt; bersuara nyaring, ukur waktu, rekam diri bila memungkinkan, lalu perbaiki.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Siapkan diri&lt;/b&gt;: busana rapi, kondisi tubuh bugar, cek tempat dan peralatan.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tampil&lt;/b&gt; dengan tenang dan akhiri dengan baik.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Refleksi&lt;/b&gt;: terima masukan dan catat yang perlu diperbaiki.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="tip"&gt;&lt;b&gt;Patokan waktu.&lt;/b&gt; Kecepatan wajar berceramah sekitar 120 sampai 150 kata per menit. Ceramah 2 sampai 3 menit berarti sekitar 260 sampai 450 kata.&lt;/div&gt;
&lt;/section&gt;

&lt;section id="kbl-b"&gt;
&lt;h3&gt;B. Teknik Berbicara&lt;/h3&gt;
&lt;h4&gt;1. Teknik vokal (suara)&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Unsur&lt;/th&gt;&lt;th&gt;Penjelasan&lt;/th&gt;&lt;th&gt;Latihan&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Volume&lt;/td&gt;&lt;td&gt;Kekuatan suara. Cukup agar terdengar sampai deretan paling belakang, tanpa berteriak.&lt;/td&gt;&lt;td&gt;Mintalah teman di sudut ruangan memberi isyarat jempol jika suara terdengar jelas.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Artikulasi&lt;/td&gt;&lt;td&gt;Kejelasan pengucapan huruf dan suku kata. Hindari menggumam atau memakan akhir kata.&lt;/td&gt;&lt;td&gt;Ucapkan tongue twister: "kuku kakak kaku kena kuku kakek".&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tempo&lt;/td&gt;&lt;td&gt;Kecepatan bicara. Terlalu cepat membuat pesan hilang, terlalu lambat membuat bosan.&lt;/td&gt;&lt;td&gt;Latih 120 sampai 150 kata per menit; lambatkan pada gagasan penting.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Jeda&lt;/td&gt;&lt;td&gt;Berhenti sejenak untuk bernapas dan memberi waktu pendengar mencerna.&lt;/td&gt;&lt;td&gt;Beri tanda / (jeda pendek) dan // (jeda panjang) pada naskah.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pernapasan&lt;/td&gt;&lt;td&gt;Napas perut menjaga suara stabil dan mengurangi gugup.&lt;/td&gt;&lt;td&gt;Tarik napas 4 hitungan, tahan 2, embuskan 6, ulangi 5 kali sebelum tampil.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pelafalan baku&lt;/td&gt;&lt;td&gt;Memakai bentuk baku dan menghindari logat atau kata gaul yang tidak pada tempatnya.&lt;/td&gt;&lt;td&gt;Ganti "gak, udah, kayak, bikin" dengan "tidak, sudah, seperti, membuat".&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;2. Teknik nonverbal (kinesik)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Kontak mata:&lt;/b&gt; sapukan pandangan ke seluruh ruangan, kiri, tengah, kanan. Jangan menatap lantai, langit-langit, atau naskah terus-menerus.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mimik wajah:&lt;/b&gt; senyum tulus saat membuka dan menutup, wajah serius saat membahas hal penting.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gestur tangan:&lt;/b&gt; gerakan terbuka dan wajar untuk menegaskan gagasan. Hindari memainkan pena, memegang baju, atau tangan di saku.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Postur:&lt;/b&gt; berdiri tegak, bahu rileks, kedua kaki selebar bahu. Hindari bersandar atau bergoyang.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Posisi dan gerak:&lt;/b&gt; berdiri di tempat yang terlihat semua hadirin; berpindah posisi secukupnya, bukan mondar-mandir.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;3. Mengatasi gugup&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Gugup itu wajar; pembicara berpengalaman pun mengalaminya. Kuncinya adalah &lt;b&gt;persiapan&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;Hafalkan pembukaan dan penutup dengan baik, sebab itu bagian yang paling menentukan kesan.&lt;/li&gt;
&lt;li&gt;Bayangkan hadirin sebagai teman yang ingin mendengar pesan Anda, bukan hakim.&lt;/li&gt;
&lt;li&gt;Jika lupa, berhenti sejenak, tarik napas, lihat catatan poin, lalu lanjutkan. Jangan meminta maaf berulang kali.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;

&lt;section id="kbl-c"&gt;
&lt;h3&gt;C. Intonasi dalam Ceramah&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Intonasi&lt;/b&gt; adalah naik turunnya nada serta tekanan dan lagu kalimat saat berbicara. Intonasi menentukan makna, suasana, dan daya persuasi. Kalimat yang sama dapat terdengar mengajak, menuntut, atau datar karena intonasinya.&lt;/p&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Unsur intonasi&lt;/th&gt;&lt;th&gt;Penjelasan&lt;/th&gt;&lt;th&gt;Contoh pemakaian dalam ceramah&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tekanan kata (aksen)&lt;/td&gt;&lt;td&gt;Menonjolkan kata kunci dengan suara lebih keras atau lebih jelas.&lt;/td&gt;&lt;td&gt;"Keselamatan adalah syarat UTAMA sebelum keterampilan."&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Nada&lt;/td&gt;&lt;td&gt;Tinggi rendahnya suara. Nada naik pada pertanyaan, turun pada pernyataan tegas.&lt;/td&gt;&lt;td&gt;"Siapa di antara kita yang tidak ingin sukses?" (nada naik di akhir)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Durasi&lt;/td&gt;&lt;td&gt;Panjang pendek pengucapan; memperpanjang kata untuk penekanan emosi.&lt;/td&gt;&lt;td&gt;"Kita harus BERSAMA-sama menjaga kebersihan."&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Jeda&lt;/td&gt;&lt;td&gt;Berhenti sejenak sebelum atau sesudah gagasan penting.&lt;/td&gt;&lt;td&gt;"Masa depan kita / ada di tangan kita sendiri."&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Lagu kalimat&lt;/td&gt;&lt;td&gt;Pola naik turun nada pada seluruh kalimat; membedakan berita, tanya, dan seru.&lt;/td&gt;&lt;td&gt;Berita turun di akhir, tanya naik di akhir, ajakan tegas dan mantap.&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;Intonasi sesuai bagian ceramah&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Pembukaan:&lt;/b&gt; hangat, ramah, tempo sedang, senyum, suara jelas.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Isi:&lt;/b&gt; variasikan nada; pelan untuk data penting, tegas untuk gagasan pokok, naik pada kalimat retoris.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ajakan:&lt;/b&gt; mantap dan meyakinkan, tempo melambat, tekanan pada kata "marilah", "hendaknya".&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Penutup:&lt;/b&gt; tenang, menurun perlahan, ucapkan terima kasih dengan tulus.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="tip"&gt;&lt;b&gt;Hindari intonasi monoton.&lt;/b&gt; Suara datar dari awal sampai akhir membuat pendengar mengantuk. Rekam suara Anda dan dengarkan: apakah ada bagian yang naik, turun, dan berjeda?&lt;/div&gt;
&lt;/section&gt;

&lt;section id="kbl-d"&gt;
&lt;h3&gt;D. Sikap Santun dalam Berceramah&lt;/h3&gt;
&lt;h4&gt;1. Sebelum tampil&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Berpakaian rapi dan sopan sesuai acara (seragam atau busana resmi).&lt;/li&gt;
&lt;li&gt;Datang lebih awal, mengecek tempat, mikrofon, dan urutan tampil.&lt;/li&gt;
&lt;li&gt;Menyapa panitia atau guru dengan sopan, dan berjalan dengan tenang menuju tempat penyajian.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. Saat tampil&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Memulai dengan salam dan sapaan yang urut dari yang paling dihormati (misalnya Bapak Kepala Sekolah, Bapak dan Ibu guru, lalu teman-teman).&lt;/li&gt;
&lt;li&gt;Memakai bahasa baku yang santun; tidak mengejek, menyinggung SARA, atau mempermalukan pihak tertentu.&lt;/li&gt;
&lt;li&gt;Menyebut data dengan jujur dan bersumber; tidak mengarang fakta.&lt;/li&gt;
&lt;li&gt;Menjaga waktu sesuai ketentuan.&lt;/li&gt;
&lt;li&gt;Mengakhiri dengan simpulan, harapan, ucapan terima kasih, dan salam penutup.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. Menghadapi tanya jawab&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Dengarkan pertanyaan sampai selesai; ucapkan terima kasih atas pertanyaannya.&lt;/li&gt;
&lt;li&gt;Jawab singkat, jelas, dan sopan; jika belum tahu, akui dengan jujur dan janjikan mencari jawabannya.&lt;/li&gt;
&lt;li&gt;Tetap tenang saat dikritik; jadikan masukan untuk perbaikan.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. Sebagai pendengar&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Memperhatikan, tidak berbicara sendiri, tidak bermain gawai.&lt;/li&gt;
&lt;li&gt;Memberi tepuk tangan, mencatat poin penting, dan memberi masukan yang membangun.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;

&lt;section id="kbl-e"&gt;
&lt;h3&gt;E. Contoh Naskah dengan Penanda Lisan&lt;/h3&gt;
&lt;p&gt;Perhatikan penanda: &lt;b&gt;/&lt;/b&gt; jeda pendek, &lt;b&gt;//&lt;/b&gt; jeda panjang, &lt;b&gt;HURUF BESAR&lt;/b&gt; tekanan kata, &lt;b&gt;(senyum)&lt;/b&gt; atau &lt;b&gt;(menatap hadirin)&lt;/b&gt; petunjuk gerak.&lt;/p&gt;
&lt;div class="naskah"&gt;
&lt;p&gt;(senyum, menatap hadirin) Assalamualaikum warahmatullahi wabarakatuh. // Yang saya hormati Bapak Kepala Sekolah, / Bapak dan Ibu guru, / serta teman-teman yang saya sayangi. //&lt;/p&gt;
&lt;p&gt;Pada kesempatan ini / izinkan saya berbicara tentang satu hal sederhana yang menentukan masa depan kita: / DISIPLIN WAKTU. //&lt;/p&gt;
&lt;p&gt;Hadirin sekalian, / disiplin waktu adalah WAJAH PERTAMA seorang pekerja. / Pekerja yang datang tepat waktu / menunjukkan bahwa ia menghargai orang lain. / Sebab, / dunia usaha sering menilai ketepatan waktu / sebelum menilai keterampilan. //&lt;/p&gt;
&lt;p&gt;Pernahkah kita membayangkan, / apa yang terjadi jika satu teknisi terlambat / dan seluruh jalur produksi menunggu? (nada naik, jeda) // Karena itu, / kebiasaan kecil seperti tiba lima menit lebih awal / sesungguhnya adalah latihan menjadi profesional. //&lt;/p&gt;
&lt;p&gt;Oleh karena itu, / MARILAH kita mulai dari diri sendiri. / Hendaknya kita bangun lebih pagi, / siapkan perlengkapan sejak malam, / dan tiba sebelum bel berbunyi. //&lt;/p&gt;
&lt;p&gt;(melambat, mantap) Kesimpulannya, / disiplin bukan beban, / melainkan bekal. // Semoga kita semua siap menjadi pekerja yang tepat waktu dan bertanggung jawab. / Terima kasih atas perhatian Bapak, Ibu, dan teman-teman. // Wassalamualaikum warahmatullahi wabarakatuh. (senyum, mengangguk)&lt;/p&gt;
&lt;/div&gt;
&lt;h4&gt;Analisis singkat&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Unsur&lt;/th&gt;&lt;th&gt;Bukti pada naskah&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pembukaan&lt;/td&gt;&lt;td&gt;Salam, sapaan berurutan, pengantar topik "Pada kesempatan ini..."&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kalimat retoris&lt;/td&gt;&lt;td&gt;"Pernahkah kita membayangkan ... ?"&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Konjungsi&lt;/td&gt;&lt;td&gt;sebab, karena itu, oleh karena itu&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kalimat ajakan&lt;/td&gt;&lt;td&gt;marilah, hendaknya&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Majas&lt;/td&gt;&lt;td&gt;"disiplin waktu adalah wajah pertama seorang pekerja" (metafora)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Simpulan, harapan, terima kasih, salam&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Teknik lisan&lt;/td&gt;&lt;td&gt;Penanda jeda, tekanan, dan petunjuk gerak&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;/section&gt;

&lt;section id="kbl-f"&gt;
&lt;h3&gt;F. Rangkuman Seluruh Materi Teks Ceramah&lt;/h3&gt;
&lt;h4&gt;1. Pengertian, ciri, dan struktur&lt;/h4&gt;
&lt;p&gt;Teks ceramah adalah teks yang memaparkan gagasan atau informasi tentang suatu topik, disampaikan lisan oleh satu penceramah kepada banyak pendengar, dengan tujuan memberi pengetahuan, menyadarkan, meyakinkan, atau mengajak. Ciri: satu arah, membahas satu topik secara sistematis, bersifat persuasif, didukung data, fakta, atau contoh.&lt;/p&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Bagian&lt;/th&gt;&lt;th&gt;Isi&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pembukaan&lt;/td&gt;&lt;td&gt;Salam, sapaan hadirin, ucapan syukur, pengantar topik&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Isi&lt;/td&gt;&lt;td&gt;Gagasan pokok dan pendukung disertai data, fakta, contoh, alasan logis, dan ajakan&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Simpulan, harapan, ucapan terima kasih, salam penutup&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;2. Gagasan pokok dan gagasan pendukung&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Pola paragraf&lt;/th&gt;&lt;th&gt;Letak gagasan pokok&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Deduktif&lt;/td&gt;&lt;td&gt;Awal paragraf, diikuti kalimat penjelas&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Induktif&lt;/td&gt;&lt;td&gt;Akhir paragraf, didahului kalimat penjelas&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Campuran&lt;/td&gt;&lt;td&gt;Awal, lalu ditegaskan kembali di akhir&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;3. Evaluasi informasi (enam kriteria)&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Kriteria&lt;/th&gt;&lt;th&gt;Pertanyaan kunci&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kredibilitas&lt;/td&gt;&lt;td&gt;Apakah penceramah ahli, berpengalaman, atau berwenang?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Keakuratan&lt;/td&gt;&lt;td&gt;Apakah data benar, dapat diverifikasi, dan bersumber jelas?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kelogisan&lt;/td&gt;&lt;td&gt;Apakah argumen masuk akal dan tidak menggeneralisasi berlebihan?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Objektivitas&lt;/td&gt;&lt;td&gt;Apakah berimbang, tidak memihak, dan bebas kepentingan tersembunyi?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kelengkapan&lt;/td&gt;&lt;td&gt;Apakah informasi cukup sehingga tidak menyesatkan?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kesesuaian tujuan&lt;/td&gt;&lt;td&gt;Apakah isi relevan dengan topik dan tujuan ceramah?&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;4. Ceramah dibandingkan dengan artikel opini&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Ceramah&lt;/th&gt;&lt;th&gt;Artikel opini&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Media&lt;/td&gt;&lt;td&gt;Lisan&lt;/td&gt;&lt;td&gt;Tulisan&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Interaksi&lt;/td&gt;&lt;td&gt;Langsung, ada tanya jawab&lt;/td&gt;&lt;td&gt;Satu arah, dibaca sendiri&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Sapaan&lt;/td&gt;&lt;td&gt;Sering memakai kita, kami, hadirin&lt;/td&gt;&lt;td&gt;Lebih objektif, jarang menyapa langsung&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kelebihan&lt;/td&gt;&lt;td&gt;Menyentuh emosi lewat intonasi dan ekspresi&lt;/td&gt;&lt;td&gt;Dapat dibaca ulang, tersebar luas, berbasis data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kekurangan&lt;/td&gt;&lt;td&gt;Sulit diulang kecuali direkam&lt;/td&gt;&lt;td&gt;Kurang interaktif dan emosional&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Persamaan: sama-sama persuasif, berargumen dengan fakta atau data, bersusunan pembuka-isi-penutup, dan membahas satu isu secara terfokus.&lt;/p&gt;
&lt;h4&gt;5. Kaidah kebahasaan&lt;/h4&gt;
&lt;div class="tw"&gt;&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Kaidah&lt;/th&gt;&lt;th&gt;Penanda dan contoh&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kata sapaan dan salam&lt;/td&gt;&lt;td&gt;Disusun dari yang paling dihormati: "Bapak dan Ibu guru yang saya hormati"&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kata ganti orang&lt;/td&gt;&lt;td&gt;saya (penceramah), kita (penceramah + pendengar), kami (penceramah dan kelompoknya), Anda/kalian (pendengar)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kalimat persuasif/imperatif&lt;/td&gt;&lt;td&gt;mari, marilah, ayo, hendaknya, sebaiknya, janganlah, -lah; ajakan didahului alasan&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kalimat retoris&lt;/td&gt;&lt;td&gt;Pertanyaan yang tidak menuntut jawaban; 2 sampai 3 kali per ceramah&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Konjungsi&lt;/td&gt;&lt;td&gt;karena, sehingga, oleh karena itu, dengan demikian, namun, selain itu, apabila&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kata kerja mental&lt;/td&gt;&lt;td&gt;menyadari, meyakini, berpendapat, menyimpulkan; padukan dengan fakta&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kosakata baku dan istilah teknis&lt;/td&gt;&lt;td&gt;tidak, sudah, seperti, membuat; jelaskan istilah asing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Majas&lt;/td&gt;&lt;td&gt;perumpamaan, metafora, repetisi, hiperbola, personifikasi, klimaks; maksimal satu menonjol per paragraf&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kalimat efektif&lt;/td&gt;&lt;td&gt;10 sampai 20 kata, satu gagasan, hindari pemborosan ("agar supaya", "demi untuk")&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;6. Menulis ceramah (rumus P-B-P-A)&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;Pernyataan&lt;/b&gt; (gagasan pokok), &lt;b&gt;Bukti&lt;/b&gt; (data atau contoh), &lt;b&gt;Penjelasan&lt;/b&gt; (hubungan bukti dengan gagasan), &lt;b&gt;Ajakan atau penghubung&lt;/b&gt; ke paragraf berikutnya. Langkah: rumuskan topik-tujuan-pendengar, kumpulkan bahan, susun kerangka, kembangkan dengan P-B-P-A, lalu sunting dan latih.&lt;/p&gt;
&lt;h4&gt;7. Menyajikan ceramah secara lisan&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Teknik vokal: volume, artikulasi, tempo, jeda, pernapasan, pelafalan baku.&lt;/li&gt;
&lt;li&gt;Nonverbal: kontak mata, mimik, gestur, postur.&lt;/li&gt;
&lt;li&gt;Intonasi: tekanan, nada, durasi, jeda, lagu kalimat.&lt;/li&gt;
&lt;li&gt;Sikap santun: busana rapi, salam dan sapaan, bahasa baku, jujur, menghargai waktu, menerima tanya jawab.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;

&lt;section id="kbl-g"&gt;
&lt;h3&gt;G. Tugas Individu HOTS: Menyajikan Teks Ceramah secara Lisan&lt;/h3&gt;
&lt;p&gt;Tugas individu berikut berupa soal berpikir tingkat tinggi (HOTS) yang menguji kemampuan Anda &lt;b&gt;menganalisis, mengevaluasi, dan merancang&lt;/b&gt; penyajian ceramah secara lisan. Gunakan seluruh materi A sampai F sebagai bekal.&lt;/p&gt;
&lt;div class="kh" id="kh"&gt;
&lt;div class="grid"&gt;
  &lt;div class="stat"&gt;&lt;b&gt;15&lt;/b&gt;&lt;span&gt;PG berbasis kasus&lt;/span&gt;&lt;/div&gt;
  &lt;div class="stat"&gt;&lt;b&gt;5&lt;/b&gt;&lt;span&gt;Esai HOTS&lt;/span&gt;&lt;/div&gt;
  &lt;div class="stat"&gt;&lt;b&gt;70:30&lt;/b&gt;&lt;span&gt;Bobot PG : Esai&lt;/span&gt;&lt;/div&gt;
  &lt;div class="stat"&gt;&lt;b&gt;75&lt;/b&gt;&lt;span&gt;KKM&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
  &lt;h3&gt;Petunjuk&lt;/h3&gt;
  &lt;ul&gt;
    &lt;li&gt;Bacalah setiap kasus dengan cermat. Jawaban tidak ada secara langsung di teks, Anda harus menganalisis, menilai, dan merancang.&lt;/li&gt;
    &lt;li&gt;Tingkat kognitif setiap soal tertera pada label C4 (menganalisis), C5 (mengevaluasi), dan C6 (mencipta).&lt;/li&gt;
    &lt;li&gt;Jawaban esai wajib &lt;b&gt;diketik sendiri&lt;/b&gt;. Fitur tempel (paste) dinonaktifkan. Esai dinilai otomatis berdasarkan cakupan konsep kunci dan kelengkapan argumen.&lt;/li&gt;
    &lt;li&gt;Nilai = PG x 70% + Esai x 30%. Hasil dikirim ke guru, lalu &lt;b&gt;unduh, cetak, dan serahkan bukti PDF&lt;/b&gt; kepada guru.&lt;/li&gt;
    &lt;li&gt;Urutan soal dan pilihan jawaban diacak untuk tiap siswa.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
  &lt;h3&gt;Identitas&lt;/h3&gt;
  &lt;div class="row"&gt;
    &lt;div&gt;&lt;label class="f" for="khNama"&gt;Nama lengkap&lt;/label&gt;&lt;input class="t" id="khNama" type="text" autocomplete="off"&gt;&lt;/div&gt;
    &lt;div&gt;&lt;label class="f" for="khKelas"&gt;Kelas&lt;/label&gt;&lt;input class="t" id="khKelas" type="text" autocomplete="off" placeholder="XI TKJ 1"&gt;&lt;/div&gt;
    &lt;div&gt;&lt;label class="f" for="khAbsen"&gt;No. Absen&lt;/label&gt;&lt;input class="t" id="khAbsen" type="number" min="1" max="99"&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div id="khBody"&gt;&lt;/div&gt;
&lt;button class="b" id="khSend" type="button"&gt;Kirim Jawaban&lt;/button&gt;
&lt;div class="msg" id="khMsg"&gt;&lt;/div&gt;
&lt;div id="khRes"&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;/section&gt;
&lt;/div&gt;

&lt;script&gt;
(function(){
var GAS_URL="https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
var MATERI="Tugas Individu HOTS - Menyajikan Teks Ceramah secara Lisan", KKM=75, LET=["A","B","C","D","E"];
function $(i){return document.getElementById(i);}
function esc(s){return String(s).replace(/&amp;/g,"&amp;amp;").replace(/&lt;/g,"&amp;lt;").replace(/&gt;/g,"&amp;gt;").replace(/"/g,"&amp;quot;");}
function shuffle(a){for(var i=a.length-1;i&gt;0;i--){var j=Math.floor(Math.random()*(i+1));var t=a[i];a[i]=a[j];a[j]=t;}return a;}

/* PG: [level, soal, benar, s1, s2, s3, s4] */
var G=[
{t:"Kasus 1: Penampilan Raka",p:[
"Raka tampil berceramah tentang bahaya berita bohong selama 3 menit. Isi ceramahnya baik: ia memakai data survei, memiliki tiga gagasan pokok, dan menutup dengan simpulan. Namun, selama tampil ia membaca naskah dari kertas tanpa menatap hadirin, bersuara pelan sehingga deretan belakang tidak mendengar, berbicara sangat cepat tanpa jeda (sekitar 200 kata per menit), memasukkan satu tangan ke saku, dan beberapa kali memakai kata 'gak' dan 'udah'. Hadirin hanya mengingat sedikit isi ceramahnya."],
q:[
["C4","Penyebab utama hadirin hanya mengingat sedikit isi ceramah Raka, meskipun isinya baik, adalah ...","kelemahan penyampaian (vokal, tempo, nonverbal) yang menghalangi pesan sampai ke pendengar","data survei yang dipakai terlalu banyak","ceramah terlalu pendek untuk tiga gagasan pokok","topik berita bohong tidak menarik bagi siswa","simpulan di akhir ceramah tidak diperlukan"],
["C5","Rencana perbaikan yang paling efektif dan realistis untuk Raka sebelum tampil berikutnya adalah ...","berlatih dengan catatan poin, melatih tempo 120-150 kata per menit dengan jeda, menaikkan volume, dan mengganti kata tidak baku","menghafal seluruh naskah kata demi kata tanpa catatan","memperpendek ceramah menjadi satu menit","mengganti topik dengan topik yang lebih ringan","memakai bahasa gaul agar terasa akrab dengan hadirin"],
["C4","Hubungan antara tempo 200 kata per menit tanpa jeda dan rendahnya daya ingat hadirin yang paling tepat adalah ...","pendengar tidak sempat mencerna data dan gagasan penting karena tidak ada jeda untuk memprosesnya","tempo cepat membuat ceramah selalu lebih persuasif","tempo cepat hanya memengaruhi kejelasan salam pembuka","tempo tidak berpengaruh pada pemahaman pendengar","jeda hanya diperlukan saat penceramah lupa naskah"]]},
{t:"Kasus 2: Dina dan Eko",p:[
"Dina berceramah dengan intonasi bervariasi, menyapa hadirin urut dari yang paling dihormati, dan memakai gestur terbuka. Akan tetapi, ia tidak menyebut satu pun data, dan ceramahnya berlangsung 5 menit padahal batas waktunya 3 menit. Eko berceramah dengan data yang lengkap dan bersumber, durasi tepat 3 menit, tetapi suaranya datar, ia tidak menyapa hadirin secara berurutan, dan menatap lantai hampir sepanjang waktu."],
q:[
["C5","Penilaian yang paling adil terhadap kedua penceramah berdasarkan enam aspek rubrik (vokal, intonasi, nonverbal, sikap santun, isi, waktu) adalah ...","keduanya memiliki kekuatan dan kelemahan pada aspek yang berbeda sehingga perlu perbaikan yang berbeda","Dina pasti lebih baik karena menarik","Eko pasti lebih baik karena memakai data","keduanya sama baik karena sama-sama tampil","keduanya tidak dapat dinilai karena temanya berbeda"],
["C4","Aspek yang paling perlu diperbaiki oleh Dina adalah ...","isi (dukungan data) dan pengelolaan waktu","intonasi dan gestur","penyebutan sapaan hormat","kemampuan menatap hadirin","volume suara"],
["C6","Jika Dina dan Eko menyusun ceramah bersama, rancangan yang paling tepat adalah ...","isi dan data dari Eko dipadukan dengan intonasi, sapaan, dan gestur Dina dalam durasi 3 menit","seluruh isi diambil dari Dina tanpa data","seluruh data dibacakan Eko dengan suara datar agar terkesan resmi","durasi diperpanjang agar semua bagian muat","sapaan dan gestur dihilangkan agar singkat"]]},
{t:"Kasus 3: Kendala saat Tampil",p:[
"Saat Sinta berceramah di aula, tiba-tiba mikrofon mati dan hadirin di barisan belakang mulai berbicara sendiri. Beberapa menit kemudian ia lupa kelanjutan paragraf kedua. Di akhir ceramah, seorang hadirin menanyakan angka statistik yang tidak ia kuasai."],
q:[
["C5","Tindakan paling tepat saat Sinta lupa kelanjutan paragraf kedua adalah ...","berhenti sejenak, menarik napas, melihat catatan poin, lalu melanjutkan dengan tenang","mengulang seluruh ceramah dari awal","meminta maaf berkali-kali dan mengakhiri ceramah","mengarang isi apa pun agar tidak diam","menyalahkan mikrofon di depan hadirin"],
["C5","Respons terbaik terhadap mikrofon yang mati dan hadirin yang ribut adalah ...","meminta bantuan panitia dengan sopan, menaikkan volume dan memperjelas artikulasi, serta menarik perhatian hadirin dengan jeda dan sapaan","berteriak sekeras mungkin tanpa jeda","menegur hadirin dengan kata-kata kasar","menghentikan ceramah dan meninggalkan panggung","membaca naskah lebih cepat agar segera selesai"],
["C6","Jawaban yang paling mencerminkan sikap santun dan kredibel atas pertanyaan statistik yang tidak dikuasai adalah ...","berterima kasih atas pertanyaannya, mengakui belum menguasai angka itu, dan berjanji mencari sumber resminya","menyebut angka perkiraan seolah-olah pasti","mengabaikan pertanyaan dan langsung menutup ceramah","mengatakan pertanyaan itu tidak penting","meminta penanya mencari sendiri tanpa penjelasan"]]},
{t:"Kasus 4: Naskah dengan Penanda Lisan",p:[
"Kutipan naskah Bayu: 'Marilah kita / TINGGALKAN kebiasaan membuang sampah sembarangan. // Sebab, / sampah yang kita buang / akan kembali kepada kita.' Tanda / berarti jeda pendek, // jeda panjang, dan huruf kapital berarti tekanan."],
q:[
["C4","Fungsi huruf kapital pada kata 'TINGGALKAN' dalam kutipan tersebut adalah ...","memberi tekanan pada kata kunci ajakan agar pendengar menangkap inti pesan","menandai kata yang harus dibaca pelan sekali","menandai kata tidak baku","menandai nama tokoh","menandai bagian yang boleh dilewati"],
["C4","Jeda panjang (//) setelah 'sembarangan' paling tepat dimaknai sebagai ...","kesempatan bagi pendengar mencerna ajakan inti sebelum alasan disampaikan","tanda penceramah lupa naskah","tanda bahwa ceramah telah selesai","tanda perpindahan topik secara tiba-tiba","tanda agar penceramah mempercepat tempo"],
["C6","Cara melisankan kalimat 'sampah yang kita buang akan kembali kepada kita' agar paling berdaya persuasi adalah ...","tempo melambat, ada jeda di tengah, nada menurun mantap pada akhir kalimat","tempo secepat mungkin dengan nada naik","dibaca datar tanpa tekanan","volume dikecilkan sampai berbisik","nada naik seperti bertanya"]]},
{t:"Kasus 5: Lembar Penilaian Guru",p:[
"Guru menilai dengan rubrik enam aspek berskala 1-4 (skor maksimal 24; nilai = skor/24 x 100). Siswa A memperoleh: vokal 3, intonasi 2, nonverbal 4, sikap santun 4, isi 3, waktu 2. Siswa B memperoleh: vokal 4, intonasi 4, nonverbal 2, sikap santun 3, isi 3, waktu 3."],
q:[
["C4","Nilai Siswa A adalah ...","75","70","80","68","85"],
["C5","Aspek yang paling mendesak dilatih Siswa A (skor terendah) adalah ...","intonasi dan pengelolaan waktu","nonverbal dan sikap santun","vokal dan isi","isi saja","sikap santun saja"],
["C5","Siswa B bernilai 79 (lebih tinggi dari Siswa A). Simpulan yang paling tepat adalah ...","nilai total yang lebih tinggi tidak berarti unggul di semua aspek; Siswa B tetap perlu melatih nonverbal","Siswa B tidak perlu berlatih lagi","Siswa A lebih baik di semua aspek","nilai total tidak berguna bagi guru","Siswa B pasti lebih unggul dalam sikap santun dan nonverbal"]]}
];
/* Esai: konsep = daftar pola regex; need = jumlah konsep minimal untuk skor penuh */
var E=[
{lv:"C4",q:"Analisislah penampilan Raka pada Kasus 1. Jelaskan minimal tiga kelemahan penyampaiannya, dampaknya bagi pendengar, dan strategi latihan tiga langkah untuk memperbaikinya.",need:5,
 c:[["tempo","cepat","jeda"],["volume","pelan","suara"],["kontak mata","menatap","pandang"],["catatan","poin","garis besar","kerangka"],["latihan","berlatih","rekam"],["baku","gaul","gak"],["gestur","tangan","saku","postur"]],
 key:"Kelemahan: tempo terlalu cepat tanpa jeda, volume pelan, tidak ada kontak mata, gestur kaku, kata tidak baku. Dampak: pesan tidak tersampaikan dan data tidak teringat. Strategi: berlatih dengan catatan poin, merekam diri, melatih tempo dan jeda, mengganti kata tidak baku."},
{lv:"C5",q:"Nilailah pendapat berikut: 'Ceramah yang isinya berdata lengkap tetapi disampaikan dengan nada datar tetap lebih baik daripada ceramah yang menarik tetapi tanpa data.' Setujukah Anda? Berikan argumen dengan minimal dua pertimbangan.",need:5,
 c:[["seimbang","saling melengkapi","keduanya","dua-duanya"],["data","fakta","akurat","bukti"],["intonasi","emosi","menarik","monoton","bosan"],["kredibil","meyakinkan","dipercaya","percaya"],["tersampaikan","dipahami","sampai","diingat"],["tujuan","persuasif","mengajak"],["simpul","oleh karena itu","dengan demikian"]],
 key:"Isi dan penyampaian saling melengkapi. Data membuat ceramah kredibel dan akurat, intonasi membuat pesan sampai dan diingat. Ceramah yang baik memadukan keduanya sesuai tujuan persuasif."},
{lv:"C6",q:"Rancanglah strategi lima langkah agar Anda tetap tenang dan efektif ketika tampil berceramah, termasuk cara menghadapi lupa naskah, mikrofon mati, dan pertanyaan yang sulit.",need:5,
 c:[["napas","tenang","rileks"],["latih","persiapan","siap"],["catatan","poin"],["jeda","berhenti sejenak"],["jujur","mengakui","akui","tidak mengarang"],["panitia","bantuan","minta tolong","meminta bantuan"],["senyum","santun","terima kasih","sopan"]],
 key:"Persiapan dan latihan, pernapasan untuk menenangkan diri, catatan poin dan jeda saat lupa, meminta bantuan panitia saat kendala teknis, menjawab jujur dan sopan atas pertanyaan sulit."},
{lv:"C6",q:"Tulislah pembukaan ceramah (4-6 kalimat) bertema 'Disiplin Selama PKL' yang memuat salam, sapaan berurutan, pertanyaan retoris, pengantar topik, dan kalimat ajakan. Beri penanda lisan berupa tanda / atau // untuk jeda.",need:5,
 c:[["assalam","selamat pagi","selamat siang","selamat sore","salam"],["hormati","hormat","hadirin","bapak","ibu","teman","rekan"],["\\?"],["pada kesempatan","izinkan","tentang","membahas","topik","disiplin"],["\\/"],["mari","marilah","ayo","hendaknya","sebaiknya"]],
 key:"Pembukaan berisi salam, sapaan urut dari yang paling dihormati, pertanyaan retoris pengait, pengantar topik disiplin PKL, ajakan (marilah/hendaknya), dan penanda jeda / atau //."},
{lv:"C5",q:"Mengapa sikap santun sangat menentukan keberhasilan menyajikan ceramah, terutama di dunia kerja atau saat PKL? Evaluasilah dengan dua contoh sikap santun dan akibatnya jika diabaikan.",need:5,
 c:[["hormat","menghargai"],["profesional","citra","kesan","reputasi"],["percaya","kepercayaan","kredibil","kredibilitas"],["baku","sopan","santun"],["sapaan","salam","etika","tata krama","terima kasih"],["klien","atasan","rekan","pimpinan","industri","dunia kerja","pkl","perusahaan"],["kerja sama","hubungan","komunikasi","pesan"]],
 key:"Sikap santun (salam, sapaan urut, bahasa baku, menghargai waktu dan penanya) membangun rasa hormat, kepercayaan, dan citra profesional. Jika diabaikan, pesan ditolak dan hubungan kerja terganggu."}
];

/* bangun soal */
var quiz=[],n=0;
shuffle(G.slice()).forEach(function(g){
  var items=shuffle(g.q.slice()).map(function(q){
    var o=shuffle([0,1,2,3,4].map(function(i){return {t:q[2+i],c:i===0};})), ci=0; o.forEach(function(x,i){if(x.c)ci=i;});
    return {lv:q[0],q:q[1],o:o,ci:ci};
  });
  quiz.push({g:g,items:items});
});
var h="";
quiz.forEach(function(b){
  h+='&lt;div class="stim"&gt;&lt;h4&gt;'+esc(b.g.t)+'&lt;/h4&gt;';b.g.p.forEach(function(p){h+='&lt;p&gt;'+esc(p)+'&lt;/p&gt;';});h+='&lt;/div&gt;';
  b.items.forEach(function(it){
    h+='&lt;div class="q"&gt;&lt;div class="qt"&gt;'+(n+1)+'. '+esc(it.q)+' &lt;span class="lv"&gt;'+it.lv+'&lt;/span&gt;&lt;/div&gt;';
    it.o.forEach(function(o,i){h+='&lt;label class="o" id="kho'+n+'_'+i+'"&gt;&lt;input type="radio" name="khq'+n+'" value="'+i+'"&gt;&lt;b&gt;'+LET[i]+'.&lt;/b&gt;&lt;span&gt;'+esc(o.t)+'&lt;/span&gt;&lt;/label&gt;';});
    h+='&lt;/div&gt;';n++;
  });
});
h+='&lt;div class="stim"&gt;&lt;h4&gt;Bagian Esai HOTS (diketik sendiri, tempel dinonaktifkan)&lt;/h4&gt;&lt;p&gt;Tulis jawaban lengkap dengan argumen dan contoh. Jawaban terlalu singkat akan mendapat skor rendah.&lt;/p&gt;&lt;/div&gt;';
E.forEach(function(e,i){
  h+='&lt;div class="q"&gt;&lt;div class="qt"&gt;'+(16+i-0)+'. '+esc(e.q)+' &lt;span class="lv"&gt;'+e.lv+'&lt;/span&gt;&lt;/div&gt;&lt;textarea class="t" id="khE'+i+'" spellcheck="false" placeholder="Ketik jawaban Anda..."&gt;&lt;/textarea&gt;&lt;div class="pw" id="khPW'+i+'"&gt;Tempel (paste) dinonaktifkan. Ketik jawaban Anda sendiri.&lt;/div&gt;&lt;div class="meta" id="khC'+i+'"&gt;0 kata&lt;/div&gt;&lt;/div&gt;';
});
$("khBody").innerHTML=h;
$("khBody").addEventListener("change",function(e){
  var t=e.target;if(!t||t.type!=="radio")return;var m=/^khq(\d+)$/.exec(t.name);if(!m)return;
  for(var i=0;i&lt;5;i++){var el=$("kho"+m[1]+"_"+i);if(el)el.className="o"+(i===+t.value?" sel":"");}
});

/* anti paste */
function words(t){var m=t.match(/[A-Za-z0-9\-']+/g);return m?m.length:0;}
E.forEach(function(e,i){
  var ta=$("khE"+i),last=0;
  function warn(){var w=$("khPW"+i);w.style.display="block";clearTimeout(warn.t);warn.t=setTimeout(function(){w.style.display="none";},3500);}
  ["paste","drop","dragover","dragenter"].forEach(function(ev){ta.addEventListener(ev,function(x){x.preventDefault();warn();});});
  ta.addEventListener("contextmenu",function(x){x.preventDefault();});
  ta.addEventListener("keydown",function(x){var k=(x.key||"").toLowerCase();if(((x.ctrlKey||x.metaKey)&amp;&amp;k==="v")||(x.shiftKey&amp;&amp;x.key==="Insert")){x.preventDefault();warn();}});
  ta.addEventListener("beforeinput",function(x){if(x.inputType==="insertFromPaste"||x.inputType==="insertFromDrop"||x.inputType==="insertFromYank"){x.preventDefault();warn();}});
  ta.addEventListener("input",function(x){var d=this.value.length-last;last=this.value.length;if(d&gt;40&amp;&amp;x.inputType!=="insertReplacementText"){this.value=this.value.slice(0,this.value.length-d);last=this.value.length;warn();}$("khC"+i).textContent=words(this.value)+" kata";});
});

/* skor esai */
function scoreEssay(txt,e){
  var low=txt.toLowerCase(),hit=0,det=[];
  e.c.forEach(function(alts){
    var ok=alts.some(function(a){return new RegExp(a,"i").test(low);});
    if(ok)hit++;det.push(ok);
  });
  var w=words(txt),s=Math.min(1,hit/e.need)*100;
  if(w&lt;25)s*=0.4;else if(w&lt;45)s*=0.75;
  return {s:Math.round(s),hit:hit,det:det,w:w};
}

var done=false,rec=null;
function setMsg(t,er){var m=$("khMsg");m.textContent=t;m.className="msg"+(er?" e":"");}
$("khSend").onclick=function(){
  if(done)return;
  var nama=$("khNama").value.replace(/\s+/g," ").trim(),kelas=$("khKelas").value.trim(),absen=$("khAbsen").value.trim();
  if(nama.length&lt;3||!kelas||!/^\d{1,2}$/.test(absen)){setMsg("Lengkapi nama, kelas, dan No. Absen.",true);return;}
  var key="hots-lisan:"+kelas.toLowerCase().replace(/\s+/g,"")+":"+absen;
  try{if(localStorage.getItem(key)){setMsg("No. Absen ini sudah mengirim jawaban.",true);return;}}catch(x){}
  var un=0,i;for(i=0;i&lt;15;i++){if(!document.querySelector('input[name="khq'+i+'"]:checked'))un++;}
  var ew=0;E.forEach(function(e,k){if(words($("khE"+k).value)&lt;15)ew++;});
  if(un&gt;0||ew&gt;0){if(!window.confirm("Belum lengkap: "+un+" soal PG kosong dan "+ew+" esai terlalu singkat (kurang dari 15 kata). Tetap kirim?"))return;}
  if(!window.confirm("Kirim jawaban sekarang? Setelah dikirim tidak dapat diubah."))return;
  var benar=0,pg=[],q=0;
  quiz.forEach(function(b){b.items.forEach(function(it){
    var r=document.querySelector('input[name="khq'+q+'"]:checked'),a=r?+r.value:-1,ok=(a===it.ci);if(ok)benar++;
    pg.push({l:a&lt;0?"-":LET[a],k:LET[it.ci],ok:ok,lv:it.lv});q++;});});
  var pgS=Math.round(benar*100/15),es=E.map(function(e,k){var t=$("khE"+k).value;var r=scoreEssay(t,e);r.t=t;return r;});
  var esS=Math.round(es.reduce(function(a,b){return a+b.s;},0)/E.length),fin=Math.round(pgS*0.7+esS*0.3);
  rec={nama:nama,kelas:kelas,absen:absen,pg:pg,es:es,pgS:pgS,esS:esS,fin:fin,benar:benar,waktu:new Date().toLocaleString("id-ID")};
  done=true;$("khSend").disabled=true;try{localStorage.setItem(key,"1");}catch(x){}
  E.forEach(function(e,k){$("khE"+k).readOnly=true;});
  var p={waktu:rec.waktu,nama:nama,kelas:kelas,absen:absen,materi:MATERI,skorPG:pgS,skorEsai:esS,nilaiAkhir:fin,benarPG:benar,totalPG:15};
  es.forEach(function(r,k){p["esai"+(k+1)]=r.t+" [skor "+r.s+"]";});
  try{fetch(GAS_URL,{method:"POST",mode:"no-cors",body:new URLSearchParams(p)}).catch(function(){});}catch(x){}
  setMsg("Jawaban berhasil dikirim.",false);
  var r='&lt;div class="res"&gt;&lt;p&gt;Nilai akhir Anda:&lt;/p&gt;&lt;div class="score"&gt;'+fin+' / 100&lt;/div&gt;&lt;p&gt;'+(fin&gt;=KKM?'&lt;span class="good"&gt;Mencapai KKM ('+KKM+').&lt;/span&gt;':'&lt;span class="bad"&gt;Belum mencapai KKM ('+KKM+').&lt;/span&gt;')+' PG '+pgS+' (benar '+benar+'/15) x 70% + Esai '+esS+' x 30%.&lt;/p&gt;';
  r+='&lt;p&gt;&lt;b&gt;Langkah berikutnya:&lt;/b&gt; unduh bukti PDF, cetak, dan serahkan kepada guru.&lt;/p&gt;&lt;button class="b" id="khPdf" type="button"&gt;Unduh / Cetak Bukti (PDF F4)&lt;/button&gt;';
  r+='&lt;h4&gt;Umpan balik esai&lt;/h4&gt;&lt;table&gt;&lt;tr&gt;&lt;th&gt;No&lt;/th&gt;&lt;th&gt;Skor&lt;/th&gt;&lt;th&gt;Kunci konsep&lt;/th&gt;&lt;/tr&gt;';
  es.forEach(function(x,k){r+='&lt;tr&gt;&lt;td&gt;'+(16+k)+'&lt;/td&gt;&lt;td&gt;'+x.s+' ('+x.hit+' dari '+E[k].c.length+' konsep, '+x.w+' kata)&lt;/td&gt;&lt;td&gt;'+esc(E[k].key)+'&lt;/td&gt;&lt;/tr&gt;';});
  r+='&lt;/table&gt;&lt;/div&gt;';
  $("khRes").innerHTML=r;$("khPdf").onclick=printProof;
  $("khRes").scrollIntoView&amp;&amp;$("khRes").scrollIntoView();
};

function printProof(){
  if(!rec)return;var old=$("khPrint");if(old)old.parentNode.removeChild(old);
  var d=document.createElement("div");d.id="khPrint";
  var h='&lt;h1&gt;BUKTI TUGAS INDIVIDU HOTS: MENYAJIKAN TEKS CERAMAH SECARA LISAN&lt;/h1&gt;&lt;p style="text-align:center;margin:0 0 6px"&gt;Bahasa Indonesia Kelas XI SMK&lt;/p&gt;';
  h+='&lt;table&gt;&lt;tr&gt;&lt;td style="width:24%"&gt;Nama&lt;/td&gt;&lt;td&gt;'+esc(rec.nama)+'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kelas / No. Absen&lt;/td&gt;&lt;td&gt;'+esc(rec.kelas)+' / '+esc(rec.absen)+'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Waktu pengiriman&lt;/td&gt;&lt;td&gt;'+esc(rec.waktu)+'&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;';
  h+='&lt;h2&gt;NILAI AKHIR: '+rec.fin+' (KKM '+KKM+') - '+(rec.fin&gt;=KKM?'Tuntas':'Belum tuntas')+'&lt;/h2&gt;&lt;table&gt;&lt;tr&gt;&lt;th&gt;Komponen&lt;/th&gt;&lt;th&gt;Bobot&lt;/th&gt;&lt;th&gt;Nilai&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pilihan ganda (benar '+rec.benar+'/15)&lt;/td&gt;&lt;td&gt;70%&lt;/td&gt;&lt;td&gt;'+rec.pgS+'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Esai (otomatis)&lt;/td&gt;&lt;td&gt;30%&lt;/td&gt;&lt;td&gt;'+rec.esS+'&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;';
  h+='&lt;h2&gt;Jawaban pilihan ganda (urutan tampil pada layar siswa)&lt;/h2&gt;&lt;table&gt;&lt;tr&gt;&lt;th&gt;No&lt;/th&gt;&lt;th&gt;Level&lt;/th&gt;&lt;th&gt;Jawaban&lt;/th&gt;&lt;th&gt;Kunci&lt;/th&gt;&lt;th&gt;Hasil&lt;/th&gt;&lt;/tr&gt;';
  rec.pg.forEach(function(x,i){h+='&lt;tr&gt;&lt;td&gt;'+(i+1)+'&lt;/td&gt;&lt;td&gt;'+x.lv+'&lt;/td&gt;&lt;td&gt;'+x.l+'&lt;/td&gt;&lt;td&gt;'+x.k+'&lt;/td&gt;&lt;td&gt;'+(x.ok?'Benar':'Salah')+'&lt;/td&gt;&lt;/tr&gt;';});
  h+='&lt;/table&gt;&lt;h2&gt;Jawaban esai&lt;/h2&gt;';
  rec.es.forEach(function(x,i){h+='&lt;p style="margin:4px 0"&gt;&lt;b&gt;'+(16+i)+'. '+esc(E[i].q)+'&lt;/b&gt; (skor '+x.s+')&lt;/p&gt;&lt;div class="nas"&gt;'+esc(x.t||"(tidak dijawab)")+'&lt;/div&gt;';});
  h+='&lt;div class="sig"&gt;&lt;div&gt;Siswa,&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;('+esc(rec.nama)+')&lt;/div&gt;&lt;div&gt;Guru Mata Pelajaran,&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;(................................)&lt;/div&gt;&lt;/div&gt;';
  d.innerHTML=h;document.body.appendChild(d);document.body.className+=" kh-printing";
  setTimeout(function(){window.print();setTimeout(function(){document.body.className=document.body.className.replace(/\s*kh-printing/g,"");},500);},150);
}
})();
&lt;/script&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/menyajikan-teks-ceramah-secara-lisan.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-569533408978744433</guid><pubDate>Thu, 22 Oct 2026 03:42:33 +0000</pubDate><atom:updated>2026-11-10T12:29:04.396+07:00</atom:updated><title>ULANGAN FORMATIF SPREADSHEET</title><description>&lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"&gt;&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;&lt;script src="https://cdn.jsdelivr.net/npm/jspdf-autotable@3.8.2/dist/jspdf.plugin.autotable.min.js"&gt;&lt;/script&gt;&lt;style&gt;:root{ --navy:#0f2742; --blue:#1d4ed8; --blue-soft:#dbeafe; --amber:#d97706; --amber-soft:#fef3c7; --amber-dark:#92400e; --teal:#0f766e; --teal-soft:#ccfbf1; --ok:#15803d; --okbg:#dcfce7; --bad:#b91c1c; --badbg:#fee2e2; --ink:#0f172a; --sub:#475569; --line:#dbe3ee; --bg:#eef2f8; --card:#ffffff; } #examHotsRoot *{box-sizing:border-box} #examHotsRoot{margin:0; font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--ink); line-height:1.55;} .wrap{max-width:920px; margin:0 auto; padding:18px 16px 60px;} .hidden{display:none !important;} h1,h2,h3{color:var(--navy); margin:0 0 8px;} .card{background:var(--card); border:1px solid var(--line); border-radius:16px; padding:20px; margin-bottom:16px; box-shadow:0 2px 10px rgba(15,39,66,.05);} .topbar{background:var(--navy); color:#fff; padding:14px 16px;} .topbar .in{max-width:920px; margin:0 auto; display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:6px;} .topbar b{font-size:15px;} .topbar span{font-size:12.5px; opacity:.85;} label.fl{display:block; font-weight:700; font-size:13px; color:var(--navy); margin-bottom:4px;} input[type=text],input[type=number],select,textarea{width:100%; padding:10px 12px; border:1.5px solid var(--line); border-radius:10px; font-size:14.5px; font-family:inherit; background:#fff; color:var(--ink);} textarea{resize:vertical; min-height:110px;} .field-grid{display:grid; grid-template-columns:1fr 1fr; gap:12px;} @media(max-width:600px){.field-grid{grid-template-columns:1fr;}} .btn{display:inline-block; border:none; border-radius:10px; padding:11px 20px; font-weight:700; font-size:14.5px; cursor:pointer; font-family:inherit;} .btn-primary{background:var(--blue); color:#fff;} .btn-primary:disabled{background:#93a7c9; cursor:not-allowed;} .btn-outline{background:#fff; color:var(--navy); border:1.5px solid var(--line);} .btn-amber{background:var(--amber); color:#fff;} .btn-teal{background:var(--teal); color:#fff;} .btn-bad{background:var(--bad); color:#fff;} .msg{padding:10px 12px; border-radius:10px; font-size:13.5px; margin-top:10px; display:none;} .msg.show{display:block;} .msg.err{background:var(--badbg); color:var(--bad);} .msg.info{background:var(--blue-soft); color:var(--blue);} .msg.ok{background:var(--okbg); color:var(--ok);} .notice ul{margin:6px 0 0; padding-left:20px;} .notice li{margin-bottom:6px; font-size:13.5px; color:var(--sub);} table.ref{border-collapse:collapse; width:100%; margin:10px 0; font-size:13.5px;} table.ref th,table.ref td{border:1px solid var(--line); padding:6px 9px; text-align:left;} table.ref th{background:var(--blue-soft); color:var(--navy);} table.ref td.num{text-align:right;} .exam-top{position:sticky; top:0; z-index:40; background:var(--navy); color:#fff; padding:10px 14px;} .exam-top .in{max-width:920px; margin:0 auto; display:flex; align-items:center; gap:10px; flex-wrap:wrap;} .exam-top .who{font-size:12.5px; font-weight:700;} .chip{font-size:12px; font-weight:700; padding:4px 10px; border-radius:999px;} .chip.timer{background:var(--amber-soft); color:var(--amber-dark);} .chip.timer.low{background:var(--badbg); color:var(--bad);} .chip.sec{background:var(--okbg); color:var(--ok);} .chip.sec.warn{background:var(--badbg); color:var(--bad);} .chip.prog{background:rgba(255,255,255,.15); color:#fff; margin-left:auto;} .numgrid{display:flex; flex-wrap:wrap; gap:6px; margin:12px 0;} .numgrid button{width:34px; height:34px; border-radius:8px; border:1.5px solid var(--line); background:#fff; font-size:12.5px; font-weight:700; cursor:pointer; color:var(--sub);} .numgrid button.answered{background:var(--okbg); border-color:var(--ok); color:var(--ok);} .numgrid button.marked{background:var(--amber-soft); border-color:var(--amber); color:var(--amber-dark);} .numgrid button.current{outline:2.5px solid var(--blue); outline-offset:1px;} .sec-label{font-size:12px; font-weight:800; color:var(--sub); margin:14px 0 2px;} .q-box{border:1.5px solid var(--line); border-radius:14px; padding:16px;} .q-head{font-size:12.5px; font-weight:800; color:var(--blue); text-transform:uppercase; letter-spacing:.03em; margin-bottom:6px;} .q-stem{font-size:15px; margin-bottom:12px;} .opt-row{display:flex; gap:10px; align-items:flex-start; padding:9px 10px; border:1.5px solid var(--line); border-radius:10px; margin-bottom:8px; cursor:pointer;} .opt-row:hover{border-color:var(--blue);} .opt-row input{margin-top:3px;} .navbtns{display:flex; justify-content:space-between; gap:10px; margin-top:16px; flex-wrap:wrap;} .navbtns .mid{display:flex; gap:10px;} .cnt-small{font-size:12px; color:var(--sub); text-align:right; margin-top:4px;} .overlay{position:fixed; inset:0; background:rgba(15,23,42,.88); z-index:9999; display:flex; align-items:center; justify-content:center; padding:20px;} .overlay .box{background:#fff; border-radius:16px; padding:24px; max-width:460px; width:100%;} .overlay.danger .box{border-top:5px solid var(--bad);} .overlay h2{margin-top:0;} .overlay .row{display:flex; gap:10px; justify-content:flex-end; margin-top:16px; flex-wrap:wrap;} .status-pill{display:inline-block; padding:6px 16px; border-radius:999px; font-weight:800; font-size:14px; margin-bottom:8px;} .status-pill.ok{background:var(--okbg); color:var(--ok);} .status-pill.no{background:var(--badbg); color:var(--bad);} .ring{width:120px; height:120px; border-radius:50%; margin:0 auto 10px; display:flex; align-items:center; justify-content:center; font-size:30px; font-weight:800; color:var(--navy); background:conic-gradient(var(--ringc,#1d4ed8) calc(var(--p,0)*1%), #e5ebf5 0);} .ring .inner{width:92px; height:92px; border-radius:50%; background:#fff; display:flex; align-items:center; justify-content:center;} .bar-row{margin-bottom:10px;} .bar-row .lbl{display:flex; justify-content:space-between; font-size:13px; font-weight:700; color:var(--navy); margin-bottom:3px;} .bar-track{height:9px; border-radius:6px; background:#e5ebf5; overflow:hidden;} .bar-fill{height:100%; border-radius:6px;} .btn-row{display:flex; gap:10px; flex-wrap:wrap; margin-top:14px;} footer{text-align:center; font-size:12px; color:var(--sub); padding:20px;} @media print{ .no-print{display:none !important;} body{background:#fff;} }&lt;/style&gt;&lt;div id="examHotsRoot"&gt;&lt;div class="topbar no-print"&gt;&lt;div class="in"&gt;&lt;div&gt;&lt;b id="hdrJudul"&gt;Ujian HOTS Spreadsheet&lt;/b&gt;&lt;br&gt;&lt;span id="hdrSub"&gt;Informatika - Kelas X SMK&lt;/span&gt;&lt;/div&gt;&lt;span id="hdrDoc"&gt;DOK/INFORMATIKA/X/UJIAN-HOTS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="wrap"&gt;&lt;div id="screenStart"&gt;&lt;div class="card"&gt;&lt;h1 id="startTitle"&gt;Ujian HOTS: Pengolah Angka (Spreadsheet)&lt;/h1&gt;&lt;p id="startDesc" style="color:var(--sub); font-size:14px;"&gt;Ujian ini menguji kemampuan berpikir tingkat tinggi (HOTS) dalam menggunakan aplikasi pengolah angka: rumus, fungsi, referensi sel, lookup, pengolahan data, dan interpretasi hasil analisis.&lt;/p&gt;&lt;table class="ref"&gt;&lt;tr&gt;&lt;th&gt;Bagian&lt;/th&gt;&lt;th&gt;Jumlah Soal&lt;/th&gt;&lt;th&gt;Poin / Soal&lt;/th&gt;&lt;th&gt;Total Poin&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;A. Pilihan Ganda&lt;/td&gt;&lt;td class="num"&gt;30&lt;/td&gt;&lt;td class="num"&gt;1&lt;/td&gt;&lt;td class="num"&gt;30&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;B. Praktik Langsung&lt;/td&gt;&lt;td class="num"&gt;15&lt;/td&gt;&lt;td class="num"&gt;3&lt;/td&gt;&lt;td class="num"&gt;45&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C. Esai&lt;/td&gt;&lt;td class="num"&gt;5&lt;/td&gt;&lt;td class="num"&gt;5&lt;/td&gt;&lt;td class="num"&gt;25&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Total&lt;/b&gt;&lt;/td&gt;&lt;td class="num"&gt;&lt;b&gt;50&lt;/b&gt;&lt;/td&gt;&lt;td class="num"&gt;-&lt;/td&gt;&lt;td class="num"&gt;&lt;b&gt;100&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="notice"&gt;&lt;b&gt;Aturan penting:&lt;/b&gt;&lt;ul id="rulesList"&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="card"&gt;&lt;h2&gt;Identitas Peserta&lt;/h2&gt;&lt;div class="field-grid"&gt;&lt;div&gt;&lt;label class="fl" for="fKelas"&gt;Kelas&lt;/label&gt;&lt;select id="fKelas"&gt;&lt;option value=""&gt;- Pilih Kelas -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl" for="fAbsen"&gt;No. Absen&lt;/label&gt;&lt;select id="fAbsen"&gt;&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl" for="fNama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="fNama" placeholder="Terisi otomatis, bisa diperbaiki jika perlu"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl" for="fToken"&gt;Token Kelas&lt;/label&gt;&lt;input type="text" id="fToken" placeholder="Diberikan oleh guru"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl" for="fKode"&gt;Kode Kesempatan Tambahan &lt;span style="font-weight:400;"&gt;(opsional)&lt;/span&gt;&lt;/label&gt;&lt;input type="text" id="fKode" placeholder="Isi hanya jika guru memberimu kode" autocomplete="off"&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-top:14px;"&gt;&lt;button class="btn btn-primary" id="btnStart"&gt;Mulai Ujian&lt;/button&gt;&lt;button class="btn btn-outline" id="btnLihat" type="button"&gt;Lihat Hasil &amp;amp; Kirim Ulang Nilai&lt;/button&gt;&lt;/div&gt;&lt;div class="msg err" id="startErr"&gt;&lt;/div&gt;&lt;/div&gt;&lt;p style="text-align:center;"&gt;&lt;button type="button" class="btn btn-outline" id="btnOpenGuru" style="padding:7px 16px; font-size:12.5px;"&gt;Pengaturan Guru&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;&lt;div id="guruCard" class="card hidden"&gt;&lt;div style="display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px;"&gt;&lt;h2 style="margin:0;"&gt;Pengaturan Guru&lt;/h2&gt;&lt;button type="button" class="btn btn-outline" id="btnGuruClose"&gt;&amp;larr; Kembali ke Panel Murid&lt;/button&gt;&lt;/div&gt;&lt;div id="guruLockBox"&gt;&lt;label class="fl" for="guruCode"&gt;Kode Akses Guru&lt;/label&gt;&lt;input type="text" id="guruCode" placeholder="Masukkan kode akses"&gt;&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnGuruUnlock"&gt;Buka Pengaturan&lt;/button&gt;&lt;/div&gt;&lt;div class="msg err" id="guruLockErr"&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="guruForm" class="hidden"&gt;&lt;div class="field-grid"&gt;&lt;div&gt;&lt;label class="fl"&gt;Judul Ujian&lt;/label&gt;&lt;input type="text" id="sJudul"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Nama Sekolah&lt;/label&gt;&lt;input type="text" id="sSekolah"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Mata Pelajaran&lt;/label&gt;&lt;input type="text" id="sMapel"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;KKM&lt;/label&gt;&lt;input type="number" id="sKKM" min="0" max="100"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Durasi Ujian (menit)&lt;/label&gt;&lt;input type="number" id="sDurasi" min="5" max="300"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Batas Pelanggaran (pindah tab)&lt;/label&gt;&lt;input type="number" id="sBatasPelanggaran" min="1" max="20"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Jumlah Percobaan&lt;/label&gt;&lt;input type="number" id="sMaxAtt" min="1" max="5"&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Tampilkan Pembahasan Setelah Ujian&lt;/label&gt;&lt;select id="sPembahasan"&gt;&lt;option value="0"&gt;Tidak&lt;/option&gt;&lt;option value="1"&gt;Ya&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-top:12px;"&gt;&lt;label class="fl"&gt;Endpoint Google Apps Script (pengiriman nilai)&lt;/label&gt;&lt;input type="text" id="sEndpoint"&gt;&lt;/div&gt;&lt;div class="btn-row"&gt;&lt;button class="btn btn-primary" id="btnSaveSettings"&gt;Simpan Pengaturan&lt;/button&gt;&lt;button class="btn btn-outline" id="btnResetSettings"&gt;Kembalikan ke Default&lt;/button&gt;&lt;button type="button" class="btn btn-outline" id="btnGuruClose2"&gt;&amp;larr; Kembali ke Panel Murid&lt;/button&gt;&lt;/div&gt;&lt;div class="msg ok" id="guruSaveMsg"&gt;&lt;/div&gt;&lt;div style="margin-top:22px; border-top:1px solid var(--line); padding-top:16px;"&gt;&lt;h3&gt;Beri Kesempatan Tambahan untuk Murid&lt;/h3&gt;&lt;p style="color:var(--sub); font-size:13.5px; margin:0 0 10px;"&gt;Untuk murid yang nilainya belum mencapai KKM dan kesempatannya sudah habis. Pilih murid, buat kode, lalu berikan kode itu kepada murid. Murid mengisi kode pada kolom &lt;b&gt;Kode Kesempatan Tambahan&lt;/b&gt; di halaman awal. Setiap kode hanya berlaku untuk satu murid dan sekali pakai; murid yang sudah mencapai KKM tidak dapat memakainya.&lt;/p&gt;&lt;div class="field-grid"&gt;&lt;div&gt;&lt;label class="fl"&gt;Kelas&lt;/label&gt;&lt;select id="tKelas"&gt;&lt;option value=""&gt;- Pilih Kelas -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;No. Absen&lt;/label&gt;&lt;select id="tAbsen"&gt;&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div&gt;&lt;label class="fl"&gt;Tambahan Kesempatan&lt;/label&gt;&lt;select id="tJumlah"&gt;&lt;option value="1"&gt;+1 kali&lt;/option&gt;&lt;option value="2"&gt;+2 kali&lt;/option&gt;&lt;option value="3"&gt;+3 kali&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="btn-row"&gt;&lt;button type="button" class="btn btn-primary" id="btnGenKode"&gt;Buat Kode Kesempatan&lt;/button&gt;&lt;/div&gt;&lt;div class="msg err" id="gKodeErr"&gt;&lt;/div&gt;&lt;div id="gKodeList" style="margin-top:10px;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="screenExam" class="hidden"&gt;&lt;div class="exam-top no-print"&gt;&lt;div class="in"&gt;&lt;span class="who" id="whoLbl"&gt;&lt;/span&gt;&lt;span class="chip timer" id="timerChip"&gt;60:00&lt;/span&gt;&lt;span class="chip sec" id="secChip"&gt;Keamanan: Aman&lt;/span&gt;&lt;span class="chip prog" id="progChip"&gt;Soal 1 dari 50&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="card"&gt;&lt;div class="numgrid" id="numGrid"&gt;&lt;/div&gt;&lt;div class="sec-label" id="qSection"&gt;&lt;/div&gt;&lt;div class="q-box" id="qBox"&gt;&lt;/div&gt;&lt;div class="navbtns"&gt;&lt;button class="btn btn-outline" id="btnPrev"&gt;&amp;larr; Sebelumnya&lt;/button&gt;&lt;div class="mid"&gt;&lt;button class="btn btn-outline" id="btnMark"&gt;Tandai untuk Ditinjau&lt;/button&gt;&lt;button class="btn btn-teal" id="btnFinish"&gt;Selesai &amp;amp; Kirim&lt;/button&gt;&lt;/div&gt;&lt;button class="btn btn-outline" id="btnNext"&gt;Berikutnya &amp;rarr;&lt;/button&gt;&lt;/div&gt;&lt;div class="msg err" id="examErr"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="screenResult" class="hidden"&gt;&lt;div class="card" id="resultPrintArea"&gt;&lt;div style="text-align:center;"&gt;&lt;h2 id="rTitle"&gt;-&lt;/h2&gt;&lt;p id="rDesc" style="color:var(--sub); font-size:13.5px;"&gt;&lt;/p&gt;&lt;div class="ring" id="ring"&gt;&lt;div class="inner"&gt;&lt;span id="rVal"&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="rPill" class="status-pill no"&gt;-&lt;/div&gt;&lt;/div&gt;&lt;table class="ref"&gt;&lt;tr&gt;&lt;th&gt;Nama&lt;/th&gt;&lt;td id="riNama"&gt;&lt;/td&gt;&lt;th&gt;Kelas&lt;/th&gt;&lt;td id="riKelas"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;No. Absen&lt;/th&gt;&lt;td id="riAbsen"&gt;&lt;/td&gt;&lt;th&gt;KKM&lt;/th&gt;&lt;td id="riKKM"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Mulai&lt;/th&gt;&lt;td id="riMulai"&gt;&lt;/td&gt;&lt;th&gt;Selesai&lt;/th&gt;&lt;td id="riSelesai"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Durasi&lt;/th&gt;&lt;td id="riDurasi"&gt;&lt;/td&gt;&lt;th&gt;Pelanggaran&lt;/th&gt;&lt;td id="riPelanggaran"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div id="rBars" style="margin-top:10px;"&gt;&lt;/div&gt;&lt;div class="msg info show" id="rSend"&gt;&lt;/div&gt;&lt;div id="rBenchmark" style="font-size:13px; color:var(--sub);"&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="btn-row no-print"&gt;&lt;button class="btn btn-primary" id="btnPdf"&gt;Unduh Bukti (PDF)&lt;/button&gt;&lt;button class="btn btn-outline" id="btnPrint"&gt;Cetak&lt;/button&gt;&lt;button class="btn btn-outline" id="btnCsv"&gt;Ekspor CSV&lt;/button&gt;&lt;button class="btn btn-teal" id="btnResend"&gt;Kirim Ulang Nilai&lt;/button&gt;&lt;button class="btn btn-outline" id="btnHome"&gt;Kembali ke Halaman Awal&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;footer&gt;Aplikasi ujian ini berjalan sepenuhnya di peramban (browser) murid. Lihat catatan keamanan pada Pengaturan Guru.&lt;/footer&gt;&lt;/div&gt;&lt;form id="gform" action="" method="POST" target="hidden_iframe" style="display:none;"&gt;&lt;input type="text" name="timestamp" id="gTimestamp"&gt;&lt;input type="text" name="nama" id="gNama"&gt;&lt;input type="text" name="kelas" id="gKelas"&gt;&lt;input type="text" name="absen" id="gAbsen"&gt;&lt;input type="text" name="judul" id="gJudul"&gt;&lt;input type="text" name="nilaiPG" id="gNilaiPG"&gt;&lt;input type="text" name="nilaiPraktik" id="gNilaiPraktik"&gt;&lt;input type="text" name="nilaiEsai" id="gNilaiEsai"&gt;&lt;input type="text" name="nilaiAkhir" id="gNilai"&gt;&lt;input type="text" name="nilaiSTS" id="gNilaiSTS"&gt;&lt;input type="text" name="status" id="gStatus"&gt;&lt;input type="text" name="durasi" id="gDurasi"&gt;&lt;input type="text" name="pelanggaran" id="gPelanggaran"&gt;&lt;input type="text" name="skor" id="gSkor"&gt;&lt;/form&gt;&lt;iframe name="hidden_iframe" style="display:none;"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;script&gt;window.addEventListener('error',function(e){try{if(!e.lineno||e.message==='Script error.')return;if(document.getElementById('examHotsErrBanner'))return;;var b=document.createElement('div');b.id='examHotsErrBanner';b.style.cssText='position:fixed;top:0;left:0;right:0;z-index:99999;background:#b91c1c;color:#fff;padding:10px 14px;font:13px/1.4 sans-serif;';b.textContent='Skrip ujian gagal (baris '+(e.lineno||'?')+', kolom '+(e.colno||'?')+'): '+(e.message||'tidak diketahui')+'. Kirim tangkapan layar pesan ini ke pengembang.';document.body.insertBefore(b,document.body.firstChild);}catch(x){}});&lt;/script&gt;&lt;script&gt;!function(){"use strict";function a(a){return document.getElementById(a)}function n(a){return String(null==a?"":a).replace(/[&amp;&lt;&gt;"']/g,function(a){return{"&amp;":"&amp;amp;","&lt;":"&amp;lt;","&gt;":"&amp;gt;",'"':"&amp;quot;","'":"&amp;#39;"}[a]})}function e(a){for(var n=a.slice(),e=n.length-1;e&gt;0;e--){var t=Math.floor(Math.random()*(e+1)),i=n[e];n[e]=n[t],n[t]=i}return n}function t(a,n,e){a&amp;&amp;(a.textContent=n,a.className="msg "+(e||"err")+(n?" show":""))}function i(a){return String(null==a?"":a).trim().toLowerCase().replace(/\s+/g," ")}function r(a){var n=String(a||"").trim();return n?n.split(/\s+/).length:0}function s(a,e,t){t=t||[];var i='&lt;table class="ref"&gt;&lt;tr&gt;'+a.map(function(a){return"&lt;th&gt;"+n(a)+"&lt;/th&gt;"}).join("")+"&lt;/tr&gt;";return e.forEach(function(a){i+="&lt;tr&gt;"+a.map(function(a,e){return"&lt;td"+(t.indexOf(e)&gt;=0?' class="num"':"")+"&gt;"+n(a)+"&lt;/td&gt;"}).join("")+"&lt;/tr&gt;"}),i+="&lt;/table&gt;"}var u="https://script.google.com/macros/s/AKfycbz5b6Jzv826yohTurjABDVDeI8rPqGE-LoLsXR_xu_uefnXKHW5oN0unN8V_fsbefhmmQ/exec",l={judul:"Ujian HOTS: Pengolah Angka (Spreadsheet)",sekolah:"",mapel:"Informatika",kkm:75,durasiMenit:60,batasPelanggaran:3,maxAtt:3,pembahasan:!1,endpoint:u},o={"X TB1":[[1,"ADHA MIRAHAYU NINGSIH"],[2,"ANGELINA AGNES NOVIOLA"],[3,"ANISSA VIRA FIRMANDA"],[4,"Awinda Rizqi Amru Anjani"],[5,"CITRA AULIA BAHARIZQI"],[6,"DHIRA CAHYA OKTAVIANINGRUM"],[7,"Ema Deny Setyaningrum"],[8,"EVITA APRILIANI"],[9,"FAIR ROCHMATUL ISHMAH"],[10,"FATMA NABILA"],[11,"FATMAWATI"],[12,"Fika Setya Wulandari"],[13,"Gelsi Zalika Thufaila"],[14,"HIDAYATUL HUSNA MAULIDA"],[15,"IZZATUL LUTHFA IMANDA"],[16,"MEISHA AYU BERLIA"],[17,"MEYLISA PUTRI MURDIANI"],[18,"MITA TRI OKTAVIA"],[19,"MUTIARA SYABILLA"],[20,"NABILA SASKIA"],[21,"NADYA VEGA RAHMADANI"],[22,"Nafisa Nailal Husna"],[23,"NAWA SYIFFA MARYAM"],[24,"NIRIN FADILLAH"],[25,"RENI RESTIA PRAMESTI"],[26,"RIZKIYA MAULIDA"],[27,"SHINTA AULIA MUKTI"],[28,"SHINTIA AMELIA PUTRI"],[29,"SUCI NUR AZAHRA"],[30,"SYAH UITTA JIHAN MAKAELA"],[31,"VARISCA INDRI ARIANI"],[32,"VILSA SAFA AZZAHRA"],[33,"YUANITA ROBBITOTUN NAJA"],[34,"ZUNI ARIFAH"]],"X TB2":[[1,"ADINDA EKA NUR AINI"],[2,"Adinda Permata Sari"],[3,"AMANDA PUSPA MAHARANI"],[4,"ANA AMELLIYA"],[5,"AULIYA NATASYA DEWI"],[6,"AYUB MAULANA"],[7,"BILQIS NGIZZATUN NISA'"],[8,"CEYSA ELVINA AMANDA"],[9,"CHAUDLI MAULIDINA AZRA"],[10,"DESTA AURA AZALIA SANI"],[11,"DINDA NURUL AMALIA"],[12,"Dwi Mayangsari"],[13,"EFRIDA DEWI ANANDA"],[14,"Emi Rara Pramudita"],[15,"FIRMAN ARYA DINAIA"],[16,"KEISYA ALISYA PUTRI ALI"],[17,"Kusnul Kusuma Wardhani"],[18,"LIBY ARUMBIYA"],[19,"Lutfia Putri"],[20,"MARISKA NUR AIRA"],[21,"NADIA NUR AZIZAH IRYSATI"],[22,"NAHLA AULIA SALSABILLA"],[23,"NAURA IZA AZAHRA"],[24,"NAZWA AKAYLA SAPUTRI"],[25,"NOVITA ADHA RIZKI ANGGRAINI"],[26,"PEMBAYUN ANGGER WIDJAYA"],[27,"REVALIA NOVIANTI TAMARA"],[28,"SAHAL HANIF YUSUF"],[29,"SEKAR WANGI"],[30,"Silvi Dwi Noviyanti"],[31,"SIRVI KHUNAIFIYAH LINA AINI NISA"],[32,"VITO ADI CANDRA"],[33,"ZAHWA"],[34,"ZIDANNAJ MAROSAT"]],"X TO1":[[1,"ACHMAT SAPTA ARYA"],[2,"ADE FASIYAN NANDO"],[3,"ALBERTUS ADELIO RONALD FAGUNDEZ"],[4,"ALLAN FADLI MUHAMMAD JIBRANNI"],[5,"ARGA SYARUL SETIYAWAN"],[6,"DEDDY SETIYAWAN"],[7,"DENNIZ PRADIVTA ALVIANDRA"],[8,"DIMAS PUTRA ERLANGGA"],[9,"DIYAS PRASETYO"],[10,"DZAKIE AZ ZAAHII"],[11,"FADHIL DIANTORO"],[12,"FAQIH UBAIDILLAH"],[13,"FAQIYUDDIN ASYRAF"],[14,"HANIF TAUFIQULHAQIM"],[15,"IQBAL HAIDAR ARIQ"],[16,"IRFAN BAGAS VEBYANTINO"],[17,"MARCELINO ADI SANTOSO"],[18,"MUHAMAD AFTAH FARKHAN NAKA"],[19,"MUHAMAD AMAR MA'RUF"],[20,"MUHAMAD ILHAM PRASETYO"],[21,"MUHAMMAD AFWAN MAULANA AZIDAN"],[22,"MUHAMMAD KAFA BIHI FADLURROZAG"],[23,"MUHAMMAD SONY TAFRIKHAN"],[24,"MUHAMMAD SYAHID SYAHPUTRA"],[25,"MUHAMMAD ZAHRUL ULUM AL BUSTHOMI"],[26,"NANDO KURNIA EKA PUTRA"],[27,"NAUFAL ZAKY MUTTAQIN"],[28,"Nizham Zafir Hariz Saputra"],[29,"NUR FATHUR RAHMATULLAH"],[30,"RADITYA PUTRA PERDANA"],[31,"RAFKA MAULANA PUTRA"],[32,"RAKA RAFIF AZMI"],[33,"REYHAN YUNAZTA PUTRA HANY"],[34,"RIZKI IBADALLAH ALBAROYA"],[35,"SEPTIAN DANANG PRASETYO"],[36,"Yehuda Bayu Seno Abimanyu"]],"X TO2":[[1,"ADHAM DZAKI IBRAHIM MUFID"],[2,"AFLAH RAPHAEL WINDRA RADITYA"],[3,"AHMAD DAVI PRIA UTAMA"],[4,"AHMAD MAULANA ABDUL MALIK"],[5,"Ahmad Shovi Maola"],[6,"ALBARASYA HERMAN"],[7,"ANDIK FEBRI FIRMANSYAH"],[8,"DIMAS SAPUTRA"],[9,"DISKA INDRA FATA WIDAKDO"],[10,"FAREL ANDRIAN PRATAMA"],[11,"Ferdita Fatma Kusuma"],[12,"GALIH GINANJAR SAPUTRO"],[13,"LEVI FASTABIKHUL KHOIR"],[14,"LIONEL NATHAN KURNIAWAN"],[15,"MUHAMAD FIRDIANSYAH"],[16,"MUHAMAD KHOIRUL ANWAR"],[17,"MUHAMAD RIJAL ALIF IBRAHIM"],[18,"MUHAMAD TANFEBRO PERMANA"],[19,"MUHAMMAD AKMAL FARIZKY"],[20,"MUHAMMAD ALWI SIHAB"],[21,"MUHAMMAD 'AQILA AZKA"],[22,"MUHAMMAD FATIH"],[23,"MUHAMMAD FAUZI NOVANTYO"],[24,"Muhammad Rafka Adi Pratama"],[25,"MUHAMMAD TONI SUPRASETYO"],[26,"NOVAN ANANDA RIZKY"],[27,"PRAKASA TEGAR ANDRIYANTO"],[28,"RAFAEL BAYU PRATAMA"],[29,"RANDI KHOIRUL AZZAM"],[30,"RIDHO AMRU BAIN"],[31,"SABRI RADHITYA GANESWARA"],[32,"SATRIA FIRMAN WIBOWO"],[33,"SYARIF HIDAYAD"],[34,"WILDAN RIZQI ARRUMMAN"],[35,"VICKY ADIKA CHANDRA"]],"X TJKT1":[[1,"ADLY SURYA HERNAWAN"],[2,"ADRIANUS FERDINAND BARRA ARDIONA"],[3,"AISYAH KHOIRUN NISA"],[4,"ALIF EVAN SANJAYA"],[5,"Alif Maulana Ashidiqie"],[6,"Alif Rizqullah Ramadhan"],[7,"ANDIKA BILLIE FAUSTINO"],[8,"ARVIN ARYASETA ATMAJA"],[9,"BRYLIANA JIHAN AMIR"],[10,"DANDRY KORNELIUS ARISANDI"],[11,"DANIEL REVELINO FEIVEL WIDJANARKO"],[12,"DWI APRIELLA NUR RAHMA"],[13,"HANAR FAIRUS NADHIR AMRULLAH"],[14,"HANI DWI RAHMAWATI"],[15,"IKHSAN ABDUL ROZAK"],[16,"JAVIEN PUTRA MAHENDRA"],[17,"Linda Putri Darmawan"],[18,"LINGGA MAHARDIKA SUSATYO"],[19,"MUAZARAH FARRAS AMELIA"],[20,"MUHAMMAD FA'DLIL ASYHARI"],[21,"MUHAMMAD FAQIH ANWARI"],[22,"MUHAMMAD SIDIQ FATHUL MU'IZ"],[23,"NAILATUSSAKINAH"],[24,"NAJMA AULYA"],[25,"Nazila Karrolina Aishi Azzahra"],[26,"RAKHUL SURYA PERMANA"],[27,"RAYA GHANIA PUTRI"],[28,"REVALINA AULIA ZAHRA"],[29,"Salma Anggun Aurellia"],[30,"SANREZ REKSI KUSUMA"],[31,"SETIYA RIZKY MAULANA"],[32,"SHARA FATIKA ADDAHLANI"],[33,"SINTA PUSPA SARI"],[34,"ZULFA IBNUL KARIM"]],"X TJKT2":[[1,"AKHMAD FAKHRI HIDAYAT"],[2,"ALIA NUR AISYAH"],[3,"ANDIKA MAULANA MUHAIMIN"],[4,"ANDRIANSYAH MAULANA"],[5,"ANUM MANURAGA"],[6,"BRIANDA OCTA PRATAMA"],[7,"DAVID LUQMAN"],[8,"DEVANDRI KUSUMA"],[9,"DIMAS SALMAN KHAFI"],[10,"DIVANI AULIA NAJMI"],[11,"DWI AYU SASKIYA"],[12,"DYLLAN DEEP YOSHAN"],[13,"DZIKRI MAULANA"],[14,"FAREL RADHITYA PRADANA"],[15,"GHIFARI SAHILA DZAKI"],[16,"INDAH NAZIFA"],[17,"JATI WEKO ADI"],[18,"JERIKO PRANATA"],[19,"KHOIRUL MUNA"],[20,"MAYLINDA QHONITA PUTRI"],[21,"MIFTAKHUL ADIYANSYAH"],[22,"MUHAMAD RESTU FAUZI"],[23,"MUHAMMAD ABADI"],[24,"MUHAMMAD FAIZ FIRMANSYAH"],[25,"MUHAMMAD IRFAN SAPUTRA"],[26,"MUHAMMAD REVA IBRAHIMMUVIQ"],[27,"NAJA ADKAR KHAIR"],[28,"NIZAM JATI WARDOYO"],[29,"NUR FIDYAH"],[30,"OKFAL NOVA PUJI SATRIAWAN"],[31,"QUINSHA VIROSYA PRICILLIA"],[32,"REZA HAFID ARIYANTO"],[33,"RICKY ARDIANSYAH"],[34,"SITI ERNAWATI"],[35,"SYAHRINA LAILA FARHANA"],[36,"WILDAN SETIO FIRMANSYAH"]]},A={"X TB1":"UH-TB1-771","X TB2":"UH-TB2-382","X TO1":"UH-TO1-905","X TO2":"UH-TO2-214","X TJKT1":"UH-TJKT1-668","X TJKT2":"UH-TJKT2-453"},d=[{stem:'Seorang staf administrasi ingin merujuk sel A1 pada worksheet "Rekap" dari rumus yang ditulis di worksheet lain tanpa berpindah layar. Penulisan alamat sel yang benar adalah ...',opts:["Rekap!A1","Rekap#A1","Rekap-&amp;gt;A1","A1(Rekap)","=A1@Rekap"],ans:0},{stem:"Dalam satu workbook terdapat worksheet Data, Rumus, dan Laporan. Rumus di worksheet Laporan perlu mengambil nilai sel B5 pada worksheet Data. Bentuk referensi yang tepat adalah ...",opts:["=Data!B5","=Data.B5","=B5(Data)","=Laporan!B5","=[Data]B5"],ans:0},{stem:'Kolom diskon pada laporan penjualan berisi angka 0,1 tetapi harus ditampilkan sebagai "10%". Format sel yang perlu diterapkan pada kolom tersebut adalah ...',opts:["Format Persentase (%)","Format Mata Uang","Format Tanggal","Format Teks","Format Pecahan"],ans:0},{stem:'Tanggal transaksi tersimpan sebagai angka serial dan tampil "45678", bukan "12/06/2026". Agar tampil sebagai tanggal tanpa mengubah nilai aslinya, langkah yang tepat adalah ...',opts:["Mengubah format sel menjadi Date","Mengetik ulang angka sebagai teks","Menghapus sel lalu mengetik ulang tanggal manual","Menambahkan rumus =TEXT() tanpa mengubah format sel","Melebarkan kolom agar tanggal muncul"],ans:0},{stem:"Admin menulis rumus =100+50*2-10/2 untuk menghitung subtotal. Berdasarkan prioritas operasi (perkalian/pembagian sebelum penjumlahan/pengurangan), hasil yang benar adalah ...",opts:["290","145","195","100","75"],ans:2},{stem:"Rumus =(C2+D2)*E2/2 digunakan untuk nilai rata-rata tertimbang. Bagian mana yang dihitung PALING AWAL sesuai prioritas operasi?",opts:["(C2+D2) karena berada dalam tanda kurung","E2/2 karena pembagian","C2 saja karena paling kiri","D2*E2 karena perkalian berdekatan","Seluruh rumus dihitung bersamaan tanpa prioritas"],ans:0},{stem:"Rumus di C2 adalah =B2*$A$1, lalu disalin ke C3:C6. Bagian rumus yang TIDAK berubah pada seluruh hasil salinan adalah ...",opts:["$A$1 karena referensi absolut","B2 karena referensi relatif","C2 karena nomor baris","Seluruh rumus, karena disalin otomatis","Tidak ada yang tetap, semua berubah"],ans:0},{stem:"Rumus komisi penjualan memakai tarif tetap di sel B1 untuk semua baris. Saat disalin ke bawah, hasil menjadi salah karena acuan tarif ikut bergeser. Penyebab paling mungkin adalah ...",opts:["Rumus memakai referensi relatif (B1), seharusnya absolut ($B$1)","Sel B1 berisi teks, bukan angka","Rumus memakai tanda $ pada kolom data penjualan","Workbook belum disimpan","Format sel B1 adalah Format Umum"],ans:0},{stem:"Dari 10 baris transaksi, 2 sel kolom nominal dikosongkan karena transaksi batal. Fungsi yang tepat untuk menghitung banyaknya transaksi yang benar-benar memiliki nominal (angka) adalah ...",opts:["COUNT","COUNTA","SUM","AVERAGE","MAX"],ans:0},{stem:"Guru ingin mengetahui nilai tertinggi dan terendah dari 30 siswa sekaligus selisihnya (rentang nilai). Pasangan fungsi yang tepat digunakan adalah ...",opts:["MAX dan MIN","SUM dan AVERAGE","COUNT dan COUNTA","ROUND dan SUM","IF dan AND"],ans:0},{stem:"Hasil rata-rata nilai adalah 78,666666. Agar laporan rapi dengan satu angka di belakang koma, fungsi yang digunakan adalah ...",opts:["ROUND(nilai,1)","ROUND(nilai,0)","SUM(nilai,1)","AVERAGE(nilai,1)","INT(nilai)"],ans:0},{stem:"Status kelulusan ditentukan: nilai&amp;ge;75 ATAU kehadiran&amp;ge;90%. Siswa dengan nilai 70 dan kehadiran 92% seharusnya berstatus ...",opts:["Lulus, karena salah satu syarat (OR) terpenuhi","Tidak Lulus, karena nilai di bawah KKM","Tidak Lulus, karena harus memenuhi kedua syarat","Lulus, hanya jika kedua syarat terpenuhi","Data tidak cukup untuk menentukan status"],ans:0},{stem:'Rumus =IF(AND(B2&amp;ge;75,C2&amp;ge;80),"Tuntas","Belum Tuntas") diterapkan pada siswa dengan B2=80 dan C2=78. Status yang dihasilkan adalah ...',opts:["Belum Tuntas, karena AND mensyaratkan kedua kondisi terpenuhi","Tuntas, karena B2 sudah memenuhi syarat","Tuntas, karena OR berlaku otomatis","Belum Tuntas, karena B2 kurang dari 75","Error, karena AND tidak bisa dipakai dalam IF"],ans:0},{stem:'Dua rumus dibandingkan: =IF(OR(A2="VIP",A2="Member"),"Diskon","Tanpa Diskon") dan =IF(AND(A2="VIP",A2="Member"),"Diskon","Tanpa Diskon"). Jika pelanggan hanya berstatus "VIP", rumus mana yang menghasilkan "Diskon"?',opts:["Hanya rumus dengan OR","Hanya rumus dengan AND","Kedua rumus memberi hasil sama","Tidak ada yang memberi diskon","Keduanya menghasilkan error"],ans:0},{stem:'Manajer ingin mengetahui JUMLAH transaksi berkategori "Elektronik" dari ratusan baris data tanpa menghitung manual. Fungsi paling tepat adalah ...',opts:["COUNTIF","SUMIF","AVERAGEIF","COUNT","SUM"],ans:0},{stem:'Untuk menjumlahkan TOTAL penjualan khusus kategori "Minuman" berdasarkan kolom Kategori, fungsi yang tepat adalah ...',opts:['SUMIF(kategori,"Minuman",total)','COUNTIF(kategori,"Minuman")',"SUM(total)",'AVERAGEIF(total,"Minuman",kategori)','IF(kategori="Minuman",total)'],ans:0},{stem:'Guru ingin mengetahui RATA-RATA nilai khusus murid jurusan "TJKT" dari data gabungan seluruh jurusan. Fungsi yang tepat adalah ...',opts:["AVERAGEIF","SUMIF","COUNTIF","AVERAGE","MAX"],ans:0},{stem:"Tabel referensi harga disusun VERTIKAL (kode di kolom paling kiri, harga di kolom kanannya). Untuk mengambil harga berdasarkan kode transaksi, fungsi yang tepat adalah ...",opts:["VLOOKUP","HLOOKUP","LEFT","SUMIF","COUNTIF"],ans:0},{stem:"Tabel predikat nilai disusun HORIZONTAL (baris pertama batas nilai, baris kedua predikat). Fungsi pencarian yang sesuai struktur tabel ini adalah ...",opts:["HLOOKUP","VLOOKUP","SUMIF","LEFT","MID"],ans:0},{stem:"Rumus =VLOOKUP(A2,$D$2:$F$10,3,FALSE) menghasilkan #N/A. Penyebab paling mungkin adalah ...",opts:["Nilai pada A2 tidak ditemukan persis di kolom pertama tabel referensi","Rumus memakai tanda $ yang salah","Argumen FALSE seharusnya dihapus","Tabel referensi terlalu besar","Kolom indeks (3) terlalu kecil"],ans:0},{stem:'Kode produk "ELK-2026-048" perlu dipecah menjadi kategori, tahun, dan nomor urut. Untuk mengambil 3 huruf kategori di depan kode, fungsi yang digunakan adalah ...',opts:["LEFT","RIGHT","MID","LEN","HLOOKUP"],ans:0},{stem:'Untuk mengambil 3 digit nomor urut terakhir dari kode "ELK-2026-048", fungsi paling tepat adalah ...',opts:["RIGHT","LEFT","LEN","VLOOKUP","MID"],ans:0},{stem:"Data 200 transaksi perlu diurutkan dari nominal terbesar ke terkecil agar transaksi tertinggi cepat ditemukan. Fitur yang digunakan adalah ...",opts:["Sort (Urutkan) dari terbesar ke terkecil","Filter","Conditional Formatting","PivotTable","AutoFill"],ans:0},{stem:'Admin hanya ingin menampilkan baris berkategori "Alat Tulis" saja tanpa menghapus data kategori lain. Fitur yang tepat digunakan adalah ...',opts:["Filter","Sort","AutoFill","ROUND","LEN"],ans:0},{stem:"Guru ingin sel nilai otomatis berwarna merah jika nilainya di bawah KKM, tanpa mengubah rumus atau menulis ulang data. Fitur yang digunakan adalah ...",opts:["Conditional Formatting","Data Validation","Freeze Panes","PivotTable","Sort"],ans:0},{stem:'Setelah menerapkan Conditional Formatting "nilai&amp;lt;75 berwarna merah", seorang siswa mengubah nilainya dari 70 menjadi 80 secara manual. Yang akan terjadi pada warna selnya adalah ...',opts:["Warna merah otomatis hilang karena kondisi tidak lagi terpenuhi","Warna tetap merah karena format sudah permanen","Sel menjadi error","Perlu menerapkan ulang Conditional Formatting secara manual","Warna berubah hijau otomatis tanpa aturan baru"],ans:0},{stem:"Untuk menunjukkan proporsi kontribusi tiap kategori produk terhadap total penjualan dalam satu periode, jenis grafik yang paling sesuai adalah ...",opts:["Diagram Lingkaran (Pie Chart)","Diagram Garis (Line Chart)","Diagram Sebar (Scatter Plot)","Diagram Area 3D tanpa label","Tabel saja tanpa grafik"],ans:0},{stem:"Manajer memiliki 1000 baris data transaksi dan ingin dengan cepat melihat TOTAL penjualan per kategori per bulan tanpa menulis rumus satu per satu. Fitur paling efisien adalah ...",opts:["PivotTable","Conditional Formatting","Freeze Panes","Data Validation","Print Area"],ans:0},{stem:'Dalam PivotTable, untuk mengganti ringkasan dari "Sum of Total" menjadi "Average of Total", langkah yang tepat adalah ...',opts:["Mengubah pengaturan Value Field Settings dari Sum menjadi Average","Menghapus PivotTable lalu membuat rumus AVERAGE manual","Mengubah format sel menjadi Persentase","Menambahkan kolom baru secara manual","Mengganti nama field Total menjadi Average"],ans:0},{stem:"Sebuah laporan menampilkan error #DIV/0! pada kolom rata-rata penjualan harian. Penyebab paling mungkin adalah ...",opts:["Rumus membagi dengan sel yang bernilai 0 atau kosong","Rumus memakai referensi absolut","Format sel adalah Tanggal","Fungsi SUM digunakan secara salah","Data terlalu banyak untuk dihitung"],ans:0}],m=[{judul:"1. Menghitung Total Transaksi",skenario:"Toko ATK Makmur Jaya mencatat tiga transaksi harian berikut.",table:s(["Barang","Jumlah","Harga Satuan (Rp)"],[["Pensil 2B",40,2500],["Buku Tulis",25,3e3],["Penghapus",30,1500]],[1,2]),instruksi:"Hitung total nilai seluruh transaksi (setiap baris = Jumlah x Harga Satuan, lalu dijumlahkan semua baris). Ketik jawaban berupa angka saja.",hint:"Format jawaban: angka, misalnya 50000",type:"number",accept:[22e4],points:3},{judul:"2. Rata-rata Nilai Ulangan",skenario:"Nilai ulangan Informatika 5 siswa: 78, 85, 90, 66, 72.",table:"",instruksi:"Hitung rata-rata nilai, lalu bulatkan ke bilangan bulat terdekat (gunakan logika ROUND 0 angka desimal). Ketik hasil akhirnya.",hint:"Format jawaban: angka bulat",type:"number",accept:[78],points:3},{judul:"3. Barang Terlaris",skenario:"Rekap penjualan lima jenis barang di Toko ATK Makmur Jaya.",table:s(["Barang","Jumlah Terjual"],[["Pensil 2B",40],["Buku Tulis",25],["Penghapus",30],["Spidol",55],["Penggaris",18]],[1]),instruksi:"Gunakan logika fungsi MAX untuk menemukan jumlah terjual tertinggi, lalu sebutkan NAMA barang dengan penjualan tertinggi tersebut.",hint:"Format jawaban: nama barang",type:"text",accept:["spidol"],points:3},{judul:"4. Pembulatan Total Gaji",skenario:"Total gaji seorang karyawan koperasi siswa (gaji pokok + tunjangan) adalah Rp2.826.800.",table:"",instruksi:"Bulatkan total gaji tersebut ke ratusan ribu rupiah terdekat menggunakan logika fungsi ROUND. Ketik hasilnya sebagai angka.",hint:"Format jawaban: angka, contoh 2500000",type:"number",accept:[28e5],points:3},{judul:"5. Status Kelulusan dengan IF",skenario:"KKM mata pelajaran adalah 75. Nilai akhir seorang siswa adalah 74.",table:"",instruksi:'Tentukan status siswa tersebut menggunakan logika =IF(nilai&amp;ge;KKM,"Tuntas","Belum Tuntas"). Ketik status yang dihasilkan.',hint:"Format jawaban: Tuntas / Belum Tuntas",type:"text",accept:["belum tuntas"],points:3},{judul:"6. Status Gabungan dengan AND",skenario:"Syarat lulus praktik: nilai&amp;ge;75 DAN kehadiran&amp;ge;85%. Seorang siswa memiliki nilai 80 dan kehadiran 90%.",table:"",instruksi:"Tentukan status kelulusan siswa tersebut menggunakan logika IF+AND. Ketik status yang dihasilkan.",hint:"Format jawaban: Lulus / Tidak Lulus",type:"text",accept:["lulus"],points:3},{judul:"7. Menghitung Jumlah Barang per Kategori",skenario:"Daftar sembilan barang dengan kategorinya di gudang koperasi.",table:s(["Barang","Kategori"],[["Pensil","Alat Tulis"],["Buku","Alat Tulis"],["Teh Kotak","Minuman"],["Penghapus","Alat Tulis"],["Roti","Makanan"],["Spidol","Alat Tulis"],["Air Mineral","Minuman"],["Biskuit","Makanan"],["Air Mineral Besar","Minuman"]]),instruksi:'Gunakan logika COUNTIF untuk menghitung berapa banyak barang yang berkategori "Alat Tulis". Ketik hasilnya sebagai angka.',hint:"Format jawaban: angka",type:"number",accept:[4],points:3},{judul:"8. Total Penjualan per Kategori",skenario:"Rekap total penjualan (Rp) per barang di kantin sekolah.",table:s(["Barang","Kategori","Total Penjualan (Rp)"],[["Teh Kotak","Minuman",45e3],["Roti","Makanan",6e4],["Air Mineral","Minuman",6e4],["Biskuit","Makanan",38e3],["Air Mineral Besar","Minuman",3e4]],[2]),instruksi:'Gunakan logika SUMIF untuk menjumlahkan Total Penjualan khusus kategori "Minuman". Ketik hasilnya sebagai angka.',hint:"Format jawaban: angka",type:"number",accept:[135e3],points:3},{judul:"9. Rata-rata Nilai per Jurusan",skenario:"Nilai Informatika enam siswa dari dua jurusan berbeda.",table:s(["Nama (inisial)","Jurusan","Nilai"],[["A","TO",70],["B","TJKT",80],["C","TO",75],["D","TJKT",85],["E","TO",65],["F","TJKT",90]],[2]),instruksi:'Gunakan logika AVERAGEIF untuk menghitung rata-rata nilai khusus jurusan "TJKT". Ketik hasilnya sebagai angka.',hint:"Format jawaban: angka",type:"number",accept:[85],points:3},{judul:"10. Referensi Absolut untuk Tarif Tetap",skenario:"Tarif pajak tetap 10% disimpan pada sel $B$1. Harga barang pada baris 2 berada di sel C2.",table:"",instruksi:"Tulis rumus untuk menghitung pajak barang baris 2 (Harga x Tarif) dengan acuan tarif yang TIDAK berubah jika rumus disalin ke bawah.",hint:"Format jawaban: rumus, contoh =C2*$B$1",type:"formula",accept:[/^=?C2\*\$B\$1$/,/^=?\$B\$1\*C2$/,/^=?C2\*B\$1$/,/^=?B\$1\*C2$/],points:3},{judul:"11. Mengekstrak Kode Kategori",skenario:'Kode produk pada gudang ditulis dengan format "ATK-2026-015".',table:"",instruksi:"Ambil 3 karakter kode kategori di bagian depan kode tersebut menggunakan logika fungsi LEFT. Ketik hasilnya.",hint:"Format jawaban: teks, contoh ATK",type:"text",accept:["atk"],points:3},{judul:"12. Mencari Harga dengan VLOOKUP",skenario:"Tabel referensi harga barang berdasarkan kode.",table:s(["Kode","Harga Satuan (Rp)"],[["ATK01",2500],["ATK02",3e3],["ATK03",1500],["ATK04",5e3]],[1])+'&lt;p style="font-size:13.5px; color:var(--sub);"&gt;Sebuah transaksi tercatat menggunakan kode &lt;b&gt;ATK03&lt;/b&gt;.&lt;/p&gt;',instruksi:"Gunakan logika VLOOKUP untuk menentukan harga satuan barang berkode ATK03 dari tabel referensi di atas. Ketik hasilnya sebagai angka.",hint:"Format jawaban: angka",type:"number",accept:[1500],points:3},{judul:"13. Menentukan Predikat dengan HLOOKUP",skenario:"Tabel predikat nilai disusun horizontal sebagai berikut.",table:s(["Batas Nilai","0","60","75","90"],[["Predikat","E","C","B","A"]]),instruksi:"Seorang siswa memperoleh nilai 82. Gunakan logika HLOOKUP (pencarian pada batas nilai terdekat yang tidak melebihi 82) untuk menentukan predikatnya. Ketik predikat tersebut.",hint:"Format jawaban: satu huruf predikat",type:"text",accept:["b"],points:3},{judul:"14. Conditional Formatting Stok Kritis",skenario:"Stok lima barang di gudang koperasi, dengan aturan Conditional Formatting: stok &amp;lt; 5 diberi warna merah (stok kritis).",table:s(["Barang","Stok"],[["Pensil 2B",12],["Buku Tulis",3],["Penghapus",8],["Spidol",2],["Penggaris",15]],[1]),instruksi:"Sebutkan nama-nama barang yang akan diberi warna merah oleh aturan Conditional Formatting tersebut (pisahkan dengan koma jika lebih dari satu).",hint:"Format jawaban: nama barang, pisahkan koma",type:"set",accept:["buku tulis","spidol"],points:3},{judul:"15. Memilih Grafik yang Tepat",skenario:"Toko ATK Makmur Jaya memiliki data total penjualan selama 12 bulan terakhir dan ingin melihat tren naik-turunnya dari waktu ke waktu.",table:"",instruksi:"Jenis grafik apa yang paling tepat digunakan untuk menampilkan tren penjualan bulanan tersebut?",hint:"Format jawaban: nama jenis grafik",type:"text",accept:["grafik garis","line chart","diagram garis","grafik line","chart garis"],points:3}],k=[{q:"Sebuah laporan transaksi menampilkan total yang salah setelah rumus pada baris pertama disalin ke seluruh baris di bawahnya. Analisislah kemungkinan penyebab kesalahan tersebut dan rancang langkah perbaikannya.",groups:[{label:"menyebut referensi relatif/absolut sebagai penyebab",words:["referensi relatif","referensi absolut","acuan bergeser","salah acuan","absolut"]},{label:"menjelaskan tanda $ pada rumus",words:["tanda $","dollar","$a$1","mutlak"]},{label:"menyebut langkah pemeriksaan/perbaikan",words:["periksa ulang","cek rumus","audit","validasi","perbaiki rumus"]}],minWords:30},{q:"Bandingkan penggunaan fungsi dasar (SUM, AVERAGE, COUNT) dengan fungsi bersyarat (SUMIF, COUNTIF, AVERAGEIF) ketika menganalisis data penjualan yang berjumlah ribuan baris dengan berbagai kategori. Jelaskan kapan masing-masing lebih tepat digunakan.",groups:[{label:"menjelaskan fungsi dasar",words:["sum","average","count","fungsi dasar"]},{label:"menjelaskan fungsi bersyarat/kriteria",words:["sumif","countif","averageif","bersyarat","kriteria","kategori"]},{label:"mengaitkan dengan efisiensi data besar",words:["data besar","efisien","otomatis","cepat","ribuan baris"]}],minWords:30},{q:"Sebuah laporan otomatis menggabungkan referensi absolut, fungsi logika (IF/AND/OR), dan fungsi lookup (VLOOKUP/HLOOKUP). Evaluasilah mengapa kombinasi ketiganya penting agar laporan tetap akurat saat disalin atau diperbarui datanya.",groups:[{label:"menyebut lookup (VLOOKUP/HLOOKUP)",words:["vlookup","hlookup","lookup","pencarian data"]},{label:"menyebut fungsi logika",words:["if","and","or","logika","kondisi"]},{label:"menyebut referensi absolut",words:["absolut","$","acuan tetap","referensi tetap"]},{label:"mengaitkan dengan akurasi/konsistensi laporan",words:["akurat","konsisten","otomatis","andal"]}],minWords:30},{q:"Seorang manajer memiliki data penjualan bulanan yang kompleks dan harus memutuskan antara menggunakan Sort, Filter, Conditional Formatting, grafik, atau PivotTable. Jelaskan bagaimana memilih metode yang paling sesuai berdasarkan kebutuhan pengambilan keputusan.",groups:[{label:"menyebut Sort/Filter",words:["sort","urut","filter","saring"]},{label:"menyebut Conditional Formatting",words:["conditional formatting","format bersyarat","warna otomatis"]},{label:"menyebut grafik",words:["grafik","chart","diagram"]},{label:"menyebut PivotTable",words:["pivottable","pivot"]}],minWords:30},{q:"Rancanglah alur kerja (workflow) pengolahan data spreadsheet yang akurat, efisien, mudah diperiksa, dan menghasilkan laporan yang dapat dipertanggungjawabkan, mulai dari input data hingga laporan akhir.",groups:[{label:"menyebut struktur data/input yang rapi",words:["struktur data","rapi","konsisten","input data"]},{label:"menyebut validasi/pemeriksaan rumus",words:["validasi","cek kesalahan","periksa rumus","audit"]},{label:"menyebut dokumentasi/keterangan",words:["dokumentasi","catatan","keterangan","label"]},{label:"menyebut efisiensi/otomatisasi",words:["efisien","otomatis","template","konsisten"]}],minWords:30}],g=function(){try{var a=localStorage.getItem("examHotsSettings_v1");if(!a)return JSON.parse(JSON.stringify(l));var n=JSON.parse(a),e=JSON.parse(JSON.stringify(l));for(var t in e)n.hasOwnProperty(t)&amp;&amp;(e[t]=n[t]);return n.hasOwnProperty("v3att")||2!==e.maxAtt||(e.maxAtt=3),e.endpoint&amp;&amp;"https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec"!==e.endpoint||(e.endpoint=u),e}catch(a){return JSON.parse(JSON.stringify(l))}}(),p=null,b=null,h=null,c=!1,I=null,f=null;function N(){try{localStorage.setItem("examHotsSettings_v1",JSON.stringify(g))}catch(a){}}function M(a,n){return a+"|"+n}var T="UH-KESEMPATAN-2026";function S(a,n,e,t){return("0000"+function(a){for(var n=3735928559,e=1103547991,t=0;t&lt;a.length;t++){var i=a.charCodeAt(t);n=Math.imul(n^i,2654435761),e=Math.imul(e^i,1597334677)}return n=Math.imul(n^n&gt;&gt;&gt;16,2246822507)^Math.imul(e^e&gt;&gt;&gt;13,3266489909),4294967296*(2097151&amp;(e=Math.imul(e^e&gt;&gt;&gt;16,2246822507)^Math.imul(n^n&gt;&gt;&gt;13,3266489909)))+(n&gt;&gt;&gt;0)}([a,String(parseInt(n,10)),e,t,T].join("|")).toString(36).toUpperCase()).slice(-4)}function R(a){return g.maxAtt+(a&amp;&amp;a.bonus||0)}function v(a){try{var n=localStorage.getItem("examHots_state_"+a);if(n){var e=JSON.parse(n);return e.sent&amp;&amp;e.attempts&amp;&amp;e.attempts.length&lt;R(e)&amp;&amp;!(e.best&amp;&amp;e.best.total&gt;=g.kkm)&amp;&amp;(e.sent=!1),e}}catch(a){}return{attempts:[],sent:!1,sentValue:0,best:null}}function D(a,n){try{localStorage.setItem("examHots_state_"+a,JSON.stringify(n))}catch(a){}}function y(a,n){return"examHots_draft_"+a+"_"+n}function L(){if(p&amp;&amp;h)try{localStorage.setItem(y(p.key,h.attemptNo),JSON.stringify(h))}catch(a){}}function U(){a("hdrJudul").textContent=g.judul,a("hdrSub").textContent=(g.mapel||"Informatika")+" - Kelas X SMK"+(g.sekolah?" - "+g.sekolah:""),a("startTitle").textContent=g.judul;var n=["&lt;b&gt;KKM = "+g.kkm+".&lt;/b&gt; Begitu nilai akhirmu mencapai atau melebihi KKM, nilai itu langsung terkirim ke guru.","Kamu punya &lt;b&gt;"+g.maxAtt+" kali kesempatan&lt;/b&gt;. Jika sampai percobaan terakhir belum mencapai KKM, sistem mengirim &lt;b&gt;nilai tertinggi&lt;/b&gt; dari seluruh percobaan.","Waktu pengerjaan &lt;b&gt;"+g.durasiMenit+" menit&lt;/b&gt;, dihitung mundur sejak ujian dimulai. Saat waktu habis, jawaban yang sudah terisi otomatis dikumpulkan.","Soal dan urutan pilihan jawaban &lt;b&gt;diacak otomatis&lt;/b&gt; setiap sesi.","Pilih &lt;b&gt;Kelas&lt;/b&gt;, lalu &lt;b&gt;No. Absen&lt;/b&gt; - nama akan terisi otomatis dari data presensi.","Masukkan &lt;b&gt;Token Kelas&lt;/b&gt; yang diberikan guru sebelum bisa memulai ujian.","&lt;b&gt;Keamanan ujian:&lt;/b&gt; jangan berpindah ke tab/aplikasi lain, keluar dari layar penuh, atau membuka alat pengembang (F12/Inspect). Sistem mencatat setiap pelanggaran; setelah mencapai &lt;b&gt;"+g.batasPelanggaran+" kali pelanggaran&lt;/b&gt;, ujian otomatis diakhiri dan ditandai untuk ditinjau guru. Deteksi ini adalah indikator, bukan bukti pasti kecurangan, dan tidak bisa mencegah penggunaan perangkat lain, kamera, atau tangkapan layar secara mutlak.","Klik kanan dan tempel (paste) dinonaktifkan pada kolom jawaban.","Status pengerjaan disimpan otomatis di peramban ini sehingga tidak mudah hilang jika halaman tidak sengaja dimuat ulang.","Jawaban tidak dapat diubah lagi setelah dikumpulkan secara final.","Setelah selesai, kamu bisa mengunduh bukti pengerjaan (PDF), mencetak, atau mengekspor rekap ke CSV."];a("rulesList").innerHTML=n.map(function(a){return"&lt;li&gt;"+a+"&lt;/li&gt;"}).join("")}function K(){var n=a("fKelas");n.value;n.innerHTML='&lt;option value=""&gt;- Pilih Kelas -&lt;/option&gt;',Object.keys(o).forEach(function(a){var e=document.createElement("option");e.value=a,e.textContent=a,n.appendChild(e)})}function E(){var n=a("secChip");n&amp;&amp;(!h||h.violations&lt;=0?(n.textContent="Keamanan: Aman",n.className="chip sec"):(n.textContent="Keamanan: "+h.violations+"x pelanggaran",n.className="chip sec warn"))}function H(e){c&amp;&amp;h&amp;&amp;!h.finishing&amp;&amp;(h.violations=(h.violations||0)+1,E(),L(),function(e){if(!a("secOverlay")){var t=document.createElement("div");t.id="secOverlay",t.className="overlay danger",t.innerHTML='&lt;div class="box"&gt;&lt;h2&gt;Pelanggaran Keamanan Terdeteksi&lt;/h2&gt;&lt;p&gt;'+n(e)+"&lt;/p&gt;&lt;p&gt;Pelanggaran ke-&lt;b&gt;"+(h?h.violations:0)+"&lt;/b&gt; dari batas &lt;b&gt;"+g.batasPelanggaran+'&lt;/b&gt;. Jika batas tercapai, ujian otomatis diakhiri dan ditandai untuk ditinjau guru.&lt;/p&gt;&lt;div class="row"&gt;&lt;button class="btn btn-bad" id="secOverlayClose"&gt;Kembali ke Ujian&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;',document.body.appendChild(t),a("secOverlayClose").addEventListener("click",function(){var n=a("secOverlay");n&amp;&amp;n.remove(),O()})}}(e),h.violations&gt;=g.batasPelanggaran&amp;&amp;z(!0,!0))}function O(){try{var a=document.documentElement;a.requestFullscreen&amp;&amp;!document.fullscreenElement&amp;&amp;a.requestFullscreen().catch(function(){})}catch(a){}}function F(){try{document.fullscreenElement&amp;&amp;document.exitFullscreen&amp;&amp;document.exitFullscreen().catch(function(){})}catch(a){}}function P(){return d.length+m.length+k.length}function w(a){return a&lt;d.length?{type:"pg",pos:a}:a&lt;d.length+m.length?{type:"praktik",pos:a-d.length}:{type:"esai",pos:a-d.length-m.length}}function j(a){return"pg"===a.type?h.pgOrder[a.pos]:"praktik"===a.type?h.praktikOrder[a.pos]:h.esaiOrder[a.pos]}function C(){var n=b.attempts.length+1,i=function(a,n){try{var e=localStorage.getItem(y(a,n));if(e)return JSON.parse(e)}catch(a){}return null}(p.key,n);if(i)h=i;else{h={attemptNo:n,pgOrder:e(d.map(function(a,n){return n})),praktikOrder:e(m.map(function(a,n){return n})),esaiOrder:e(k.map(function(a,n){return n})),pgOptOrder:[],answers:new Array(P()).fill(null),marked:new Array(P()).fill(!1),idx:0,violations:0,finishing:!1,timeLeft:60*g.durasiMenit,startedAt:Date.now()};for(var r=0;r&lt;d.length;r++)h.pgOptOrder.push(e([0,1,2,3,4]))}c=!0,a("whoLbl").textContent=p.nama+" - "+p.kelas+" / No. "+p.absen,E(),t(a("examErr"),""),Y(),J(),B(),$("exam"),O(),x(),f=setInterval(function(){if(h){h.timeLeft--;var n=a("timerChip");n.textContent=function(a){a=Math.max(0,0|a);var n=Math.floor(a/60),e=a%60;return(n&lt;10?"0":"")+n+":"+(e&lt;10?"0":"")+e}(h.timeLeft),n.className="chip timer"+(h.timeLeft&lt;=120?" low":""),h.timeLeft%5==0&amp;&amp;L(),h.timeLeft&lt;=0&amp;&amp;(x(),z(!0,!1))}},1e3),L()}function x(){f&amp;&amp;(clearInterval(f),f=null)}function B(){var n=0;h.answers.forEach(function(a){null!==a&amp;&amp;""!==a&amp;&amp;n++}),a("progChip").textContent="Soal "+(h.idx+1)+" dari "+P()+" - "+n+" terjawab"}function Y(){for(var n="",e=P(),t=0;t&lt;e;t++){var i=[""],r=h.answers[t];null!==r&amp;&amp;""!==r&amp;&amp;i.push("answered"),h.marked[t]&amp;&amp;i.push("marked"),t===h.idx&amp;&amp;i.push("current"),n+='&lt;button type="button" class="'+i.join(" ").trim()+'" data-i="'+t+'"&gt;'+(t+1)+"&lt;/button&gt;"}a("numGrid").innerHTML=n,Array.prototype.forEach.call(a("numGrid").querySelectorAll("button"),function(a){a.addEventListener("click",function(){V(parseInt(a.getAttribute("data-i"),10))})})}function G(){var n=w(h.idx);j(n);if("pg"===n.type){var e=document.querySelector('input[name="curPg"]:checked');h.answers[h.idx]=e?e.value:null}else if("praktik"===n.type){var t=a("praktikInput");h.answers[h.idx]=t?t.value:""}else{var i=a("esaiInput");h.answers[h.idx]=i?i.value:""}}function J(){var e=w(h.idx),t=j(e),i="";if("pg"===e.type){a("qSection").textContent="BAGIAN A - PILIHAN GANDA";var s=d[t],u=h.pgOptOrder[e.pos],l=["A","B","C","D","E"];i='&lt;div class="q-head"&gt;Soal '+(h.idx+1)+'&lt;/div&gt;&lt;div class="q-stem"&gt;'+s.stem+"&lt;/div&gt;",u.forEach(function(a,n){var e=null!==h.answers[h.idx]&amp;&amp;parseInt(h.answers[h.idx],10)===n?"checked":"";i+='&lt;label class="opt-row"&gt;&lt;input type="radio" name="curPg" value="'+n+'" '+e+"&gt;&lt;span&gt;&lt;b&gt;"+l[n]+".&lt;/b&gt; "+s.opts[a]+"&lt;/span&gt;&lt;/label&gt;"})}else if("praktik"===e.type){a("qSection").textContent="BAGIAN B - PRAKTIK LANGSUNG";var o=m[t];i='&lt;div class="q-head"&gt;Soal '+(h.idx+1)+'&lt;/div&gt;&lt;div class="q-stem"&gt;&lt;b&gt;'+o.judul+"&lt;/b&gt;&lt;br&gt;"+o.skenario+"&lt;/div&gt;"+(o.table||"")+'&lt;p style="font-size:14px;"&gt;'+o.instruksi+'&lt;/p&gt;&lt;label class="fl"&gt;Jawaban&lt;/label&gt;&lt;input type="text" id="praktikInput" autocomplete="off" value="'+n(h.answers[h.idx]||"")+'"&gt;&lt;div class="cnt-small"&gt;'+n(o.hint||"")+"&lt;/div&gt;"}else{a("qSection").textContent="BAGIAN C - ESAI";var A=k[t];i='&lt;div class="q-head"&gt;Soal '+(h.idx+1)+'&lt;/div&gt;&lt;div class="q-stem"&gt;'+A.q+'&lt;/div&gt;&lt;label class="fl"&gt;Jawaban (minimal '+(A.minWords||30)+' kata)&lt;/label&gt;&lt;textarea id="esaiInput"&gt;'+n(h.answers[h.idx]||"")+'&lt;/textarea&gt;&lt;div class="cnt-small"&gt;&lt;span id="esaiWc"&gt;0&lt;/span&gt; kata&lt;/div&gt;'}a("qBox").innerHTML=i,function(n){if("pg"===n.type)Array.prototype.forEach.call(document.querySelectorAll('input[name="curPg"]'),function(a){a.addEventListener("change",function(){G(),Y(),B(),L()})});else if("praktik"===n.type){var e=a("praktikInput");e.addEventListener("paste",function(a){a.preventDefault()}),e.addEventListener("input",function(){G(),Y(),B(),L()})}else{var t=a("esaiInput");t.addEventListener("paste",function(a){a.preventDefault()}),t.addEventListener("input",function(){G(),a("esaiWc").textContent=r(t.value),Y(),B(),L()}),a("esaiWc").textContent=r(t.value)}}(e),a("btnMark").textContent=h.marked[h.idx]?"Batalkan Tanda Tinjau":"Tandai untuk Ditinjau",a("btnPrev").disabled=0===h.idx,a("btnNext").disabled=h.idx===P()-1}function V(a){G(),h.idx=Math.max(0,Math.min(P()-1,a)),J(),Y(),B(),L()}function Z(a,n){if(null==n||""===String(n).trim())return{score:0,ok:!1};if("number"===a.type){var e=(r=n,""===(s=String(null==r?"":r).replace(/[^\d\-]/g,""))?NaN:parseFloat(s)),t=a.accept[0];return isNaN(e)?{score:0,ok:!1}:e===t?{score:a.points,ok:!0}:Math.abs(e-t)&lt;=.1*Math.abs(t)?{score:Math.round(.5*a.points*10)/10,ok:!1}:{score:0,ok:!1}}var r,s;if("text"===a.type){var u=i(n),l=a.accept.indexOf(u)&gt;=0;return{score:l?a.points:0,ok:l}}if("formula"===a.type){var o=String(n).replace(/\s+/g,"").toUpperCase(),A=a.accept.some(function(a){return a.test(o)});return{score:A?a.points:0,ok:A}}if("set"===a.type){var d=String(n).split(/[,\n]/).map(i).filter(Boolean),m=a.accept.slice().sort(),k=d.slice().sort();if(m.length===k.length&amp;&amp;m.every(function(a,n){return a===k[n]}))return{score:a.points,ok:!0};var g=d.filter(function(n){return a.accept.indexOf(n)&gt;=0}).length;return g&gt;0?{score:Math.round(a.points*(g/m.length)*10)/10,ok:!1}:{score:0,ok:!1}}return{score:0,ok:!1}}function W(a,n){var e=String(n||"");if(r(e)&lt;(a.minWords||30))return{score:0,matched:0,total:a.groups.length,feedback:"Jawaban terlalu singkat (minimal "+(a.minWords||30)+" kata)."};var t=i(e),s=[],u=[];a.groups.forEach(function(a){a.words.some(function(a){return t.indexOf(a)&gt;=0})?s.push(a.label):u.push(a.label)});var l=s.length/a.groups.length,o=Math.round(100*l),A="Estimasi otomatis berbasis kata kunci (dapat diverifikasi guru). Aspek yang sudah tampak: "+(s.length?s.join("; "):"-")+". Aspek yang masih kurang: "+(u.length?u.join("; "):"-")+".";return{score:o,matched:s.length,total:a.groups.length,feedback:A}}function $(n){["start","exam","result"].forEach(function(e){a("screen"+e.charAt(0).toUpperCase()+e.slice(1)).classList.toggle("hidden",e!==n)})}function _(){var n=a("fKelas").value,e=a("fAbsen").value,i=a("fNama").value.trim(),r=a("fToken").value.trim();if(t(a("startErr"),""),n)if(e)if(i)if(r)if(A[n]&amp;&amp;r.toUpperCase()===A[n].toUpperCase()){(p={kelas:n,absen:String(parseInt(e,10)),nama:i}).key=M(p.kelas,p.absen),b=v(p.key);var s=(a("fKode").value||"").trim();if(s){var u=function(a,n,e){var t=/^KT([1-3])-([A-Z0-9]{4})-([A-Z0-9]{4})$/.exec(String(a||"").trim().toUpperCase());return t&amp;&amp;S(n,e,t[1],t[2])===t[3]?{n:parseInt(t[1],10),id:t[2]+t[3]}:null}(s,p.kelas,p.absen);if(!u)return void t(a("startErr"),"Kode Kesempatan Tambahan tidak valid untuk murid ini. Periksa penulisan kode atau hubungi guru.");if(b.usedCodes=b.usedCodes||[],b.usedCodes.indexOf(u.id)&gt;=0)return void t(a("startErr"),"Kode ini sudah pernah dipakai.");if(b.best&amp;&amp;b.best.total&gt;=g.kkm)return void t(a("startErr"),"Nilaimu sudah mencapai KKM, kode tambahan tidak diperlukan.");b.usedCodes.push(u.id),b.bonus=(b.bonus||0)+u.n,b.attempts.length&lt;R(b)&amp;&amp;(b.sent=!1),D(p.key,b),a("fKode").value=""}if(b.sent||b.attempts.length&gt;=R(b))return na(!1),void $("result");D(p.key,b),C()}else t(a("startErr"),"Token Kelas salah. Minta token yang benar kepada guru.");else t(a("startErr"),"Masukkan Token Kelas yang diberikan guru.");else t(a("startErr"),"Nama tidak ditemukan untuk No. Absen ini. Hubungi guru.");else t(a("startErr"),"Pilih No. Absen terlebih dahulu.");else t(a("startErr"),"Pilih Kelas terlebih dahulu.")}function q(){var n,e,i,r;(n=a("fKelas").value,e=a("fAbsen").value,i=a("fNama").value.trim(),r=a("fToken").value.trim(),t(a("startErr"),""),n?e?i?r?A[n]&amp;&amp;r.toUpperCase()===A[n].toUpperCase()?((p={kelas:n,absen:String(parseInt(e,10)),nama:i}).key=M(p.kelas,p.absen),1):(t(a("startErr"),"Token Kelas salah. Minta token yang benar kepada guru."),0):(t(a("startErr"),"Masukkan Token Kelas yang diberikan guru."),0):(t(a("startErr"),"Nama tidak ditemukan untuk No. Absen ini. Hubungi guru."),0):(t(a("startErr"),"Pilih No. Absen terlebih dahulu."),0):(t(a("startErr"),"Pilih Kelas terlebih dahulu."),0))&amp;&amp;((b=v(p.key)).best&amp;&amp;b.attempts.length?(na(!1),$("result")):t(a("startErr"),"Belum ada hasil ujian untuk murid ini di perangkat ini. Nilai tersimpan di peramban yang dipakai saat mengerjakan."))}function z(n,e){if(h&amp;&amp;!h.finishing){h.finishing=!0,G(),x(),c=!1,F();var t=function(){for(var a=[],n=[],e=[],t=0,i=0,r=0,s=0;s&lt;d.length;s++){var u=j({type:"pg",pos:s}),l=d[u],o=h.answers[s],A=h.pgOptOrder[s],g=(null!==o?A[parseInt(o,10)]:null)===l.ans;g&amp;&amp;(t+=1),a.push({n:s+1,ok:g,answered:null!==o})}for(var p=0;p&lt;m.length;p++){var b=d.length+p,c=j({type:"praktik",pos:p}),I=m[c],f=Z(I,h.answers[b]);i+=f.score,n.push({n:b+1,judul:I.judul,score:f.score,max:I.points,ok:f.ok})}for(var N=0;N&lt;k.length;N++){var M=d.length+m.length+N,T=j({type:"esai",pos:N}),S=W(k[T],h.answers[M]),R=Math.round(S.score/100*5*10)/10;r+=R,e.push({n:M+1,score:R,max:5,feedback:S.feedback})}return{pg:t,praktik:i=Math.round(10*i)/10,esai:r=Math.round(10*r)/10,total:Math.round(Math.min(100,t+i+r)),pgDetail:a,praktikDetail:n,esaiDetail:e,violations:h.violations||0,startedAt:h.startedAt,finishedAt:Date.now()}}();t.auto=!!n,t.flagged=!!e,t.no=b.attempts.length+1,b.attempts.push(t),(!b.best||t.total&gt;b.best.total)&amp;&amp;(b.best=t);var i=!1;if(t.total&gt;=g.kkm?(b.sent=!0,b.sentValue=t.total,i=!0):b.attempts.length&gt;=R(b)&amp;&amp;(b.sent=!0,b.sentValue=b.best.total,i=!0),D(p.key,b),function(a,n){try{localStorage.removeItem(y(a,n))}catch(a){}}(p.key,h.attemptNo),I=t,na(i),$("result"),i){var r=b.sentValue;a("rSend").textContent="Mengirim nilai ke guru...",a("rSend").className="msg info show",X(r,t).then(function(a){Q(a,r,!1),!1===a.ok&amp;&amp;(b.sent=!0,D(p.key,b))})}}}function X(n,e){if(!g.endpoint)return Promise.resolve({ok:!1,msg:"Endpoint belum dikonfigurasi."});a("gTimestamp").value=(new Date).toISOString(),a("gNama").value=p.nama,a("gKelas").value=p.kelas,a("gAbsen").value=p.absen,a("gJudul").value=g.judul,a("gNilaiPG").value=e.pg,a("gNilaiPraktik").value=e.praktik,a("gNilaiEsai").value=e.esai,a("gNilai").value=n,a("gNilaiSTS").value=n,a("gSkor").value=n,a("gStatus").value=n&gt;=g.kkm?"Tuntas":"Belum Tuntas",a("gDurasi").value=Math.round((e.finishedAt-e.startedAt)/6e4)+" menit",a("gPelanggaran").value=e.violations+(e.flagged?" (ditandai untuk ditinjau)":"");var t=function(){try{a("gform").action=g.endpoint,a("gform").submit()}catch(a){}};if(!window.fetch||!window.URLSearchParams)return t(),Promise.resolve({ok:null,msg:"Dikirim, tetapi peramban tidak dapat memastikan penerimaan."});var i=new URLSearchParams;return["timestamp","nama","kelas","absen","judul","nilaiPG","nilaiPraktik","nilaiEsai","nilaiAkhir","nilaiSTS","status","durasi","pelanggaran","skor"].forEach(function(a){var n=document.querySelector('#gform [name="'+a+'"]');i.append(a,n?n.value:"")}),fetch(g.endpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"},body:i.toString(),redirect:"follow"}).then(function(a){return a.text()}).then(function(a){return"OK"===(a=String(a||"").trim())?{ok:!0}:/^ERROR/i.test(a)?{ok:!1,msg:"Server menolak: "+a.slice(0,120)}:{ok:!1,msg:'Server tidak membalas dengan benar (URL harus milik skrip penerima nilai Kode_Apps_Script_Ujian_HOTS.gs). Pastikan deployment Apps Script: "Jalankan sebagai: Saya" dan "Yang memiliki akses: Siapa saja", lalu Deploy versi baru.'}}).catch(function(){return t(),{ok:null,msg:"Tidak dapat memastikan penerimaan (jaringan/izin akses). Pengiriman dicoba lewat jalur cadangan."}})}function Q(n,e,t){var i=a("rSend");i&amp;&amp;(!0===n.ok?(i.textContent="Nilai "+e+" "+(t?"dikirim ulang":"dikirim")+" dan DITERIMA server pukul "+(new Date).toLocaleTimeString("id-ID")+".",i.className="msg ok show"):!1===n.ok?(i.textContent="Nilai BELUM terkirim. "+n.msg+" Simpan bukti PDF dan hubungi guru.",i.className="msg err show"):(i.textContent=n.msg+' Jika tidak muncul di rekap guru, tekan "Kirim Ulang Nilai" atau hubungi guru.',i.className="msg info show"))}function aa(a,e,t,i){var r=t?Math.round(e/t*100):0;return'&lt;div class="bar-row"&gt;&lt;div class="lbl"&gt;&lt;span&gt;'+n(a)+"&lt;/span&gt;&lt;span&gt;"+e+" / "+t+'&lt;/span&gt;&lt;/div&gt;&lt;div class="bar-track"&gt;&lt;div class="bar-fill" style="width:'+r+"%; background:"+i+';"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;'}function na(n){var e=b.best,t=b.attempts.length,i=e?e.total:0,r=e&amp;&amp;e.total&gt;=g.kkm;I=e||I,a("rVal").textContent=i,a("ring").style.setProperty("--p",i),a("ring").style.setProperty("--ringc",r?"#15803d":"#d97706");var s=a("rPill");if(b.sent?(s.textContent=r?"TUNTAS - KKM TERCAPAI":"BELUM TUNTAS - NILAI TERTINGGI DIKIRIM",s.className="status-pill "+(r?"ok":"no")):(s.textContent="BELUM MENCAPAI KKM ("+g.kkm+")",s.className="status-pill no"),0!==t&amp;&amp;e)if(b.sent&amp;&amp;!n)a("rTitle").textContent="Nilai kamu sudah tercatat",a("rDesc").textContent="Kesempatan mengerjakan sudah selesai. Nilai yang tercatat: "+b.sentValue+".";else if(b.sent)a("rTitle").textContent=r?"Selamat, kamu mencapai KKM!":"Kesempatan terakhir selesai",a("rDesc").textContent=r?"Nilai akhirmu "+e.total+" dan sudah dikirim ke guru.":"KKM belum tercapai. Nilai tertinggi dari "+t+" percobaan ("+e.total+") dikirim ke guru.";else{var u=R(b)-t,l=b.attempts[t-1];a("rTitle").textContent="Nilaimu belum mencapai KKM",a("rDesc").textContent="Nilai percobaan ini "+l.total+" (tertinggi sementara "+e.total+"). Kamu masih punya "+u+" kesempatan."}else a("rTitle").textContent="Belum ada data",a("rDesc").textContent="";e&amp;&amp;e.violations&gt;0&amp;&amp;(a("rDesc").textContent+=" Tercatat "+e.violations+" pelanggaran keamanan pada percobaan ini"+(e.flagged?" (ditandai untuk ditinjau guru).":".")),a("riNama").textContent=p.nama,a("riKelas").textContent=p.kelas,a("riAbsen").textContent=p.absen,a("riKKM").textContent=g.kkm,e&amp;&amp;(a("riMulai").textContent=new Date(e.startedAt).toLocaleString("id-ID"),a("riSelesai").textContent=new Date(e.finishedAt).toLocaleString("id-ID"),a("riDurasi").textContent=Math.round((e.finishedAt-e.startedAt)/6e4)+" menit",a("riPelanggaran").textContent=e.violations+(e.flagged?" (ditinjau guru)":""));var o="";e&amp;&amp;(o+=aa("A. Pilihan Ganda",e.pg,30,"#1d4ed8"),o+=aa("B. Praktik Langsung",e.praktik,45,"#d97706"),o+=aa("C. Esai",e.esai,25,"#0f766e")),a("rBars").innerHTML=o;var A=a("rSend");g.endpoint?b.sent?(A.textContent="Nilai "+b.sentValue+" tercatat dan dikirim ke guru"+(n?"":" (pada sesi sebelumnya)")+".",A.className="msg info show"):(A.textContent="Nilai belum dikirim ke guru. Nilai dikirim otomatis begitu KKM tercapai atau setelah percobaan terakhir.",A.className="msg info show"):(A.textContent="Endpoint pengiriman nilai belum dikonfigurasi guru - nilai hanya tersimpan di peramban ini.",A.className="msg info show"),a("rBenchmark").textContent="Catatan: nilai esai merupakan estimasi otomatis berbasis pencocokan kata kunci dan perlu diverifikasi guru."}document.addEventListener("visibilitychange",function(){document.hidden&amp;&amp;H("Berpindah ke tab atau aplikasi lain")}),window.addEventListener("blur",function(){document.hidden||H("Jendela ujian kehilangan fokus")}),document.addEventListener("fullscreenchange",function(){c&amp;&amp;!document.fullscreenElement&amp;&amp;H("Keluar dari mode layar penuh")}),document.addEventListener("keydown",function(a){if(c){var n=(a.key||"").toLowerCase(),e=(a.ctrlKey||a.metaKey)&amp;&amp;a.shiftKey&amp;&amp;("i"===n||"j"===n||"c"===n||"k"===n);("f12"===n||e||(a.ctrlKey||a.metaKey)&amp;&amp;"u"===n)&amp;&amp;(a.preventDefault(),H("Mencoba membuka alat pengembang browser"))}}),document.addEventListener("contextmenu",function(a){c&amp;&amp;a.preventDefault()}),document.addEventListener("paste",function(a){c&amp;&amp;a.preventDefault()});function ea(){a("guruCard").classList.remove("hidden"),a("screenStart").classList.add("hidden"),a("screenExam").classList.add("hidden"),a("screenResult").classList.add("hidden");try{window.location.hash="guru"}catch(a){}}function ta(){a("guruCard").classList.add("hidden"),a("guruLockBox").classList.remove("hidden"),a("guruForm").classList.add("hidden"),a("guruCode").value="",t(a("guruLockErr"),"");try{"#guru"===window.location.hash&amp;&amp;(window.location.hash="")}catch(a){}$("start")}function ia(){"#guru"===window.location.hash&amp;&amp;ea()}function ra(){var e=a("tKelas").value,i=a("tAbsen").value,r=parseInt(a("tJumlah").value,10)||1;if(t(a("gKodeErr"),""),e&amp;&amp;i){var s=((o[e]||[]).filter(function(a){return String(a[0])===String(i)})[0]||[])[1]||"",u=function(a,n,e){for(var t="ABCDEFGHJKLMNPQRSTUVWXYZ23456789",i="",r=0;r&lt;4;r++)i+=t.charAt(Math.floor(32*Math.random()));return"KT"+e+"-"+i+"-"+S(a,n,e,i)}(e,i,r),l=document.createElement("div");l.className="msg ok show",l.style.marginBottom="6px",l.innerHTML="&lt;b&gt;"+n(s)+"&lt;/b&gt; ("+n(e)+", No. "+n(i)+") &amp;mdash; +"+r+' kesempatan&lt;br&gt;Kode: &lt;b style="font-size:17px; letter-spacing:1px; user-select:all;"&gt;'+n(u)+"&lt;/b&gt;";var A=a("gKodeList");A.insertBefore(l,A.firstChild)}else t(a("gKodeErr"),"Pilih kelas dan No. Absen murid terlebih dahulu.")}function sa(){var n;!(n=a("tKelas"))||n.options.length&gt;1||Object.keys(o).forEach(function(a){var e=document.createElement("option");e.value=a,e.textContent=a,n.appendChild(e)}),a("sJudul").value=g.judul,a("sSekolah").value=g.sekolah,a("sMapel").value=g.mapel,a("sKKM").value=g.kkm,a("sDurasi").value=g.durasiMenit,a("sBatasPelanggaran").value=g.batasPelanggaran,a("sMaxAtt").value=g.maxAtt,a("sPembahasan").value=g.pembahasan?"1":"0",a("sEndpoint").value=g.endpoint}K(),U();var ua={btnStart:function(){_()},btnPrev:function(){V(h.idx-1)},btnNext:function(){V(h.idx+1)},btnMark:function(){h.marked[h.idx]=!h.marked[h.idx],J(),Y(),L()},btnFinish:function(){!function(){G();var n=0,e=0;h.answers.forEach(function(a){null!==a&amp;&amp;""!==a||n++}),h.marked.forEach(function(a){a&amp;&amp;e++});var t=document.createElement("div");t.id="finishOverlay",t.className="overlay",t.innerHTML='&lt;div class="box"&gt;&lt;h2&gt;Kumpulkan Jawaban?&lt;/h2&gt;&lt;p&gt;Soal belum terjawab: &lt;b&gt;'+n+"&lt;/b&gt;. Soal ditandai untuk ditinjau: &lt;b&gt;"+e+'&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;Jawaban tidak dapat diubah lagi setelah dikumpulkan.&lt;/p&gt;&lt;div class="row"&gt;&lt;button class="btn btn-outline" id="finishCancel"&gt;Batal&lt;/button&gt;&lt;button class="btn btn-primary" id="finishOk"&gt;Ya, Kumpulkan&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;',document.body.appendChild(t),a("finishCancel").addEventListener("click",function(){t.remove()}),a("finishOk").addEventListener("click",function(){t.remove(),z(!1,!1)})}()},btnPdf:function(){!function(){if(I){var a=new(window.jspdf?window.jspdf.jsPDF:window.jsPDF)({unit:"mm",format:"a4"}),n=I;a.setFontSize(13),a.text(g.judul,14,16),a.setFontSize(9),a.text("Bukti Pengerjaan Ujian - "+(g.sekolah||""),14,22),a.autoTable({startY:28,theme:"grid",styles:{fontSize:9},head:[["Nama","Kelas","No. Absen","Tanggal"]],body:[[p.nama,p.kelas,p.absen,new Date(n.finishedAt).toLocaleString("id-ID")]]});var e=a.lastAutoTable.finalY+6;a.autoTable({startY:e,theme:"grid",styles:{fontSize:9},head:[["Bagian","Skor","Maksimal"]],body:[["A. Pilihan Ganda",n.pg,30],["B. Praktik Langsung",n.praktik,45],["C. Esai (estimasi otomatis)",n.esai,25],["Nilai Akhir",n.total,100]]}),e=a.lastAutoTable.finalY+6,a.autoTable({startY:e,theme:"grid",styles:{fontSize:9},head:[["KKM","Status","Durasi","Pelanggaran Keamanan"]],body:[[g.kkm,n.total&gt;=g.kkm?"Tuntas":"Belum Tuntas",Math.round((n.finishedAt-n.startedAt)/6e4)+" menit",n.violations+(n.flagged?" (ditinjau guru)":"")]]}),e=a.lastAutoTable.finalY+6;var t=n.pgDetail.map(function(a){return["A"+a.n,a.answered?a.ok?"Benar":"Salah":"Kosong"]});a.autoTable({startY:e,theme:"grid",styles:{fontSize:8},margin:{bottom:14},head:[["No. Soal PG","Hasil"]],body:t}),a.addPage();var i=n.praktikDetail.map(function(a){return["B"+a.n,a.judul,a.score+" / "+a.max]});a.autoTable({startY:16,theme:"grid",styles:{fontSize:8},head:[["No.","Praktik","Skor"]],body:i}),e=a.lastAutoTable.finalY+6;var r=n.esaiDetail.map(function(a){return["C"+a.n,a.score+" / "+a.max]});a.autoTable({startY:e,theme:"grid",styles:{fontSize:8},head:[["No. Esai","Skor"]],body:r}),e=a.lastAutoTable.finalY+8,a.setFontSize(8),a.text("Catatan: nilai esai adalah estimasi otomatis berbasis kata kunci dan dapat diverifikasi ulang oleh guru.",14,e),a.save("Bukti_Ujian_HOTS_"+p.nama.replace(/\s+/g,"_")+".pdf")}}()},btnPrint:function(){window.print()},btnCsv:function(){!function(){if(I){var a=I,n=[["Nama",p.nama],["Kelas",p.kelas],["No. Absen",p.absen],["Nilai PG",a.pg],["Nilai Praktik",a.praktik],["Nilai Esai",a.esai],["Nilai Akhir",a.total],["KKM",g.kkm],["Status",a.total&gt;=g.kkm?"Tuntas":"Belum Tuntas"],["Durasi (menit)",Math.round((a.finishedAt-a.startedAt)/6e4)],["Pelanggaran",a.violations]].map(function(a){return a.map(function(a){return'"'+String(a).replace(/"/g,'""')+'"'}).join(",")}).join("\n"),e=new Blob([n],{type:"text/csv;charset=utf-8;"}),t=document.createElement("a");t.href=URL.createObjectURL(e),t.download="Nilai_Ujian_HOTS_"+p.nama.replace(/\s+/g,"_")+".csv",document.body.appendChild(t),t.click(),t.remove()}}()},btnHome:function(){c=!1,F();try{window.location.hash=""}catch(a){}$("start")},btnGuruUnlock:function(){"GURU-HOTS-2026"===a("guruCode").value.trim().toUpperCase()?(t(a("guruLockErr"),""),a("guruLockBox").classList.add("hidden"),a("guruForm").classList.remove("hidden"),sa()):t(a("guruLockErr"),"Kode akses salah.")},btnResend:function(){!function(){var n=a("rSend"),e=a("btnResend");if(!b||!b.best)return n.textContent="Belum ada nilai untuk dikirim.",void(n.className="msg err show");if(!g.endpoint)return n.textContent="Endpoint pengiriman nilai belum dikonfigurasi guru, sehingga nilai tidak dapat dikirim.",void(n.className="msg err show");if(!e.disabled){var t=b.best;n.textContent="Mengirim...",n.className="msg info show",e.disabled=!0;var i=e.textContent;e.textContent="Mengirim...",X(t.total,t).then(function(a){Q(a,t.total,!0),!1!==a.ok&amp;&amp;(b.resentAt=Date.now(),b.resentCount=(b.resentCount||0)+1,D(p.key,b)),setTimeout(function(){e.disabled=!1,e.textContent=i},4e3)})}}()},btnLihat:function(){q()},btnGenKode:function(){ra()},btnSaveSettings:function(){g.judul=a("sJudul").value.trim()||l.judul,g.sekolah=a("sSekolah").value.trim(),g.mapel=a("sMapel").value.trim()||l.mapel,g.kkm=Math.max(0,Math.min(100,parseInt(a("sKKM").value,10)||l.kkm)),g.durasiMenit=Math.max(5,parseInt(a("sDurasi").value,10)||l.durasiMenit),g.batasPelanggaran=Math.max(1,parseInt(a("sBatasPelanggaran").value,10)||l.batasPelanggaran),g.maxAtt=Math.max(1,parseInt(a("sMaxAtt").value,10)||l.maxAtt),g.pembahasan="1"===a("sPembahasan").value,g.endpoint=a("sEndpoint").value.trim(),N(),U(),t(a("guruSaveMsg"),"Pengaturan tersimpan di peramban ini.","ok")},btnResetSettings:function(){g=JSON.parse(JSON.stringify(l)),N(),U(),sa(),t(a("guruSaveMsg"),"Pengaturan dikembalikan ke default.","ok")},btnOpenGuru:function(){ea()},btnGuruClose:function(){ta()},btnGuruClose2:function(){ta()}};document.addEventListener("click",function(a){for(var n=a.target;n&amp;&amp;n!==document;){if(n.id&amp;&amp;ua.hasOwnProperty(n.id))return a.preventDefault(),void ua[n.id]();n=n.parentNode}}),document.addEventListener("change",function(n){var e,t,i=n.target&amp;&amp;n.target.id;"fKelas"===i?(e=a("fKelas").value,(t=a("fAbsen")).innerHTML='&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;',a("fNama").value="",(o[e]||[]).forEach(function(a){var n=document.createElement("option");n.value=a[0],n.textContent=a[0],t.appendChild(n)})):"fAbsen"===i?function(){var n=a("fKelas").value,e=a("fAbsen").value,t=(o[n]||[]).filter(function(a){return String(a[0])===String(e)})[0];a("fNama").value=t?t[1]:""}():"tKelas"===i&amp;&amp;function(){var n=a("tAbsen");n.innerHTML='&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;',(o[a("tKelas").value]||[]).forEach(function(a){var e=document.createElement("option");e.value=a[0],e.textContent=a[0]+" - "+a[1],n.appendChild(e)})}()}),window.addEventListener("load",function(){var n=a("fKelas");n&amp;&amp;n.options.length&lt;2&amp;&amp;K(),U()}),window.addEventListener("hashchange",ia),ia(),window.__examHots={PG:d,PRAKTIK:m,ESAI:k,ROSTER:o,CLASS_TOKENS:A,scorePraktik:Z,scoreEsai:W,settings:g}}();&lt;/script&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/ulangan-formatif-spreadsheet.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-6933410701014625663</guid><pubDate>Fri, 16 Oct 2026 02:16:55 +0000</pubDate><atom:updated>2026-10-16T09:16:55.634+07:00</atom:updated><title>Latihan Soal Tipe AKM: Pokok Bahasan Teks Ceramah</title><description>&lt;style&gt;
.kq{--ink:#14213d;--mut:#4a5568;--pri:#1d4ed8;--pri2:#1e3a8a;--ok:#047857;--bad:#b91c1c;--bg:#f1f5f9;--card:#ffffff;--line:#cbd5e1;font-family:Segoe UI,Roboto,Arial,sans-serif;color:var(--ink);line-height:1.6;max-width:880px;margin:0 auto}
.kq *{box-sizing:border-box}
.kq .hero{background:linear-gradient(135deg,#1e3a8a,#1d4ed8 60%,#0ea5e9);color:#fff;border-radius:18px;padding:26px 22px;margin-bottom:16px}
.kq .hero h2{margin:0 0 6px;font-size:1.55em;color:#fff;border:0}
.kq .hero p{margin:0;color:#e0f2fe}
.kq .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px}
.kq .stat{background:var(--card);border:2px solid var(--line);border-radius:14px;padding:12px;text-align:center}
.kq .stat b{display:block;font-size:1.6em;color:var(--pri2)}
.kq .stat span{font-size:.85em;color:var(--mut)}
.kq .card{background:var(--card);border:2px solid var(--line);border-radius:14px;padding:16px 18px;margin-bottom:16px}
.kq .card h3{margin:0 0 8px;font-size:1.1em;color:var(--pri2)}
.kq .card ul{margin:0;padding-left:20px}
.kq .card li{margin:4px 0}
.kq .warn{background:#fff7ed;border-color:#fb923c}
.kq label.f{display:block;font-weight:700;margin:10px 0 4px}
.kq input.t{width:100%;padding:11px 12px;border:2px solid #94a3b8;border-radius:10px;font-size:1em;color:var(--ink);background:#fff}
.kq input.t:focus{outline:3px solid #93c5fd;border-color:var(--pri)}
.kq .row{display:grid;grid-template-columns:2fr 1fr 1fr;gap:10px}
.kq button.b{background:var(--pri);color:#fff;border:0;border-radius:12px;padding:13px 20px;font-size:1em;font-weight:700;cursor:pointer;margin-top:14px}
.kq button.b:hover{background:var(--pri2)}
.kq button.g{background:#fff;color:var(--pri2);border:2px solid var(--pri)}
.kq .msg{margin-top:12px;font-weight:600}
.kq .msg.e{color:var(--bad)}
.kq .done{background:#ecfdf5;border-color:#34d399}
@media(max-width:640px){.kq .grid{grid-template-columns:repeat(2,1fr)}.kq .row{grid-template-columns:1fr}}

#kqStage{position:fixed;left:0;top:0;right:0;bottom:0;z-index:2147483647;background:#f1f5f9;overflow:auto;font-family:Segoe UI,Roboto,Arial,sans-serif;color:#14213d;line-height:1.6;-webkit-user-select:none;user-select:none}
#kqStage *{box-sizing:border-box}
#kqStage .top{position:sticky;top:0;z-index:5;background:#1e3a8a;color:#fff;padding:10px 14px;display:flex;flex-wrap:wrap;gap:8px 18px;align-items:center;justify-content:space-between}
#kqStage .top b{font-size:1.02em}
#kqStage .pill{background:#fff;color:#1e3a8a;border-radius:999px;padding:3px 12px;font-weight:700;font-size:.9em}
#kqStage .pill.v{background:#fee2e2;color:#991b1b}
#kqStage .bar{height:6px;background:#93c5fd}
#kqStage .bar i{display:block;height:6px;background:#22c55e;width:0}
#kqStage .wrap{max-width:860px;margin:0 auto;padding:16px 14px 60px}
#kqStage .stim{background:#fffbeb;border:2px solid #f59e0b;border-radius:14px;padding:14px 16px;margin:18px 0 10px}
#kqStage .stim h4{margin:0 0 8px;color:#92400e;font-size:1.05em}
#kqStage .stim p{margin:0 0 8px}
#kqStage .q{background:#fff;border:2px solid #cbd5e1;border-radius:14px;padding:14px 16px;margin:10px 0}
#kqStage .q .qt{font-weight:700;margin-bottom:8px}
#kqStage .o{display:flex;gap:10px;align-items:flex-start;padding:9px 10px;border:2px solid #e2e8f0;border-radius:10px;margin:6px 0;cursor:pointer;background:#f8fafc}
#kqStage .o:hover{border-color:#60a5fa;background:#eff6ff}
#kqStage .o input{margin-top:5px;width:18px;height:18px;flex:none}
#kqStage .o.sel{border-color:#1d4ed8;background:#dbeafe}
#kqStage .o b{flex:none;color:#1e3a8a}
#kqStage button.b{background:#1d4ed8;color:#fff;border:0;border-radius:12px;padding:13px 22px;font-size:1em;font-weight:700;cursor:pointer;margin:8px 8px 0 0}
#kqStage button.g{background:#fff;color:#1e3a8a;border:2px solid #1d4ed8}
#kqStage .res{background:#fff;border:2px solid #cbd5e1;border-radius:18px;padding:24px;text-align:center;margin-top:20px}
#kqStage .big{font-size:3.4em;font-weight:800;line-height:1.1}
#kqStage .ok{color:#047857}#kqStage .no{color:#b91c1c}
#kqStage .rv{text-align:left;margin-top:14px;font-size:.92em}
#kqStage .rv div{padding:4px 0;border-bottom:1px solid #e2e8f0}
#kqModal{position:fixed;left:0;top:0;right:0;bottom:0;z-index:2147483647;background:rgba(15,23,42,.88);display:flex;align-items:center;justify-content:center;padding:18px}
#kqModal div{background:#fff;color:#14213d;border-radius:16px;padding:22px;max-width:440px;border:4px solid #dc2626;font-family:Segoe UI,Roboto,Arial,sans-serif}
#kqModal h3{margin:0 0 8px;color:#b91c1c}
#kqModal button{background:#dc2626;color:#fff;border:0;border-radius:10px;padding:11px 20px;font-weight:700;cursor:pointer;margin-top:10px}
#kqPrint{display:none;font-family:Arial,sans-serif;color:#000;font-size:11pt}
#kqPrint h1{font-size:15pt;margin:0 0 4px;text-align:center}
#kqPrint h2{font-size:11pt;margin:10px 0 4px}
#kqPrint table{border-collapse:collapse;width:100%;margin-bottom:6px}
#kqPrint td,#kqPrint th{border:1px solid #000;padding:3px 6px;font-size:10pt;text-align:left}
#kqPrint .sub{text-align:center;margin:0 0 8px}
#kqPrint .two{display:flex;gap:10px}
#kqPrint .two table{width:50%}
#kqPrint .sig{display:flex;justify-content:space-between;margin-top:18px;page-break-inside:avoid}
@media print{@page{size:215mm 330mm;margin:12mm}body.kq-printing&gt;*:not(#kqPrint){display:none!important}body.kq-printing #kqPrint{display:block!important}}
&lt;/style&gt;

&lt;div class="kq" id="kq"&gt;
  &lt;div class="hero"&gt;
    &lt;h2&gt;Latihan Soal Tipe AKM: Pokok Bahasan Teks Ceramah&lt;/h2&gt;
    &lt;p&gt;Bahasa Indonesia Kelas XI SMK. Evaluasi informasi, perbandingan teks, dan kaidah kebahasaan ceramah.&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class="grid"&gt;
    &lt;div class="stat"&gt;&lt;b&gt;50&lt;/b&gt;&lt;span&gt;Soal AKM&lt;/span&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;b&gt;5&lt;/b&gt;&lt;span&gt;Pilihan jawaban (A-E)&lt;/span&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;b&gt;75&lt;/b&gt;&lt;span&gt;KKM&lt;/span&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;b&gt;3x&lt;/b&gt;&lt;span&gt;Kesempatan&lt;/span&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="card"&gt;
    &lt;h3&gt;Cakupan materi&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Evaluasi informasi dalam teks ceramah (gagasan pokok, gagasan pendukung, enam kriteria evaluasi)&lt;/li&gt;
      &lt;li&gt;Membandingkan isi teks ceramah dengan teks artikel opini&lt;/li&gt;
      &lt;li&gt;Kaidah kebahasaan teks ceramah bagian 1 (kata ganti, kata teknis, kata persuasif)&lt;/li&gt;
      &lt;li&gt;Kaidah kebahasaan teks ceramah bagian 2 (sapaan, kalimat retoris, konjungsi, majas, kalimat efektif, dan lainnya)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
  &lt;div class="card"&gt;
    &lt;h3&gt;Aturan nilai&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Setiap soal bernilai 2 poin (skor maksimal 100). Urutan teks, soal, dan pilihan jawaban diacak untuk setiap siswa dan setiap percobaan.&lt;/li&gt;
      &lt;li&gt;Jika nilai suatu percobaan sudah 75 atau lebih, nilai itu langsung dikirim dan disimpan sebagai nilai akhir.&lt;/li&gt;
      &lt;li&gt;Jika dalam 3 percobaan belum mencapai 75, nilai tertinggi dari ketiga percobaan yang dikirim dan disimpan.&lt;/li&gt;
      &lt;li&gt;Gunakan Nama, Kelas, dan No. Absen yang sama pada setiap percobaan.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
  &lt;div class="card warn"&gt;
    &lt;h3&gt;Aturan keamanan&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Soal tampil satu layar penuh. Dilarang berpindah tab, membuka aplikasi lain, atau menekan tombol kembali selama mengerjakan.&lt;/li&gt;
      &lt;li&gt;Setiap pindah tab/aplikasi dicatat sebagai pelanggaran dan dilaporkan ke guru. Pada pelanggaran ke-3, jawaban otomatis dikirim.&lt;/li&gt;
      &lt;li&gt;Salin (copy), klik kanan, dan tombol pintas tertentu dinonaktifkan.&lt;/li&gt;
      &lt;li&gt;Setelah selesai, unduh/cetak bukti pengerjaan (PDF) dan serahkan ke guru.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
  &lt;div class="card"&gt;
    &lt;h3&gt;Identitas siswa&lt;/h3&gt;
    &lt;div class="row"&gt;
      &lt;div&gt;&lt;label class="f" for="kqNama"&gt;Nama lengkap&lt;/label&gt;&lt;input class="t" id="kqNama" type="text" autocomplete="off" placeholder="Nama lengkap"&gt;&lt;/div&gt;
      &lt;div&gt;&lt;label class="f" for="kqKelas"&gt;Kelas&lt;/label&gt;&lt;input class="t" id="kqKelas" type="text" autocomplete="off" placeholder="XI TKJ 1"&gt;&lt;/div&gt;
      &lt;div&gt;&lt;label class="f" for="kqAbsen"&gt;No. Absen&lt;/label&gt;&lt;input class="t" id="kqAbsen" type="number" min="1" max="99" placeholder="Contoh: 12"&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;button class="b" id="kqStart" type="button"&gt;Mulai Mengerjakan&lt;/button&gt;
    &lt;div class="msg" id="kqMsg"&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div id="kqStatus"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
(function(){
var GAS_URL="https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
var MATERI="Latihan Soal Tipe AKM - Teks Ceramah";
var KKM=75, MAXA=3, MAXV=3, LET=["A","B","C","D","E"];

/* Format soal: [tingkat, soal, jawaban benar, salah1, salah2, salah3, salah4]  (e=mudah, m=sedang, h=sulit; tidak ditampilkan) */
var G=[
{t:"Teks 1: Ceramah Keselamatan Kerja",p:[
"(1) Assalamualaikum warahmatullahi wabarakatuh. Bapak Kepala Sekolah, Bapak dan Ibu guru, serta teman-teman siswa kelas XI yang saya hormati. Puji syukur marilah kita panjatkan kepada Tuhan Yang Maha Esa atas kesehatan pagi ini. Izinkan saya menyampaikan renungan singkat tentang keselamatan kerja di bengkel.",
"(2) Hadirin sekalian, keselamatan kerja adalah syarat utama sebelum kita berbicara tentang keterampilan. Sebab, tangan yang terampil tidak ada gunanya jika terluka karena kelalaian. Sebagian besar kecelakaan kerja disebabkan oleh kelalaian manusia, bukan oleh kerusakan mesin.",
"(3) Mengapa kita perlu memakai alat pelindung diri? Karena kacamata pelindung menjaga mata dari percikan logam, sarung tangan menjaga tangan dari panas, dan sepatu kerja menjaga kaki dari benda jatuh. Selain itu, perusahaan besar biasanya menolak lulusan yang tidak terbiasa dengan aturan keselamatan.",
"(4) Saya sendiri pernah bekerja sebagai teknisi selama dua belas tahun di sebuah pabrik komponen otomotif. Dari pengalaman itu saya meyakini bahwa kebiasaan kecil, seperti merapikan alat setelah dipakai, mencegah kecelakaan besar. Semua pekerja yang rajin menaati aturan pasti tidak pernah mengalami kecelakaan.",
"(5) Oleh karena itu, marilah kita mulai dari diri sendiri. Hendaknya kita memakai alat pelindung diri setiap kali memasuki bengkel. Mari kita jadikan keselamatan sebagai budaya, bukan sekadar kewajiban. Demikian yang dapat saya sampaikan. Wassalamualaikum warahmatullahi wabarakatuh."],
q:[
["e","Bagian pembukaan pada teks ceramah di atas ditandai oleh ...","salam, sapaan kepada hadirin, ucapan syukur, dan pengantar topik","penjelasan tentang kelalaian manusia","ajakan memakai alat pelindung diri","simpulan dan harapan penceramah","pengalaman penceramah bekerja di pabrik"],
["e","Gagasan pokok paragraf (2) adalah ...","keselamatan kerja merupakan syarat utama sebelum keterampilan","kecelakaan kerja hanya terjadi di pabrik","tangan yang terampil selalu mengalami luka","mesin bengkel sering mengalami kerusakan","keterampilan tidak penting bagi pekerja"],
["m","Pernyataan berikut yang merupakan contoh (gagasan pendukung) bagi alasan perlunya alat pelindung diri adalah ...","kacamata pelindung menjaga mata dari percikan logam","keselamatan dijadikan budaya","penceramah bekerja selama dua belas tahun","semua pekerja yang taat tidak pernah celaka","marilah kita mulai dari diri sendiri"],
["m","Kredibilitas penceramah pada teks tersebut diperkuat oleh ...","pengalamannya selama dua belas tahun sebagai teknisi pabrik komponen otomotif","ajakan memakai alat pelindung diri","salam dan sapaan di awal ceramah","penggunaan kata ganti kita","penggunaan kalimat tanya pada paragraf (3)"],
["h","Pernyataan 'Semua pekerja yang rajin menaati aturan pasti tidak pernah mengalami kecelakaan' paling lemah jika dinilai dari kriteria ...","kelogisan, karena menggeneralisasi secara berlebihan","kredibilitas, karena penceramah tidak berpengalaman","kesesuaian tujuan, karena tidak membahas keselamatan","kelengkapan, karena tidak ada salam penutup","keakuratan, karena menyebut data yang salah"]]},

{t:"Teks 2: Ceramah Pengurus OSIS tentang Berita Bohong",p:[
"(1) Selamat pagi, hadirin yang saya hormati. Pada kesempatan ini kami dari pengurus OSIS ingin mengajak kita semua membahas bahaya berita bohong di media sosial.",
"(2) Berita bohong menyebar lebih cepat daripada klarifikasi. Banyak orang membagikan kabar tanpa membacanya sampai selesai. Akibatnya, pesan yang belum tentu benar dapat diterima sebagai kenyataan oleh ratusan orang dalam hitungan menit.",
"(3) Lalu, apa yang harus kita lakukan? Pertama, periksa sumbernya. Kedua, bandingkan dengan media lain. Ketiga, tahan jari kita sebelum membagikan. Menurut saya, kebiasaan tiga langkah itu dapat mengurangi penyebaran hoaks secara signifikan.",
"(4) Beberapa teman mengatakan bahwa media sosial itu sumber kebohongan terbesar, sehingga sebaiknya semua siswa dilarang memakainya. Padahal, media sosial juga dipakai untuk belajar, berjualan, dan berkarya.",
"(5) Dengan demikian, yang perlu kita perbaiki adalah cara kita menyikapi informasi, bukan menghapus media sosialnya. Marilah kita menjadi pembaca yang cerdas."],
q:[
["e","Kata 'kami' pada paragraf (1) merujuk pada ...","penceramah dan kelompoknya (pengurus OSIS) tanpa melibatkan pendengar","penceramah dan seluruh pendengar","pendengar saja","penceramah seorang diri","seluruh pengguna media sosial"],
["e","Ungkapan yang menandai opini penceramah pada paragraf (3) adalah ...","Menurut saya","Pertama, periksa sumbernya","Kedua, bandingkan dengan media lain","Lalu, apa yang harus kita lakukan","Ketiga, tahan jari kita"],
["m","Kalimat 'Lalu, apa yang harus kita lakukan?' termasuk ...","kalimat retoris yang dipakai untuk menarik perhatian dan mengantar penjelasan","kalimat tanya yang menuntut jawaban tertulis dari pendengar","kalimat perintah yang bersifat larangan","kalimat berita yang berisi fakta","kalimat seru yang mengungkapkan kemarahan"],
["m","Fungsi konjungsi 'Dengan demikian' pada paragraf (5) adalah ...","menyimpulkan uraian sebelumnya","menyatakan pertentangan","menyatakan syarat","menambahkan keterangan","menyatakan urutan waktu"],
["h","Klaim sebagian teman bahwa 'media sosial itu sumber kebohongan terbesar' paling tepat dinilai ...","tidak objektif dan tidak didukung data, karena hanya melihat sisi negatif","sangat akurat, karena sesuai dengan fakta penyebaran berita bohong","sangat kredibel, karena disampaikan oleh banyak orang","lengkap, karena membahas semua manfaat media sosial","sesuai tujuan, karena mengajak siswa berhenti membaca berita"]]},

{t:"Teks 3: Dua Teks tentang Membaca Buku",p:[
"Teks A (ceramah): Hadirin yang berbahagia, marilah kita bertanya pada diri sendiri: kapan terakhir kali kita menamatkan sebuah buku? Saya yakin, buku yang kita baca hari ini akan menentukan siapa kita lima tahun lagi. Oleh karena itu, ayo kita sisihkan lima belas menit setiap hari untuk membaca.",
"Teks B (artikel opini): Rendahnya minat baca remaja Indonesia tercermin dalam laporan PISA yang menempatkan kemampuan membaca siswa di bawah rata-rata negara OECD. Fakta ini menunjukkan bahwa budaya baca belum menjadi kebiasaan. Karena itu, sekolah perlu menyediakan waktu membaca rutin dan perpustakaan yang menarik."],
q:[
["e","Persamaan Teks A dan Teks B adalah ...","sama-sama bersifat persuasif karena mengajak agar gemar membaca","sama-sama disampaikan secara lisan","sama-sama menyapa langsung pendengar","sama-sama memakai data resmi","sama-sama ditulis tanpa struktur"],
["e","Ciri yang hanya tampak pada Teks A adalah ...","sapaan 'hadirin' dan kata ganti 'kita'","rujukan pada laporan PISA","kalimat argumentatif berbasis data","penyebutan tokoh ahli","paparan kebijakan sekolah"],
["m","Kelebihan Teks B dibandingkan Teks A dalam hal kualitas informasi adalah ...","memuat rujukan data yang dapat diverifikasi","menyentuh emosi melalui intonasi","memungkinkan tanya jawab langsung","memakai sapaan yang akrab","menggunakan kalimat ajakan yang kuat"],
["m","Kekurangan Teks A bila dijadikan rujukan informasi adalah ...","keyakinan penceramah tidak disertai data atau sumber yang jelas","tidak memiliki ajakan kepada pendengar","tidak membahas topik membaca","terlalu banyak memakai istilah teknis","strukturnya tidak memiliki pembuka"],
["h","Seorang siswa akan menyusun pidato yang menyentuh emosi sekaligus meyakinkan dengan bukti. Strategi yang paling tepat adalah ...","menggabungkan sapaan dan ajakan seperti Teks A dengan data bersumber seperti Teks B","memakai sapaan saja tanpa data agar singkat","memakai data saja tanpa sapaan agar terkesan resmi","menyalin Teks B secara utuh","menambahkan keyakinan pribadi sebanyak mungkin"]]},

{t:"Teks 4: Ceramah Palang Merah Remaja",p:[
"(1) Hadirin yang saya hormati, izinkan saya memperkenalkan diri. Saya Dina, perwakilan Palang Merah Remaja.",
"(2) Pada musim pancaroba, daya tahan tubuh atau imunitas kita mudah menurun. Oleh sebab itu, kita perlu mengonsumsi gizi seimbang dan cukup tidur.",
"(3) Marilah kita membiasakan mencuci tangan. Hendaknya setiap kelas menyediakan sabun. Mari kita jaga kesehatan bersama-sama agar kegiatan belajar tidak terganggu."],
q:[
["e","Kata 'hadirin' pada paragraf (1) termasuk ...","sapaan kepada pendengar (orang kedua)","kata ganti orang pertama tunggal","kata ganti orang pertama jamak","kata teknis kesehatan","kata persuasif berupa anjuran"],
["e","Kata teknis bertema kesehatan dalam teks adalah ...","imunitas dan gizi seimbang","pancaroba dan sabun","hadirin dan saya","marilah dan hendaknya","kelas dan belajar"],
["e","Kata persuasif berupa ajakan pada paragraf (3) adalah ...","marilah dan mari kita","hadirin dan saya","imunitas dan gizi","pancaroba dan kegiatan","terganggu dan sabun"],
["m","Kata 'kita' pada 'daya tahan tubuh atau imunitas kita' mencakup ...","penceramah dan seluruh pendengar","penceramah saja","pendengar saja","penceramah dan kelompoknya tanpa pendengar","Palang Merah Remaja saja"],
["m","Kata 'hendaknya' pada paragraf (3) termasuk kata persuasif berupa ...","anjuran","ajakan","larangan","pertanyaan","seruan"]]},

{t:"Teks 5: Ceramah Literasi Keuangan",p:[
"(1) Bapak dan Ibu guru serta adik-adik yang saya banggakan, uang itu ibarat air: jika tidak dialirkan dengan baik, ia akan menguap tanpa bekas.",
"(2) Siapa di antara kita yang tidak ingin punya tabungan? Namun, apabila setiap hari kita jajan tanpa batas, bagaimana tabungan itu dapat terbentuk?",
"(3) Menabung itu penting, penting untuk masa depan, penting untuk keluarga, dan penting untuk cita-cita kita.",
"(4) Gak usah nunggu kaya buat nabung, dong. Mulai aja dari sepuluh ribu per minggu.",
"(5) Agar supaya tabungan kita tumbuh, mari kita catat pengeluaran setiap hari."],
q:[
["e","Majas pada kalimat 'uang itu ibarat air' di paragraf (1) adalah ...","perumpamaan (simile)","hiperbola","personifikasi","repetisi","klimaks"],
["e","Kalimat 'Siapa di antara kita yang tidak ingin punya tabungan?' merupakan ...","kalimat retoris","kalimat perintah","kalimat larangan","kalimat berita","kalimat sapaan"],
["m","Pengulangan kata 'penting' pada paragraf (3) merupakan majas ...","repetisi","metafora","hiperbola","personifikasi","perumpamaan"],
["m","Bentuk baku dari 'Gak usah nunggu kaya buat nabung, dong.' adalah ...","Tidak usah menunggu kaya untuk menabung.","Gak usah menunggu kaya untuk menabung dong.","Tidak usah nunggu kaya buat menabung.","Tak usah nunggu kaya untuk nabung, deh.","Nggak usah tunggu kaya buat menabung."],
["h","Perbaikan paling tepat untuk kalimat paragraf (5) agar menjadi kalimat efektif adalah ...","Agar tabungan kita tumbuh, mari kita catat pengeluaran setiap hari (kata 'supaya' dihapus karena bermakna sama dengan 'agar').","mengganti 'mari' dengan 'ayo' saja","menambahkan 'demi untuk' di depan kalimat","mengubah kalimat menjadi kalimat tanya","menghapus kata 'tabungan'"]]},

{t:"Teks 6: Ceramah Kebersihan Kelas",p:[
"(1) Yang saya hormati Bapak dan Ibu guru serta teman-teman sekalian. Alhamdulillah, kita masih diberi kesempatan untuk berkumpul. Pada kesempatan ini saya akan berbicara tentang kebersihan kelas.",
"(2) Kelas yang bersih membuat kita nyaman belajar. Ruangan yang rapi membuat pikiran lebih tenang. Meja yang tertata membuat kita mudah menemukan buku. Karena itu, kebersihan kelas sangat memengaruhi kenyamanan belajar.",
"(3) Banyak siswa mengabaikan jadwal piket. Sebagian meninggalkan sampah di laci. Sebagian lain enggan menyapu karena merasa itu tugas petugas kebersihan. Inilah sebabnya kelas sering kotor.",
"(4) Kesimpulannya, kebersihan kelas adalah tanggung jawab kita bersama. Semoga kita semua dapat menjaganya. Terima kasih atas perhatian Bapak, Ibu, dan teman-teman. Wassalamualaikum warahmatullahi wabarakatuh."],
q:[
["e","Paragraf (1) pada ceramah tersebut berfungsi sebagai ...","pembukaan yang memuat sapaan, syukur, dan pengantar topik","isi yang memuat gagasan pokok","penutup yang memuat simpulan","bagian data pendukung","bagian ajakan akhir"],
["m","Pola paragraf (2) berdasarkan letak gagasan pokoknya adalah ...","campuran","deduktif","induktif","naratif","tidak memiliki gagasan pokok"],
["m","Pola paragraf (3) adalah ...","induktif, karena gagasan pokok ada di akhir paragraf","deduktif, karena gagasan pokok ada di awal paragraf","campuran, karena gagasan pokok ada di awal dan akhir","deduktif, karena didahului kalimat utama","induktif, karena tidak memiliki gagasan pendukung"],
["e","Gagasan pendukung pada paragraf (2) adalah ...","ruangan yang rapi membuat pikiran lebih tenang","kelas sering kotor","jadwal piket diabaikan","kebersihan kelas adalah tanggung jawab bersama","penceramah bersyukur atas pertemuan"],
["m","Kalimat 'Sebagian lain enggan menyapu karena merasa itu tugas petugas kebersihan' pada paragraf (3) berfungsi sebagai ...","gagasan pendukung berupa alasan penyebab kelas kotor","gagasan pokok paragraf","salam pembuka","simpulan ceramah","ajakan bertindak"]]},

{t:"Teks 7: Ceramah Seorang Konten Kreator tentang Investasi",p:[
"(1) Salam sejahtera, hadirin sekalian. Hari ini saya akan menjelaskan mengapa remaja harus berinvestasi saham sekarang juga.",
"(2) Saya adalah seorang konten kreator keuangan dengan satu juta pengikut. Saya yakin saham pasti selalu naik dalam jangka panjang, jadi tidak ada risiko sama sekali.",
"(3) Teman saya membeli saham sebuah perusahaan minggu lalu dan kini untungnya dua kali lipat. Buktinya jelas bahwa semua orang bisa kaya dalam seminggu.",
"(4) Selain itu, perlu diketahui bahwa Otoritas Jasa Keuangan menyediakan layanan edukasi dan pengaduan bagi investor pemula, dan produk investasi yang legal terdaftar di lembaga resmi.",
"(5) Maka, jangan tunggu lagi. Segera beli saham yang saya rekomendasikan."],
q:[
["e","Pernyataan 'saham pasti selalu naik, jadi tidak ada risiko sama sekali' tidak tepat karena ...","semua bentuk investasi mengandung risiko sehingga klaim itu tidak akurat","penceramah memakai sapaan formal","kalimat itu terlalu pendek","penceramah belum mengucapkan salam","saham hanya dijual pada akhir pekan"],
["m","Bukti berupa keuntungan teman penceramah pada paragraf (3) lemah karena ...","hanya berasal dari satu kasus pribadi lalu digeneralisasi untuk semua orang","teman penceramah tidak boleh membeli saham","keuntungan dua kali lipat tidak mungkin terjadi","ditulis tanpa memakai konjungsi","disampaikan pada paragraf ketiga"],
["m","Informasi yang paling kredibel dan dapat diverifikasi terdapat pada paragraf ...","(4), karena merujuk pada lembaga resmi (OJK)","(2), karena penceramah memiliki banyak pengikut","(3), karena berdasarkan pengalaman teman","(5), karena berisi ajakan membeli","(1), karena memuat salam"],
["h","Kelemahan paling serius dari sisi objektivitas ceramah tersebut adalah ...","mendorong pendengar membeli saham yang direkomendasikannya tanpa menyebut risiko dan kemungkinan kepentingan pribadi","tidak memakai kata ganti orang pertama","menyebut nama lembaga resmi","memakai salam pembuka yang sopan","menyampaikan pengalaman pribadi secara singkat"],
["h","Rekomendasi perbaikan yang paling tepat bagi penceramah adalah ...","menyertakan data resmi, menjelaskan risiko, dan membedakan fakta dari opini","memperbanyak ajakan agar pendengar segera membeli","menghapus semua data supaya lebih singkat","menambah jumlah pengikut sebelum berceramah","mengganti topik menjadi hiburan"]]},

{t:"Teks 8: Ceramah Persiapan Praktik Kerja Lapangan",p:[
"(1) Hadirin yang saya hormati, tahun ini kita akan melaksanakan Praktik Kerja Lapangan. Tujuan ceramah ini adalah mengajak kita semua menyiapkan diri agar disiplin selama PKL.",
"(2) Disiplin waktu adalah wajah pertama seorang pekerja. Pekerja yang datang tepat waktu menunjukkan bahwa ia menghargai orang lain. Dunia usaha menilai ketepatan waktu sebelum menilai keterampilan.",
"(3) Omong-omong, kantin sekolah kita sekarang menjual banyak jenis makanan baru, mulai dari mi ayam sampai es buah. Harganya terjangkau dan rasanya enak.",
"(4) Disiplin juga tampak pada cara kita berpakaian dan menaati prosedur kerja. Seragam yang rapi dan kepatuhan pada SOP mencerminkan tanggung jawab. Jadi, mari kita siapkan diri sejak sekarang."],
q:[
["e","Tujuan ceramah tersebut dinyatakan pada paragraf (1), yaitu ...","mengajak siswa menyiapkan diri agar disiplin selama PKL","memperkenalkan menu kantin baru","mengumumkan jadwal ujian","mengkritik pekerja yang terlambat","menjelaskan cara membuat SOP"],
["e","Gagasan pokok paragraf (2) adalah ...","disiplin waktu adalah wajah pertama seorang pekerja","dunia usaha menilai keterampilan lebih dulu","pekerja sering terlambat","seragam harus rapi","PKL dilaksanakan tahun ini"],
["m","Paragraf yang tidak sesuai dengan tujuan ceramah (kriteria kesesuaian tujuan) adalah ...","paragraf (3), karena membahas kantin sekolah","paragraf (2), karena membahas ketepatan waktu","paragraf (4), karena membahas seragam dan SOP","paragraf (1), karena memuat tujuan","tidak ada, semua paragraf sesuai"],
["m","Gagasan pendukung paragraf (4) adalah ...","seragam yang rapi dan kepatuhan pada SOP mencerminkan tanggung jawab","disiplin adalah wajah pertama pekerja","kantin menjual es buah","ketepatan waktu dinilai lebih dulu","PKL dilaksanakan tahun ini"],
["h","Kalimat 'Dunia usaha menilai ketepatan waktu sebelum menilai keterampilan' paling tepat dievaluasi sebagai ...","klaim yang perlu diverifikasi dengan data, misalnya survei industri","fakta mutlak karena diucapkan penceramah","opini yang tidak boleh dipakai dalam ceramah","gagasan pokok seluruh ceramah","kalimat retoris"]]},

{t:"Teks 9: Dua Teks tentang Menjaga Sungai",p:[
"Teks A (ceramah): Bapak dan Ibu yang dimuliakan, sungai adalah anugerah. Marilah kita jaga, jangan buang sampah ke dalamnya! Sungai yang bersih akan menghidupi anak cucu kita.",
"Teks B (artikel opini): Menurut data instansi lingkungan hidup, pencemaran sungai di wilayah perkotaan meningkat dalam lima tahun terakhir. Karena itu, pemerintah daerah perlu memperketat pengawasan pembuangan limbah dan menyediakan tempat sampah terpilah."],
q:[
["e","Perbedaan media penyampaian Teks A dan Teks B adalah ...","Teks A disampaikan lisan, Teks B berupa tulisan","Teks A tertulis, Teks B lisan","keduanya disampaikan lisan","keduanya berupa tulisan","Teks A direkam, Teks B disiarkan"],
["e","Ciri teks artikel opini yang tampak pada Teks B adalah ...","memakai rujukan data dari instansi resmi dan bahasa yang lebih objektif","menyapa pembaca dengan 'Bapak dan Ibu'","memakai kata ganti 'kita' berulang","memakai kalimat seru","menggunakan salam pembuka"],
["m","Kelebihan Teks A dibandingkan Teks B adalah ...","mampu menyentuh emosi pendengar secara langsung lewat sapaan, intonasi, dan ekspresi","dapat dibaca ulang kapan saja","memuat data resmi yang rinci","tersebar luas melalui media daring","bersifat sangat objektif"],
["m","Kekurangan Teks B dibandingkan Teks A adalah ...","tidak ada interaksi langsung sehingga kurang menyentuh emosi pembaca","tidak memuat data","tidak dapat dibaca ulang","tidak memiliki struktur","tidak membahas isu pencemaran"],
["h","Simpulan perbandingan yang paling tepat dan berbasis bukti dari kedua teks adalah ...","Teks A kuat pada kedekatan emosional lewat sapaan dan ajakan, sedangkan Teks B kuat pada argumen berbasis data yang dapat diverifikasi","Teks A lebih baik karena tidak memerlukan data apa pun","Teks B lebih baik karena memakai sapaan yang akrab","kedua teks sama persis karena membahas sungai","kedua teks tidak dapat dibandingkan karena topiknya berbeda"]]},

{t:"Teks 10: Kutipan Ceramah tentang Bahasa Baku di Dunia Kerja",p:[
"(1) Yang terhormat Bapak Kepala Sekolah, Bapak dan Ibu guru, serta rekan-rekan siswa.",
"(2) Kami meyakini bahwa kemampuan berbahasa yang baik membuka banyak pintu pekerjaan.",
"(3) Apabila kita terbiasa berbahasa baku, wawancara kerja akan terasa lebih mudah.",
"(4) Namun, banyak siswa masih memakai kata kayak dan bikin dalam surat lamaran.",
"(5) Sebaiknya kita membiasakan diri menggunakan kata seperti dan membuat."],
q:[
["e","Fragmen (1) menunjukkan kaidah kebahasaan ...","kata sapaan yang disusun dari yang paling dihormati","majas perumpamaan","kalimat retoris","kata kerja mental","kalimat efektif"],
["e","Kata kerja mental pada fragmen (2) adalah ...","meyakini","membuka","berbahasa","kemampuan","pekerjaan"],
["m","Konjungsi 'apabila' pada fragmen (3) menyatakan hubungan ...","syarat (pengandaian)","sebab","pertentangan","simpulan","urutan"],
["m","Konjungsi 'namun' pada fragmen (4) menyatakan ...","pertentangan antara harapan pada (3) dan kenyataan","sebab-akibat","simpulan akhir","penambahan","syarat"],
["h","Pasangan fragmen dan kaidah kebahasaan berikut yang tepat adalah ...","(2) kata kerja mental penanda opini; (3) konjungsi syarat; (4) kata tidak baku; (5) kalimat persuasif tingkat halus","(2) kalimat retoris; (3) konjungsi pertentangan; (4) kata teknis; (5) kalimat larangan","(2) majas hiperbola; (3) konjungsi simpulan; (4) kata baku; (5) kalimat berita","(2) kata ganti orang kedua; (3) konjungsi sebab; (4) kata baku; (5) kalimat tanya","(2) kata sapaan; (3) konjungsi urutan; (4) kata teknis; (5) kalimat seru"]]}
];

function $(id){return document.getElementById(id);}
function esc(s){return String(s).replace(/&amp;/g,"&amp;amp;").replace(/&lt;/g,"&amp;lt;").replace(/&gt;/g,"&amp;gt;").replace(/"/g,"&amp;quot;");}
function shuffle(a){for(var i=a.length-1;i&gt;0;i--){var j=Math.floor(Math.random()*(i+1));var t=a[i];a[i]=a[j];a[j]=t;}return a;}

/* ---------- penyimpanan ---------- */
function lsGet(k){try{var v=localStorage.getItem(k);return v?JSON.parse(v):null;}catch(e){return null;}}
function lsSet(k,o){try{localStorage.setItem(k,JSON.stringify(o));}catch(e){}}
function sGet(k){
  try{ if(window.storage&amp;&amp;window.storage.get){
    return window.storage.get(k,true).then(function(r){return (r&amp;&amp;r.value)?JSON.parse(r.value):lsGet(k);}).catch(function(){return lsGet(k);});
  }}catch(e){}
  return Promise.resolve(lsGet(k));
}
function sSet(k,o){
  lsSet(k,o);
  try{ if(window.storage&amp;&amp;window.storage.set){ return window.storage.set(k,JSON.stringify(o),true).catch(function(){}); } }catch(e){}
  return Promise.resolve();
}
function keyOf(kelas,absen){return "kqc:"+kelas.toLowerCase().replace(/\s+/g,"")+":"+absen;}

/* ---------- kuis ---------- */
function buildQuiz(){
  var gs=shuffle(G.slice()), out=[];
  gs.forEach(function(g){
    var qs=shuffle(g.q.slice()).map(function(q){
      var opts=shuffle([0,1,2,3,4].map(function(i){return {t:q[2+i],c:i===0};}));
      var ci=0; opts.forEach(function(o,i){if(o.c)ci=i;});
      return {lvl:q[0],q:q[1],o:opts,ci:ci};
    });
    out.push({g:g,items:qs});
  });
  return out;
}

var rec=null, quiz=null, ans=[], viol=0, active=false, attemptNo=1, startT=0, lastV=0, stage=null, keyStr="";

function renderStage(){
  var n=0,h='&lt;div class="top"&gt;&lt;b&gt;'+esc(MATERI)+'&lt;/b&gt;&lt;span&gt;&lt;span class="pill"&gt;Percobaan '+attemptNo+' dari '+MAXA+'&lt;/span&gt; &lt;span class="pill" id="kqCnt"&gt;Terjawab 0/50&lt;/span&gt; &lt;span class="pill" id="kqViol"&gt;Pelanggaran 0/'+MAXV+'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="bar"&gt;&lt;i id="kqBar"&gt;&lt;/i&gt;&lt;/div&gt;&lt;div class="wrap"&gt;';
  h+='&lt;div class="stim" style="background:#eff6ff;border-color:#60a5fa"&gt;&lt;p&gt;&lt;b&gt;'+esc(rec.nama)+'&lt;/b&gt; | Kelas '+esc(rec.kelas)+' | No. Absen '+esc(rec.absen)+'&lt;/p&gt;&lt;p style="margin:0"&gt;Pilih satu jawaban paling tepat untuk setiap soal. Jangan berpindah tab atau aplikasi sampai jawaban dikirim.&lt;/p&gt;&lt;/div&gt;';
  quiz.forEach(function(blk){
    h+='&lt;div class="stim"&gt;&lt;h4&gt;'+esc(blk.g.t)+'&lt;/h4&gt;';
    blk.g.p.forEach(function(p){h+='&lt;p&gt;'+esc(p)+'&lt;/p&gt;';});
    h+='&lt;/div&gt;';
    blk.items.forEach(function(it){
      h+='&lt;div class="q"&gt;&lt;div class="qt"&gt;'+(n+1)+'. '+esc(it.q)+'&lt;/div&gt;';
      it.o.forEach(function(o,i){
        h+='&lt;label class="o" id="kqo'+n+'_'+i+'"&gt;&lt;input type="radio" name="kqq'+n+'" value="'+i+'"&gt;&lt;b&gt;'+LET[i]+'.&lt;/b&gt;&lt;span&gt;'+esc(o.t)+'&lt;/span&gt;&lt;/label&gt;';
      });
      h+='&lt;/div&gt;'; n++;
    });
  });
  h+='&lt;button class="b" id="kqSubmit" type="button"&gt;Kirim Jawaban&lt;/button&gt;&lt;/div&gt;';
  stage.innerHTML=h;
  stage.addEventListener("change",function(e){
    var t=e.target; if(!t||t.type!=="radio")return;
    var m=/^kqq(\d+)$/.exec(t.name); if(!m)return;
    var qi=+m[1]; ans[qi]=+t.value;
    for(var i=0;i&lt;5;i++){var el=$("kqo"+qi+"_"+i); if(el)el.className="o"+(i===ans[qi]?" sel":"");}
    var c=0; for(var k=0;k&lt;50;k++){if(ans[k]!==undefined)c++;}
    $("kqCnt").textContent="Terjawab "+c+"/50"; $("kqBar").style.width=(c*2)+"%";
  });
  $("kqSubmit").onclick=function(){submitAttempt(false);};
}

function modal(title,text){
  var old=$("kqModal"); if(old)old.parentNode.removeChild(old);
  var m=document.createElement("div"); m.id="kqModal";
  m.innerHTML='&lt;div&gt;&lt;h3&gt;'+esc(title)+'&lt;/h3&gt;&lt;p&gt;'+esc(text)+'&lt;/p&gt;&lt;button type="button" id="kqModalOk"&gt;Mengerti, kembali mengerjakan&lt;/button&gt;&lt;/div&gt;';
  document.body.appendChild(m);
  $("kqModalOk").onclick=function(){m.parentNode.removeChild(m); goFull();};
}
function goFull(){try{var e=stage||document.documentElement; var f=e.requestFullscreen||e.webkitRequestFullscreen; if(f&amp;&amp;!document.fullscreenElement)f.call(e);}catch(x){}}
function exitFull(){try{if(document.fullscreenElement&amp;&amp;document.exitFullscreen)document.exitFullscreen();}catch(x){}}

function violate(why){
  if(!active)return;
  var now=Date.now(); if(now-lastV&lt;1500)return; lastV=now;
  viol++;
  var el=$("kqViol"); if(el){el.textContent="Pelanggaran "+viol+"/"+MAXV; el.className="pill v";}
  if(viol&gt;=MAXV){ submitAttempt(true); return; }
  modal("Peringatan "+viol+" dari "+MAXV,"Terdeteksi: "+why+". Jangan berpindah halaman atau aplikasi. Pada pelanggaran ke-"+MAXV+", jawaban Anda otomatis dikirim dan dicatat ke guru.");
}
function onVis(){if(document.hidden)violate("berpindah tab atau aplikasi");}
function onBlur(){violate("meninggalkan halaman soal");}
function onPop(){ if(active){history.pushState({kq:1},"",location.href); violate("menekan tombol kembali");} }
function onUnload(e){ if(active){e.preventDefault(); e.returnValue="Jawaban belum dikirim."; return "Jawaban belum dikirim.";} }
function onClickCap(e){ if(!active)return; var a=e.target&amp;&amp;e.target.closest?e.target.closest("a"):null; if(a&amp;&amp;!(stage&amp;&amp;stage.contains(a))){e.preventDefault();e.stopPropagation();} }
function onCtx(e){if(active)e.preventDefault();}
function onCopy(e){if(active)e.preventDefault();}
function onKey(e){
  if(!active)return;
  var k=(e.key||"").toLowerCase();
  if(e.key==="F12"||((e.ctrlKey||e.metaKey)&amp;&amp;(k==="c"||k==="x"||k==="u"||k==="s"||k==="p"||k==="a"||k==="f"))||((e.ctrlKey||e.metaKey)&amp;&amp;e.shiftKey&amp;&amp;(k==="i"||k==="j"||k==="c"))||(e.altKey&amp;&amp;(k==="arrowleft"||k==="arrowright"))){e.preventDefault();e.stopPropagation();}
}
function lock(on){
  var f=on?"addEventListener":"removeEventListener";
  document[f]("visibilitychange",onVis); window[f]("blur",onBlur); window[f]("popstate",onPop);
  window[f]("beforeunload",onUnload); document[f]("click",onClickCap,true);
  document[f]("contextmenu",onCtx); document[f]("copy",onCopy); document[f]("cut",onCopy); document[f]("keydown",onKey,true);
  if(on){ document.body.style.overflow="hidden"; try{history.pushState({kq:1},"",location.href);}catch(e){} }
  else { document.body.style.overflow=""; }
}

function beginAttempt(){
  attemptNo=rec.attempts.length+1; quiz=buildQuiz(); ans=[]; viol=0; lastV=0; startT=Date.now();
  stage=document.createElement("div"); stage.id="kqStage"; document.body.appendChild(stage);
  renderStage(); active=true; lock(true); goFull(); stage.scrollTop=0;
}

function submitAttempt(auto){
  if(!active)return;
  var un=0,i; for(i=0;i&lt;50;i++){if(ans[i]===undefined)un++;}
  if(!auto&amp;&amp;un&gt;0&amp;&amp;!window.confirm("Masih ada "+un+" soal belum dijawab (dihitung salah). Kirim sekarang?"))return;
  active=false; lock(false); exitFull();
  var mo=$("kqModal"); if(mo)mo.parentNode.removeChild(mo);
  var det=[],benar=0,n=0;
  quiz.forEach(function(blk){blk.items.forEach(function(it){
    var a=ans[n], ok=(a===it.ci); if(ok)benar++;
    det.push({l:(a===undefined?"-":LET[a]),k:LET[it.ci],ok:ok,v:it.lvl}); n++;
  });});
  var score=Math.round(benar*100/50);
  var at={score:score,benar:benar,t:new Date().toISOString(),viol:viol,auto:!!auto,det:det};
  rec.attempts.push(at);
  var fin=null;
  if(score&gt;=KKM){ fin={score:score,source:"kkm",idx:rec.attempts.length-1}; }
  else if(rec.attempts.length&gt;=MAXA){
    var bi=0; rec.attempts.forEach(function(a,k){if(a.score&gt;rec.attempts[bi].score)bi=k;});
    fin={score:rec.attempts[bi].score,source:"tertinggi",idx:bi};
  }
  if(fin){ rec.final=fin; sendGas(); }
  sSet(keyStr,rec);
  showResult(at,fin,auto);
}

function sendGas(){
  if(!GAS_URL||!rec.final)return;
  var f=rec.final, fa=rec.attempts[f.idx], p={
    waktu:new Date().toLocaleString("id-ID"), nama:rec.nama, kelas:rec.kelas, absen:rec.absen, materi:MATERI,
    nilaiAkhir:f.score, skorPG:f.score, skorEsai:"-", benarPG:fa.benar, totalPG:50,
    sumber:(f.source==="kkm"?"Mencapai KKM":"Nilai tertinggi"), percobaan:rec.attempts.length,
    skor1:rec.attempts[0]?rec.attempts[0].score:"", skor2:rec.attempts[1]?rec.attempts[1].score:"", skor3:rec.attempts[2]?rec.attempts[2].score:"",
    pelanggaran:rec.attempts.reduce(function(s,a){return s+a.viol;},0),
    jawaban:fa.det.map(function(d,i){return (i+1)+":"+d.l;}).join(",")
  };
  try{fetch(GAS_URL,{method:"POST",mode:"no-cors",body:new URLSearchParams(p)}).catch(function(){});}catch(e){}
}

function showResult(at,fin,auto){
  var pass=at.score&gt;=KKM, left=MAXA-rec.attempts.length;
  var h='&lt;div class="top"&gt;&lt;b&gt;'+esc(MATERI)+'&lt;/b&gt;&lt;span class="pill"&gt;Hasil percobaan '+rec.attempts.length+'&lt;/span&gt;&lt;/div&gt;&lt;div class="wrap"&gt;&lt;div class="res"&gt;';
  if(auto)h+='&lt;p class="no"&gt;&lt;b&gt;Jawaban dikirim otomatis karena pelanggaran keamanan mencapai batas.&lt;/b&gt;&lt;/p&gt;';
  h+='&lt;p&gt;Skor percobaan ini:&lt;/p&gt;&lt;div class="big '+(pass?'ok':'no')+'"&gt;'+at.score+' / 100&lt;/div&gt;';
  h+='&lt;p&gt;Benar '+at.benar+' dari 50 soal. '+(pass?'Mencapai KKM ('+KKM+').':'Belum mencapai KKM ('+KKM+').')+'&lt;/p&gt;';
  if(fin){
    h+='&lt;p&gt;&lt;b&gt;Nilai akhir yang dikirim dan disimpan: '+fin.score+'&lt;/b&gt; ('+(fin.source==="kkm"?'mencapai KKM':'nilai tertinggi dari '+rec.attempts.length+' percobaan')+').&lt;/p&gt;';
    h+='&lt;p&gt;Silakan unduh bukti pengerjaan, cetak, dan serahkan kepada guru.&lt;/p&gt;';
    h+='&lt;button class="b" id="kqPdf" type="button"&gt;Unduh / Cetak Bukti (PDF)&lt;/button&gt;';
  } else {
    h+='&lt;p&gt;Sisa kesempatan: &lt;b&gt;'+left+'&lt;/b&gt;. Nilai belum dikirim; nilai dikirim jika Anda mencapai '+KKM+' atau setelah percobaan ke-'+MAXA+'.&lt;/p&gt;';
    h+='&lt;button class="b" id="kqAgain" type="button"&gt;Coba Lagi&lt;/button&gt;';
  }
  h+='&lt;button class="b g" id="kqClose" type="button"&gt;Tutup&lt;/button&gt;';
  if(fin){
    h+='&lt;div class="rv"&gt;&lt;b&gt;Rincian jawaban (percobaan terbaik):&lt;/b&gt;';
    rec.attempts[fin.idx].det.forEach(function(d,i){h+='&lt;div&gt;No. '+(i+1)+': jawaban '+d.l+', kunci '+d.k+' - '+(d.ok?'&lt;span class="ok"&gt;benar&lt;/span&gt;':'&lt;span class="no"&gt;salah&lt;/span&gt;')+'&lt;/div&gt;';});
    h+='&lt;/div&gt;';
  }
  h+='&lt;/div&gt;&lt;/div&gt;';
  stage.innerHTML=h; stage.scrollTop=0;
  if($("kqAgain"))$("kqAgain").onclick=function(){closeStage();beginAttempt();};
  if($("kqPdf"))$("kqPdf").onclick=printProof;
  $("kqClose").onclick=function(){closeStage();showStatus();};
}
function closeStage(){ if(stage&amp;&amp;stage.parentNode)stage.parentNode.removeChild(stage); stage=null; }

/* ---------- bukti cetak F4 ---------- */
function printProof(){
  if(!rec||!rec.final)return;
  var f=rec.final, fa=rec.attempts[f.idx], old=$("kqPrint"); if(old)old.parentNode.removeChild(old);
  var d=document.createElement("div"); d.id="kqPrint";
  var h='&lt;h1&gt;BUKTI PENGERJAAN LATIHAN SOAL TIPE AKM&lt;/h1&gt;&lt;p class="sub"&gt;'+esc(MATERI)+' - Bahasa Indonesia Kelas XI SMK&lt;/p&gt;';
  h+='&lt;table&gt;&lt;tr&gt;&lt;td style="width:28%"&gt;Nama&lt;/td&gt;&lt;td&gt;'+esc(rec.nama)+'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kelas&lt;/td&gt;&lt;td&gt;'+esc(rec.kelas)+'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;No. Absen&lt;/td&gt;&lt;td&gt;'+esc(rec.absen)+'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tanggal cetak&lt;/td&gt;&lt;td&gt;'+esc(new Date().toLocaleString("id-ID"))+'&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;';
  h+='&lt;h2&gt;Riwayat percobaan&lt;/h2&gt;&lt;table&gt;&lt;tr&gt;&lt;th&gt;Percobaan&lt;/th&gt;&lt;th&gt;Waktu&lt;/th&gt;&lt;th&gt;Benar&lt;/th&gt;&lt;th&gt;Skor&lt;/th&gt;&lt;th&gt;Pelanggaran&lt;/th&gt;&lt;/tr&gt;';
  rec.attempts.forEach(function(a,i){h+='&lt;tr&gt;&lt;td&gt;'+(i+1)+(i===f.idx?' (dipakai)':'')+'&lt;/td&gt;&lt;td&gt;'+esc(new Date(a.t).toLocaleString("id-ID"))+'&lt;/td&gt;&lt;td&gt;'+a.benar+'/50&lt;/td&gt;&lt;td&gt;'+a.score+'&lt;/td&gt;&lt;td&gt;'+a.viol+'&lt;/td&gt;&lt;/tr&gt;';});
  h+='&lt;/table&gt;&lt;p&gt;&lt;b&gt;NILAI AKHIR: '+f.score+'&lt;/b&gt; (KKM '+KKM+') - '+(f.score&gt;=KKM?'Tuntas':'Belum tuntas')+'. Sumber nilai: '+(f.source==="kkm"?'percobaan yang mencapai KKM':'nilai tertinggi dari 3 percobaan')+'.&lt;/p&gt;';
  h+='&lt;h2&gt;Jawaban pada percobaan yang dipakai&lt;/h2&gt;&lt;div class="two"&gt;';
  for(var c=0;c&lt;2;c++){
    h+='&lt;table&gt;&lt;tr&gt;&lt;th&gt;No&lt;/th&gt;&lt;th&gt;Jawaban&lt;/th&gt;&lt;th&gt;Kunci&lt;/th&gt;&lt;th&gt;Hasil&lt;/th&gt;&lt;/tr&gt;';
    for(var i=c*25;i&lt;c*25+25;i++){var x=fa.det[i]; h+='&lt;tr&gt;&lt;td&gt;'+(i+1)+'&lt;/td&gt;&lt;td&gt;'+x.l+'&lt;/td&gt;&lt;td&gt;'+x.k+'&lt;/td&gt;&lt;td&gt;'+(x.ok?'Benar':'Salah')+'&lt;/td&gt;&lt;/tr&gt;';}
    h+='&lt;/table&gt;';
  }
  h+='&lt;/div&gt;&lt;p style="font-size:9pt"&gt;Catatan: urutan soal dan pilihan jawaban diacak untuk setiap siswa, sehingga nomor di atas mengikuti urutan tampilan pada layar siswa.&lt;/p&gt;';
  h+='&lt;div class="sig"&gt;&lt;div&gt;Siswa,&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;('+esc(rec.nama)+')&lt;/div&gt;&lt;div&gt;Guru Mata Pelajaran,&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;(................................)&lt;/div&gt;&lt;/div&gt;';
  d.innerHTML=h; document.body.appendChild(d);
  document.body.className+=" kq-printing";
  setTimeout(function(){
    window.print();
    setTimeout(function(){document.body.className=document.body.className.replace(/\s*kq-printing/g,"");},500);
  },150);
}

/* ---------- status &amp; mulai ---------- */
function showStatus(){
  var s=$("kqStatus"); if(!s)return;
  if(rec&amp;&amp;rec.final){
    s.innerHTML='&lt;div class="card done"&gt;&lt;h3&gt;Pengerjaan selesai&lt;/h3&gt;&lt;p&gt;Nilai akhir tersimpan: &lt;b&gt;'+rec.final.score+'&lt;/b&gt; ('+(rec.final.source==="kkm"?'mencapai KKM':'nilai tertinggi')+'). Semua kesempatan sudah selesai.&lt;/p&gt;&lt;button class="b" id="kqPdf2" type="button"&gt;Unduh / Cetak Bukti (PDF)&lt;/button&gt;&lt;/div&gt;';
    $("kqPdf2").onclick=printProof;
  } else s.innerHTML="";
}
function setMsg(t,e){var m=$("kqMsg"); m.textContent=t; m.className="msg"+(e?" e":"");}

function start(){
  var nama=$("kqNama").value.replace(/\s+/g," ").trim(), kelas=$("kqKelas").value.replace(/\s+/g," ").trim(), absen=$("kqAbsen").value.trim();
  if(nama.length&lt;3){setMsg("Isi nama lengkap dengan benar.",true);return;}
  if(!kelas){setMsg("Isi kelas.",true);return;}
  if(!/^\d{1,2}$/.test(absen)||+absen&lt;1){setMsg("No. Absen harus angka 1 sampai 99.",true);return;}
  absen=String(+absen); keyStr=keyOf(kelas,absen); setMsg("Memeriksa data...",false);
  sGet(keyStr).then(function(r){
    rec=r||{nama:nama,kelas:kelas,absen:absen,attempts:[],final:null};
    rec.nama=nama; rec.kelas=kelas; rec.absen=absen;
    if(rec.final||rec.attempts.length&gt;=MAXA){
      if(!rec.final&amp;&amp;rec.attempts.length&gt;=MAXA){ var bi=0; rec.attempts.forEach(function(a,k){if(a.score&gt;rec.attempts[bi].score)bi=k;}); rec.final={score:rec.attempts[bi].score,source:"tertinggi",idx:bi}; sendGas(); sSet(keyStr,rec); }
      setMsg("Kesempatan untuk No. Absen ini sudah selesai.",true); showStatus(); return;
    }
    setMsg("",false);
    if(!window.confirm("Mulai percobaan ke-"+(rec.attempts.length+1)+" dari "+MAXA+"?\n\nSoal tampil layar penuh. Jangan berpindah tab/aplikasi: pelanggaran ke-"+MAXV+" otomatis mengirim jawaban."))return;
    beginAttempt();
  });
}
$("kqStart").onclick=start;
})();
&lt;/script&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/latihan-soal-tipe-akm-pokok-bahasan.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-8348578144324146868</guid><pubDate>Mon, 12 Oct 2026 05:47:39 +0000</pubDate><atom:updated>2026-10-13T07:49:53.014+07:00</atom:updated><title>Analisis Data dan Fitur Lanjut Spreadsheet</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Analisis Data dan Fitur Lanjut Spreadsheet - Informatika Kelas X SMK&lt;/title&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&amp;family=IBM+Plex+Sans:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/jspdf-autotable@3.8.2/dist/jspdf.plugin.autotable.min.js"&gt;&lt;/script&gt;
&lt;style&gt;
  :root{
    --brand:#15803D; --brand-dark:#0D5A2A; --surface:#EEF7F1; --paper:#FFFFFF;
    --ink:#16221B; --ink-soft:#4C5C52; --line:#D7E6DC;
    --amber:#D99A1B; --green:#15803D; --red:#D6412C;
  }
  *{box-sizing:border-box; margin:0; padding:0;}
  html{scroll-behavior:smooth;}
  body{font-family:'IBM Plex Sans',sans-serif; background:#F3F7F4; color:var(--ink); line-height:1.6;}
  .mono{font-family:'IBM Plex Mono',monospace;}

  .page-shell{max-width:1100px; margin:16px auto; background:#FFFFFF; border-radius:14px;
    box-shadow:0 8px 32px rgba(10,40,20,.14),0 1px 3px rgba(10,40,20,.08); border:1px solid var(--line);}

  header.hero{background:var(--brand); color:#fff; padding:34px 5% 30px; border-radius:14px 14px 0 0; position:relative; overflow:hidden;}
  header.hero::before{content:""; position:absolute; inset:0; background-image:
    linear-gradient(rgba(255,255,255,.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.07) 1px, transparent 1px);
    background-size:28px 28px; pointer-events:none;}
  header.hero &gt; *{position:relative; z-index:1;}
  .kop{text-align:center; font-family:'IBM Plex Mono',monospace; font-size:.72rem; letter-spacing:.12em; text-transform:uppercase; color:#BFEBCF; margin-bottom:8px;}
  h1.title{font-family:'IBM Plex Mono',monospace; font-weight:700; font-size:clamp(1.5rem,3.6vw,2.3rem); text-align:center; line-height:1.3;}
  .subtitle{text-align:center; margin-top:10px; color:#DFF3E6; font-size:.98rem; max-width:70ch; margin-left:auto; margin-right:auto;}
  .meta-strip{margin-top:26px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:1px; background:rgba(255,255,255,.18); border:1px solid rgba(255,255,255,.18);}
  .meta-cell{background:var(--brand-dark); padding:12px 14px; text-align:center;}
  .meta-cell .k{font-family:'IBM Plex Mono',monospace; font-size:.62rem; letter-spacing:.08em; text-transform:uppercase; color:#9FDFB5;}
  .meta-cell .v{margin-top:4px; font-size:.9rem; font-weight:700;}

  main{padding:0;}
  .section{padding:32px 5%; border-bottom:1px solid #ECEFF3;}
  .section:last-child{border-bottom:none;}
  .section-tag{font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-soft); margin-bottom:8px;}
  h2.section-title{font-family:'IBM Plex Mono',monospace; font-size:clamp(1.15rem,2vw,1.5rem); font-weight:700; color:var(--brand-dark); margin-bottom:14px;}
  h3.sub-title{font-family:'IBM Plex Mono',monospace; font-size:1rem; font-weight:600; color:var(--brand-dark); margin:20px 0 10px; display:flex; align-items:center; gap:9px;}
  h3.sub-title::before{content:""; width:8px; height:8px; background:var(--amber); display:inline-block; border-radius:1px;}
  p{color:#33403A; max-width:80ch; margin-bottom:10px;}
  ul,ol{margin:6px 0 14px 1.3em; max-width:80ch;}
  li{margin-bottom:6px; color:#33403A;}
  strong{color:var(--brand-dark);}

  .callout{border-left:3px solid var(--brand); background:var(--surface); padding:14px 18px; border-radius:0 8px 8px 0; margin:14px 0; max-width:80ch;}
  .callout .lbl{font-family:'IBM Plex Mono',monospace; font-size:.68rem; letter-spacing:.1em; text-transform:uppercase; color:var(--brand-dark); font-weight:700; margin-bottom:6px;}
  .callout.warn{border-left-color:var(--red); background:#FCEBE8;}
  .callout.warn .lbl{color:#9B3324;}

  .card-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:14px; margin:16px 0 6px;}
  .card{background:#FFFFFF; border:1.5px solid var(--line); border-radius:8px; padding:16px 18px; box-shadow:0 2px 8px rgba(10,40,20,.05);}
  .card .tag{font-family:'IBM Plex Mono',monospace; font-size:.64rem; letter-spacing:.1em; text-transform:uppercase; color:#fff; background:var(--brand); padding:3px 8px; border-radius:3px; display:inline-block; margin-bottom:10px;}
  .card h4{font-family:'IBM Plex Mono',monospace; font-size:.94rem; margin-bottom:6px; color:var(--brand-dark);}
  .card p{font-size:.87rem; margin-bottom:0;}
  .card code{font-family:'IBM Plex Mono',monospace; background:var(--surface); padding:1px 6px; border-radius:3px; font-size:.85em; color:var(--brand-dark);}

  .table-scroll{overflow-x:auto; margin:16px 0;}
  table.compare{width:100%; border-collapse:collapse; font-size:.88rem; min-width:460px;}
  table.compare th, table.compare td{border:1px solid var(--line); padding:10px 12px; text-align:left;}
  table.compare th{background:var(--brand); color:#fff; font-family:'IBM Plex Mono',monospace; font-size:.72rem; letter-spacing:.04em; text-transform:uppercase;}
  table.compare tr:nth-child(even) td{background:var(--surface);}

  .summary-box{background:var(--brand-dark); color:#fff; border-radius:8px; padding:22px 24px; margin-top:8px;}
  .summary-box h4{font-family:'IBM Plex Mono',monospace; color:#B8ECC9; font-size:.82rem; letter-spacing:.08em; text-transform:uppercase; margin-bottom:12px;}
  .summary-box ul{margin:0 0 0 1.1em; max-width:none;}
  .summary-box li{color:#E3F4E9; margin-bottom:7px;}

  .task-panel{border:1.5px dashed var(--brand); border-radius:8px; padding:24px 26px; background:repeating-linear-gradient(135deg,#FFFFFF,#FFFFFF 18px,var(--surface) 18px,var(--surface) 19px);}
  .task-head{display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:12px;}
  .task-head .badge{font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.08em; text-transform:uppercase; background:var(--amber); color:#3A2A00; padding:4px 10px; border-radius:3px; font-weight:700;}
  .task-panel h3{font-family:'IBM Plex Mono',monospace; font-size:1.2rem; color:var(--brand-dark);}
  .steps-num{counter-reset:step; margin:14px 0; max-width:80ch;}
  .steps-num li{counter-increment:step; list-style:none; position:relative; padding-left:32px; margin-bottom:12px;}
  .steps-num li::before{content:counter(step); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; font-weight:700; color:#fff; background:var(--brand); width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:.78rem;}
  table.rubrik{width:100%; border-collapse:collapse; margin-top:16px; font-size:.86rem;}
  table.rubrik th, table.rubrik td{border:1px solid #C9D6CC; padding:9px 11px; text-align:left;}
  table.rubrik th{background:var(--brand-dark); color:#fff; font-family:'IBM Plex Mono',monospace; font-size:.73rem; letter-spacing:.04em; text-transform:uppercase;}
  table.rubrik tr:nth-child(even) td{background:#EFF5F0;}

  .field label{display:block; font-family:'IBM Plex Mono',monospace; font-size:.68rem; letter-spacing:.06em; text-transform:uppercase; color:var(--brand-dark); margin-bottom:6px;}
  .field input,.field select{width:100%; padding:10px 12px; border-radius:6px; border:1px solid var(--line); background:#fff; font-size:.92rem;}
  .student-info{display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:14px; margin:14px 0;}

  .primary-btn{font-family:'IBM Plex Mono',monospace; font-weight:600; font-size:.9rem; padding:13px 26px; border-radius:7px; border:none; cursor:pointer; background:var(--brand); color:#fff;}
  .primary-btn:hover{filter:brightness(1.08);}
  .primary-btn.ghost{background:#fff; color:var(--brand); border:1.5px solid var(--brand);}
  .primary-btn:disabled{opacity:.5; cursor:not-allowed;}

  .excel-grid{border-collapse:collapse; margin:10px 0 14px; font-family:'IBM Plex Mono',monospace; font-size:.82rem;}
  .excel-grid th, .excel-grid td{border:1px solid #BFD4C6; padding:6px 10px; text-align:center; min-width:70px;}
  .excel-grid th{background:#E3F0E7; color:var(--brand-dark); font-weight:700;}
  .excel-grid th.rownum{background:#E3F0E7; min-width:30px;}
  .sheet-grid td.g-label{text-align:left; background:#F7FAF8; color:var(--ink-soft); font-family:'IBM Plex Sans',sans-serif; font-size:.78rem; min-width:110px;}
  .sheet-input{width:100%; min-width:92px; border:1px solid var(--line); border-radius:4px; padding:5px 6px; font-family:'IBM Plex Mono',monospace; font-size:.8rem; text-align:right; background:#fff;}
  .sheet-input.formula{text-align:left; background:#FFFDF2;}
  .sheet-input:focus{outline:none; border-color:var(--brand); box-shadow:0 0 0 2px rgba(21,128,61,.18);}
  .sheet-input.err{border-color:var(--red); color:var(--red); background:#FCEBE8;}
  .sheet-input.ref{background:#F3F3F3; color:var(--ink-soft); text-align:center; cursor:not-allowed;}
  tr.hi-max td{background:#D9F2E1 !important; font-weight:700;}
  table.final-table td.cell-ok{background:#E3F7EA; color:#0D5A2A; font-weight:600;}
  table.final-table td.cell-bad{background:#FCE6E3; color:#9B3324; font-weight:600;}
  table.final-table td.cell-empty{background:#F3F3F3; color:#8A8A8A; font-style:italic;}
  .task-actions{display:flex; gap:10px; flex-wrap:wrap; margin:14px 0;}

  .flag{color:var(--red); font-weight:700; display:none;}
  .flag.show{display:inline;}

  .result-panel{display:none; margin-top:18px; background:#fff; border:1.5px solid var(--green); border-radius:8px; padding:22px 24px;}
  .score-lbl{font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-soft);}
  .score{font-family:'IBM Plex Mono',monospace; font-size:2.3rem; font-weight:700; color:var(--green);}
  #uploadMsg,#pdfMsg{font-family:'IBM Plex Mono',monospace; font-size:.82rem; color:#8A6A1E; margin-left:10px;}

  footer{background:var(--surface); text-align:center; padding:20px 5% 28px; font-family:'IBM Plex Mono',monospace; font-size:.72rem; color:var(--ink-soft); border-top:1px solid var(--line); border-radius:0 0 14px 14px;}

  @media(max-width:700px){header.hero{padding:26px 5% 24px;} .section{padding:24px 5%;}}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="page-shell"&gt;
&lt;header class="hero"&gt;
  &lt;div class="kop"&gt;Informatika - Fase E - Kelas X SMK&lt;/div&gt;
  &lt;h1 class="title"&gt;Analisis Data dan Fitur Lanjut Spreadsheet&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Murid mampu menerapkan fungsi lanjutan, lookup, sort, filter, conditional formatting, grafik, dan PivotTable untuk menganalisis data secara efektif.&lt;/p&gt;
  &lt;div class="meta-strip"&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Elemen CP&lt;/div&gt;&lt;div class="v"&gt;Pengolah Angka Lanjut&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK Kelas X&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;1 Pertemuan - 4 JP&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Model&lt;/div&gt;&lt;div class="v"&gt;Penjelasan, Praktik &amp; Evaluasi Individu&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;
  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="section-tag"&gt;Capaian &amp; Tujuan Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Ke Mana Materi Ini Menuju&lt;/h2&gt;
    &lt;p&gt;Murid mampu menerapkan &lt;strong&gt;fungsi lanjutan, lookup, sort, filter, conditional formatting, grafik,&lt;/strong&gt; dan &lt;strong&gt;PivotTable&lt;/strong&gt; untuk menganalisis data secara efektif pada aplikasi spreadsheet.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Rincian Kegiatan (4 JP x 45 menit)&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Tahap&lt;/th&gt;&lt;th&gt;Kegiatan&lt;/th&gt;&lt;th&gt;Waktu&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pendahuluan&lt;/td&gt;&lt;td&gt;Apersepsi ("Bagaimana toko besar bisa menganalisis ribuan transaksi hanya dalam hitungan detik?"), penyampaian tujuan&lt;/td&gt;&lt;td&gt;15 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Eksplorasi Konsep&lt;/td&gt;&lt;td&gt;Penjelasan fungsi lanjutan (SUMIF/COUNTIF/AVERAGEIF), lookup (VLOOKUP/HLOOKUP), sort &amp; filter, conditional formatting, grafik, dan PivotTable&lt;/td&gt;&lt;td&gt;55 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Demonstrasi&lt;/td&gt;&lt;td&gt;Guru mendemonstrasikan penerapan fungsi lanjutan dan lookup pada contoh data&lt;/td&gt;&lt;td&gt;25 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Praktik Individu&lt;/td&gt;&lt;td&gt;Murid mengerjakan tugas praktik mandiri langsung pada Lembar Kerja Spreadsheet di halaman ini&lt;/td&gt;&lt;td&gt;45 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Evaluasi &amp; Pengumpulan&lt;/td&gt;&lt;td&gt;Sistem menilai hasil kerja secara otomatis, murid mengunduh bukti PDF&lt;/td&gt;&lt;td&gt;20 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Refleksi dan penguatan konsep&lt;/td&gt;&lt;td&gt;10 menit&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="fungsi-lanjutan"&gt;
    &lt;div class="section-tag"&gt;Analisis Data&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fungsi Lanjutan (Fungsi Bersyarat)&lt;/h2&gt;
    &lt;p&gt;Fungsi lanjutan memungkinkan perhitungan dilakukan hanya pada data yang memenuhi &lt;strong&gt;syarat (kriteria)&lt;/strong&gt; tertentu, sehingga analisis data dalam tabel besar menjadi lebih cepat dan akurat tanpa harus memilah data secara manual.&lt;/p&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;COUNTIF&lt;/h4&gt;&lt;p&gt;Menghitung banyak sel dalam suatu range yang memenuhi satu syarat. Contoh: &lt;code&gt;=COUNTIF(B2:B9,"Alat Tulis")&lt;/code&gt; menghitung berapa jenis barang berkategori Alat Tulis.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;SUMIF&lt;/h4&gt;&lt;p&gt;Menjumlahkan nilai pada suatu range yang memenuhi satu syarat tertentu. Contoh: &lt;code&gt;=SUMIF(B2:B9,"Alat Tulis",E2:E9)&lt;/code&gt; menjumlahkan Total hanya untuk kategori Alat Tulis.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;AVERAGEIF&lt;/h4&gt;&lt;p&gt;Menghitung rata-rata nilai pada suatu range yang memenuhi satu syarat tertentu. Contoh: &lt;code&gt;=AVERAGEIF(B2:B9,"Jasa",E2:E9)&lt;/code&gt; menghitung rata-rata Total khusus kategori Jasa.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;SUMIFS / COUNTIFS&lt;/h4&gt;&lt;p&gt;Versi lanjutan dari SUMIF/COUNTIF yang mampu memeriksa &lt;strong&gt;lebih dari satu syarat sekaligus&lt;/strong&gt;. Contoh: &lt;code&gt;=SUMIFS(E2:E9,B2:B9,"Jasa",C2:C9,"&amp;gt;=20")&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Catatan Penting&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Syarat (kriteria) berupa teks ditulis dalam tanda kutip, misalnya &lt;code&gt;"Jasa"&lt;/code&gt;, sedangkan syarat berupa perbandingan angka ditulis seperti &lt;code&gt;"&amp;gt;=30"&lt;/code&gt; atau &lt;code&gt;"&amp;lt;15"&lt;/code&gt;, tetap di dalam tanda kutip.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="fungsi-teks"&gt;
    &lt;div class="section-tag"&gt;Pengolahan Teks&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fungsi Teks (STRING)&lt;/h2&gt;
    &lt;p&gt;Fungsi teks (string) digunakan untuk &lt;strong&gt;mengambil atau memotong sebagian karakter&lt;/strong&gt; dari sebuah teks/kode, misalnya mengambil kode kategori atau kode lokasi dari sebuah Kode Transaksi tanpa harus mengetik ulang secara manual.&lt;/p&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;LEFT&lt;/h4&gt;&lt;p&gt;Mengambil sejumlah karakter dari &lt;strong&gt;sisi kiri (awal)&lt;/strong&gt; teks. Contoh: &lt;code&gt;=LEFT("AT01A",2)&lt;/code&gt; menghasilkan "AT".&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;RIGHT&lt;/h4&gt;&lt;p&gt;Mengambil sejumlah karakter dari &lt;strong&gt;sisi kanan (akhir)&lt;/strong&gt; teks. Contoh: &lt;code&gt;=RIGHT("AT01A",1)&lt;/code&gt; menghasilkan "A".&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;MID&lt;/h4&gt;&lt;p&gt;Mengambil sejumlah karakter mulai dari &lt;strong&gt;posisi tertentu di tengah&lt;/strong&gt; teks. Contoh: &lt;code&gt;=MID("AT01A",3,2)&lt;/code&gt; menghasilkan "01" (mulai karakter ke-3, sepanjang 2 karakter).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;LEN&lt;/h4&gt;&lt;p&gt;Menghitung &lt;strong&gt;jumlah karakter&lt;/strong&gt; dalam sebuah teks. Contoh: &lt;code&gt;=LEN("AT01A")&lt;/code&gt; menghasilkan 5.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Contoh Penerapan&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Sebuah Kode Transaksi "AT01A" dapat dipecah menjadi tiga informasi sekaligus: &lt;code&gt;LEFT(sel,2)&lt;/code&gt; mengambil "AT" (kode kategori), &lt;code&gt;MID(sel,3,2)&lt;/code&gt; mengambil "01" (nomor urut), dan &lt;code&gt;RIGHT(sel,1)&lt;/code&gt; mengambil "A" (kode gudang) - semua dari satu kode tanpa perlu kolom terpisah untuk tiap bagiannya.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="lookup"&gt;
    &lt;div class="section-tag"&gt;Pencarian Data&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fungsi Lookup (VLOOKUP &amp; HLOOKUP)&lt;/h2&gt;
    &lt;p&gt;Fungsi lookup digunakan untuk &lt;strong&gt;mencari data secara otomatis&lt;/strong&gt; dari tabel referensi berdasarkan satu kata kunci, sehingga tidak perlu mencari dan mengetik data referensi secara manual satu per satu.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;VLOOKUP (Vertical Lookup)&lt;/h3&gt;
    &lt;p&gt;Mencari data pada tabel referensi yang tersusun &lt;strong&gt;vertikal (per kolom)&lt;/strong&gt;. Struktur dasarnya: &lt;code&gt;=VLOOKUP(nilai_dicari, tabel_referensi, nomor_kolom, FALSE)&lt;/code&gt;.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Kode&lt;/th&gt;&lt;th&gt;Nama Barang&lt;/th&gt;&lt;th&gt;Harga&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K01&lt;/td&gt;&lt;td&gt;Kabel UTP&lt;/td&gt;&lt;td&gt;850000&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K02&lt;/td&gt;&lt;td&gt;Router Wi-Fi&lt;/td&gt;&lt;td&gt;320000&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;K03&lt;/td&gt;&lt;td&gt;Switch 8 Port&lt;/td&gt;&lt;td&gt;275000&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Contoh Penerapan&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Jika tabel referensi di atas berada pada range &lt;code&gt;H2:J4&lt;/code&gt;, rumus &lt;code&gt;=VLOOKUP("K02",H2:J4,3,FALSE)&lt;/code&gt; akan mencari baris dengan kode "K02" lalu mengambil nilai pada kolom ke-3 dari tabel referensi tersebut, yaitu 320000. Parameter terakhir &lt;code&gt;FALSE&lt;/code&gt; berarti pencarian harus &lt;strong&gt;persis sama (exact match)&lt;/strong&gt; - ini penting agar hasil pencarian tidak keliru.&lt;/p&gt;
    &lt;/div&gt;
    &lt;h3 class="sub-title"&gt;HLOOKUP (Horizontal Lookup)&lt;/h3&gt;
    &lt;p&gt;Prinsip kerjanya sama dengan VLOOKUP, namun digunakan ketika tabel referensi tersusun &lt;strong&gt;horizontal (per baris)&lt;/strong&gt;. Strukturnya: &lt;code&gt;=HLOOKUP(nilai_dicari, tabel_referensi, nomor_baris, FALSE)&lt;/code&gt;.&lt;/p&gt;
  &lt;/section&gt;

  &lt;section class="section" id="sort-filter"&gt;
    &lt;div class="section-tag"&gt;Penyajian Data&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Sort (Urutkan) dan Filter (Saring)&lt;/h2&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Mengurutkan&lt;/span&gt;&lt;h4&gt;Sort Ascending / Descending&lt;/h4&gt;&lt;p&gt;Mengurutkan seluruh baris data berdasarkan satu kolom, dari terkecil ke terbesar (A-Z) atau sebaliknya (Z-A), tanpa menghilangkan data apa pun.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Mengurutkan&lt;/span&gt;&lt;h4&gt;Custom Sort (Multi-level)&lt;/h4&gt;&lt;p&gt;Mengurutkan data berdasarkan lebih dari satu kolom sekaligus, misalnya urutkan dulu berdasarkan Kategori, baru berdasarkan Total di dalam tiap kategori.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Menyaring&lt;/span&gt;&lt;h4&gt;Filter (AutoFilter)&lt;/h4&gt;&lt;p&gt;Menyembunyikan sementara baris yang tidak memenuhi kriteria tertentu, sehingga hanya baris yang relevan yang ditampilkan - data lain tidak terhapus, hanya disembunyikan.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Menyaring&lt;/span&gt;&lt;h4&gt;Filter by Condition&lt;/h4&gt;&lt;p&gt;Menyaring data berdasarkan syarat tertentu, misalnya hanya menampilkan baris dengan Total di atas rata-rata, atau Kategori tertentu saja.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Perbedaan Penting&lt;/div&gt;
      &lt;p style="margin:0;"&gt;&lt;strong&gt;Sort&lt;/strong&gt; mengubah urutan seluruh data secara permanen, sedangkan &lt;strong&gt;Filter&lt;/strong&gt; hanya menyembunyikan baris yang tidak sesuai kriteria sementara - data aslinya tetap utuh dan dapat ditampilkan kembali kapan saja.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="conditional-formatting"&gt;
    &lt;div class="section-tag"&gt;Visual Data&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Conditional Formatting (Format Bersyarat)&lt;/h2&gt;
    &lt;p&gt;Conditional Formatting memberi tampilan visual (warna, bar, ikon) secara &lt;strong&gt;otomatis&lt;/strong&gt; pada sel berdasarkan nilainya, sehingga pola dan data penting pada tabel besar langsung terlihat tanpa harus membaca satu per satu.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Jenis Aturan&lt;/th&gt;&lt;th&gt;Contoh Tampilan&lt;/th&gt;&lt;th&gt;Kegunaan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Highlight Cell Rules&lt;/td&gt;&lt;td&gt;Sel merah untuk nilai &amp;lt; 60&lt;/td&gt;&lt;td&gt;Menyorot sel yang memenuhi syarat tertentu (lebih besar, lebih kecil, sama dengan, dsb)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Color Scales&lt;/td&gt;&lt;td&gt;Gradasi hijau (tinggi) ke merah (rendah)&lt;/td&gt;&lt;td&gt;Menampilkan sebaran nilai tinggi-rendah dalam satu range secara visual&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Data Bars&lt;/td&gt;&lt;td&gt;Bilah warna sepanjang nilai sel&lt;/td&gt;&lt;td&gt;Membandingkan besar-kecilnya nilai antar sel secara sekilas&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Icon Sets&lt;/td&gt;&lt;td&gt;Panah/bintang/lampu lalu lintas&lt;/td&gt;&lt;td&gt;Memberi indikator status (naik/turun, baik/buruk) secara ringkas&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="grafik"&gt;
    &lt;div class="section-tag"&gt;Visualisasi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Grafik (Chart)&lt;/h2&gt;
    &lt;p&gt;Grafik mengubah data tabel menjadi bentuk visual agar pola, perbandingan, dan tren data lebih mudah dipahami secara sekilas.&lt;/p&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Perbandingan&lt;/span&gt;&lt;h4&gt;Bar/Column Chart&lt;/h4&gt;&lt;p&gt;Cocok untuk membandingkan nilai antar kategori, misalnya total penjualan tiap jenis barang.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Tren Waktu&lt;/span&gt;&lt;h4&gt;Line Chart&lt;/h4&gt;&lt;p&gt;Cocok untuk menunjukkan perubahan data dari waktu ke waktu, misalnya penjualan per bulan selama setahun.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Proporsi&lt;/span&gt;&lt;h4&gt;Pie Chart&lt;/h4&gt;&lt;p&gt;Cocok untuk menunjukkan proporsi/persentase bagian terhadap keseluruhan, misalnya kontribusi tiap kategori terhadap total penjualan.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Gabungan&lt;/span&gt;&lt;h4&gt;Combo Chart&lt;/h4&gt;&lt;p&gt;Menggabungkan dua jenis grafik (mis. bar dan line) dalam satu tampilan untuk membandingkan dua jenis data berbeda sekaligus.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pivot"&gt;
    &lt;div class="section-tag"&gt;Ringkasan Data&lt;/div&gt;
    &lt;h2 class="section-title"&gt;PivotTable&lt;/h2&gt;
    &lt;p&gt;PivotTable adalah fitur untuk &lt;strong&gt;meringkas dan mengelompokkan&lt;/strong&gt; data tabel besar menjadi tabel ringkasan baru secara otomatis - tanpa perlu menulis rumus manual - sehingga pola dan total per kelompok data langsung terlihat.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Empat Area Utama PivotTable&lt;/h3&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Area&lt;/span&gt;&lt;h4&gt;Filters&lt;/h4&gt;&lt;p&gt;Menyaring data mana saja yang akan ditampilkan dalam PivotTable, misalnya hanya data bulan tertentu.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Area&lt;/span&gt;&lt;h4&gt;Rows&lt;/h4&gt;&lt;p&gt;Menentukan kategori yang menjadi baris pengelompokan, misalnya Kategori Barang/Jasa.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Area&lt;/span&gt;&lt;h4&gt;Columns&lt;/h4&gt;&lt;p&gt;Menentukan kategori yang menjadi kolom pengelompokan, misalnya Bulan Transaksi.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Area&lt;/span&gt;&lt;h4&gt;Values&lt;/h4&gt;&lt;p&gt;Nilai yang dihitung/diringkas (dijumlah, dirata-rata, dihitung) untuk tiap kelompok, misalnya Total Penjualan.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Contoh Penerapan&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Dari tabel transaksi berisi puluhan baris data penjualan per barang, PivotTable dapat langsung menghasilkan tabel ringkas "Total Penjualan per Kategori" hanya dengan menyeret (drag) kolom Kategori ke area Rows dan kolom Total ke area Values - tanpa menulis satu pun rumus SUM manual.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="section-tag"&gt;Penguatan Konsep&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rangkuman Materi&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;h4&gt;Poin-Poin Kunci&lt;/h4&gt;
      &lt;ul&gt;
        &lt;li&gt;Fungsi lanjutan (COUNTIF, SUMIF, AVERAGEIF, SUMIFS/COUNTIFS) menghitung data berdasarkan satu atau lebih syarat tertentu.&lt;/li&gt;
        &lt;li&gt;Fungsi teks (LEFT, RIGHT, MID, LEN) mengambil atau memotong sebagian karakter dari sebuah teks/kode.&lt;/li&gt;
        &lt;li&gt;Fungsi lookup (VLOOKUP/HLOOKUP) mencari data secara otomatis dari tabel referensi berdasarkan kata kunci, dengan exact match (FALSE) agar hasil akurat.&lt;/li&gt;
        &lt;li&gt;Sort mengurutkan seluruh data secara permanen; Filter menyembunyikan sementara data yang tidak memenuhi kriteria.&lt;/li&gt;
        &lt;li&gt;Conditional Formatting menyorot data penting secara visual (warna, data bar, ikon) berdasarkan nilainya secara otomatis.&lt;/li&gt;
        &lt;li&gt;Grafik (Bar, Line, Pie, Combo) membantu memahami pola dan perbandingan data secara visual sesuai kebutuhan analisis.&lt;/li&gt;
        &lt;li&gt;PivotTable meringkas data besar menjadi tabel kelompok (Rows, Columns, Values, Filters) tanpa rumus manual.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="identitas"&gt;
    &lt;div class="section-tag"&gt;Praktik &amp; Evaluasi Individu&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Isi Data Diri&lt;/h2&gt;
    &lt;p&gt;Kerjakan tugas praktik spreadsheet berikut secara &lt;strong&gt;mandiri&lt;/strong&gt; langsung di halaman ini. Hasil kerja dinilai otomatis oleh sistem dan langsung dikirim ke guru begitu tombol penilaian ditekan.&lt;/p&gt;
    &lt;div class="student-info"&gt;
      &lt;div class="field"&gt;&lt;label&gt;Kelas&lt;/label&gt;
        &lt;select id="kelasADFL"&gt;
          &lt;option value=""&gt;-- Pilih Kelas --&lt;/option&gt;
          &lt;option value="X TB1"&gt;X TB1&lt;/option&gt;&lt;option value="X TB2"&gt;X TB2&lt;/option&gt;
          &lt;option value="X TO1"&gt;X TO1&lt;/option&gt;&lt;option value="X TO2"&gt;X TO2&lt;/option&gt;
          &lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;&lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;
        &lt;/select&gt;
      &lt;/div&gt;
      &lt;div class="field"&gt;&lt;label&gt;No. Absen&lt;/label&gt;
        &lt;select id="noAbsenADFL" disabled&gt;
          &lt;option value=""&gt;- Pilih kelas dulu -&lt;/option&gt;
        &lt;/select&gt;
      &lt;/div&gt;
      &lt;div class="field"&gt;&lt;label&gt;Nama Lengkap (otomatis)&lt;/label&gt;&lt;input type="text" id="namaADFL" readonly placeholder="- Pilih no. absen dulu -"&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="praktik"&gt;
    &lt;div class="section-tag"&gt;Tugas Praktik Individu&lt;/div&gt;
    &lt;div class="task-panel"&gt;
      &lt;div class="task-head"&gt;
        &lt;span class="badge"&gt;Praktik Mandiri&lt;/span&gt;
        &lt;span class="badge" style="background:var(--brand-dark); color:#fff;"&gt;45 menit praktik + penilaian otomatis&lt;/span&gt;
      &lt;/div&gt;
      &lt;h3&gt;Analisis Penjualan Koperasi Sekolah&lt;/h3&gt;
      &lt;p&gt;Kamu berperan sebagai staf administrasi &lt;strong&gt;Koperasi Sekolah&lt;/strong&gt; yang mencatat penjualan barang dan jasa selama satu bulan terakhir. Kerjakan instruksi bernomor berikut &lt;strong&gt;secara langsung&lt;/strong&gt; dengan mengetikkan data dan rumus sendiri pada Lembar Kerja Spreadsheet di bawah - seluruh sel tidak menerima tempel (paste), harus diketik sendiri dan dihitung sungguhan oleh sistem, persis seperti bekerja di Excel/Google Sheets.&lt;/p&gt;

      &lt;div class="card" style="background:var(--surface); border-style:dashed; margin-top:16px;"&gt;
        &lt;span class="tag"&gt;Referensi Latihan - Jangan Disalin-Tempel&lt;/span&gt;

        &lt;p style="font-size:.78rem; font-weight:600; color:var(--brand-dark); margin-top:12px;"&gt;1. Keadaan Awal (Data Sebelum Latihan Dikerjakan)&lt;/p&gt;
        &lt;div class="table-scroll" id="dataAwalWrap" style="margin:8px 0 4px;"&gt;&lt;/div&gt;

        &lt;p style="font-size:.78rem; font-weight:600; color:var(--brand-dark); margin-top:16px;"&gt;2. Instruksi Soal (Ketikkan Sendiri di Lembar Kerja di Bawah)&lt;/p&gt;
        &lt;ol class="steps-num" id="instruksiSteps" style="margin-top:10px;"&gt;&lt;/ol&gt;

        &lt;p style="font-size:.78rem; font-weight:600; color:var(--brand-dark); margin-top:16px;"&gt;3. Keadaan Akhir (Patokan Setelah Instruksi Dikerjakan)&lt;/p&gt;
        &lt;p style="font-size:.8rem; color:var(--ink-soft); margin-top:2px;"&gt;Gunakan tabel ini sebagai patokan hasil akhir yang benar - bukan untuk disalin, tetapi untuk mengecek hasil pekerjaanmu sendiri setelah selesai mengetik rumus.&lt;/p&gt;
        &lt;div class="table-scroll" id="keadaanAkhirWrap" style="margin:8px 0 4px;"&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;h3 class="sub-title" style="margin-top:22px;"&gt;Tabel Referensi VLOOKUP &amp; HLOOKUP (Tidak Bisa Diubah)&lt;/h3&gt;
      &lt;p style="font-size:.84rem;"&gt;Tabel berikut dipakai sebagai sumber data oleh rumus VLOOKUP dan HLOOKUP di Lembar Kerja - sudah terisi otomatis, tidak perlu dan tidak bisa diketik ulang.&lt;/p&gt;
      &lt;div class="table-scroll" id="refTableWrap"&gt;&lt;/div&gt;

      &lt;h3 class="sub-title" style="margin-top:22px;"&gt;Lembar Kerja Spreadsheet (Dihitung Langsung oleh Sistem)&lt;/h3&gt;
      &lt;p style="font-size:.86rem;"&gt;Ketikkan data pada kolom &lt;strong&gt;A&lt;/strong&gt; sampai &lt;strong&gt;D&lt;/strong&gt; dan &lt;strong&gt;G&lt;/strong&gt; (baris 2-9) sesuai contoh Data Awal, lalu ketikkan rumus pada kolom &lt;strong&gt;E&lt;/strong&gt; (Total), &lt;strong&gt;F&lt;/strong&gt; (Status), &lt;strong&gt;H-J&lt;/strong&gt; (fungsi STRING), &lt;strong&gt;K&lt;/strong&gt; (VLOOKUP), &lt;strong&gt;L&lt;/strong&gt; (HLOOKUP), serta sel ringkasan E11-E14, diawali tanda "=".&lt;/p&gt;
      &lt;div class="table-scroll" id="gridWrap"&gt;&lt;/div&gt;
      &lt;p class="flag" id="pasteWarning" style="margin:6px 0 0;"&gt;[PERINGATAN] Percobaan tempel terdeteksi pada lembar kerja - seluruh data dan rumus harus diketik sendiri.&lt;/p&gt;

      &lt;div class="task-actions"&gt;
        &lt;button id="btnSortGrid" class="primary-btn ghost"&gt;Urutkan Berdasarkan Total (Sort)&lt;/button&gt;
        &lt;button id="btnFilterGrid" class="primary-btn ghost"&gt;Tampilkan Hanya di Atas Rata-rata (Filter)&lt;/button&gt;
        &lt;button id="btnResetGrid" class="primary-btn ghost"&gt;Reset Lembar Kerja&lt;/button&gt;
      &lt;/div&gt;
      &lt;div id="sortedResult"&gt;&lt;/div&gt;
      &lt;div id="filterResult"&gt;&lt;/div&gt;

      &lt;h3 class="sub-title"&gt;Grafik Total Penjualan (Otomatis)&lt;/h3&gt;
      &lt;canvas id="dataChart" width="640" height="220" style="max-width:100%; background:#fff; border:1px solid var(--line); border-radius:8px; display:block;"&gt;&lt;/canvas&gt;

      &lt;div class="task-actions" style="margin-top:20px;"&gt;
        &lt;button id="btnHitung" class="primary-btn"&gt;Hitung Nilai &amp; Kirim ke Guru&lt;/button&gt;
        &lt;span id="uploadMsg"&gt;&lt;/span&gt;
      &lt;/div&gt;

      &lt;div class="result-panel" id="resultPanel"&gt;
        &lt;div class="score-lbl"&gt;Nilai Praktik (Otomatis)&lt;/div&gt;
        &lt;div class="score" id="scoreValue"&gt;0&lt;/div&gt;
        &lt;p id="resultDetail" style="font-size:.84rem; color:var(--ink-soft);"&gt;&lt;/p&gt;

        &lt;h3 class="sub-title" style="margin-top:18px;"&gt;Hasil Akhir Pengerjaan Tabel&lt;/h3&gt;
        &lt;p style="font-size:.8rem; color:var(--ink-soft);"&gt;Sel berwarna hijau = benar, merah = salah, abu-abu = belum diisi.&lt;/p&gt;
        &lt;div id="finalTableWrap"&gt;&lt;/div&gt;

        &lt;h3 class="sub-title" style="margin-top:18px;"&gt;Tabel Ringkasan per Kategori (Simulasi PivotTable)&lt;/h3&gt;
        &lt;div id="pivotTableWrap"&gt;&lt;/div&gt;

        &lt;div class="task-actions" style="margin-top:16px;"&gt;
          &lt;button id="btnUnduhPDF" class="primary-btn ghost"&gt;Unduh Bukti (PDF - 1 Halaman Tabel Hasil)&lt;/button&gt;
        &lt;/div&gt;
        &lt;span id="pdfMsg" class="mono" style="font-size:.78rem; color:var(--ink-soft);"&gt;&lt;/span&gt;
      &lt;/div&gt;

      &lt;h3 class="sub-title" style="margin-top:22px;"&gt;Format Penilaian&lt;/h3&gt;
      &lt;p&gt;Pekerjaan dinilai dari ketepatan data yang diketik, rumus Total dan Status, rumus ringkasan SUM/AVERAGE, fungsi lanjutan SUMIF/COUNTIF, fungsi STRING (LEFT/MID/RIGHT), serta fungsi VLOOKUP dan HLOOKUP, tanpa ada percobaan salin-tempel.&lt;/p&gt;
      &lt;table class="rubrik"&gt;
        &lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Indikator&lt;/th&gt;&lt;th&gt;Bobot&lt;/th&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Ketepatan Data Entri&lt;/td&gt;&lt;td&gt;Kolom A-D dan G terisi sesuai Data Awal, diketik sendiri tanpa salin-tempel&lt;/td&gt;&lt;td&gt;15%&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Rumus Total &amp; Status&lt;/td&gt;&lt;td&gt;Kolom E (perkalian) dan F (IF bertingkat) terisi benar pada seluruh baris&lt;/td&gt;&lt;td&gt;15%&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Rumus Ringkasan&lt;/td&gt;&lt;td&gt;SUM dan AVERAGE pada E11-E12 terisi dan bernilai benar&lt;/td&gt;&lt;td&gt;10%&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Fungsi Lanjutan&lt;/td&gt;&lt;td&gt;SUMIF dan COUNTIF pada E13-E14 terisi dan bernilai benar&lt;/td&gt;&lt;td&gt;15%&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Fungsi STRING&lt;/td&gt;&lt;td&gt;LEFT (H), MID (I), dan RIGHT (J) mengekstrak Kode Transaksi dengan tepat&lt;/td&gt;&lt;td&gt;20%&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Fungsi Lookup&lt;/td&gt;&lt;td&gt;VLOOKUP (K - Diskon) dan HLOOKUP (L - Poin Reward) merujuk tabel referensi dengan tepat&lt;/td&gt;&lt;td&gt;25%&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;
&lt;/main&gt;
&lt;footer&gt;ANALISIS DATA DAN FITUR LANJUT SPREADSHEET - INFORMATIKA - FASE E KELAS X SMK&lt;/footer&gt;
&lt;/div&gt;

&lt;script&gt;
const GAS_ENDPOINT = "https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec";
const JUDUL_MATERI = "Analisis Data dan Fitur Lanjut Spreadsheet";

// ===================== DAFTAR NAMA MURID (DARI ABSENSI) =====================
const STUDENTS = {"X TB1":[[1,"Adha Mirahayu Ningsih"],[2,"Angelina Agnes Noviola"],[3,"Anissa Vira Firmanda"],[4,"Awinda Rizqi Amru Anjani"],[5,"Chaudli Maulidina Azra"],[6,"Dhira Cahya Oktavianingrum"],[7,"Ema Deny Setyaningrum"],[8,"Evita Apriliani"],[9,"Fair Rochmatul Ishmah"],[10,"Fatma Nabila"],[11,"Fatmawati"],[12,"Fika Setya Wulandari"],[13,"Gelsi Zalika Thufaila"],[14,"Hidayatul Husna Maulida"],[15,"Izzatul Luthfa Imanda"],[16,"Meisha Ayu Berlia"],[17,"Meylisa Putri Murdiani"],[18,"Mita Tri Oktavia"],[19,"Mutiara Syabilla"],[20,"Nabila Saskia"],[21,"Nadya Vega Rahmadani"],[22,"Nafisa Nailal Husna"],[23,"Nawa Syiffa Maryam"],[24,"Nirin Fadillah"],[25,"Reni Restia Pramesti"],[26,"Rizkiya Maulida"],[27,"Shinta Aulia Mukti"],[28,"Shintia Amelia Putri"],[29,"Suci Nur Azahra"],[30,"Syah Uitta Jihan Makaela"],[31,"Varisca Indri Ariani"],[32,"Vilsa Safa Azzahra"],[33,"Yuanita Robbitotun Naja"],[34,"Zuni Arifah"]],"X TB2":[[1,"Adinda Eka Nur Aini"],[2,"Adinda Permata Sari"],[3,"Amanda Puspa Maharani"],[4,"Ana Amelliya"],[5,"Auliya Natasya Dewi"],[6,"Ayub Maulana"],[7,"Bilqis Ngizzatun Nisa'"],[8,"Ceysa Elvina Amanda"],[9,"Citra Aulia Baharizqi"],[10,"Desta Aura Azalia Sani"],[11,"Dinda Nurul Amalia"],[12,"Dwi Mayangsari"],[13,"Efrida Dewi Ananda"],[14,"Emi Rara Pramudita"],[15,"Firman Arya Dinaia"],[16,"Keisya Alisya Putri Ali"],[17,"Kusnul Kusuma Wardhani"],[18,"Liby Arumbiya"],[19,"Lutfia Putri"],[20,"Mariska Nur Aira"],[21,"Nadia Nur Azizah Irysati"],[22,"Nahla Aulia Salsabilla"],[23,"Naura Iza Azahra"],[24,"Nazwa Akayla Saputri"],[25,"Novita Adha Rizki Anggraini"],[26,"Pembayun Angger Widjaya"],[27,"Revalia Novianti Tamara"],[28,"Sahal Hanif Yusuf"],[29,"Sekar Wangi"],[30,"Silvi Dwi Noviyanti"],[31,"Sirvi Khunaifiyah Lina Aini Nisa"],[32,"Vito Adi Candra"],[33,"Zahwa"],[34,"Zidannaj Marosat"]],"X TO1":[[1,"Achmat Sapta Arya"],[2,"Ade Fasiyan Nando"],[3,"Albertus Adelio Ronald Fagundez"],[4,"Allan Fadli Muhammad Jibranni"],[5,"Arga Syarul Setiyawan"],[6,"Deddy Setiyawan"],[7,"Denniz Pradivta Alviandra"],[8,"Dimas Putra Erlangga"],[9,"Diyas Prasetyo"],[10,"Dzakie Az Zaahii"],[11,"Fadhil Diantoro"],[12,"Faqih Ubaidillah"],[13,"Faqiyuddin Asyraf"],[14,"Hanif Taufiqulhaqim"],[15,"Iqbal Haidar Ariq"],[16,"Irfan Bagas Vebyantino"],[17,"Marcelino Adi Santoso"],[18,"Muhamad Aftah Farkhan Naka"],[19,"Muhamad Amar Ma'Ruf"],[20,"Muhamad Ilham Prasetyo"],[21,"Muhammad Afwan Maulana Azidan"],[22,"Muhammad Kafa Bihi Fadlurrozag"],[23,"Muhammad Sony Tafrikhan"],[24,"Muhammad Syahid Syahputra"],[25,"Muhammad Zahrul Ulum Al Busthomi"],[26,"Nando Kurnia Eka Putra"],[27,"Naufal Zaky Muttaqin"],[28,"Nizham Zafir Hariz Saputra"],[29,"Nur Fathur Rahmatullah"],[30,"Raditya Putra Perdana"],[31,"Rafka Maulana Putra"],[32,"Raka Rafif Azmi"],[33,"Reyhan Yunazta Putra Hany"],[34,"Rizki Ibadallah Albaroya"],[35,"Septian Danang Prasetyo"],[36,"Yehuda Bayu Seno Abimanyu"]],"X TO2":[[1,"Adham Dzaki Ibrahim Mufid"],[2,"Aflah Raphael Windra Raditya"],[3,"Ahmad Davi Pria Utama"],[4,"Ahmad Maulana Abdul Malik"],[5,"Ahmad Shovi Maola"],[6,"Albarasya Herman"],[7,"Andik Febri Firmansyah"],[8,"Dimas Saputra"],[9,"Diska Indra Fata Widakdo"],[10,"Farel Andrian Pratama"],[11,"Ferdita Fatma Kusuma"],[12,"Galih Ginanjar Saputro"],[13,"Levi Fastabikhul Khoir"],[14,"Lionel Nathan Kurniawan"],[15,"Muhamad Firdiansyah"],[16,"Muhamad Khoirul Anwar"],[17,"Muhamad Rijal Alif Ibrahim"],[18,"Muhamad Tanfebro Permana"],[19,"Muhammad Akmal Farizky"],[20,"Muhammad Alwi Sihab"],[21,"Muhammad 'Aqila Azka"],[22,"Muhammad Fatih"],[23,"Muhammad Fauzi Novantyo"],[24,"Muhammad Rafka Adi Pratama"],[25,"Muhammad Toni Suprasetyo"],[26,"Novan Ananda Rizky"],[27,"Prakasa Tegar Andriyanto"],[28,"Rafael Bayu Pratama"],[29,"Randi Khoirul Azzam"],[30,"Ridho Amru Bain"],[31,"Sabri Radhitya Ganeswara"],[32,"Satria Firman Wibowo"],[33,"Syarif Hidayad"],[34,"Wildan Rizqi Arrumman"],[35,"Vicky Adika Chandra"]],"X TJKT1":[[1,"Adly Surya Hernawan"],[2,"Adrianus Ferdinand Barra Ardiona"],[3,"Aisyah Khoirun Nisa"],[4,"Alif Evan Sanjaya"],[5,"Alif Maulana Ashidiqie"],[6,"Alif Rizqullah Ramadhan"],[7,"Andika Billie Faustino"],[8,"Arvin Aryaseta Atmaja"],[9,"Bryliana Jihan Amir"],[10,"Dandry Kornelius Arisandi"],[11,"Daniel Revelino Feivel Widjanarko"],[12,"Dwi Apriella Nur Rahma"],[13,"Guntur Saktiawan"],[14,"Hanar Fairus Nadhir Amrullah"],[15,"Hani Dwi Rahmawati"],[16,"Ikhsan Abdul Rozak"],[17,"Javien Putra Mahendra"],[18,"Linda Putri Darmawan"],[19,"Lingga Mahardika Susatyo"],[20,"Muazarah Farras Amelia"],[21,"Muhammad Fa'Dlil Asyhari"],[22,"Muhammad Faqih Anwari"],[23,"Muhammad Sidiq Fathul Mu'Iz"],[24,"Nailatussakinah"],[25,"Najma Aulya"],[26,"Nazila Karrolina Aishi Azzahra"],[27,"Rakhul Surya Permana"],[28,"Raya Ghania Putri"],[29,"Revalina Aulia Zahra"],[30,"Salma Anggun Aurellia"],[31,"Sanrez Reksi Kusuma"],[32,"Setiya Rizky Maulana"],[33,"Shara Fatika Addahlani"],[34,"Sinta Puspa Sari"],[35,"Zulfa Ibnul Karim"]],"X TJKT2":[[1,"Akhmad Fakhri Hidayat"],[2,"Alia Nur Aisyah"],[3,"Andika Maulana Muhaimin"],[4,"Andriansyah Maulana"],[5,"Anum Manuraga"],[6,"Brianda Octa Pratama"],[7,"David Luqman"],[8,"Devandri Kusuma"],[9,"Dimas Salman Khafi"],[10,"Divani Aulia Najmi"],[11,"Dwi Ayu Saskiya"],[12,"Dyllan Deep Yoshan"],[13,"Dzikri Maulana"],[14,"Farel Radhitya Pradana"],[15,"Ghifari Sahila Dzaki"],[16,"Indah Nazifa"],[17,"Jati Weko Adi"],[18,"Jeriko Pranata"],[19,"Khoirul Muna"],[20,"Maylinda Qhonita Putri"],[21,"Miftakhul Adiyansyah"],[22,"Muhamad Restu Fauzi"],[23,"Muhammad Abadi"],[24,"Muhammad Faiz Firmansyah"],[25,"Muhammad Irfan Saputra"],[26,"Muhammad Reva Ibrahimmuviq"],[27,"Naja Adkar Khair"],[28,"Nizam Jati Wardoyo"],[29,"Nur Fidyah"],[30,"Okfal Nova Puji Satriawan"],[31,"Quinsha Virosya Pricillia"],[32,"Reza Hafid Ariyanto"],[33,"Ricky Ardiansyah"],[34,"Siti Ernawati"],[35,"Syahrina Laila Farhana"],[36,"Wildan Setio Firmansyah"]]}
;

function initIdentitasADFL(){
  var kelasSel = document.getElementById('kelasADFL');
  var noAbsenSel = document.getElementById('noAbsenADFL');
  var namaInput = document.getElementById('namaADFL');
  if(!kelasSel || !noAbsenSel || !namaInput){
    console.error('Field identitas (Kelas/No. Absen/Nama) tidak ditemukan di halaman - cek id kelasADFL/noAbsenADFL/namaADFL.');
    return;
  }
  kelasSel.addEventListener('change', function(e){
    var kelasVal = e.target.value;
    namaInput.value='';
    if(!kelasVal || !STUDENTS[kelasVal]){
      noAbsenSel.disabled=true;
      noAbsenSel.innerHTML='&lt;option value=""&gt;- Pilih kelas dulu -&lt;/option&gt;';
      namaInput.placeholder='- Pilih no. absen dulu -';
      return;
    }
    noAbsenSel.disabled=false;
    var opts = ['&lt;option value=""&gt;- Pilih No. Absen -&lt;/option&gt;'].concat(
      STUDENTS[kelasVal].map(function(s){ return '&lt;option value="'+s[0]+'"&gt;'+s[0]+'&lt;/option&gt;'; })
    );
    noAbsenSel.innerHTML = opts.join('');
    namaInput.placeholder='- Pilih no. absen dulu -';
  });

  noAbsenSel.addEventListener('change', function(e){
    var kelasVal = kelasSel.value;
    var noVal = parseInt(e.target.value);
    if(!kelasVal || !STUDENTS[kelasVal] || !noVal){ namaInput.value=''; namaInput.placeholder='- Pilih no. absen dulu -'; return; }
    var found = STUDENTS[kelasVal].find(function(s){ return s[0]===noVal; });
    namaInput.value = found ? found[1] : '';
    if(!found) namaInput.placeholder='Nama tidak ditemukan - hubungi guru';
  });
}

if(document.readyState === 'loading'){
  document.addEventListener('DOMContentLoaded', initIdentitasADFL);
} else {
  initIdentitasADFL();
}

// ===================== DATA AWAL (TUNGGAL - KOPERASI SEKOLAH) =====================
const DATA = {
  kolomA:"Nama Bahan/Jasa", kolomB:"Kategori",
  items:[
    ["Buku Tulis (pack)","Alat Tulis",42,18000,"AT01A"],
    ["Pulpen (pack)","Alat Tulis",55,15000,"AT02A"],
    ["Snack Ringan (pcs)","Makanan",80,5000,"MK01B"],
    ["Air Mineral (botol)","Minuman",95,4000,"MN01B"],
    ["Masker Medis (box)","Kesehatan",20,25000,"KS01C"],
    ["Map Plastik (pcs)","Alat Tulis",38,6000,"AT03A"],
    ["Jasa Fotokopi","Jasa",12,50000,"JS01D"],
    ["Jasa Print Tugas","Jasa",28,7000,"JS02D"]
  ]
};
// Tabel referensi VLOOKUP: Kode Kategori (2 huruf awal Kode Transaksi) -&gt; Diskon (%)
const VLOOKUP_REF = [["AT","Alat Tulis",5],["MK","Makanan",2],["MN","Minuman",2],["KS","Kesehatan",3],["JS","Jasa",0]];
// Tabel referensi HLOOKUP: Status -&gt; Poin Reward
const HLOOKUP_REF = [["Laris","Cukup Laris","Kurang Laris"],[20,10,5]];

let REG = {};
let gridPasteFlag = false;

function buildDataAwalTable(data){
  let html = `&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;Baris&lt;/th&gt;&lt;th&gt;${data.kolomA}&lt;/th&gt;&lt;th&gt;${data.kolomB}&lt;/th&gt;&lt;th&gt;Jumlah Terjual&lt;/th&gt;&lt;th&gt;Harga Satuan&lt;/th&gt;&lt;th&gt;Kode Transaksi&lt;/th&gt;&lt;/tr&gt;`;
  data.items.forEach((item,i)=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${i+2}&lt;/td&gt;&lt;td&gt;${item[0]}&lt;/td&gt;&lt;td&gt;${item[1]}&lt;/td&gt;&lt;td&gt;${item[2]}&lt;/td&gt;&lt;td&gt;${item[3].toLocaleString('id-ID')}&lt;/td&gt;&lt;td&gt;${item[4]}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;';
  return html;
}

function buildInstruksiSteps(data){
  return [
    `Ketikkan data &lt;strong&gt;${data.kolomA}&lt;/strong&gt;, &lt;strong&gt;${data.kolomB}&lt;/strong&gt;, &lt;strong&gt;Jumlah Terjual&lt;/strong&gt;, dan &lt;strong&gt;Harga Satuan&lt;/strong&gt; sesuai cell masing-masing pada kolom A sampai D (baris 2 sampai 9), seperti contoh pada tabel Data Awal di atas.`,
    `Ketikkan &lt;strong&gt;Kode Transaksi&lt;/strong&gt; pada kolom G (baris 2 sampai 9) sesuai contoh pada tabel Data Awal di atas - kode ini akan dipakai oleh fungsi STRING dan Lookup di langkah berikutnya.`,
    `Hitunglah kolom &lt;strong&gt;TOTAL&lt;/strong&gt; (kolom E) dengan ketentuan Jumlah Terjual dikalikan Harga Satuan.&lt;br&gt;Cara: klik cell E2 &amp;gt; ketik &lt;span class="mono"&gt;=C2*D2&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk E3 sampai E9.`,
    `Isilah kolom &lt;strong&gt;STATUS&lt;/strong&gt; (kolom F) menggunakan rumus IF bertingkat (nested IF): jika Jumlah Terjual 30 kali atau lebih maka "Laris", jika 15 sampai 29 kali maka "Cukup Laris", selain itu "Kurang Laris".&lt;br&gt;Cara: klik cell F2 &amp;gt; ketik &lt;span class="mono"&gt;=IF(C2&amp;gt;=30,"Laris",IF(C2&amp;gt;=15,"Cukup Laris","Kurang Laris"))&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk F3 sampai F9.`,
    `Hitunglah &lt;strong&gt;Total Keseluruhan&lt;/strong&gt; penjualan pada cell E11.&lt;br&gt;Cara: klik cell E11 &amp;gt; ketik &lt;span class="mono"&gt;=SUM(E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Hitunglah &lt;strong&gt;Rata-rata&lt;/strong&gt; Total penjualan pada cell E12.&lt;br&gt;Cara: klik cell E12 &amp;gt; ketik &lt;span class="mono"&gt;=AVERAGE(E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Praktikkan &lt;strong&gt;fungsi lanjutan SUMIF&lt;/strong&gt;: hitung Total Penjualan khusus kategori "Alat Tulis" pada cell E13.&lt;br&gt;Cara: klik cell E13 &amp;gt; ketik &lt;span class="mono"&gt;=SUMIF(B2:B9,"Alat Tulis",E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Praktikkan &lt;strong&gt;fungsi lanjutan COUNTIF&lt;/strong&gt;: hitung banyaknya jenis barang/jasa kategori "Alat Tulis" pada cell E14.&lt;br&gt;Cara: klik cell E14 &amp;gt; ketik &lt;span class="mono"&gt;=COUNTIF(B2:B9,"Alat Tulis")&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Praktikkan &lt;strong&gt;fungsi STRING LEFT&lt;/strong&gt;: ambil 2 huruf pertama Kode Transaksi sebagai Kode Kategori pada kolom H.&lt;br&gt;Cara: klik cell H2 &amp;gt; ketik &lt;span class="mono"&gt;=LEFT(G2,2)&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk H3 sampai H9.`,
    `Praktikkan &lt;strong&gt;fungsi STRING MID&lt;/strong&gt;: ambil 2 digit Nomor Urut mulai dari karakter ke-3 Kode Transaksi pada kolom I.&lt;br&gt;Cara: klik cell I2 &amp;gt; ketik &lt;span class="mono"&gt;=MID(G2,3,2)&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk I3 sampai I9.`,
    `Praktikkan &lt;strong&gt;fungsi STRING RIGHT&lt;/strong&gt;: ambil 1 huruf terakhir Kode Transaksi sebagai Kode Gudang pada kolom J.&lt;br&gt;Cara: klik cell J2 &amp;gt; ketik &lt;span class="mono"&gt;=RIGHT(G2,1)&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk J3 sampai J9.`,
    `Praktikkan &lt;strong&gt;fungsi VLOOKUP&lt;/strong&gt;: cari besar Diskon (%) dari Tabel Referensi VLOOKUP (N2:O6) berdasarkan Kode Kategori pada kolom H, isikan pada kolom K.&lt;br&gt;Cara: klik cell K2 &amp;gt; ketik &lt;span class="mono"&gt;=VLOOKUP(H2,$N$2:$O$6,2,FALSE)&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk K3 sampai K9.`,
    `Praktikkan &lt;strong&gt;fungsi HLOOKUP&lt;/strong&gt;: cari besar Poin Reward dari Tabel Referensi HLOOKUP (Q2:S3) berdasarkan Status pada kolom F, isikan pada kolom L.&lt;br&gt;Cara: klik cell L2 &amp;gt; ketik &lt;span class="mono"&gt;=HLOOKUP(F2,$Q$2:$S$3,2,FALSE)&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk L3 sampai L9.`,
    `Klik tombol "Urutkan Berdasarkan Total (Sort)" untuk mempraktikkan fitur &lt;strong&gt;Sort&lt;/strong&gt; - amati data terurut dari penjualan tertinggi ke terendah.`,
    `Klik tombol "Tampilkan Hanya di Atas Rata-rata (Filter)" untuk mempraktikkan fitur &lt;strong&gt;Filter&lt;/strong&gt; data.`,
    `Amati baris dengan Total tertinggi yang otomatis tersorot warna hijau (simulasi &lt;strong&gt;Conditional Formatting&lt;/strong&gt;) serta grafik batang (fitur &lt;strong&gt;Chart&lt;/strong&gt;) yang terbentuk otomatis dari data Total.`,
    `Klik tombol "Hitung Nilai &amp; Kirim ke Guru" untuk melihat penilaian otomatis, tabel hasil akhir, dan Tabel Ringkasan per Kategori (simulasi &lt;strong&gt;PivotTable&lt;/strong&gt;), lalu unduh bukti PDF.`
  ];
}

function statusTier(jumlah){
  if(jumlah&gt;=30) return 'Laris';
  if(jumlah&gt;=15) return 'Cukup Laris';
  return 'Kurang Laris';
}
function buildKeadaanAkhirTable(data){
  const totals = data.items.map(item=&gt;item[2]*item[3]);
  const sum = totals.reduce((a,b)=&gt;a+b,0);
  const avg = sum/totals.length;
  let sumifAlatTulis=0, countifAlatTulis=0;
  data.items.forEach((item,i)=&gt;{ if(item[1]==='Alat Tulis'){ sumifAlatTulis+=totals[i]; countifAlatTulis++; } });
  let html = `&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;Baris&lt;/th&gt;&lt;th&gt;${data.kolomA}&lt;/th&gt;&lt;th&gt;${data.kolomB}&lt;/th&gt;&lt;th&gt;Jumlah Terjual&lt;/th&gt;&lt;th&gt;Harga Satuan&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;`;
  data.items.forEach((item,i)=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${i+2}&lt;/td&gt;&lt;td&gt;${item[0]}&lt;/td&gt;&lt;td&gt;${item[1]}&lt;/td&gt;&lt;td&gt;${item[2]}&lt;/td&gt;&lt;td&gt;${item[3].toLocaleString('id-ID')}&lt;/td&gt;&lt;td&gt;${totals[i].toLocaleString('id-ID')}&lt;/td&gt;&lt;td&gt;${statusTier(item[2])}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;';

  html += `&lt;p style="font-size:.78rem; font-weight:600; color:var(--brand-dark); margin-top:14px;"&gt;Hasil Fungsi STRING dan Lookup&lt;/p&gt;`;
  html += `&lt;table class="compare" style="margin-top:4px;"&gt;&lt;tr&gt;&lt;th&gt;Baris&lt;/th&gt;&lt;th&gt;Kode Transaksi&lt;/th&gt;&lt;th&gt;LEFT (Kode Kategori)&lt;/th&gt;&lt;th&gt;MID (No Urut)&lt;/th&gt;&lt;th&gt;RIGHT (Kode Gudang)&lt;/th&gt;&lt;th&gt;VLOOKUP (Diskon %)&lt;/th&gt;&lt;th&gt;HLOOKUP (Poin Reward)&lt;/th&gt;&lt;/tr&gt;`;
  data.items.forEach((item,i)=&gt;{
    const kode = item[4];
    const left = kode.slice(0,2);
    const mid = kode.slice(2,4);
    const right = kode.slice(-1);
    const refRow = VLOOKUP_REF.find(r=&gt;r[0]===left);
    const tier = statusTier(item[2]);
    const poin = tier==='Laris' ? HLOOKUP_REF[1][0] : tier==='Cukup Laris' ? HLOOKUP_REF[1][1] : HLOOKUP_REF[1][2];
    html += `&lt;tr&gt;&lt;td&gt;${i+2}&lt;/td&gt;&lt;td&gt;${kode}&lt;/td&gt;&lt;td&gt;${left}&lt;/td&gt;&lt;td&gt;${mid}&lt;/td&gt;&lt;td&gt;${right}&lt;/td&gt;&lt;td&gt;${refRow?refRow[2]:'-'}%&lt;/td&gt;&lt;td&gt;${poin}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;';

  html += `&lt;table class="compare" style="margin-top:10px;"&gt;&lt;tr&gt;&lt;th&gt;Ringkasan (E11-E14)&lt;/th&gt;&lt;th&gt;Nilai Patokan&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Total Keseluruhan (SUM)&lt;/td&gt;&lt;td&gt;${sum.toLocaleString('id-ID')}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Rata-rata (AVERAGE)&lt;/td&gt;&lt;td&gt;${avg.toLocaleString('id-ID')}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Total Kategori "Alat Tulis" (SUMIF)&lt;/td&gt;&lt;td&gt;${sumifAlatTulis.toLocaleString('id-ID')}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Jumlah Jenis Kategori "Alat Tulis" (COUNTIF)&lt;/td&gt;&lt;td&gt;${countifAlatTulis}&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;`;
  return html;
}

document.getElementById('dataAwalWrap').innerHTML = buildDataAwalTable(DATA);
document.getElementById('instruksiSteps').innerHTML = buildInstruksiSteps(DATA).map(s=&gt;`&lt;li&gt;${s}&lt;/li&gt;`).join('');
document.getElementById('keadaanAkhirWrap').innerHTML = buildKeadaanAkhirTable(DATA);

function buildRefTableWrap(){
  let html = '&lt;div style="display:flex; flex-wrap:wrap; gap:22px; align-items:flex-start;"&gt;';
  html += '&lt;div&gt;&lt;p class="mono" style="font-size:.74rem; font-weight:700; color:var(--brand-dark); margin-bottom:6px;"&gt;Tabel VLOOKUP (N2:O6) - Kode Kategori &amp;rarr; Diskon %&lt;/p&gt;&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;N (Kode)&lt;/th&gt;&lt;th&gt;Kategori&lt;/th&gt;&lt;th&gt;O (Diskon %)&lt;/th&gt;&lt;/tr&gt;';
  VLOOKUP_REF.forEach((row,i)=&gt;{
    const r=i+2;
    html += `&lt;tr&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="N${r}" value="${row[0]}" readonly&gt;&lt;/td&gt;&lt;td class="g-label"&gt;${row[1]}&lt;/td&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="O${r}" value="${row[2]}" readonly&gt;&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  html += '&lt;div&gt;&lt;p class="mono" style="font-size:.74rem; font-weight:700; color:var(--brand-dark); margin-bottom:6px;"&gt;Tabel HLOOKUP (Q2:S3) - Status &amp;rarr; Poin Reward&lt;/p&gt;&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Q&lt;/th&gt;&lt;th&gt;R&lt;/th&gt;&lt;th&gt;S&lt;/th&gt;&lt;/tr&gt;';
  html += `&lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="Q2" value="${HLOOKUP_REF[0][0]}" readonly&gt;&lt;/td&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="R2" value="${HLOOKUP_REF[0][1]}" readonly&gt;&lt;/td&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="S2" value="${HLOOKUP_REF[0][2]}" readonly&gt;&lt;/td&gt;&lt;/tr&gt;`;
  html += `&lt;tr&gt;&lt;th class="rownum"&gt;3&lt;/th&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="Q3" value="${HLOOKUP_REF[1][0]}" readonly&gt;&lt;/td&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="R3" value="${HLOOKUP_REF[1][1]}" readonly&gt;&lt;/td&gt;&lt;td&gt;&lt;input class="sheet-input ref" data-addr="S3" value="${HLOOKUP_REF[1][2]}" readonly&gt;&lt;/td&gt;&lt;/tr&gt;`;
  html += '&lt;/table&gt;&lt;/div&gt;';
  html += '&lt;/div&gt;';
  return html;
}

function renderGrid(){
  const data = DATA;
  document.getElementById('refTableWrap').innerHTML = buildRefTableWrap();
  let rows = '';
  rows += `&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;th&gt;C&lt;/th&gt;&lt;th&gt;D&lt;/th&gt;&lt;th&gt;E&lt;/th&gt;&lt;th&gt;F&lt;/th&gt;&lt;th&gt;G&lt;/th&gt;&lt;th&gt;H&lt;/th&gt;&lt;th&gt;I&lt;/th&gt;&lt;th&gt;J&lt;/th&gt;&lt;th&gt;K&lt;/th&gt;&lt;th&gt;L&lt;/th&gt;&lt;/tr&gt;`;
  rows += `&lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="g-label"&gt;${data.kolomA}&lt;/td&gt;&lt;td class="g-label"&gt;${data.kolomB}&lt;/td&gt;&lt;td class="g-label"&gt;Jumlah Terjual&lt;/td&gt;&lt;td class="g-label"&gt;Harga Satuan&lt;/td&gt;&lt;td class="g-label"&gt;Total&lt;/td&gt;&lt;td class="g-label"&gt;Status&lt;/td&gt;&lt;td class="g-label"&gt;Kode Transaksi&lt;/td&gt;&lt;td class="g-label"&gt;Kode Kategori (LEFT)&lt;/td&gt;&lt;td class="g-label"&gt;No Urut (MID)&lt;/td&gt;&lt;td class="g-label"&gt;Kode Gudang (RIGHT)&lt;/td&gt;&lt;td class="g-label"&gt;Diskon % (VLOOKUP)&lt;/td&gt;&lt;td class="g-label"&gt;Poin Reward (HLOOKUP)&lt;/td&gt;&lt;/tr&gt;`;
  data.items.forEach((item,i)=&gt;{
    const r=i+2;
    rows += `&lt;tr&gt;
      &lt;th class="rownum"&gt;${r}&lt;/th&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input plain" data-addr="A${r}" value="" placeholder="Baris ${r} - lihat Data Awal"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input plain" data-addr="B${r}" value="" placeholder="Kategori"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="number" class="sheet-input plain" data-addr="C${r}" value="" placeholder="Jumlah"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="number" class="sheet-input plain" data-addr="D${r}" value="" placeholder="Harga"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="E${r}" data-raw="" placeholder="=C${r}*D${r}"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="F${r}" data-raw="" placeholder='=IF(C${r}&amp;gt;=30,"Laris",IF(C${r}&amp;gt;=15,"Cukup Laris","Kurang Laris"))'&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input plain" data-addr="G${r}" value="" placeholder="Kode Transaksi"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="H${r}" data-raw="" placeholder="=LEFT(G${r},2)"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="I${r}" data-raw="" placeholder="=MID(G${r},3,2)"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="J${r}" data-raw="" placeholder="=RIGHT(G${r},1)"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="K${r}" data-raw="" placeholder="=VLOOKUP(H${r},$N$2:$O$6,2,FALSE)"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="L${r}" data-raw="" placeholder="=HLOOKUP(F${r},$Q$2:$S$3,2,FALSE)"&gt;&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  rows += `&lt;tr&gt;&lt;td colspan="13" style="height:6px; border:none; background:transparent;"&gt;&lt;/td&gt;&lt;/tr&gt;`;
  const sumRows = [
    ['11','Total Keseluruhan','E11', `=SUM(E2:E9)`],
    ['12','Rata-rata Penjualan','E12', `=AVERAGE(E2:E9)`],
    ['13','Total Kategori "Alat Tulis" (SUMIF)','E13', `=SUMIF(B2:B9,"Alat Tulis",E2:E9)`],
    ['14','Jumlah Jenis Kategori "Alat Tulis" (COUNTIF)','E14', `=COUNTIF(B2:B9,"Alat Tulis")`]
  ];
  sumRows.forEach(sr=&gt;{
    rows += `&lt;tr&gt;
      &lt;th class="rownum"&gt;${sr[0]}&lt;/th&gt;
      &lt;td class="g-label" colspan="4"&gt;${sr[1]}&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="${sr[2]}" data-raw="" placeholder="${sr[3]}"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  document.getElementById('gridWrap').innerHTML = `&lt;table class="excel-grid sheet-grid"&gt;${rows}&lt;/table&gt;`;
  gridPasteFlag = false;
  const warnEl = document.getElementById('pasteWarning');
  if(warnEl) warnEl.classList.remove('show');
  bindGridEvents();
  recalcAll();
}

function bindGridEvents(){
  document.querySelectorAll('.sheet-input').forEach(inp=&gt;{
    if(inp.classList.contains('ref')) return;
    inp.addEventListener('paste', (e)=&gt;{
      e.preventDefault();
      gridPasteFlag = true;
      const warnEl = document.getElementById('pasteWarning');
      if(warnEl) warnEl.classList.add('show');
    });
    if(inp.classList.contains('formula')){
      inp.addEventListener('focus', ()=&gt;{ inp.value = inp.dataset.raw || ''; });
      inp.addEventListener('input', ()=&gt;{ inp.dataset.raw = inp.value; recalcAll(inp); });
    } else {
      inp.addEventListener('input', ()=&gt;{ recalcAll(); });
    }
    inp.addEventListener('blur', ()=&gt;{ recalcAll(); });
  });
}

// ===================== FORMULA ENGINE =====================
function buildRegistry(){
  const reg = {};
  document.querySelectorAll('.sheet-input').forEach(inp=&gt;{ reg[inp.dataset.addr]=inp; });
  return reg;
}
function cellRawValue(addr){
  const el = REG[addr];
  if(!el) return '';
  if(el.classList.contains('formula')) return el.dataset.raw!==undefined ? el.dataset.raw : '';
  return el.value;
}
function evalAddr(addr, stack){
  addr = addr.toUpperCase().replace(/\$/g,'');
  if(stack.has(addr)) return 0;
  const raw = cellRawValue(addr);
  const s = String(raw===undefined||raw===null?'':raw).trim();
  if(s==='') return 0;
  if(s.startsWith('=')){
    stack.add(addr);
    const v = evalFormula(s.slice(1), stack);
    stack.delete(addr);
    return v;
  }
  const num = parseFloat(s);
  return isNaN(num) ? s : num;
}
function expandRange(a,b){
  const ma=a.match(/([A-Z]+)(\d+)/), mb=b.match(/([A-Z]+)(\d+)/);
  if(!ma||!mb) return [];
  const c1=ma[1].charCodeAt(0), c2=mb[1].charCodeAt(0);
  const r1=parseInt(ma[2]), r2=parseInt(mb[2]);
  const addrs=[];
  for(let c=Math.min(c1,c2); c&lt;=Math.max(c1,c2); c++){
    for(let r=Math.min(r1,r2); r&lt;=Math.max(r1,r2); r++) addrs.push(String.fromCharCode(c)+r);
  }
  return addrs;
}
function rangeValues(rangeStr, stack){
  const parts = rangeStr.split(':');
  if(parts.length!==2) return [];
  const addrs = expandRange(parts[0].replace(/\$/g,''), parts[1].replace(/\$/g,''));
  return addrs.map(addr=&gt;{ const v=evalAddr(addr, stack); const n=typeof v==='number'?v:parseFloat(v); return isNaN(n)?0:n; });
}
function resolveRefsInExpr(s, stack){
  return s.replace(/\$?([A-Z]{1,2})\$?(\d{1,3})/g, (m,col,row)=&gt;{
    const v=evalAddr(col+row, stack);
    return typeof v==='number' ? v : JSON.stringify(v);
  });
}
function evalAddr0(tok, stack){
  const t=tok.trim();
  if(/^\$?[A-Z]{1,2}\$?\d{1,3}$/.test(t)) return evalAddr(t.replace(/\$/g,''), stack);
  const n=parseFloat(t);
  return isNaN(n)?0:n;
}
function splitArgs(s){
  const args=[]; let depth=0, cur='', inQ=false;
  for(let i=0;i&lt;s.length;i++){
    const ch=s[i];
    if(ch==='"') inQ=!inQ;
    if(ch===',' &amp;&amp; !inQ &amp;&amp; depth===0){ args.push(cur); cur=''; continue; }
    if(ch==='(') depth++;
    if(ch===')') depth--;
    cur+=ch;
  }
  if(cur.trim().length || args.length) args.push(cur);
  return args.map(a=&gt;a.trim());
}
function resolveCriteriaArg(a, stack){
  let t = a.trim();
  if(/^".*"$/.test(t)) return t.slice(1,-1);
  if(/^\$?[A-Z]{1,2}\$?\d{1,3}$/.test(t)) return evalAddr(t.replace(/\$/g,''), stack);
  return t;
}
function matchCriteria(val, crit){
  crit = String(crit).trim();
  const m = crit.match(/^(&gt;=|&lt;=|&lt;&gt;|&gt;|&lt;|=)(.*)$/);
  if(m){
    const op = m[1]; const rhsRaw = m[2].trim();
    const rhsNum = parseFloat(rhsRaw);
    const valNum = parseFloat(val);
    if(!isNaN(rhsNum) &amp;&amp; !isNaN(valNum) &amp;&amp; String(rhsRaw).match(/^-?\d+(\.\d+)?$/)){
      switch(op){
        case '&gt;=': return valNum&gt;=rhsNum;
        case '&lt;=': return valNum&lt;=rhsNum;
        case '&gt;': return valNum&gt;rhsNum;
        case '&lt;': return valNum&lt;rhsNum;
        case '&lt;&gt;': return valNum!==rhsNum;
        case '=': return valNum===rhsNum;
      }
    }
    switch(op){
      case '&lt;&gt;': return String(val).trim().toLowerCase()!==rhsRaw.toLowerCase();
      case '=': return String(val).trim().toLowerCase()===rhsRaw.toLowerCase();
      default: return false;
    }
  }
  const critNum = parseFloat(crit);
  const valNum = parseFloat(val);
  if(!isNaN(critNum) &amp;&amp; String(crit).trim().match(/^-?\d+(\.\d+)?$/) &amp;&amp; !isNaN(valNum)) return valNum===critNum;
  return String(val).trim().toLowerCase()===crit.toLowerCase();
}
function computeFunc(fn, argsStr, stack){
  const args = splitArgs(argsStr);
  if(['SUM','AVERAGE','MAX','MIN','COUNT','COUNTA'].includes(fn)){
    let vals=[];
    args.forEach(a=&gt;{
      if(/^\$?[A-Z]{1,2}\$?\d{1,3}:\$?[A-Z]{1,2}\$?\d{1,3}$/.test(a)) vals=vals.concat(rangeValues(a, stack));
      else vals.push(evalAddr0(a, stack));
    });
    if(fn==='SUM') return String(vals.reduce((x,y)=&gt;x+y,0));
    if(fn==='AVERAGE') return String(vals.length?vals.reduce((x,y)=&gt;x+y,0)/vals.length:0);
    if(fn==='MAX') return String(vals.length?Math.max(...vals):0);
    if(fn==='MIN') return String(vals.length?Math.min(...vals):0);
    if(fn==='COUNT') return String(vals.filter(v=&gt;!isNaN(v)).length);
    if(fn==='COUNTA') return String(vals.length);
  }
  if(fn==='SUMIF' || fn==='COUNTIF'){
    const rangeStr = args[0].replace(/\$/g,'');
    const addrs = rangeStr.includes(':') ? expandRange(rangeStr.split(':')[0], rangeStr.split(':')[1]) : [rangeStr];
    const criteria = resolveCriteriaArg(args[1], stack);
    let sumAddrs = addrs;
    if(fn==='SUMIF' &amp;&amp; args[2]){
      const sumRangeStr = args[2].replace(/\$/g,'');
      sumAddrs = sumRangeStr.includes(':') ? expandRange(sumRangeStr.split(':')[0], sumRangeStr.split(':')[1]) : [sumRangeStr];
    }
    let count=0, sum=0;
    addrs.forEach((addr,i)=&gt;{
      const val = evalAddr(addr, stack);
      if(matchCriteria(val, criteria)){
        count++;
        if(fn==='SUMIF'){
          const sAddr = sumAddrs[i]!==undefined ? sumAddrs[i] : addr;
          const sv = evalAddr(sAddr, stack);
          const n = typeof sv==='number'?sv:parseFloat(sv);
          sum += isNaN(n)?0:n;
        }
      }
    });
    return fn==='SUMIF' ? String(sum) : String(count);
  }
  if(fn==='ROUND'){
    const resolved = resolveRefsInExpr(args[0], stack);
    const val = parseFloat(resolved)||0;
    const digits = parseInt(args[1])||0;
    return String(Math.round(val*Math.pow(10,digits))/Math.pow(10,digits));
  }
  if(fn==='LEFT'||fn==='RIGHT'||fn==='MID'||fn==='LEN'){
    const text = String(resolveCriteriaArg(args[0], stack));
    if(fn==='LEN') return String(text.length);
    if(fn==='LEFT'){
      const n = args[1]!==undefined ? (parseInt(resolveCriteriaArg(args[1], stack))||0) : 1;
      return JSON.stringify(text.slice(0, n));
    }
    if(fn==='RIGHT'){
      const n = args[1]!==undefined ? (parseInt(resolveCriteriaArg(args[1], stack))||0) : 1;
      return JSON.stringify(n&gt;0 ? text.slice(-n) : '');
    }
    if(fn==='MID'){
      const start = parseInt(resolveCriteriaArg(args[1], stack))||1;
      const n = parseInt(resolveCriteriaArg(args[2], stack))||0;
      return JSON.stringify(text.slice(start-1, start-1+n));
    }
  }
  if(fn==='VLOOKUP' || fn==='HLOOKUP'){
    const lookupVal = resolveCriteriaArg(args[0], stack);
    const tableStr = args[1].replace(/\$/g,'');
    const [startAddr, endAddr] = tableStr.split(':');
    const mA = startAddr.match(/([A-Z]+)(\d+)/), mB = endAddr.match(/([A-Z]+)(\d+)/);
    const colStart = mA[1].charCodeAt(0), colEnd = mB[1].charCodeAt(0);
    const rowStart = parseInt(mA[2]), rowEnd = parseInt(mB[2]);
    const idx = parseInt(resolveCriteriaArg(args[2], stack))||1;
    let result = '#N/A';
    if(fn==='VLOOKUP'){
      for(let r=rowStart; r&lt;=rowEnd; r++){
        const firstVal = evalAddr(String.fromCharCode(colStart)+r, stack);
        if(String(firstVal).trim().toLowerCase()===String(lookupVal).trim().toLowerCase()){
          const targetCol = colStart + idx - 1;
          result = evalAddr(String.fromCharCode(targetCol)+r, stack);
          break;
        }
      }
    } else {
      for(let c=colStart; c&lt;=colEnd; c++){
        const firstVal = evalAddr(String.fromCharCode(c)+rowStart, stack);
        if(String(firstVal).trim().toLowerCase()===String(lookupVal).trim().toLowerCase()){
          const targetRow = rowStart + idx - 1;
          result = evalAddr(String.fromCharCode(c)+targetRow, stack);
          break;
        }
      }
    }
    return typeof result==='number' ? String(result) : JSON.stringify(result);
  }
  if(fn==='AND'||fn==='OR'){
    const bools = args.map(a=&gt;{
      const resolved = resolveRefsInExpr(a, stack);
      try{ return !!Function('"use strict"; return ('+resolved+')')(); }catch(e){ return false; }
    });
    const result = fn==='AND' ? bools.every(Boolean) : bools.some(Boolean);
    return String(result);
  }
  if(fn==='IF'){
    const condResolved = resolveRefsInExpr(args[0], stack);
    let condVal=false;
    try{ condVal = !!Function('"use strict"; return ('+condResolved+')')(); }catch(e){ condVal=false; }
    return condVal ? args[1] : args[2];
  }
  return '0';
}
function evalFormula(expr, stack){
  expr = expr.trim();
  let changed=true, safety=0;
  while(changed &amp;&amp; safety&lt;40){
    changed=false; safety++;
    const next = expr.replace(/\b(VLOOKUP|HLOOKUP|SUMIF|COUNTIF|LEFT|RIGHT|MID|LEN|SUM|AVERAGE|MAX|MIN|COUNT|COUNTA|ROUND|AND|OR|IF)\s*\(([^()]*)\)/i, (m, fn, argsStr)=&gt;{
      changed=true;
      return computeFunc(fn.toUpperCase(), argsStr, stack);
    });
    expr = next;
  }
  expr = expr.replace(/\$?([A-Z]{1,2})\$?(\d{1,3})/g, (m, col, row)=&gt;{
    const v = evalAddr(col+row, stack);
    return typeof v==='number' ? v : JSON.stringify(v);
  });
  try{
    return Function('"use strict"; return ('+expr+')')();
  }catch(e){ return '#ERROR'; }
}
function formatNum(n){
  if(Number.isInteger(n)) return String(n);
  return (Math.round(n*100)/100).toString();
}
function recalcAll(activeInput){
  REG = buildRegistry();
  document.querySelectorAll('.sheet-input.formula').forEach(inp=&gt;{
    if(inp===activeInput || inp===document.activeElement) return;
    const addr = inp.dataset.addr;
    const raw = (inp.dataset.raw!==undefined ? inp.dataset.raw : '').trim();
    if(raw===''){ inp.value=''; inp.classList.remove('err'); return; }
    let val;
    try{ val = raw.startsWith('=') ? evalFormula(raw.slice(1), new Set()) : raw; }
    catch(e){ val='#ERROR'; }
    if(val==='#ERROR' || (typeof val==='number' &amp;&amp; Number.isNaN(val))){
      inp.value='#ERROR'; inp.classList.add('err');
    } else {
      inp.classList.remove('err');
      inp.value = (typeof val==='number') ? formatNum(val) : val;
    }
  });
  highlightTopRow();
  drawChart();
}
function highlightTopRow(){
  let maxVal=-Infinity, maxRow=null;
  for(let r=2;r&lt;=9;r++){
    const el=REG['E'+r];
    if(!el) continue;
    const v=parseFloat(el.value);
    if(!isNaN(v) &amp;&amp; v&gt;maxVal){ maxVal=v; maxRow=r; }
  }
  document.querySelectorAll('#gridWrap tr').forEach(tr=&gt;tr.classList.remove('hi-max'));
  if(maxRow){
    const marker=REG['E'+maxRow];
    if(marker){ const tr=marker.closest('tr'); if(tr) tr.classList.add('hi-max'); }
  }
}
function drawChart(){
  const canvas=document.getElementById('dataChart');
  if(!canvas) return;
  const ctx=canvas.getContext('2d');
  const w=canvas.width, h=canvas.height;
  ctx.clearRect(0,0,w,h);
  const vals=[];
  for(let r=2;r&lt;=9;r++){
    const el=REG['E'+r];
    const v=el?parseFloat(el.value):NaN;
    vals.push(isNaN(v)?0:v);
  }
  const maxV=Math.max(...vals,1);
  const padding=30, barGap=10;
  const barW=(w-padding*2-barGap*(vals.length-1))/vals.length;
  ctx.textAlign='center'; ctx.font='9px IBM Plex Mono, monospace';
  vals.forEach((v,i)=&gt;{
    const barH = maxV&gt;0 ? (v/maxV)*(h-60) : 0;
    const x = padding + i*(barW+barGap);
    const y = h-30-barH;
    ctx.fillStyle='#15803D';
    ctx.fillRect(x,y,barW,barH);
    ctx.fillStyle='#16221B';
    const aEl = REG['A'+(i+2)];
    const label = (aEl &amp;&amp; aEl.value.trim()) ? aEl.value.trim() : DATA.items[i][0];
    ctx.fillText(label.split(' ')[0], x+barW/2, h-14);
  });
}
function formatRupiah(n){
  if(isNaN(n)) return '-';
  return 'Rp' + Math.round(n).toLocaleString('id-ID');
}

document.getElementById('btnSortGrid').addEventListener('click', ()=&gt;{
  const rows = DATA.items.map((item,i)=&gt;{
    const r=i+2;
    const aEl=REG['A'+r], bEl=REG['B'+r], eEl=REG['E'+r], fEl=REG['F'+r];
    const nama = (aEl &amp;&amp; aEl.value.trim()) ? aEl.value.trim() : '(belum diisi)';
    const kategori = (bEl &amp;&amp; bEl.value.trim()) ? bEl.value.trim() : '-';
    const total = eEl?parseFloat(eEl.value):NaN;
    const status = fEl?fEl.value:'';
    return {nama, kategori, total:isNaN(total)?0:total, status};
  });
  rows.sort((a,b)=&gt;b.total-a.total);
  let html = `&lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;Peringkat&lt;/th&gt;&lt;th&gt;${DATA.kolomA}&lt;/th&gt;&lt;th&gt;${DATA.kolomB}&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;`;
  rows.forEach((row,i)=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${i+1}&lt;/td&gt;&lt;td&gt;${row.nama}&lt;/td&gt;&lt;td&gt;${row.kategori}&lt;/td&gt;&lt;td&gt;${formatRupiah(row.total)}&lt;/td&gt;&lt;td&gt;${row.status &amp;&amp; row.status!=='#ERROR' ? row.status : '-'}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  document.getElementById('sortedResult').innerHTML = html;
});

document.getElementById('btnFilterGrid').addEventListener('click', ()=&gt;{
  REG = buildRegistry();
  const avgEl = REG['E12'];
  const avg = avgEl ? parseFloat(avgEl.value) : NaN;
  if(isNaN(avg)){ document.getElementById('filterResult').innerHTML = '&lt;p style="font-size:.84rem; color:var(--red);"&gt;Isi dahulu rumus Rata-rata (E12) sebelum menggunakan fitur Filter.&lt;/p&gt;'; return; }
  const rows = DATA.items.map((item,i)=&gt;{
    const r=i+2;
    const aEl=REG['A'+r], bEl=REG['B'+r], eEl=REG['E'+r], fEl=REG['F'+r];
    const nama = (aEl &amp;&amp; aEl.value.trim()) ? aEl.value.trim() : '(belum diisi)';
    const kategori = (bEl &amp;&amp; bEl.value.trim()) ? bEl.value.trim() : '-';
    const total = eEl?parseFloat(eEl.value):NaN;
    const status = fEl?fEl.value:'';
    return {nama, kategori, total:isNaN(total)?0:total, status};
  }).filter(row=&gt;row.total&gt;avg);
  let html = `&lt;div class="table-scroll"&gt;&lt;p style="font-size:.8rem; color:var(--ink-soft); margin-bottom:6px;"&gt;Menampilkan baris dengan Total di atas rata-rata (&amp;gt; ${formatRupiah(avg)})&lt;/p&gt;&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;${DATA.kolomA}&lt;/th&gt;&lt;th&gt;${DATA.kolomB}&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;`;
  if(rows.length===0) html += `&lt;tr&gt;&lt;td colspan="4" style="text-align:center; color:var(--ink-soft);"&gt;Tidak ada baris di atas rata-rata&lt;/td&gt;&lt;/tr&gt;`;
  rows.forEach(row=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${row.nama}&lt;/td&gt;&lt;td&gt;${row.kategori}&lt;/td&gt;&lt;td&gt;${formatRupiah(row.total)}&lt;/td&gt;&lt;td&gt;${row.status &amp;&amp; row.status!=='#ERROR' ? row.status : '-'}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  document.getElementById('filterResult').innerHTML = html;
});

document.getElementById('btnResetGrid').addEventListener('click', ()=&gt;{
  if(confirm('Reset seluruh rumus pada lembar kerja?')){
    document.getElementById('sortedResult').innerHTML='';
    document.getElementById('filterResult').innerHTML='';
    document.getElementById('resultPanel').style.display='none';
    lastResult = null;
    renderGrid();
  }
});

// ===================== PENILAIAN OTOMATIS =====================
function evaluateWork(){
  REG = buildRegistry();
  const data = DATA;
  const rows = data.items.map((item,i)=&gt;{
    const r=i+2;
    const nama = REG['A'+r] ? REG['A'+r].value.trim() : '';
    const kategori = REG['B'+r] ? REG['B'+r].value.trim() : '';
    const jumlahRaw = REG['C'+r] ? REG['C'+r].value.trim() : '';
    const hargaRaw = REG['D'+r] ? REG['D'+r].value.trim() : '';
    const totalRaw = REG['E'+r] ? REG['E'+r].value : '';
    const statusRaw = REG['F'+r] ? REG['F'+r].value.trim() : '';

    const namaOk = nama.length&gt;0 &amp;&amp; nama.toLowerCase()===item[0].toLowerCase();
    const kategoriOk = kategori.length&gt;0 &amp;&amp; kategori.toLowerCase()===item[1].toLowerCase();
    const jumlahOk = jumlahRaw!=='' &amp;&amp; parseFloat(jumlahRaw)===item[2];
    const hargaOk = hargaRaw!=='' &amp;&amp; parseFloat(hargaRaw)===item[3];

    const expectedTotal = evalFormula(`C${r}*D${r}`, new Set());
    const totalOk = totalRaw!=='' &amp;&amp; totalRaw!=='#ERROR' &amp;&amp; !isNaN(parseFloat(totalRaw)) &amp;&amp; Math.round(parseFloat(totalRaw))===Math.round(expectedTotal);

    const expectedStatus = evalFormula(`IF(C${r}&gt;=30,"Laris",IF(C${r}&gt;=15,"Cukup Laris","Kurang Laris"))`, new Set());
    const statusOk = statusRaw!=='' &amp;&amp; statusRaw!=='#ERROR' &amp;&amp; statusRaw.toLowerCase()===String(expectedStatus).toLowerCase();

    const kodeRaw = REG['G'+r] ? REG['G'+r].value.trim() : '';
    const kodeOk = kodeRaw.length&gt;0 &amp;&amp; kodeRaw.toUpperCase()===item[4].toUpperCase();

    const leftRaw = REG['H'+r] ? REG['H'+r].value.trim() : '';
    const expectedLeft = evalFormula(`LEFT(G${r},2)`, new Set());
    const leftOk = leftRaw!=='' &amp;&amp; leftRaw!=='#ERROR' &amp;&amp; leftRaw.toUpperCase()===String(expectedLeft).toUpperCase();

    const midRaw = REG['I'+r] ? REG['I'+r].value.trim() : '';
    const expectedMid = evalFormula(`MID(G${r},3,2)`, new Set());
    const midOk = midRaw!=='' &amp;&amp; midRaw!=='#ERROR' &amp;&amp; midRaw.toUpperCase()===String(expectedMid).toUpperCase();

    const rightRaw = REG['J'+r] ? REG['J'+r].value.trim() : '';
    const expectedRight = evalFormula(`RIGHT(G${r},1)`, new Set());
    const rightOk = rightRaw!=='' &amp;&amp; rightRaw!=='#ERROR' &amp;&amp; rightRaw.toUpperCase()===String(expectedRight).toUpperCase();

    const vlookupRaw = REG['K'+r] ? REG['K'+r].value.trim() : '';
    const expectedVlookup = evalFormula(`VLOOKUP(H${r},$N$2:$O$6,2,FALSE)`, new Set());
    const vlookupOk = vlookupRaw!=='' &amp;&amp; vlookupRaw!=='#ERROR' &amp;&amp; vlookupRaw!=='#N/A' &amp;&amp; !isNaN(parseFloat(vlookupRaw)) &amp;&amp; !isNaN(parseFloat(expectedVlookup)) &amp;&amp; parseFloat(vlookupRaw)===parseFloat(expectedVlookup);

    const hlookupRaw = REG['L'+r] ? REG['L'+r].value.trim() : '';
    const expectedHlookup = evalFormula(`HLOOKUP(F${r},$Q$2:$S$3,2,FALSE)`, new Set());
    const hlookupOk = hlookupRaw!=='' &amp;&amp; hlookupRaw!=='#ERROR' &amp;&amp; hlookupRaw!=='#N/A' &amp;&amp; !isNaN(parseFloat(hlookupRaw)) &amp;&amp; !isNaN(parseFloat(expectedHlookup)) &amp;&amp; parseFloat(hlookupRaw)===parseFloat(expectedHlookup);

    return {no:i+1, r, nama, kategori, jumlah:jumlahRaw, harga:hargaRaw, total:totalRaw, status:statusRaw,
      kode:kodeRaw, left:leftRaw, mid:midRaw, right:rightRaw, vlookup:vlookupRaw, hlookup:hlookupRaw,
      namaOk, kategoriOk, jumlahOk, hargaOk, totalOk, statusOk, kodeOk, leftOk, midOk, rightOk, vlookupOk, hlookupOk};
  });

  const expectedSum = evalFormula('SUM(E2:E9)', new Set());
  const expectedAvg = evalFormula('AVERAGE(E2:E9)', new Set());
  const expectedSumif = evalFormula('SUMIF(B2:B9,"Alat Tulis",E2:E9)', new Set());
  const expectedCountif = evalFormula('COUNTIF(B2:B9,"Alat Tulis")', new Set());

  const sumRaw = REG['E11']?REG['E11'].value:'';
  const avgRaw = REG['E12']?REG['E12'].value:'';
  const sumifRaw = REG['E13']?REG['E13'].value:'';
  const countifRaw = REG['E14']?REG['E14'].value:'';

  const closeEnough=(raw,expected)=&gt;{ const x=parseFloat(raw); return raw!=='' &amp;&amp; raw!=='#ERROR' &amp;&amp; !isNaN(x) &amp;&amp; Math.abs(x-expected)&lt;1; };

  const summary = {
    sum:{raw:sumRaw, ok:closeEnough(sumRaw, expectedSum), label:'Total Keseluruhan (SUM)'},
    avg:{raw:avgRaw, ok:closeEnough(avgRaw, expectedAvg), label:'Rata-rata (AVERAGE)'},
    sumif:{raw:sumifRaw, ok:closeEnough(sumifRaw, expectedSumif), label:'Total Kategori Alat Tulis (SUMIF)'},
    countif:{raw:countifRaw, ok:closeEnough(countifRaw, expectedCountif), label:'Jumlah Jenis Alat Tulis (COUNTIF)'}
  };

  return {rows, summary};
}

function scoreWork(){
  const ev = evaluateWork();
  const n = ev.rows.length;
  let dataCorrect=0, totalCorrect=0, statusCorrect=0, leftCorrect=0, midCorrect=0, rightCorrect=0, vlookupCorrect=0, hlookupCorrect=0;
  ev.rows.forEach(row=&gt;{
    if(row.namaOk &amp;&amp; row.kategoriOk &amp;&amp; row.jumlahOk &amp;&amp; row.hargaOk &amp;&amp; row.kodeOk) dataCorrect++;
    if(row.totalOk) totalCorrect++;
    if(row.statusOk) statusCorrect++;
    if(row.leftOk) leftCorrect++;
    if(row.midOk) midCorrect++;
    if(row.rightOk) rightCorrect++;
    if(row.vlookupOk) vlookupCorrect++;
    if(row.hlookupOk) hlookupCorrect++;
  });
  const dataScore = Math.round(dataCorrect/n*100);
  const totalScore = Math.round(totalCorrect/n*100);
  const statusScore = Math.round(statusCorrect/n*100);
  const totalStatusScore = Math.round((totalScore+statusScore)/2);
  const ringkasanCorrect = [ev.summary.sum.ok, ev.summary.avg.ok].filter(Boolean).length;
  const ringkasanScore = Math.round(ringkasanCorrect/2*100);
  const lanjutCorrect = [ev.summary.sumif.ok, ev.summary.countif.ok].filter(Boolean).length;
  const lanjutScore = Math.round(lanjutCorrect/2*100);
  const leftScore = Math.round(leftCorrect/n*100);
  const midScore = Math.round(midCorrect/n*100);
  const rightScore = Math.round(rightCorrect/n*100);
  const stringScore = Math.round((leftScore+midScore+rightScore)/3);
  const vlookupScore = Math.round(vlookupCorrect/n*100);
  const hlookupScore = Math.round(hlookupCorrect/n*100);
  const lookupScore = Math.round((vlookupScore+hlookupScore)/2);

  let finalScore = Math.round(dataScore*0.15 + totalStatusScore*0.15 + ringkasanScore*0.10 + lanjutScore*0.15 + stringScore*0.20 + lookupScore*0.25);
  const pastePenalty = gridPasteFlag;
  if(pastePenalty) finalScore = Math.round(finalScore*0.5);

  return {ev, dataScore, totalScore, statusScore, totalStatusScore, ringkasanScore, lanjutScore,
    leftScore, midScore, rightScore, stringScore, vlookupScore, hlookupScore, lookupScore,
    finalScore, pastePenalty};
}

function cellClass(val, ok){
  if(!val || String(val).trim()==='') return 'cell-empty';
  return ok ? 'cell-ok' : 'cell-bad';
}

function buildPivotSummary(ev){
  const groups = {};
  ev.rows.forEach(row=&gt;{
    const kat = row.kategori &amp;&amp; row.kategori.trim() ? row.kategori.trim() : '(belum diisi)';
    const total = parseFloat(row.total);
    if(!groups[kat]) groups[kat] = {count:0, sum:0};
    groups[kat].count++;
    if(!isNaN(total)) groups[kat].sum += total;
  });
  let html = `&lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;Kategori&lt;/th&gt;&lt;th&gt;Jumlah Jenis&lt;/th&gt;&lt;th&gt;Total Penjualan&lt;/th&gt;&lt;/tr&gt;`;
  Object.keys(groups).forEach(kat=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${kat}&lt;/td&gt;&lt;td&gt;${groups[kat].count}&lt;/td&gt;&lt;td&gt;${formatRupiah(groups[kat].sum)}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  return html;
}

let lastResult = null;

document.getElementById('btnHitung').addEventListener('click', ()=&gt;{
  const noAbsen=document.getElementById('noAbsenADFL').value.trim();
  const nama=document.getElementById('namaADFL').value.trim();
  const kelas=document.getElementById('kelasADFL').value.trim();
  const msg=document.getElementById('uploadMsg');
  if(!noAbsen||!nama||!kelas){ msg.textContent='Lengkapi Kelas, No. Absen, dan Nama terlebih dahulu.'; return; }

  const result = scoreWork();
  lastResult = {noAbsen, nama, kelas, result};

  msg.textContent='';
  const panel=document.getElementById('resultPanel');
  panel.style.display='block';
  document.getElementById('scoreValue').textContent = result.finalScore;
  document.getElementById('resultDetail').innerHTML =
    `Data Entri (15%): &lt;strong&gt;${result.dataScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Rumus Total &amp; Status (15%): &lt;strong&gt;${result.totalStatusScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Rumus Ringkasan (10%): &lt;strong&gt;${result.ringkasanScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Fungsi Lanjutan (15%): &lt;strong&gt;${result.lanjutScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Fungsi STRING (20%): &lt;strong&gt;${result.stringScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Fungsi Lookup (25%): &lt;strong&gt;${result.lookupScore}&lt;/strong&gt;` +
    (result.pastePenalty ? '&lt;br&gt;&lt;span style="color:var(--red); font-weight:700;"&gt;[PERINGATAN] Terdeteksi percobaan tempel pada lembar kerja - nilai dipotong 50%&lt;/span&gt;' : '') +
    ' - Mengirim ke guru...';

  const ev = result.ev;
  let html = `&lt;div class="table-scroll"&gt;&lt;table class="compare final-table"&gt;
    &lt;tr&gt;&lt;th&gt;No&lt;/th&gt;&lt;th&gt;${DATA.kolomA}&lt;/th&gt;&lt;th&gt;${DATA.kolomB}&lt;/th&gt;&lt;th&gt;Jumlah Terjual&lt;/th&gt;&lt;th&gt;Harga Satuan&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;`;
  ev.rows.forEach(row=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${row.no}&lt;/td&gt;
      &lt;td class="${cellClass(row.nama,row.namaOk)}"&gt;${row.nama||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.kategori,row.kategoriOk)}"&gt;${row.kategori||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.jumlah,row.jumlahOk)}"&gt;${row.jumlah||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.harga,row.hargaOk)}"&gt;${row.harga||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.total,row.totalOk)}"&gt;${row.total||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.status,row.statusOk)}"&gt;${row.status||'-'}&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  html += `&lt;p style="font-size:.8rem; font-weight:600; color:var(--brand-dark); margin-top:14px;"&gt;Hasil Fungsi STRING dan Lookup&lt;/p&gt;`;
  html += `&lt;div class="table-scroll"&gt;&lt;table class="compare final-table"&gt;
    &lt;tr&gt;&lt;th&gt;No&lt;/th&gt;&lt;th&gt;Kode Transaksi&lt;/th&gt;&lt;th&gt;LEFT&lt;/th&gt;&lt;th&gt;MID&lt;/th&gt;&lt;th&gt;RIGHT&lt;/th&gt;&lt;th&gt;VLOOKUP (Diskon)&lt;/th&gt;&lt;th&gt;HLOOKUP (Poin)&lt;/th&gt;&lt;/tr&gt;`;
  ev.rows.forEach(row=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${row.no}&lt;/td&gt;
      &lt;td class="${cellClass(row.kode,row.kodeOk)}"&gt;${row.kode||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.left,row.leftOk)}"&gt;${row.left||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.mid,row.midOk)}"&gt;${row.mid||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.right,row.rightOk)}"&gt;${row.right||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.vlookup,row.vlookupOk)}"&gt;${row.vlookup||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.hlookup,row.hlookupOk)}"&gt;${row.hlookup||'-'}&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  html += `&lt;div class="table-scroll" style="margin-top:10px;"&gt;&lt;table class="compare final-table"&gt;
    &lt;tr&gt;&lt;th&gt;Ringkasan&lt;/th&gt;&lt;th&gt;Nilai&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;${ev.summary.sum.label}&lt;/td&gt;&lt;td class="${cellClass(ev.summary.sum.raw, ev.summary.sum.ok)}"&gt;${ev.summary.sum.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;${ev.summary.avg.label}&lt;/td&gt;&lt;td class="${cellClass(ev.summary.avg.raw, ev.summary.avg.ok)}"&gt;${ev.summary.avg.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;${ev.summary.sumif.label}&lt;/td&gt;&lt;td class="${cellClass(ev.summary.sumif.raw, ev.summary.sumif.ok)}"&gt;${ev.summary.sumif.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;${ev.summary.countif.label}&lt;/td&gt;&lt;td class="${cellClass(ev.summary.countif.raw, ev.summary.countif.ok)}"&gt;${ev.summary.countif.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;`;
  document.getElementById('finalTableWrap').innerHTML = html;
  document.getElementById('pivotTableWrap').innerHTML = buildPivotSummary(ev);
  document.getElementById('pdfMsg').textContent='';

  fetch(GAS_ENDPOINT,{method:'POST',mode:'no-cors',headers:{'Content-Type':'application/json'},
    body:JSON.stringify({noAbsen,nama,kelas,skor:result.finalScore,judulMateri:JUDUL_MATERI})
  }).then(()=&gt;{
    document.getElementById('resultDetail').innerHTML = document.getElementById('resultDetail').innerHTML.replace('Mengirim ke guru...','Nilai sudah terkirim ke guru.');
  }).catch(()=&gt;{
    document.getElementById('resultDetail').innerHTML = document.getElementById('resultDetail').innerHTML.replace('Mengirim ke guru...','Gagal terkirim otomatis, tunjukkan skor ini ke guru.');
  });
});

// ===================== UNDUH BUKTI (PDF 1 HALAMAN - TABEL HASIL AKHIR) =====================
document.getElementById('btnUnduhPDF').addEventListener('click', downloadHasilPDF);

function downloadHasilPDF(){
  const msg = document.getElementById('pdfMsg');
  if(!lastResult){ msg.textContent='Klik "Hitung Nilai &amp; Kirim ke Guru" terlebih dahulu.'; return; }
  if(!window.jspdf){ alert('Pustaka pembuat PDF gagal dimuat. Pastikan koneksi internet aktif lalu muat ulang halaman.'); return; }

  const { jsPDF } = window.jspdf;
  const doc = new jsPDF({ unit:'mm', format:'a4' });
  const marginX = 14;
  let y = 16;
  const ev = lastResult.result.ev;

  doc.setFont('helvetica','bold'); doc.setFontSize(13); doc.setTextColor(20,30,25);
  doc.text('HASIL AKHIR TUGAS PRAKTIK INDIVIDU', marginX, y); y+=6;
  doc.setFontSize(10); doc.setTextColor(21,128,61);
  doc.text(JUDUL_MATERI, marginX, y); y+=6;
  doc.setFont('helvetica','normal'); doc.setFontSize(9.5); doc.setTextColor(50,60,55);
  doc.text(`Kelas: ${lastResult.kelas}   |   No. Absen: ${lastResult.noAbsen}   |   Nama: ${lastResult.nama}`, marginX, y); y+=6;
  doc.setFont('helvetica','bold'); doc.setFontSize(11); doc.setTextColor(13,90,42);
  doc.text(`Nilai Praktik (Otomatis): ${lastResult.result.finalScore}`, marginX, y); y+=8;

  doc.autoTable({
    startY: y,
    head: [[ 'No', DATA.kolomA, DATA.kolomB, 'Jumlah Terjual', 'Harga Satuan', 'Total', 'Status' ]],
    body: ev.rows.map(row=&gt;[row.no, row.nama||'-', row.kategori||'-', row.jumlah||'-', row.harga||'-', row.total||'-', row.status||'-']),
    theme: 'grid',
    headStyles: { fillColor: [21,128,61], textColor: 255, fontStyle: 'bold', fontSize: 8 },
    bodyStyles: { fontSize: 8 },
    margin: { left: marginX, right: marginX },
    didParseCell: function(d){
      if(d.section==='body'){
        const row = ev.rows[d.row.index];
        const okMap = [true, row.namaOk, row.kategoriOk, row.jumlahOk, row.hargaOk, row.totalOk, row.statusOk];
        if(d.column.index&gt;0){
          d.cell.styles.fillColor = okMap[d.column.index] ? [227,247,234] : [252,230,227];
          d.cell.styles.textColor = okMap[d.column.index] ? [13,90,42] : [155,51,36];
        }
      }
    }
  });

  let yStr = doc.lastAutoTable.finalY + 6;
  doc.setFont('helvetica','bold'); doc.setFontSize(9); doc.setTextColor(20,30,25);
  doc.text('Hasil Fungsi STRING dan Lookup', marginX, yStr); yStr+=4;
  doc.autoTable({
    startY: yStr,
    head: [[ 'No', 'Kode Transaksi', 'LEFT', 'MID', 'RIGHT', 'VLOOKUP (Diskon)', 'HLOOKUP (Poin)' ]],
    body: ev.rows.map(row=&gt;[row.no, row.kode||'-', row.left||'-', row.mid||'-', row.right||'-', row.vlookup||'-', row.hlookup||'-']),
    theme: 'grid',
    headStyles: { fillColor: [21,128,61], textColor: 255, fontStyle: 'bold', fontSize: 8 },
    bodyStyles: { fontSize: 8 },
    margin: { left: marginX, right: marginX },
    didParseCell: function(d){
      if(d.section==='body'){
        const row = ev.rows[d.row.index];
        const okMap = [true, row.kodeOk, row.leftOk, row.midOk, row.rightOk, row.vlookupOk, row.hlookupOk];
        if(d.column.index&gt;0){
          d.cell.styles.fillColor = okMap[d.column.index] ? [227,247,234] : [252,230,227];
          d.cell.styles.textColor = okMap[d.column.index] ? [13,90,42] : [155,51,36];
        }
      }
    }
  });

  let y2 = doc.lastAutoTable.finalY + 6;
  doc.autoTable({
    startY: y2,
    head: [['Ringkasan','Nilai']],
    body: [
      [ev.summary.sum.label, ev.summary.sum.raw||'-'],
      [ev.summary.avg.label, ev.summary.avg.raw||'-'],
      [ev.summary.sumif.label, ev.summary.sumif.raw||'-'],
      [ev.summary.countif.label, ev.summary.countif.raw||'-']
    ],
    theme: 'grid',
    headStyles: { fillColor: [13,90,42], textColor: 255, fontStyle: 'bold', fontSize: 8 },
    bodyStyles: { fontSize: 8 },
    margin: { left: marginX, right: marginX },
    didParseCell: function(d){
      if(d.section==='body' &amp;&amp; d.column.index===1){
        const keys=['sum','avg','sumif','countif'];
        const ok = ev.summary[keys[d.row.index]].ok;
        d.cell.styles.fillColor = ok ? [227,247,234] : [252,230,227];
        d.cell.styles.textColor = ok ? [13,90,42] : [155,51,36];
      }
    }
  });

  let y3 = doc.lastAutoTable.finalY + 8;
  doc.setFont('helvetica','normal'); doc.setFontSize(8); doc.setTextColor(90,100,95);
  doc.text('Dokumen ini dicetak otomatis oleh sistem sebagai bukti hasil akhir pengerjaan tugas praktik individu.', marginX, y3); y3+=12;
  doc.setFontSize(9.5); doc.setTextColor(20,30,25);
  const todayStr = new Date().toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric'});
  doc.text('......................, ' + todayStr, marginX, y3); y3+=14;
  doc.text('( ' + lastResult.nama + ' )', marginX, y3); y3+=5;
  doc.setFontSize(8.5); doc.setTextColor(90,100,95);
  doc.text('Siswa', marginX, y3);

  msg.textContent='';
  const safeNama = lastResult.nama.replace(/[^a-z0-9]+/gi,'_');
  doc.save('Hasil-Praktik-Spreadsheet-' + safeNama + '.pdf');
}

renderGrid();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/analisis-data-dan-fitur-lanjut.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-8762818644595775780</guid><pubDate>Thu, 08 Oct 2026 03:55:31 +0000</pubDate><atom:updated>2026-10-08T10:55:31.647+07:00</atom:updated><title>SIM-Asesmen CBT 2026</title><description>&lt;!-- KODE ASESMEN CBT UNTUK BLOGGER / BLOGSPOT - SMK NEGERI 1 TUNTANG --&gt;
&lt;div style="position: relative; width: 100%; max-width: 100%; margin: 20px auto; font-family: system-ui, -apple-system, sans-serif;"&gt;
  &lt;div style="background: linear-gradient(135deg, #1e40af, #3b82f6); color: white; padding: 12px 18px; border-radius: 14px 14px 0 0; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);"&gt;
    &lt;div style="display: flex; align-items: center; gap: 8px;"&gt;
      &lt;span style="display: inline-block; width: 10px; height: 10px; background: #10b981; border-radius: 50%;"&gt;&lt;/span&gt;
      &lt;strong style="font-size: 14px;"&gt;PORTAL ULANGAN DIGITAL CBT - SMK N 1 TUNTANG&lt;/strong&gt;
    &lt;/div&gt;
    &lt;a href="https://ais-dev-lmzrqdc6fuqxt3vuandtt7-270455445011.asia-southeast1.run.app?embed=true" target="_blank" rel="noopener noreferrer" style="background: rgba(255,255,255,0.2); color: white; padding: 6px 14px; border-radius: 8px; font-size: 12px; font-weight: 600; text-decoration: none; border: 1px solid rgba(255,255,255,0.3);"&gt;
      Buka Layar Penuh ↗
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div style="width: 100%; height: 850px; min-height: 600px; background: #f8fafc; border: 2px solid #3b82f6; border-top: none; border-radius: 0 0 14px 14px; overflow: hidden; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.1);"&gt;
    &lt;iframe 
      src="https://ais-dev-lmzrqdc6fuqxt3vuandtt7-270455445011.asia-southeast1.run.app?embed=true" 
      style="width: 100%; height: 100%; border: none; display: block;" 
      title="SIM-Asesmen Digital CBT"
      allow="fullscreen; clipboard-read; clipboard-write;" 
      allowfullscreen="true"
      loading="lazy"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;
&lt;/div&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/sim-asesmen-cbt-2026.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-5019502899315011722</guid><pubDate>Thu, 08 Oct 2026 03:07:20 +0000</pubDate><atom:updated>2026-10-08T10:07:20.795+07:00</atom:updated><title>Kaidah Kebahasaan Teks Ceramah (Bagian 2) dan Menulis Teks Ceramah</title><description>&lt;style&gt;
.kbt{--ink:#14213D;--ink2:#46526A;--paper:#F5F2EA;--card:#FFFFFF;--line:#DDD6C5;--amber:#E8A33D;--amberd:#7A4D0B;--blue:#1F5FA8;--green:#2F7D57;--red:#B83A32;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:var(--ink);background:var(--paper);line-height:1.65;font-size:15px;border-radius:14px;overflow:hidden;max-width:900px;margin:0 auto;text-align:left}
.kbt *{box-sizing:border-box}
.kbt .hero{background:linear-gradient(135deg,#14213D 0%,#243B6E 100%);color:#fff;padding:30px 26px 26px}
.kbt .hero .code{font-size:12px;letter-spacing:.06em;color:#F5D493;font-weight:700}
.kbt .hero h1{font-size:27px;line-height:1.25;margin:8px 0 6px;color:#fff;font-weight:800}
.kbt .hero p.sub{margin:0;font-size:14px;color:#D9E2F5}
.kbt .hero .tp{background:rgba(255,255,255,.12);border-left:4px solid var(--amber);padding:12px 14px;border-radius:6px;margin-top:16px;font-size:14px;color:#fff}
.kbt .nav{position:sticky;top:0;z-index:20;display:flex;gap:8px;flex-wrap:wrap;padding:10px 16px;background:#FFFFFF;border-bottom:1px solid var(--line)}
.kbt .nav a{font-size:13px;font-weight:700;text-decoration:none;color:var(--ink);background:#F1EEE4;border:1px solid var(--line);padding:7px 13px;border-radius:20px}
.kbt .nav a:hover{background:#E8E2D0}
.kbt .body{padding:20px 16px 10px}
.kbt .card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:22px 20px;margin-bottom:20px}
.kbt .sec-head{display:flex;align-items:center;gap:12px;margin:0 0 16px;padding-bottom:12px;border-bottom:3px solid var(--ink)}
.kbt .sec-head .num{flex:0 0 auto;width:34px;height:34px;border-radius:50%;background:var(--ink);color:#fff;font-weight:800;display:flex;align-items:center;justify-content:center;font-size:15px}
.kbt .sec-head h2{margin:0;font-size:20px;line-height:1.3;color:var(--ink)}
.kbt h3{font-size:16.5px;color:var(--amberd);margin:24px 0 8px;line-height:1.35}
.kbt h3.first{margin-top:4px}
.kbt h4{font-size:15px;margin:16px 0 6px;color:var(--ink)}
.kbt p{font-size:15px;margin:8px 0;color:var(--ink)}
.kbt ul,.kbt ol{font-size:15px;padding-left:22px;margin:8px 0}
.kbt li{margin-bottom:6px;color:var(--ink)}
.kbt .callout{border-radius:8px;padding:12px 14px;margin:14px 0;font-size:14.5px;border:1px solid var(--line);border-left-width:5px}
.kbt .callout b.t{display:block;margin-bottom:3px}
.kbt .callout.info{background:#EAF2FC;border-color:#B9D2EE;border-left-color:var(--blue)}
.kbt .callout.tip{background:#E8F5EE;border-color:#B5DCC6;border-left-color:var(--green)}
.kbt .callout.warn{background:#FFF4DD;border-color:#F0D49A;border-left-color:var(--amber)}
.kbt .tw{overflow-x:auto;margin:10px 0 16px;border:1px solid var(--line);border-radius:8px}
.kbt table.tb{width:100%;border-collapse:collapse;font-size:13.5px;min-width:520px}
.kbt table.tb th{background:var(--ink);color:#fff;text-align:left;padding:9px 10px;font-weight:700}
.kbt table.tb td{padding:9px 10px;border-top:1px solid var(--line);vertical-align:top;color:var(--ink)}
.kbt table.tb tr:nth-child(even) td{background:#FAF8F2}
.kbt .flow{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0}
.kbt .flow span{background:#F1EEE4;border:1px solid var(--line);border-radius:20px;padding:6px 12px;font-size:13px;font-weight:700}
.kbt .flow span.go{background:var(--ink);color:#fff;border-color:var(--ink)}
.kbt .legend{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0}
.kbt .hl{padding:1px 4px;border-radius:4px;font-weight:600}
.kbt .hl.s{background:#DCEBFF;color:#0E3A73}
.kbt .hl.r{background:#FFD9D6;color:#8A1F17}
.kbt .hl.a{background:#D9F2E3;color:#145A36}
.kbt .hl.j{background:#EBDDFB;color:#4B2380}
.kbt .hl.m{background:#FFF0A0;color:#5B4B00}
.kbt .hl.d{background:#D5F0F2;color:#0B4F55}
.kbt .hl.v{background:#FADCEE;color:#7A1B55}
.kbt .sample{background:#FCFBF7;border:1px solid var(--line);border-left:5px solid var(--amber);border-radius:8px;padding:16px 18px;margin:12px 0}
.kbt .sample .lbl{font-size:12px;font-weight:800;letter-spacing:.05em;color:var(--amberd);margin:10px 0 2px}
.kbt .sample .lbl:first-child{margin-top:0}
.kbt .sample p{margin:4px 0 8px}
.kbt .sample h4.judul{font-size:17px;margin:0 0 8px}
.kbt details{border:1px solid var(--line);border-radius:8px;padding:10px 14px;margin:10px 0;background:#FFFFFF}
.kbt details summary{cursor:pointer;font-weight:700;font-size:14.5px;color:var(--ink)}
.kbt details[open] summary{margin-bottom:8px}
.kbt .qbox{background:#FAF8F2;border:1px solid var(--line);border-radius:8px;padding:12px 14px;margin:12px 0}
.kbt .qbox .qn{font-size:12px;font-weight:800;color:var(--amberd);letter-spacing:.05em}
.kbt .rangkuman{background:#E8F5EE;border:1px solid #B5DCC6;border-radius:8px;padding:14px 18px}
.kbt .grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.kbt label.fl{display:block;font-size:13px;font-weight:600;color:var(--ink2);margin:12px 0 4px}
.kbt input[type=text],.kbt input[type=number],.kbt textarea{width:100%;padding:10px 12px;border:1.5px solid #C9C1AC;border-radius:8px;font-size:15px;background:#fff;color:var(--ink);font-family:inherit}
.kbt textarea{min-height:120px;resize:vertical;line-height:1.55}
.kbt input:focus,.kbt textarea:focus{outline:3px solid #F5D493;border-color:var(--amber)}
.kbt .cnt{font-size:12px;color:var(--ink2);text-align:right;margin-top:3px}
.kbt .pwarn{display:none;font-size:13px;color:var(--red);font-weight:700;margin-top:4px}
.kbt .pwarn.show{display:block}
.kbt .btn{display:inline-block;border:none;border-radius:8px;padding:12px 20px;font-size:15px;font-weight:700;cursor:pointer;margin:14px 8px 0 0;font-family:inherit}
.kbt .btn.pri{background:var(--ink);color:#fff}
.kbt .btn.pri:hover{background:#000}
.kbt .btn.grn{background:var(--green);color:#fff}
.kbt .btn.grn:hover{background:#256646}
.kbt .btn.ghost{background:#fff;color:var(--ink);border:1.5px solid #C9C1AC}
.kbt .btn.chip{padding:6px 12px;font-size:13px;margin:4px 6px 0 0;background:#F1EEE4;color:var(--ink);border:1px solid var(--line);border-radius:20px}
.kbt .btn.chip:hover{background:#E8E2D0}
.kbt .hidden{display:none}
.kbt .err{display:none;background:#FBEAE8;border:1px solid #E5B4AF;color:#8A1F17;border-radius:8px;padding:10px 12px;font-size:14px;margin-top:12px;font-weight:600}
.kbt .resbox{border-radius:12px;padding:20px;text-align:center;margin-top:18px;border:2px solid var(--line);background:#FAF8F2}
.kbt .resbox.pass{background:#E8F5EE;border-color:var(--green)}
.kbt .resbox.fail{background:#FBEAE8;border-color:var(--red)}
.kbt .resbox .big{font-size:56px;font-weight:800;line-height:1.1}
.kbt .resbox.pass .big{color:var(--green)}
.kbt .resbox.fail .big{color:var(--red)}
.kbt .resbox .cat{font-size:16px;font-weight:700;margin-top:2px}
.kbt .resbox .meta{font-size:13px;color:var(--ink2);margin-top:6px}
.kbt .gbar{margin:12px 0}
.kbt .gbar .gl{display:flex;justify-content:space-between;font-size:13.5px;font-weight:700;margin-bottom:4px}
.kbt .gbar .track{height:10px;background:#E4DFD0;border-radius:6px;overflow:hidden}
.kbt .gbar .fill{height:100%;background:var(--amber);border-radius:6px}
.kbt .irow{display:flex;gap:10px;padding:9px 0;border-top:1px solid var(--line);font-size:14px}
.kbt .irow .bd{flex:0 0 auto;min-width:74px;text-align:center;font-size:12px;font-weight:800;border-radius:6px;padding:3px 6px;height:fit-content}
.kbt .irow .bd.ok{background:#D9F2E3;color:#145A36}
.kbt .irow .bd.pt{background:#FFF0A0;color:#5B4B00}
.kbt .irow .bd.no{background:#FFD9D6;color:#8A1F17}
.kbt .irow .tx{flex:1 1 auto}
.kbt .irow .tx small{display:block;color:var(--ink2);font-size:12.5px;margin-top:2px}
.kbt .irow .sc{flex:0 0 auto;font-weight:800;font-size:13px;white-space:nowrap}
.kbt .foot{padding:6px 20px 24px;text-align:center;font-size:12px;color:var(--ink2)}
@media (max-width:640px){.kbt .grid3{grid-template-columns:1fr}.kbt .hero h1{font-size:23px}.kbt .card{padding:18px 14px}.kbt .resbox .big{font-size:46px}}
#kbtPrint{display:none}
@media print{
  body &gt; *:not(#kbtPrint){display:none !important}
  #kbtPrint{display:block !important;position:static;width:100%;color:#000;background:#fff;font-family:Arial,Helvetica,sans-serif}
  @page{size:215mm 330mm;margin:14mm}
}
#kbtPrint .kop{text-align:center;border-bottom:3px solid #000;padding-bottom:8px;margin-bottom:10px}
#kbtPrint .kop h2{margin:0;font-size:16px}
#kbtPrint .kop p{margin:2px 0 0;font-size:11.5px}
#kbtPrint table.idt{width:100%;border-collapse:collapse;font-size:12.5px;margin-bottom:10px}
#kbtPrint table.idt td{padding:3px 4px;vertical-align:top}
#kbtPrint table.idt td.l{width:110px;color:#333}
#kbtPrint .skor{border:2px solid #000;border-radius:4px;padding:8px 12px;margin-bottom:12px;display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap}
#kbtPrint .skor div{font-size:11px}
#kbtPrint .skor b{display:block;font-size:19px}
#kbtPrint h3{font-size:13px;margin:12px 0 4px;border-bottom:1px solid #000;padding-bottom:2px}
#kbtPrint .jdl{font-size:15px;font-weight:700;margin:4px 0 6px}
#kbtPrint .lb{font-size:11px;font-weight:700;margin-top:6px;color:#333}
#kbtPrint .tk{font-size:12px;line-height:1.5;white-space:pre-wrap;border:1px solid #999;border-radius:3px;padding:6px 8px;margin-top:2px}
#kbtPrint table.rb{width:100%;border-collapse:collapse;font-size:11px;margin-top:4px}
#kbtPrint table.rb th,#kbtPrint table.rb td{border:1px solid #777;padding:3px 5px;text-align:left;vertical-align:top}
#kbtPrint table.rb th{background:#E5E5E5}
#kbtPrint table.rb tr.gh td{background:#F0F0F0;font-weight:700}
#kbtPrint table.rb tr{break-inside:avoid;page-break-inside:avoid}
#kbtPrint .ttd{display:flex;justify-content:space-between;margin-top:26px;font-size:12px;break-inside:avoid;page-break-inside:avoid}
#kbtPrint .ttd div{width:45%;text-align:center}
#kbtPrint .ttd .gr{margin-top:48px;border-top:1px solid #000;padding-top:3px}
#kbtPrint .nt{font-size:10.5px;color:#444;margin-top:10px}
&lt;/style&gt;

&lt;div class="kbt" id="kbtRoot"&gt;

&lt;div class="hero"&gt;
  &lt;div class="code"&gt;DOK/B.INDO/XI/CERAMAH-02&lt;/div&gt;
  &lt;h1&gt;Kaidah Kebahasaan Teks Ceramah (Bagian 2) dan Menulis Teks Ceramah&lt;/h1&gt;
  &lt;p class="sub"&gt;Bahasa Indonesia - Kelas XI SMK - Materi, contoh beranotasi, latihan, dan tugas individu menulis ceramah dengan penilaian otomatis&lt;/p&gt;
  &lt;div class="tp"&gt;&lt;b&gt;Tujuan Pembelajaran:&lt;/b&gt; Murid mampu menganalisis kaidah kebahasaan secara mendalam dan menulis teks ceramah secara kreatif.&lt;/div&gt;
&lt;/div&gt;

&lt;div class="nav" id="kbtNav"&gt;
  &lt;a href="#kbt-peta"&gt;Peta Materi&lt;/a&gt;
  &lt;a href="#kbt-kaidah"&gt;Kaidah Kebahasaan&lt;/a&gt;
  &lt;a href="#kbt-menulis"&gt;Menulis Ceramah&lt;/a&gt;
  &lt;a href="#kbt-contoh"&gt;Contoh&lt;/a&gt;
  &lt;a href="#kbt-latihan"&gt;Latihan&lt;/a&gt;
  &lt;a href="#kbt-tugas"&gt;Tugas Individu&lt;/a&gt;
&lt;/div&gt;

&lt;div class="body"&gt;

&lt;!-- ================= 1. PETA MATERI ================= --&gt;
&lt;div class="card" id="kbt-peta"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;1&lt;/div&gt;&lt;h2&gt;Peta Materi dan Tujuan Belajar&lt;/h2&gt;&lt;/div&gt;

  &lt;div class="callout info"&gt;&lt;b class="t"&gt;Ingat kembali (Bagian 1)&lt;/b&gt;
  Teks ceramah adalah pemaparan gagasan secara lisan di hadapan pendengar. Strukturnya: &lt;b&gt;pembukaan&lt;/b&gt;, &lt;b&gt;isi&lt;/b&gt;, dan &lt;b&gt;penutup&lt;/b&gt;. Isi ceramah dibangun dari &lt;b&gt;gagasan pokok&lt;/b&gt; (kalimat utama) dan &lt;b&gt;gagasan pendukung&lt;/b&gt; (data, contoh, alasan). Informasinya perlu dievaluasi dengan enam kriteria: kredibilitas, keakuratan, kelogisan, objektivitas, kelengkapan, dan kesesuaian tujuan. Pada Bagian 2 ini kita mendalami &lt;b&gt;bahasanya&lt;/b&gt; lalu &lt;b&gt;menulis ceramah sendiri&lt;/b&gt;.&lt;/div&gt;

  &lt;h3 class="first"&gt;Indikator Pencapaian&lt;/h3&gt;
  &lt;ol&gt;
    &lt;li&gt;Menjelaskan fungsi sembilan kaidah kebahasaan teks ceramah beserta penandanya.&lt;/li&gt;
    &lt;li&gt;Mengidentifikasi dan menganalisis kaidah kebahasaan pada teks ceramah.&lt;/li&gt;
    &lt;li&gt;Memperbaiki kalimat agar baku, efektif, dan persuasif.&lt;/li&gt;
    &lt;li&gt;Menyusun kerangka dan menulis teks ceramah yang runtut, persuasif, dan kreatif.&lt;/li&gt;
    &lt;li&gt;Menyunting tulisan sendiri berdasarkan rubrik penilaian.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h3&gt;Alur Belajar&lt;/h3&gt;
  &lt;div class="flow"&gt;
    &lt;span&gt;1. Pelajari kaidah&lt;/span&gt;&lt;span&gt;2. Pelajari langkah menulis&lt;/span&gt;&lt;span&gt;3. Amati contoh beranotasi&lt;/span&gt;&lt;span&gt;4. Kerjakan latihan&lt;/span&gt;&lt;span&gt;5. Tulis ceramahmu&lt;/span&gt;&lt;span&gt;6. Dinilai otomatis&lt;/span&gt;&lt;span class="go"&gt;7. Unduh PDF, cetak, serahkan ke guru&lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ================= 2. KAIDAH KEBAHASAAN ================= --&gt;
&lt;div class="card" id="kbt-kaidah"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;2&lt;/div&gt;&lt;h2&gt;Kaidah Kebahasaan Teks Ceramah (Pendalaman)&lt;/h2&gt;&lt;/div&gt;

  &lt;p&gt;&lt;b&gt;Kaidah kebahasaan&lt;/b&gt; adalah ciri-ciri bahasa yang khas pada suatu jenis teks. Ceramah dibawakan secara lisan, sehingga pendengar hanya sekali mendengar dan tidak bisa membaca ulang. Karena itu bahasa ceramah harus memenuhi tiga tuntutan: (1) &lt;b&gt;jelas&lt;/b&gt; dalam sekali dengar, (2) &lt;b&gt;akrab&lt;/b&gt; sehingga membangun hubungan dengan pendengar, dan (3) &lt;b&gt;meyakinkan&lt;/b&gt; karena didukung alasan dan data. Sembilan kaidah berikut perlu kamu kuasai.&lt;/p&gt;

  &lt;h3&gt;2.1 Kata Sapaan dan Salam&lt;/h3&gt;
  &lt;p&gt;Kata sapaan dipakai untuk menyapa pendengar, sedangkan salam dipakai untuk membuka dan menutup ceramah. Keduanya menunjukkan rasa hormat dan membuka kontak dengan pendengar.&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Sapaan disusun dari yang paling dihormati ke yang lain: &lt;i&gt;Bapak dan Ibu guru yang saya hormati, teman-teman yang saya banggakan&lt;/i&gt;.&lt;/li&gt;
    &lt;li&gt;Salam disesuaikan dengan situasi dan keberagaman pendengar: &lt;i&gt;Assalamualaikum warahmatullahi wabarakatuh, Selamat pagi, Salam sejahtera, Om Swastiastu, Namo Buddhaya, Shalom&lt;/i&gt;.&lt;/li&gt;
    &lt;li&gt;Sapaan boleh diulang di bagian penutup untuk menegaskan kedekatan: &lt;i&gt;Hadirin yang saya hormati, demikian ceramah singkat saya&lt;/i&gt;.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;2.2 Kata Ganti Orang (Persona)&lt;/h3&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Kata&lt;/th&gt;&lt;th&gt;Makna&lt;/th&gt;&lt;th&gt;Efek bagi pendengar&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;saya&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Penceramah sendiri&lt;/td&gt;&lt;td&gt;Personal dan jujur&lt;/td&gt;&lt;td&gt;Saya yakin kita mampu berubah.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;kita&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Penceramah dan pendengar (inklusif)&lt;/td&gt;&lt;td&gt;Dekat, tidak menggurui, terasa senasib&lt;/td&gt;&lt;td&gt;Marilah kita jaga kebersihan sekolah.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;kami&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Penceramah dan kelompoknya, tanpa pendengar (eksklusif)&lt;/td&gt;&lt;td&gt;Formal, mewakili kelompok&lt;/td&gt;&lt;td&gt;Kami dari OSIS mengimbau seluruh siswa menaati tata tertib.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Anda, Saudara, kalian&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Pendengar&lt;/td&gt;&lt;td&gt;Langsung menyapa, tetapi bisa terasa menunjuk&lt;/td&gt;&lt;td&gt;Apakah Anda sudah memeriksa kebenaran berita itu?&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
  &lt;div class="callout tip"&gt;&lt;b class="t"&gt;Tips&lt;/b&gt;Gunakan &lt;b&gt;kita&lt;/b&gt; saat membahas kesalahan atau kebiasaan buruk bersama agar pendengar tidak merasa dihakimi. Gunakan &lt;b&gt;saya&lt;/b&gt; saat menyatakan pendapat atau keyakinan pribadi.&lt;/div&gt;

  &lt;h3&gt;2.3 Kalimat Persuasif dan Imperatif (Ajakan)&lt;/h3&gt;
  &lt;p&gt;Tujuan ceramah bukan hanya memberi tahu, tetapi juga &lt;b&gt;mengajak&lt;/b&gt; pendengar bertindak. Kalimat persuasif berisi bujukan yang disertai alasan, sedangkan kalimat imperatif berisi perintah atau larangan. Penanda umumnya: &lt;i&gt;mari, marilah, ayo, hendaknya, hendaklah, janganlah, mohon, harap, sebaiknya&lt;/i&gt;, dan akhiran &lt;i&gt;-lah&lt;/i&gt; pada kata kerja (&lt;i&gt;periksalah, jagalah, ingatlah&lt;/i&gt;).&lt;/p&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Tingkat&lt;/th&gt;&lt;th&gt;Ciri&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Halus&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Saran dan harapan, tidak memaksa&lt;/td&gt;&lt;td&gt;Sebaiknya kita memeriksa sumber berita sebelum membagikannya.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Sedang&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Ajakan bersama dengan kata mari atau marilah&lt;/td&gt;&lt;td&gt;Marilah kita biasakan membuang sampah pada tempatnya.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Tegas&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Larangan atau perintah, dipakai untuk hal yang berbahaya&lt;/td&gt;&lt;td&gt;Janganlah mengoperasikan mesin tanpa alat pelindung diri!&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
  &lt;div class="callout tip"&gt;&lt;b class="t"&gt;Rumus ajakan yang meyakinkan&lt;/b&gt;Alasan dahulu, ajakan kemudian. Contoh: &lt;i&gt;Hoaks dapat memecah belah masyarakat. Oleh karena itu, marilah kita periksa kebenaran berita sebelum membagikannya.&lt;/i&gt; Ajakan tanpa alasan terasa seperti perintah kosong.&lt;/div&gt;

  &lt;h3&gt;2.4 Kalimat Retoris&lt;/h3&gt;
  &lt;p&gt;&lt;b&gt;Kalimat retoris&lt;/b&gt; adalah kalimat tanya yang tidak menuntut jawaban karena jawabannya sudah jelas bagi pendengar. Fungsinya menggugah perhatian, memancing pendengar berpikir, atau menegaskan gagasan.&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Sebagai pengait di pembukaan: &lt;i&gt;Pernahkah kita membayangkan sekolah tanpa sampah berserakan?&lt;/i&gt;&lt;/li&gt;
    &lt;li&gt;Sebagai peralihan: &lt;i&gt;Lalu, apa yang dapat kita lakukan mulai hari ini?&lt;/i&gt;&lt;/li&gt;
    &lt;li&gt;Sebagai penegas: &lt;i&gt;Siapa lagi yang akan menjaga masa depan kita kalau bukan kita sendiri?&lt;/i&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;div class="callout warn"&gt;&lt;b class="t"&gt;Batasi pemakaian&lt;/b&gt;Cukup dua sampai tiga kalimat retoris dalam satu ceramah. Terlalu banyak pertanyaan membuat pendengar lelah dan ceramah terasa menginterogasi.&lt;/div&gt;

  &lt;h3&gt;2.5 Konjungsi (Kata Penghubung)&lt;/h3&gt;
  &lt;p&gt;Konjungsi menjaga alur penalaran ceramah supaya pendengar mudah mengikuti hubungan antargagasan.&lt;/p&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Jenis&lt;/th&gt;&lt;th&gt;Fungsi&lt;/th&gt;&lt;th&gt;Contoh kata&lt;/th&gt;&lt;th&gt;Contoh kalimat&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Sebab-akibat&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Menyatakan alasan atau akibat&lt;/td&gt;&lt;td&gt;karena, sebab, sehingga, akibatnya&lt;/td&gt;&lt;td&gt;Sampah menumpuk sehingga selokan tersumbat.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Simpulan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Menarik kesimpulan dari alasan&lt;/td&gt;&lt;td&gt;oleh karena itu, dengan demikian, jadi&lt;/td&gt;&lt;td&gt;Dengan demikian, disiplin adalah bekal utama dunia kerja.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Pertentangan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Membandingkan atau membantah&lt;/td&gt;&lt;td&gt;namun, tetapi, akan tetapi, sebaliknya&lt;/td&gt;&lt;td&gt;Teknologi memudahkan hidup, namun penggunaannya perlu bijak.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Penambahan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Menambah gagasan&lt;/td&gt;&lt;td&gt;selain itu, bahkan, lagi pula&lt;/td&gt;&lt;td&gt;Selain itu, kebiasaan baik dapat menular kepada teman.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Urutan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Mengurutkan gagasan&lt;/td&gt;&lt;td&gt;pertama, kedua, selanjutnya, terakhir&lt;/td&gt;&lt;td&gt;Pertama, kita perlu menyadari bahayanya.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Pengandaian&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Menyatakan syarat&lt;/td&gt;&lt;td&gt;jika, apabila, seandainya&lt;/td&gt;&lt;td&gt;Jika kita teliti, kecelakaan kerja dapat dicegah.&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
  &lt;div class="callout warn"&gt;&lt;b class="t"&gt;Kesalahan umum&lt;/b&gt;Jangan menjadikan anak kalimat sebagai kalimat berdiri sendiri. Salah: &lt;i&gt;Karena sampah menumpuk.&lt;/i&gt; Benar: &lt;i&gt;Banjir terjadi karena sampah menumpuk di selokan.&lt;/i&gt;&lt;/div&gt;

  &lt;h3&gt;2.6 Kata Kerja Mental dan Ungkapan Sikap&lt;/h3&gt;
  &lt;p&gt;Kata kerja mental menyatakan aktivitas batin: &lt;i&gt;menyadari, meyakini, percaya, berpendapat, menyimpulkan, memahami, berharap, mengimbau&lt;/i&gt;. Kata-kata ini menandai bahwa penceramah sedang menyampaikan &lt;b&gt;pendapat atau sikap&lt;/b&gt;, bukan fakta yang bisa langsung diuji.&lt;/p&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Jenis informasi&lt;/th&gt;&lt;th&gt;Ciri&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Fakta&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Dapat dibuktikan dengan data atau sumber&lt;/td&gt;&lt;td&gt;Menurut data lembaga resmi, ribuan aplikasi pinjaman ilegal telah diblokir.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Opini&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Pandangan penceramah, ditandai kata kerja mental&lt;/td&gt;&lt;td&gt;Saya yakin siswa SMK mampu bersikap teliti.&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
  &lt;p&gt;Ceramah yang baik memadukan keduanya: opini diperkuat fakta. Opini tanpa fakta mudah dibantah, sedangkan fakta tanpa opini terasa dingin.&lt;/p&gt;

  &lt;h3&gt;2.7 Kosakata: Istilah Teknis, Kata Baku, dan Diksi&lt;/h3&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;b&gt;Istilah teknis&lt;/b&gt; sesuai topik (misalnya &lt;i&gt;hoaks, literasi digital, alat pelindung diri, wirausaha&lt;/i&gt;) menunjukkan penguasaan materi. Jelaskan istilah yang mungkin asing: &lt;i&gt;hoaks, yaitu berita bohong yang sengaja disebarkan&lt;/i&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;Kata baku&lt;/b&gt; dipakai karena ceramah adalah situasi resmi. Hindari kata percakapan sehari-hari.&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;Diksi&lt;/b&gt; (pilihan kata) harus tepat, santun, dan mudah dipahami pendengar.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Tidak baku&lt;/th&gt;&lt;th&gt;Baku&lt;/th&gt;&lt;th&gt;Tidak baku&lt;/th&gt;&lt;th&gt;Baku&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;gak, nggak, enggak&lt;/td&gt;&lt;td&gt;tidak&lt;/td&gt;&lt;td&gt;kayak&lt;/td&gt;&lt;td&gt;seperti&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;banget&lt;/td&gt;&lt;td&gt;sekali, sangat&lt;/td&gt;&lt;td&gt;gimana&lt;/td&gt;&lt;td&gt;bagaimana&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;aja&lt;/td&gt;&lt;td&gt;saja&lt;/td&gt;&lt;td&gt;emang&lt;/td&gt;&lt;td&gt;memang&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;udah&lt;/td&gt;&lt;td&gt;sudah&lt;/td&gt;&lt;td&gt;cuma&lt;/td&gt;&lt;td&gt;hanya&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;bikin&lt;/td&gt;&lt;td&gt;membuat&lt;/td&gt;&lt;td&gt;pake&lt;/td&gt;&lt;td&gt;pakai&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;tapi&lt;/td&gt;&lt;td&gt;tetapi&lt;/td&gt;&lt;td&gt;gue, lo&lt;/td&gt;&lt;td&gt;saya, Anda&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;nih, dong, sih, deh&lt;/td&gt;&lt;td&gt;(dihilangkan)&lt;/td&gt;&lt;td&gt;kok, lho&lt;/td&gt;&lt;td&gt;(dihilangkan)&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;

  &lt;h3&gt;2.8 Majas dan Gaya Bahasa untuk Kreativitas&lt;/h3&gt;
  &lt;p&gt;Majas membuat ceramah berkesan dan mudah diingat. Namun majas hanya bumbu: gunakan secukupnya dan pastikan tetap benar secara isi.&lt;/p&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Majas&lt;/th&gt;&lt;th&gt;Pengertian&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Perumpamaan (simile)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Perbandingan langsung memakai kata ibarat, bagaikan, laksana, bak&lt;/td&gt;&lt;td&gt;Disiplin ibarat kompas yang menuntun kita menuju tujuan.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Metafora&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Perbandingan tanpa kata pembanding&lt;/td&gt;&lt;td&gt;Jempol kita adalah senjata: bisa menyebar kebenaran, bisa menyebar kebohongan.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Repetisi (anafora)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Pengulangan kata di awal kalimat untuk penekanan&lt;/td&gt;&lt;td&gt;Jangan malas belajar. Jangan takut mencoba. Jangan menyerah sebelum berusaha.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Hiperbola&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Pernyataan dilebih-lebihkan untuk menegaskan&lt;/td&gt;&lt;td&gt;Hoaks menyebar secepat kilat.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Personifikasi&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Benda diberi sifat manusia&lt;/td&gt;&lt;td&gt;Sampah menjerit minta dipungut.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Klimaks&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Urutan gagasan dari yang ringan ke yang paling penting&lt;/td&gt;&lt;td&gt;Mulailah dari diri sendiri, keluarga, sekolah, hingga bangsa.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Peribahasa&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Ungkapan tetap berisi nasihat&lt;/td&gt;&lt;td&gt;Sedia payung sebelum hujan.&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
  &lt;div class="callout tip"&gt;&lt;b class="t"&gt;Aturan praktis&lt;/b&gt;Maksimal satu majas yang menonjol per paragraf, dan pilih majas yang berkaitan dengan dunia pendengar (misalnya bengkel, dapur, atau kantor untuk siswa SMK).&lt;/div&gt;

  &lt;h3&gt;2.9 Kalimat Efektif untuk Telinga&lt;/h3&gt;
  &lt;p&gt;Kalimat dalam ceramah sebaiknya pendek sampai sedang (sekitar 10 sampai 20 kata), memuat satu gagasan, dan bervariasi panjangnya agar tidak monoton. Hindari kata mubazir dan susunan yang berbelit.&lt;/p&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Kurang efektif&lt;/th&gt;&lt;th&gt;Efektif&lt;/th&gt;&lt;th&gt;Alasan&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Para hadirin sekalian yang saya hormati&lt;/td&gt;&lt;td&gt;Hadirin yang saya hormati&lt;/td&gt;&lt;td&gt;Para dan sekalian berlebihan (mubazir)&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Agar supaya kita semua sehat&lt;/td&gt;&lt;td&gt;Agar kita semua sehat&lt;/td&gt;&lt;td&gt;Agar dan supaya bermakna sama&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Demi untuk masa depan kita&lt;/td&gt;&lt;td&gt;Demi masa depan kita&lt;/td&gt;&lt;td&gt;Demi dan untuk bermakna sama&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kita harus saling bantu-membantu satu sama lain&lt;/td&gt;&lt;td&gt;Kita harus saling membantu&lt;/td&gt;&lt;td&gt;Saling sudah bermakna timbal balik&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Narkoba itu bahaya, kita harus menghindarinya, itu merusak&lt;/td&gt;&lt;td&gt;Narkoba merusak masa depan sehingga kita harus menjauhinya.&lt;/td&gt;&lt;td&gt;Gagasan dirangkai dengan konjungsi&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;

  &lt;h3&gt;Ringkasan Sembilan Kaidah&lt;/h3&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Kaidah&lt;/th&gt;&lt;th&gt;Fungsi&lt;/th&gt;&lt;th&gt;Penanda&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Salam dan sapaan&lt;/td&gt;&lt;td&gt;Membuka kontak dan menghormati&lt;/td&gt;&lt;td&gt;Assalamualaikum, Selamat pagi, Bapak dan Ibu, hadirin&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kata ganti kita/kami&lt;/td&gt;&lt;td&gt;Membangun kedekatan&lt;/td&gt;&lt;td&gt;kita, kami, saya&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kalimat ajakan&lt;/td&gt;&lt;td&gt;Mendorong pendengar bertindak&lt;/td&gt;&lt;td&gt;mari, marilah, hendaknya, janganlah, -lah&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kalimat retoris&lt;/td&gt;&lt;td&gt;Menggugah dan menegaskan&lt;/td&gt;&lt;td&gt;kalimat tanya tanpa jawaban&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Konjungsi&lt;/td&gt;&lt;td&gt;Menjaga alur penalaran&lt;/td&gt;&lt;td&gt;karena, sehingga, oleh karena itu, namun, pertama&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kata kerja mental&lt;/td&gt;&lt;td&gt;Menyatakan sikap dan pendapat&lt;/td&gt;&lt;td&gt;menyadari, meyakini, berpendapat, menyimpulkan&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kosakata baku dan istilah teknis&lt;/td&gt;&lt;td&gt;Resmi, tepat, dan berwibawa&lt;/td&gt;&lt;td&gt;sudah, tidak, sangat, istilah topik&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Majas&lt;/td&gt;&lt;td&gt;Membuat ceramah berkesan&lt;/td&gt;&lt;td&gt;ibarat, bagaikan, repetisi, metafora&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Kalimat efektif&lt;/td&gt;&lt;td&gt;Jelas dalam sekali dengar&lt;/td&gt;&lt;td&gt;kalimat ringkas, tanpa kata mubazir&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;!-- ================= 3. MENULIS CERAMAH ================= --&gt;
&lt;div class="card" id="kbt-menulis"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;3&lt;/div&gt;&lt;h2&gt;Menulis Teks Ceramah&lt;/h2&gt;&lt;/div&gt;
  &lt;p&gt;Menulis ceramah berarti &lt;b&gt;menulis untuk didengar&lt;/b&gt;. Naskahnya harus enak diucapkan, mudah diikuti dalam sekali dengar, dan membuat pendengar tergerak. Ikuti lima langkah berikut.&lt;/p&gt;

  &lt;h3&gt;Langkah 1. Rumuskan Topik, Tujuan, dan Pendengar&lt;/h3&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Pertanyaan&lt;/th&gt;&lt;th&gt;Yang perlu diputuskan&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Apa topiknya?&lt;/td&gt;&lt;td&gt;Satu topik sempit dan dekat dengan dunia siswa SMK&lt;/td&gt;&lt;td&gt;Saring Sebelum Sharing: Cerdas Menghadapi Hoaks&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Apa tujuannya?&lt;/td&gt;&lt;td&gt;Menginformasikan, menyadarkan, atau mengajak&lt;/td&gt;&lt;td&gt;Menyadarkan dan mengajak memeriksa berita&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Siapa pendengarnya?&lt;/td&gt;&lt;td&gt;Usia, latar belakang, dan tingkat pengetahuan&lt;/td&gt;&lt;td&gt;Guru dan teman sekelas&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Di mana dan berapa lama?&lt;/td&gt;&lt;td&gt;Situasi dan durasi&lt;/td&gt;&lt;td&gt;Upacara atau kegiatan kelas, 2 sampai 3 menit (sekitar 250 sampai 400 kata)&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;

  &lt;h3&gt;Langkah 2. Kumpulkan Bahan&lt;/h3&gt;
  &lt;ul&gt;
    &lt;li&gt;Tentukan &lt;b&gt;tiga gagasan pokok&lt;/b&gt; yang akan dibahas pada bagian isi.&lt;/li&gt;
    &lt;li&gt;Siapkan &lt;b&gt;minimal dua gagasan pendukung&lt;/b&gt; untuk setiap gagasan pokok: data, contoh nyata, alasan logis, atau kutipan.&lt;/li&gt;
    &lt;li&gt;Gunakan &lt;b&gt;sumber yang jelas&lt;/b&gt; (lembaga resmi, buku, artikel ilmiah, media terpercaya) dan sebutkan sumbernya dalam teks.&lt;/li&gt;
    &lt;li&gt;Uji bahanmu dengan &lt;b&gt;enam kriteria evaluasi&lt;/b&gt; pada Bagian 1. Jangan mengarang data atau angka.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Langkah 3. Susun Kerangka&lt;/h3&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Bagian&lt;/th&gt;&lt;th&gt;Unsur yang perlu ada&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Pembukaan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;1) Salam pembuka. 2) Sapaan kepada pendengar. 3) Pengait perhatian (pertanyaan retoris, cerita singkat, fakta, atau peribahasa). 4) Pengantar topik dalam satu kalimat.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Isi&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Tiga paragraf. Setiap paragraf memuat gagasan pokok, bukti atau contoh, penjelasan, dan penghubung ke paragraf berikutnya. Sisipkan ajakan dan satu majas.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;b&gt;Penutup&lt;/b&gt;&lt;/td&gt;&lt;td&gt;1) Simpulan satu kalimat. 2) Harapan. 3) Ajakan akhir atau slogan. 4) Ucapan terima kasih. 5) Salam penutup.&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;

  &lt;h3&gt;Langkah 4. Kembangkan Menjadi Teks&lt;/h3&gt;
  &lt;h4&gt;Rumus paragraf isi: P-B-P-A&lt;/h4&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;b&gt;P&lt;/b&gt;ernyataan: kalimat utama berisi gagasan pokok.&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;B&lt;/b&gt;ukti: data, sumber, atau contoh (gagasan pendukung).&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;P&lt;/b&gt;enjelasan: akibat atau hubungan bukti dengan gagasan pokok, gunakan konjungsi sebab-akibat.&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;A&lt;/b&gt;jakan atau penghubung: kalimat ajakan atau transisi ke gagasan berikutnya.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;div class="qbox"&gt;&lt;div class="qn"&gt;CONTOH SATU PARAGRAF P-B-P-A&lt;/div&gt;
  (P) Kita perlu menyadari bahwa hoaks menyebar lebih cepat daripada klarifikasi. (B) Menurut data lembaga resmi, ribuan konten hoaks ditemukan setiap tahun. (P) Akibatnya, banyak orang termakan kabar bohong sehingga muncul kepanikan. (A) Oleh karena itu, marilah kita periksa kebenaran berita sebelum membagikannya.&lt;/div&gt;

  &lt;h4&gt;Teknik pembuka yang kreatif&lt;/h4&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;b&gt;Pertanyaan retoris:&lt;/b&gt; Pernahkah kita menyesal setelah menekan tombol kirim?&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;Cerita singkat:&lt;/b&gt; Minggu lalu, seorang teman saya hampir tertipu pesan hadiah palsu...&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;Fakta atau data:&lt;/b&gt; Setiap hari, jutaan pesan beredar di media sosial, dan tidak semuanya benar...&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;Peribahasa:&lt;/b&gt; Sedia payung sebelum hujan, begitu pula kita perlu waspada sebelum membagikan berita.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h4&gt;Teknik penutup yang berkesan&lt;/h4&gt;
  &lt;ul&gt;
    &lt;li&gt;Simpulkan inti ceramah dalam satu kalimat.&lt;/li&gt;
    &lt;li&gt;Tutup dengan slogan singkat yang mudah diingat, misalnya &lt;i&gt;Saring sebelum sharing!&lt;/i&gt;&lt;/li&gt;
    &lt;li&gt;Berikan satu aksi kecil yang bisa dilakukan pendengar hari ini.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Langkah 5. Sunting dan Latih&lt;/h3&gt;
  &lt;p&gt;Baca naskah dengan suara nyaring dan ukur waktunya. Bagian yang sulit diucapkan biasanya terlalu panjang atau berbelit. Gunakan daftar periksa berikut.&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;Ada salam pembuka dan sapaan di pembukaan.&lt;/li&gt;
    &lt;li&gt;Topik disebut jelas di pembukaan.&lt;/li&gt;
    &lt;li&gt;Isi terdiri atas tiga paragraf dengan gagasan pokok berbeda.&lt;/li&gt;
    &lt;li&gt;Setiap gagasan pokok punya pendukung berupa contoh atau data bersumber.&lt;/li&gt;
    &lt;li&gt;Kata ganti kita atau kami digunakan untuk membangun kedekatan.&lt;/li&gt;
    &lt;li&gt;Ada kalimat ajakan dan konjungsi yang menjaga alur.&lt;/li&gt;
    &lt;li&gt;Ada kata kerja mental yang menyatakan sikap.&lt;/li&gt;
    &lt;li&gt;Tidak ada kata tidak baku, dan kalimat tidak berbelit.&lt;/li&gt;
    &lt;li&gt;Ada kalimat retoris dan minimal satu majas.&lt;/li&gt;
    &lt;li&gt;Penutup memuat simpulan, harapan, terima kasih, dan salam.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

&lt;!-- ================= 4. CONTOH ================= --&gt;
&lt;div class="card" id="kbt-contoh"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;4&lt;/div&gt;&lt;h2&gt;Contoh Teks Ceramah Beranotasi&lt;/h2&gt;&lt;/div&gt;
  &lt;p&gt;Perhatikan bagaimana kaidah kebahasaan ditandai dengan warna pada contoh berikut. Teks ini hanya contoh model. Ceramahmu harus karya sendiri dengan topik pilihanmu.&lt;/p&gt;
  &lt;div class="legend"&gt;
    &lt;span class="hl s"&gt;Salam dan sapaan&lt;/span&gt;&lt;span class="hl r"&gt;Kalimat retoris&lt;/span&gt;&lt;span class="hl a"&gt;Kalimat ajakan&lt;/span&gt;&lt;span class="hl j"&gt;Konjungsi&lt;/span&gt;&lt;span class="hl m"&gt;Majas&lt;/span&gt;&lt;span class="hl d"&gt;Contoh, data, sumber&lt;/span&gt;&lt;span class="hl v"&gt;Kata kerja mental, harapan&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class="sample"&gt;
    &lt;h4 class="judul"&gt;Saring Sebelum Sharing: Cerdas Menghadapi Hoaks&lt;/h4&gt;
    &lt;div class="lbl"&gt;PEMBUKAAN&lt;/div&gt;
    &lt;p&gt;&lt;span class="hl s"&gt;Assalamualaikum warahmatullahi wabarakatuh. Selamat pagi, Bapak dan Ibu guru serta teman-teman yang saya hormati.&lt;/span&gt; &lt;span class="hl r"&gt;Pernahkah kita menerima pesan berantai yang membuat jantung berdebar, lalu tanpa berpikir panjang langsung kita teruskan ke banyak grup?&lt;/span&gt; Pagi ini saya akan menyampaikan ceramah singkat bertema Saring Sebelum Sharing, yaitu cara cerdas menghadapi hoaks di media sosial.&lt;/p&gt;
    &lt;div class="lbl"&gt;ISI&lt;/div&gt;
    &lt;p&gt;&lt;span class="hl j"&gt;Pertama&lt;/span&gt;, kita perlu &lt;span class="hl v"&gt;menyadari&lt;/span&gt; bahwa hoaks menyebar jauh lebih cepat daripada klarifikasi. &lt;span class="hl d"&gt;Menurut data Kementerian Komunikasi dan Informatika&lt;/span&gt;, ribuan konten hoaks berhasil diidentifikasi setiap tahun. &lt;span class="hl j"&gt;Akibatnya&lt;/span&gt;, banyak orang termakan kabar bohong &lt;span class="hl j"&gt;sehingga&lt;/span&gt; terjadi kepanikan, kerugian, bahkan perpecahan di tengah masyarakat.&lt;/p&gt;
    &lt;p&gt;&lt;span class="hl j"&gt;Kedua&lt;/span&gt;, kita harus mengenali ciri-ciri hoaks. &lt;span class="hl d"&gt;Misalnya&lt;/span&gt;, judulnya provokatif dan memakai huruf kapital berlebihan, sumbernya tidak jelas, serta mendesak kita untuk segera menyebarkannya. &lt;span class="hl m"&gt;Hoaks ibarat api kecil di tumpukan jerami&lt;/span&gt;; jika tidak segera dipadamkan, ia akan membakar apa saja. &lt;span class="hl j"&gt;Oleh karena itu&lt;/span&gt;, sebelum membagikan pesan apa pun, &lt;span class="hl a"&gt;periksalah&lt;/span&gt; dahulu kebenarannya.&lt;/p&gt;
    &lt;p&gt;&lt;span class="hl j"&gt;Ketiga&lt;/span&gt;, kita dapat membiasakan tiga langkah sederhana: cek sumbernya, cek faktanya, dan cek pemberitaan di media resmi. &lt;span class="hl v"&gt;Saya yakin&lt;/span&gt; siswa SMK yang terbiasa teliti di bengkel dan laboratorium pasti mampu teliti pula di dunia digital. &lt;span class="hl j"&gt;Jadi&lt;/span&gt;, &lt;span class="hl a"&gt;marilah kita jadikan&lt;/span&gt; &lt;span class="hl m"&gt;jempol kita sebagai penebar kebenaran, bukan penyebar kebohongan&lt;/span&gt;.&lt;/p&gt;
    &lt;div class="lbl"&gt;PENUTUP&lt;/div&gt;
    &lt;p&gt;&lt;span class="hl s"&gt;Hadirin yang saya hormati&lt;/span&gt;, demikian ceramah singkat dari saya. &lt;span class="hl j"&gt;Dengan demikian&lt;/span&gt;, &lt;span class="hl a"&gt;mari kita bersama-sama menjaga&lt;/span&gt; ruang digital agar tetap sehat. &lt;span class="hl v"&gt;Semoga&lt;/span&gt; kita menjadi generasi yang cerdas, jujur, dan bertanggung jawab. &lt;span class="hl a"&gt;Ingat, saring sebelum sharing!&lt;/span&gt; &lt;span class="hl s"&gt;Terima kasih atas perhatian Bapak, Ibu, dan teman-teman. Wassalamualaikum warahmatullahi wabarakatuh.&lt;/span&gt;&lt;/p&gt;
  &lt;/div&gt;

  &lt;h3&gt;Analisis Singkat&lt;/h3&gt;
  &lt;div class="tw"&gt;&lt;table class="tb"&gt;
    &lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Temuan pada contoh&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Gagasan pokok isi&lt;/td&gt;&lt;td&gt;(1) Hoaks menyebar lebih cepat daripada klarifikasi, (2) ciri-ciri hoaks, (3) langkah sederhana menangkal hoaks.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Gagasan pendukung&lt;/td&gt;&lt;td&gt;Data lembaga resmi, contoh ciri hoaks (judul provokatif, sumber tidak jelas), tiga langkah cek sumber, fakta, dan media resmi.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Pengait pembukaan&lt;/td&gt;&lt;td&gt;Kalimat retoris tentang pesan berantai yang langsung diteruskan.&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Majas&lt;/td&gt;&lt;td&gt;Perumpamaan (hoaks ibarat api kecil) dan metafora (jempol sebagai penebar kebenaran).&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Persuasi&lt;/td&gt;&lt;td&gt;Setiap ajakan didahului alasan (marilah, mari kita, periksalah) dan ditegaskan dengan slogan.&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;!-- ================= 5. LATIHAN ================= --&gt;
&lt;div class="card" id="kbt-latihan"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;5&lt;/div&gt;&lt;h2&gt;Latihan Analisis Cepat&lt;/h2&gt;&lt;/div&gt;
  &lt;p&gt;Kerjakan dahulu di buku catatanmu, lalu buka pembahasan untuk memeriksa jawabanmu.&lt;/p&gt;

  &lt;div class="qbox"&gt;&lt;div class="qn"&gt;LATIHAN 1 - MENGIDENTIFIKASI KAIDAH&lt;/div&gt;
  Sebutkan kaidah kebahasaan pada kalimat: &lt;i&gt;"Mengapa kita sering lupa bahwa sampah kecil pun dapat menyumbat sungai?"&lt;/i&gt;
  &lt;details&gt;&lt;summary&gt;Lihat pembahasan&lt;/summary&gt;Kalimat tersebut memuat &lt;b&gt;kalimat retoris&lt;/b&gt; (pertanyaan yang tidak menuntut jawaban) dan &lt;b&gt;kata ganti kita&lt;/b&gt; (inklusif) yang membangun kedekatan dengan pendengar.&lt;/details&gt;&lt;/div&gt;

  &lt;div class="qbox"&gt;&lt;div class="qn"&gt;LATIHAN 2 - MEMBAKUKAN KALIMAT&lt;/div&gt;
  Ubahlah menjadi kalimat baku dan persuasif: &lt;i&gt;"Gue udah bilang, jangan nyontek aja, nggak keren banget!"&lt;/i&gt;
  &lt;details&gt;&lt;summary&gt;Lihat pembahasan&lt;/summary&gt;Contoh jawaban: &lt;i&gt;Saya sudah mengingatkan, janganlah menyontek, sebab perbuatan itu tidak terpuji.&lt;/i&gt; Kata gue, udah, nyontek, aja, nggak, banget diganti kata baku, dan ditambahkan alasan (sebab) agar ajakan lebih meyakinkan.&lt;/details&gt;&lt;/div&gt;

  &lt;div class="qbox"&gt;&lt;div class="qn"&gt;LATIHAN 3 - MENGGUNAKAN KONJUNGSI&lt;/div&gt;
  Gabungkan dengan konjungsi yang tepat: &lt;i&gt;Banyak siswa terlambat. Pelajaran pertama sering terlewat.&lt;/i&gt;
  &lt;details&gt;&lt;summary&gt;Lihat pembahasan&lt;/summary&gt;Contoh jawaban: &lt;i&gt;Banyak siswa terlambat sehingga pelajaran pertama sering terlewat.&lt;/i&gt; Konjungsi &lt;b&gt;sehingga&lt;/b&gt; menyatakan hubungan sebab-akibat.&lt;/details&gt;&lt;/div&gt;

  &lt;div class="qbox"&gt;&lt;div class="qn"&gt;LATIHAN 4 - MENENTUKAN MAJAS&lt;/div&gt;
  Tentukan majas pada kalimat: &lt;i&gt;"Disiplin ibarat kompas yang menuntun kita menuju tujuan."&lt;/i&gt;
  &lt;details&gt;&lt;summary&gt;Lihat pembahasan&lt;/summary&gt;&lt;b&gt;Perumpamaan (simile)&lt;/b&gt;, karena membandingkan disiplin dengan kompas secara langsung memakai kata pembanding &lt;i&gt;ibarat&lt;/i&gt;.&lt;/details&gt;&lt;/div&gt;

  &lt;div class="qbox"&gt;&lt;div class="qn"&gt;LATIHAN 5 - MENYUNTING KALIMAT EFEKTIF&lt;/div&gt;
  Sunting kalimat berikut: &lt;i&gt;"Para hadirin sekalian yang saya hormati, agar supaya kita semua dapat sukses, kita harus saling bantu-membantu."&lt;/i&gt;
  &lt;details&gt;&lt;summary&gt;Lihat pembahasan&lt;/summary&gt;Contoh jawaban: &lt;i&gt;Hadirin yang saya hormati, agar kita semua dapat sukses, kita harus saling membantu.&lt;/i&gt; Kata mubazir yang dihapus: para, sekalian, supaya, dan bentuk bantu-membantu diganti membantu.&lt;/details&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;!-- ================= 6. RANGKUMAN ================= --&gt;
&lt;div class="card" id="kbt-rangkuman"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;6&lt;/div&gt;&lt;h2&gt;Rangkuman&lt;/h2&gt;&lt;/div&gt;
  &lt;div class="rangkuman"&gt;&lt;ul&gt;
    &lt;li&gt;Kaidah kebahasaan ceramah: salam dan sapaan, kata ganti kita atau kami, kalimat ajakan, kalimat retoris, konjungsi, kata kerja mental, kosakata baku dan istilah teknis, majas, serta kalimat efektif.&lt;/li&gt;
    &lt;li&gt;Ajakan yang meyakinkan selalu didahului alasan, dan opini diperkuat data yang bersumber jelas.&lt;/li&gt;
    &lt;li&gt;Langkah menulis ceramah: rumuskan topik dan pendengar, kumpulkan bahan, susun kerangka, kembangkan dengan rumus P-B-P-A, lalu sunting dan latih.&lt;/li&gt;
    &lt;li&gt;Ceramah ditulis untuk didengar, sehingga kalimatnya ringkas, runtut, dan enak diucapkan.&lt;/li&gt;
  &lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;!-- ================= 7. TUGAS INDIVIDU ================= --&gt;
&lt;div class="card" id="kbt-tugas"&gt;
  &lt;div class="sec-head"&gt;&lt;div class="num"&gt;7&lt;/div&gt;&lt;h2&gt;Tugas Individu: Menulis Teks Ceramah&lt;/h2&gt;&lt;/div&gt;

  &lt;p&gt;Tulislah &lt;b&gt;satu teks ceramah&lt;/b&gt; bertopik pilihanmu secara kreatif, langsung di halaman ini. Sistem menilai tulisanmu secara &lt;b&gt;otomatis&lt;/b&gt; berdasarkan rubrik 100 poin. Kamu boleh memeriksa dan memperbaiki tulisanmu berulang kali sebelum mengunduh hasil akhir.&lt;/p&gt;

  &lt;div class="callout warn"&gt;&lt;b class="t"&gt;Ketentuan&lt;/b&gt;
  &lt;ol style="margin:4px 0 0"&gt;
    &lt;li&gt;Naskah &lt;b&gt;wajib diketik sendiri&lt;/b&gt;. Fitur tempel (paste) dan tarik-lepas teks dinonaktifkan.&lt;/li&gt;
    &lt;li&gt;Panjang naskah minimal &lt;b&gt;200 kata&lt;/b&gt;: pembukaan, isi (minimal &lt;b&gt;3 paragraf&lt;/b&gt;, pisahkan dengan menekan Enter), dan penutup.&lt;/li&gt;
    &lt;li&gt;Topik bebas dan harus sesuai dengan judul yang kamu tulis. Gunakan data atau sumber yang benar, jangan mengarang angka.&lt;/li&gt;
    &lt;li&gt;Setelah nilai muncul, &lt;b&gt;unduh&lt;/b&gt; bukti mengerjakan (PDF F4), &lt;b&gt;cetak&lt;/b&gt;, lalu &lt;b&gt;serahkan ke guru mata pelajaran&lt;/b&gt;.&lt;/li&gt;
  &lt;/ol&gt;&lt;/div&gt;

  &lt;h3&gt;Rubrik Penilaian Otomatis&lt;/h3&gt;
  &lt;div id="kbtRubrik"&gt;&lt;/div&gt;

  &lt;h3&gt;Identitas&lt;/h3&gt;
  &lt;div class="grid3"&gt;
    &lt;div&gt;&lt;label class="fl" for="kbtAbsen"&gt;No. Absen&lt;/label&gt;&lt;input type="number" id="kbtAbsen" min="1" placeholder="Contoh: 14"&gt;&lt;/div&gt;
    &lt;div&gt;&lt;label class="fl" for="kbtNama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="kbtNama" placeholder="Nama lengkapmu"&gt;&lt;/div&gt;
    &lt;div&gt;&lt;label class="fl" for="kbtKelas"&gt;Kelas&lt;/label&gt;&lt;input type="text" id="kbtKelas" placeholder="Contoh: XI TKR 1"&gt;&lt;/div&gt;
  &lt;/div&gt;

  &lt;h3&gt;Naskah Ceramah&lt;/h3&gt;
  &lt;label class="fl" for="kbtJudul"&gt;Judul / Topik Ceramah&lt;/label&gt;
  &lt;input type="text" id="kbtJudul" maxlength="120" placeholder="Contoh: Saring Sebelum Sharing: Cerdas Menghadapi Hoaks"&gt;
  &lt;div class="pwarn" id="kbtW0"&gt;Menempel (paste) tidak diperbolehkan. Silakan ketik sendiri.&lt;/div&gt;
  &lt;div style="margin-top:6px"&gt;&lt;span style="font-size:12.5px;color:var(--ink2)"&gt;Butuh ide topik? Klik salah satu:&lt;/span&gt;&lt;br&gt;
    &lt;button type="button" class="btn chip" data-topik="Disiplin Kerja Bekal Menuju Dunia Industri"&gt;Disiplin Kerja&lt;/button&gt;
    &lt;button type="button" class="btn chip" data-topik="Waspada Pinjaman Online Ilegal bagi Generasi Muda"&gt;Pinjaman Online Ilegal&lt;/button&gt;
    &lt;button type="button" class="btn chip" data-topik="Budaya Keselamatan dan Kesehatan Kerja di Bengkel Sekolah"&gt;Keselamatan Kerja&lt;/button&gt;
    &lt;button type="button" class="btn chip" data-topik="Bijak Bermedia Sosial di Era Digital"&gt;Bijak Bermedia Sosial&lt;/button&gt;
    &lt;button type="button" class="btn chip" data-topik="Berani Berwirausaha Sejak Bangku SMK"&gt;Wirausaha Siswa SMK&lt;/button&gt;
    &lt;button type="button" class="btn chip" data-topik="Jaga Kebersihan Lingkungan Sekolah Kita"&gt;Kebersihan Lingkungan&lt;/button&gt;
  &lt;/div&gt;

  &lt;label class="fl" for="kbtPem"&gt;1. Pembukaan (salam, sapaan, pengait, pengantar topik)&lt;/label&gt;
  &lt;textarea id="kbtPem" maxlength="900" placeholder="Ketik pembukaan ceramahmu di sini (tempel/paste tidak diperbolehkan)..."&gt;&lt;/textarea&gt;
  &lt;div class="cnt" id="kbtC1"&gt;0 kata&lt;/div&gt;&lt;div class="pwarn" id="kbtW1"&gt;Menempel (paste) tidak diperbolehkan. Silakan ketik sendiri.&lt;/div&gt;

  &lt;label class="fl" for="kbtIsi"&gt;2. Isi (minimal 3 paragraf, tekan Enter untuk paragraf baru)&lt;/label&gt;
  &lt;textarea id="kbtIsi" maxlength="3000" style="min-height:240px" placeholder="Ketik isi ceramahmu di sini: tiga gagasan pokok beserta pendukungnya..."&gt;&lt;/textarea&gt;
  &lt;div class="cnt" id="kbtC2"&gt;0 kata&lt;/div&gt;&lt;div class="pwarn" id="kbtW2"&gt;Menempel (paste) tidak diperbolehkan. Silakan ketik sendiri.&lt;/div&gt;

  &lt;label class="fl" for="kbtPen"&gt;3. Penutup (simpulan, harapan, ajakan, terima kasih, salam)&lt;/label&gt;
  &lt;textarea id="kbtPen" maxlength="900" placeholder="Ketik penutup ceramahmu di sini..."&gt;&lt;/textarea&gt;
  &lt;div class="cnt" id="kbtC3"&gt;0 kata&lt;/div&gt;&lt;div class="pwarn" id="kbtW3"&gt;Menempel (paste) tidak diperbolehkan. Silakan ketik sendiri.&lt;/div&gt;

  &lt;div style="font-size:14px;font-weight:700;margin-top:10px"&gt;Total: &lt;span id="kbtTotal"&gt;0&lt;/span&gt; kata (target minimal 200)&lt;/div&gt;

  &lt;div class="err" id="kbtErr"&gt;&lt;/div&gt;

  &lt;button type="button" class="btn pri" id="kbtBtnNilai"&gt;Nilai Tulisan Saya&lt;/button&gt;
  &lt;button type="button" class="btn ghost" id="kbtBtnReset"&gt;Hapus Semua&lt;/button&gt;

  &lt;div id="kbtHasil" class="hidden"&gt;
    &lt;div id="kbtResBox"&gt;&lt;/div&gt;
    &lt;div id="kbtGroups"&gt;&lt;/div&gt;
    &lt;h3&gt;Rincian per Indikator&lt;/h3&gt;
    &lt;div id="kbtItems"&gt;&lt;/div&gt;
    &lt;div class="callout info"&gt;&lt;b class="t"&gt;Catatan&lt;/b&gt;Penilaian otomatis memeriksa penanda kaidah kebahasaan dan struktur pada tulisanmu. Perbaiki indikator yang belum tercapai lalu tekan Nilai Tulisan Saya lagi. Guru tetap dapat meninjau kualitas isi tulisanmu secara langsung.&lt;/div&gt;
    &lt;button type="button" class="btn grn" id="kbtBtnPdf"&gt;Unduh Bukti Mengerjakan (PDF F4)&lt;/button&gt;
    &lt;p style="font-size:13px;color:var(--ink2);margin-top:8px"&gt;Pada jendela cetak, pilih &lt;b&gt;Simpan sebagai PDF&lt;/b&gt; (ukuran kertas F4 atau 215 x 330 mm sudah diatur otomatis). Cetak dokumen tersebut, lalu &lt;b&gt;serahkan ke guru mata pelajaran&lt;/b&gt; sebagai bukti pengerjaan.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;&lt;!-- /body --&gt;

&lt;div class="foot"&gt;DOK/B.INDO/XI/CERAMAH-02 - Kaidah Kebahasaan Teks Ceramah (Bagian 2) dan Menulis Teks Ceramah&lt;/div&gt;
&lt;/div&gt;&lt;!-- /kbt --&gt;

&lt;script&gt;
(function(){
var GAS_URL = ""; /* Opsional: isi dengan alamat Google Apps Script untuk mengirim skor ke guru. Kosongkan jika tidak dipakai. */

/*ENGINE-START*/
var GROUPS = {
  A:'Struktur Teks',
  B:'Kaidah Kebahasaan',
  C:'Gagasan Pokok dan Pendukung',
  D:'Kreativitas dan Gaya Bahasa',
  E:'Panjang dan Kerapian'
};
var SALAM = ["assalamualaikum","assalamu'alaikum","salam sejahtera","selamat pagi","selamat siang","selamat sore","selamat malam","om swastiastu","shalom","namo buddhaya","salam damai","salam hormat","salam kebajikan"];
var SAPAAN = ["hadirin","bapak","ibu","saudara","saudari","teman","adik","rekan","sahabat","yang terhormat","yang saya hormati","yang kami hormati","yang saya banggakan","yang saya cintai","yang saya sayangi","para guru","para siswa","para peserta"];
var AJAKAN = ["mari","marilah","ayo","ayolah","hendaknya","hendaklah","janganlah","jangan","mohon","harap","sebaiknya","periksalah","jagalah","ingatlah","cobalah","belajarlah","perhatikanlah","camkanlah","bersikaplah","gunakanlah","lakukanlah","hindarilah","tingkatkanlah","biasakanlah","jadikanlah","bersatulah","bersyukurlah","laksanakanlah"];
var KONJ = ["oleh karena itu","oleh sebab itu","dengan demikian","akan tetapi","selain itu","lagi pula","akibatnya","sehingga","namun","tetapi","sebaliknya","bahkan","apabila","jika","seandainya","sedangkan","jadi","karena","sebab"];
var MENTAL = ["menyadari","sadar","meyakini","yakin","percaya","berpendapat","menyimpulkan","merasa","memahami","mengetahui","berharap","mengharapkan","mengimbau","menurut saya","saya rasa","saya kira"];
var CONTOH = ["misalnya","contohnya","contoh","umpamanya","semisal","sebagai contoh"];
var URUTAN = ["pertama-tama","pertama","kedua","ketiga","keempat","selanjutnya","berikutnya","terakhir","kemudian","lalu"];
var MAJAS = ["ibarat","ibaratnya","bagaikan","bagai","laksana","bak","umpama","umpamanya","pepatah","peribahasa"];
var TIDAKBAKU = ["gak","nggak","ngga","enggak","banget","aja","udah","nih","dong","sih","kayak","gimana","emang","cuma","kok","deh","lho","loh","gue","lo","lu","pake","bikin","tapi"];
var STOP = ["yang","dengan","untuk","dalam","pada","dari","bagi","sebagai","adalah","agar","tentang","serta","atau","kita","cara","akan","dapat","lebih","antara","oleh","kami","saya"];
var SUMBER_RE = /(\bmenurut (?!saya|aku)[a-z]+|\bberdasarkan\b|\bdata\b|\bsurvei\b|\bpenelitian\b|\briset\b|\blaporan\b|\bkementerian\b|\bbadan\b|\blembaga\b|\bstatistik\b|\bpersen\b|%|\b(19|20)[0-9]{2}\b)/i;

function kbtRe(list, flags){ return new RegExp('\\b(?:' + list.join('|') + ')\\b', flags || 'gi'); }
function kbtCount(t, list){ var m = t.match(kbtRe(list)); return m ? m.length : 0; }
function kbtDistinct(t, list){
  var s = t.toLowerCase(), found = [];
  list.forEach(function(p){
    var re = new RegExp('\\b' + p + '\\b','g');
    if(re.test(s)){ found.push(p); re.lastIndex = 0; s = s.replace(re,' '); }
  });
  return found;
}
function kbtWords(t){ var m = (t || '').trim().match(/[A-Za-z0-9']+/g); return m ? m.length : 0; }
function kbtSentences(t){
  var m = (t || '').replace(/\s+/g,' ').match(/[^.!?]+[.!?]*/g) || [];
  return m.map(function(s){ return s.trim(); }).filter(function(s){ return s.length &gt; 2; });
}
function kbtParas(t){
  return (t || '').split(/\n+/).map(function(s){ return s.trim(); }).filter(function(s){ return s.length &gt; 0; });
}
function kbtKeywords(judul){
  var w = (judul || '').toLowerCase().match(/[a-z]{4,}/g) || [];
  var out = [];
  w.forEach(function(x){ if(STOP.indexOf(x) &lt; 0 &amp;&amp; out.indexOf(x) &lt; 0) out.push(x); });
  return out;
}

var IND = [
 {g:'A', label:'Salam pembuka pada bagian pembukaan', max:5,
  tip:'Awali ceramah dengan salam, misalnya Assalamualaikum atau Selamat pagi.',
  fn:function(c){ var m = c.pem.match(kbtRe(SALAM)); return [m ? 5 : 0, m ? 'Ditemukan salam: ' + m[0] : 'Belum ada salam pembuka.']; }},
 {g:'A', label:'Topik disebut jelas di pembukaan', max:5,
  tip:'Sebutkan topik ceramah (kata kunci judul) pada pembukaan.',
  fn:function(c){ var lp = c.pem.toLowerCase(); var h = c.kw.filter(function(k){ return lp.indexOf(k) &gt;= 0; }); return [h.length ? 5 : 0, h.length ? 'Kata kunci di pembukaan: ' + h.slice(0,3).join(', ') : 'Kata kunci judul belum muncul di pembukaan.']; }},
 {g:'A', label:'Isi terdiri atas minimal 3 paragraf', max:5,
  tip:'Pisahkan isi menjadi tiga paragraf atau lebih dengan menekan Enter.',
  fn:function(c){ var n = c.paras.length; return [n &gt;= 3 ? 5 : (n === 2 ? 3 : (n === 1 ? 1 : 0)), 'Jumlah paragraf isi: ' + n]; }},
 {g:'A', label:'Penutup memuat simpulan/harapan dan salam/terima kasih', max:5,
  tip:'Tutup dengan simpulan atau harapan, lalu terima kasih dan salam penutup.',
  fn:function(c){
    var a = /\b(demikian|semoga|harapan|berharap|mengharapkan|simpulan|kesimpulan|akhir kata|sekian)\b/i.test(c.pen);
    var b = /(terima kasih|wassalam|salam|sampai jumpa|om shanti)/i.test(c.pen);
    return [(a &amp;&amp; b) ? 5 : ((a || b) ? 3 : 0), 'Simpulan/harapan: ' + (a ? 'ada' : 'belum') + ', salam/terima kasih: ' + (b ? 'ada' : 'belum')];
  }},

 {g:'B', label:'Kata sapaan kepada pendengar di pembukaan', max:5,
  tip:'Sapa pendengar, misalnya Bapak dan Ibu guru serta teman-teman yang saya hormati.',
  fn:function(c){ var m = c.pem.match(kbtRe(SAPAAN)); return [m ? 5 : 0, m ? 'Ditemukan sapaan: ' + m[0] : 'Belum ada kata sapaan.']; }},
 {g:'B', label:'Kata ganti kita atau kami (minimal 3 kali)', max:5,
  tip:'Gunakan kata kita atau kami untuk membangun kedekatan dengan pendengar.',
  fn:function(c){ var n = kbtCount(c.all, ["kita","kami"]); return [n &gt;= 3 ? 5 : (n &gt;= 1 ? 3 : 0), 'Jumlah kita/kami: ' + n]; }},
 {g:'B', label:'Kalimat ajakan atau persuasif (minimal 2)', max:5,
  tip:'Tambahkan ajakan dengan penanda seperti marilah, mari kita, hendaknya, atau janganlah.',
  fn:function(c){ var n = kbtCount(c.all, AJAKAN); return [n &gt;= 2 ? 5 : (n === 1 ? 3 : 0), 'Penanda ajakan ditemukan: ' + n]; }},
 {g:'B', label:'Konjungsi penalaran (minimal 3 jenis berbeda)', max:5,
  tip:'Gunakan konjungsi seperti karena, sehingga, oleh karena itu, dengan demikian, namun, atau jika.',
  fn:function(c){ var f = kbtDistinct(c.all, KONJ); return [f.length &gt;= 3 ? 5 : (f.length === 2 ? 3 : (f.length === 1 ? 1 : 0)), 'Konjungsi ditemukan: ' + (f.length ? f.slice(0,5).join(', ') : 'tidak ada')]; }},
 {g:'B', label:'Kata kerja mental atau ungkapan sikap (minimal 2 jenis)', max:5,
  tip:'Nyatakan sikapmu dengan kata seperti menyadari, meyakini, berpendapat, menyimpulkan, atau berharap.',
  fn:function(c){ var f = kbtDistinct(c.all, MENTAL); return [f.length &gt;= 2 ? 5 : (f.length === 1 ? 3 : 0), 'Ditemukan: ' + (f.length ? f.slice(0,4).join(', ') : 'tidak ada')]; }},
 {g:'B', label:'Istilah dan kata kunci topik konsisten di seluruh teks', max:5,
  tip:'Ulangi kata kunci dari judul secara wajar di seluruh teks agar ceramah tetap fokus pada topik.',
  fn:function(c){
    var low = c.all.toLowerCase(), n = 0;
    c.kw.forEach(function(k){ var m = low.match(new RegExp(k,'g')); n += m ? m.length : 0; });
    return [n &gt;= 4 ? 5 : (n &gt;= 2 ? 3 : (n &gt;= 1 ? 1 : 0)), 'Kemunculan kata kunci judul: ' + n + ' kali'];
  }},
 {g:'B', label:'Menggunakan kata baku (tanpa kata percakapan)', max:5,
  tip:'Ganti kata tidak baku dengan kata baku, misalnya gak menjadi tidak, banget menjadi sekali.',
  fn:function(c){ var f = kbtDistinct(c.all, TIDAKBAKU); return [f.length === 0 ? 5 : (f.length &lt;= 2 ? 3 : 0), f.length ? 'Kata tidak baku: ' + f.join(', ') : 'Tidak ditemukan kata tidak baku.']; }},

 {g:'C', label:'Gagasan pendukung berupa contoh', max:5,
  tip:'Tambahkan contoh nyata pada isi dengan kata penanda seperti misalnya atau contohnya.',
  fn:function(c){ var n = kbtCount(c.isi, CONTOH); return [n &gt;= 1 ? 5 : 0, 'Penanda contoh pada isi: ' + n]; }},
 {g:'C', label:'Gagasan pendukung berupa data atau sumber', max:5,
  tip:'Sertakan data atau sumber yang jelas, misalnya menurut lembaga resmi, berdasarkan survei, atau tahun tertentu.',
  fn:function(c){ var ok = SUMBER_RE.test(c.isi); return [ok ? 5 : 0, ok ? 'Penanda data/sumber ditemukan pada isi.' : 'Belum ada penanda data atau sumber pada isi.']; }},
 {g:'C', label:'Penanda urutan gagasan pokok (pertama, kedua, dan seterusnya)', max:5,
  tip:'Awali tiap paragraf isi dengan penanda urutan: pertama, kedua, ketiga, selanjutnya, atau terakhir.',
  fn:function(c){ var f = kbtDistinct(c.isi, URUTAN); return [f.length &gt;= 2 ? 5 : (f.length === 1 ? 3 : 0), 'Penanda urutan: ' + (f.length ? f.join(', ') : 'tidak ada')]; }},
 {g:'C', label:'Paragraf isi berkembang (minimal 2 kalimat per paragraf)', max:5,
  tip:'Kembangkan setiap paragraf isi dengan kalimat utama dan kalimat penjelas (rumus P-B-P-A).',
  fn:function(c){
    if(!c.paras.length) return [0, 'Belum ada paragraf isi.'];
    var good = c.paras.filter(function(p){ return kbtSentences(p).length &gt;= 2; }).length;
    var r = good / c.paras.length;
    return [r &gt;= 0.67 ? 5 : (r &gt;= 0.34 ? 3 : 0), good + ' dari ' + c.paras.length + ' paragraf memiliki minimal 2 kalimat'];
  }},

 {g:'D', label:'Kalimat retoris (kalimat tanya penggugah)', max:5,
  tip:'Tambahkan satu pertanyaan retoris, misalnya Pernahkah kita membayangkan ...?',
  fn:function(c){ var n = (c.all.match(/\?/g) || []).length; return [n &gt;= 1 ? 5 : 0, 'Jumlah kalimat tanya: ' + n]; }},
 {g:'D', label:'Majas atau peribahasa (perumpamaan kreatif)', max:5,
  tip:'Gunakan perumpamaan dengan ibarat, bagaikan, atau laksana, atau sisipkan peribahasa.',
  fn:function(c){ var m = c.all.match(kbtRe(MAJAS)); return [m ? 5 : 0, m ? 'Penanda majas: ' + m[0] : 'Belum ada majas atau peribahasa.']; }},
 {g:'D', label:'Penegasan: seruan atau pengulangan (repetisi)', max:5,
  tip:'Tutup dengan seruan atau slogan berakhiran tanda seru, atau ulangi kata di awal tiga kalimat (anafora).',
  fn:function(c){
    var ex = (c.all.match(/!/g) || []).length;
    var map = {}, best = 0, bw = '';
    c.sents.forEach(function(s){ var w = (s.toLowerCase().match(/[a-z]+/) || [''])[0]; if(w.length &gt;= 3){ map[w] = (map[w] || 0) + 1; if(map[w] &gt; best){ best = map[w]; bw = w; } } });
    var ok = ex &gt;= 1 || best &gt;= 3;
    return [ok ? 5 : 0, ex &gt;= 1 ? 'Ada seruan (tanda seru): ' + ex : (best &gt;= 3 ? 'Pengulangan kata awal kalimat: ' + bw : 'Belum ada seruan atau repetisi.')];
  }},

 {g:'E', label:'Panjang naskah (minimal 200 kata)', max:5,
  tip:'Tambahkan gagasan pendukung, contoh, atau penjelasan hingga naskah mencapai minimal 200 kata.',
  fn:function(c){ var w = c.words; return [w &gt;= 200 ? 5 : (w &gt;= 150 ? 3 : (w &gt;= 100 ? 1 : 0)), 'Jumlah kata: ' + w]; }},
 {g:'E', label:'Ejaan: huruf kapital awal kalimat dan tanda baca akhir', max:5,
  tip:'Awali setiap kalimat dengan huruf kapital dan akhiri setiap paragraf dengan tanda baca (titik, tanya, atau seru).',
  fn:function(c){
    var tot = c.sents.length, okc = 0;
    c.sents.forEach(function(s){ if(/^[A-Z0-9"'(]/.test(s)) okc++; });
    var ratio = tot ? okc / tot : 0;
    var blocks = [c.pem].concat(c.paras).concat([c.pen]);
    var punct = blocks.every(function(b){ return /[.!?]["')]?$/.test(b.trim()); });
    var capOk = ratio &gt;= 0.9;
    return [(capOk &amp;&amp; punct) ? 5 : ((capOk || punct) ? 3 : 0), 'Kalimat berawal kapital: ' + okc + ' dari ' + tot + '; akhir paragraf bertanda baca: ' + (punct ? 'ya' : 'belum semua')];
  }}
];

function kbtEvaluate(judul, pem, isi, pen){
  var all = pem + '\n' + isi + '\n' + pen;
  var c = {judul:judul, pem:pem, isi:isi, pen:pen, all:all, paras:kbtParas(isi), kw:kbtKeywords(judul), sents:kbtSentences(all), words:kbtWords(all)};
  var items = [], gs = {}, total = 0;
  Object.keys(GROUPS).forEach(function(k){ gs[k] = {score:0, max:0}; });
  IND.forEach(function(it){
    var r = it.fn(c);
    items.push({g:it.g, label:it.label, max:it.max, score:r[0], detail:r[1], tip:it.tip});
    gs[it.g].score += r[0]; gs[it.g].max += it.max; total += r[0];
  });
  var capped = false;
  if(c.words &lt; 100 &amp;&amp; total &gt; 60){ total = 60; capped = true; }
  var cat = total &gt;= 90 ? 'Sangat Baik' : (total &gt;= 80 ? 'Baik' : (total &gt;= 75 ? 'Cukup Baik (mencapai KKM)' : (total &gt;= 60 ? 'Perlu Ditingkatkan' : 'Belum Memadai')));
  return {total:total, cat:cat, items:items, groups:gs, words:c.words, paras:c.paras.length, sents:c.sents.length, capped:capped};
}
/*ENGINE-END*/

function $(id){ return document.getElementById(id); }
function esc(s){ var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }

/* ---- Rubrik statis dari IND ---- */
(function(){
  var h = '&lt;div class="tw"&gt;&lt;table class="tb"&gt;&lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Indikator&lt;/th&gt;&lt;th&gt;Skor&lt;/th&gt;&lt;/tr&gt;';
  Object.keys(GROUPS).forEach(function(k){
    var sum = 0, rows = '';
    IND.forEach(function(it){ if(it.g === k){ sum += it.max; rows += '&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;' + esc(it.label) + '&lt;/td&gt;&lt;td&gt;' + it.max + '&lt;/td&gt;&lt;/tr&gt;'; } });
    h += '&lt;tr&gt;&lt;td&gt;&lt;b&gt;' + esc(GROUPS[k]) + '&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;' + sum + '&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;' + rows;
  });
  h += '&lt;tr&gt;&lt;td&gt;&lt;b&gt;Total&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;100&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;';
  h += '&lt;p style="font-size:13px;color:var(--ink2)"&gt;KKM = 75. Jika naskah kurang dari 100 kata, nilai akhir dibatasi maksimal 60.&lt;/p&gt;';
  $('kbtRubrik').innerHTML = h;
})();

/* ---- Navigasi ---- */
Array.prototype.forEach.call(document.querySelectorAll('#kbtNav a'), function(a){
  a.addEventListener('click', function(e){
    e.preventDefault();
    var t = $(a.getAttribute('href').slice(1));
    if(t &amp;&amp; t.scrollIntoView) t.scrollIntoView({behavior:'smooth', block:'start'});
  });
});

/* ---- Editor: hitung kata dan blokir paste ---- */
var fields = [
  {el:'kbtPem', cnt:'kbtC1', warn:'kbtW1'},
  {el:'kbtIsi', cnt:'kbtC2', warn:'kbtW2'},
  {el:'kbtPen', cnt:'kbtC3', warn:'kbtW3'},
  {el:'kbtJudul', cnt:null, warn:'kbtW0'}
];
var downloaded = false;
function updateCounts(){
  var tot = 0;
  fields.forEach(function(f){
    if(!f.cnt) return;
    var n = kbtWords($(f.el).value);
    $(f.cnt).textContent = n + ' kata';
    tot += n;
  });
  $('kbtTotal').textContent = tot;
}
fields.forEach(function(f){
  var ta = $(f.el);
  var warn = function(){
    var w = $(f.warn); if(!w) return;
    w.classList.add('show');
    clearTimeout(w._t);
    w._t = setTimeout(function(){ w.classList.remove('show'); }, 3500);
  };
  ta.addEventListener('input', function(){ downloaded = false; updateCounts(); });
  ta.addEventListener('paste', function(e){ e.preventDefault(); warn(); });
  ta.addEventListener('drop', function(e){ e.preventDefault(); warn(); });
  ta.addEventListener('dragover', function(e){ e.preventDefault(); });
  ta.addEventListener('contextmenu', function(e){ e.preventDefault(); warn(); });
  ta.addEventListener('beforeinput', function(e){
    var t = e.inputType || '';
    if(t === 'insertFromPaste' || t === 'insertFromDrop' || t === 'insertFromYank' || t === 'insertFromPasteAsQuotation'){ e.preventDefault(); warn(); }
  });
  ta.addEventListener('keydown', function(e){
    var k = (e.key || '').toLowerCase();
    if(((e.ctrlKey || e.metaKey) &amp;&amp; k === 'v') || (e.shiftKey &amp;&amp; k === 'insert')){ e.preventDefault(); warn(); }
  });
});
Array.prototype.forEach.call(document.querySelectorAll('#kbt-tugas button[data-topik]'), function(b){
  b.addEventListener('click', function(){ $('kbtJudul').value = b.getAttribute('data-topik'); updateCounts(); });
});

/* ---- Penilaian ---- */
var last = null;
function showErr(msg){ var e = $('kbtErr'); e.textContent = msg; e.style.display = 'block'; }
function hideErr(){ $('kbtErr').style.display = 'none'; }

$('kbtBtnNilai').addEventListener('click', function(){
  var absen = $('kbtAbsen').value.trim(), nama = $('kbtNama').value.trim(), kelas = $('kbtKelas').value.trim();
  var judul = $('kbtJudul').value.trim(), pem = $('kbtPem').value.trim(), isi = $('kbtIsi').value.trim(), pen = $('kbtPen').value.trim();
  if(!absen || !nama || !kelas){ showErr('Isi No. Absen, Nama Lengkap, dan Kelas terlebih dahulu.'); return; }
  if(kbtWords(judul) &lt; 2){ showErr('Tulis judul atau topik ceramah (minimal 2 kata).'); return; }
  if(!pem || !isi || !pen){ showErr('Lengkapi pembukaan, isi, dan penutup ceramahmu sebelum dinilai.'); return; }
  hideErr();
  var r = kbtEvaluate(judul, pem, isi, pen);
  last = {absen:absen, nama:nama, kelas:kelas, judul:judul, pem:pem, isi:isi, pen:pen, res:r, time:new Date()};
  renderResult(r);
});

function renderResult(r){
  var pass = r.total &gt;= 75;
  $('kbtResBox').innerHTML = '&lt;div class="resbox ' + (pass ? 'pass' : 'fail') + '"&gt;&lt;div style="font-size:13px;color:var(--ink2);font-weight:700"&gt;Nilai Otomatis&lt;/div&gt;&lt;div class="big"&gt;' + r.total + '&lt;/div&gt;&lt;div class="cat"&gt;' + esc(r.cat) + '&lt;/div&gt;&lt;div class="meta"&gt;' + r.words + ' kata - ' + r.paras + ' paragraf isi - ' + r.sents + ' kalimat - KKM 75' + (r.capped ? ' - nilai dibatasi 60 karena naskah kurang dari 100 kata' : '') + '&lt;/div&gt;&lt;/div&gt;';
  var g = '';
  Object.keys(GROUPS).forEach(function(k){
    var s = r.groups[k], pct = s.max ? Math.round(s.score / s.max * 100) : 0;
    g += '&lt;div class="gbar"&gt;&lt;div class="gl"&gt;&lt;span&gt;' + esc(GROUPS[k]) + '&lt;/span&gt;&lt;span&gt;' + s.score + ' / ' + s.max + '&lt;/span&gt;&lt;/div&gt;&lt;div class="track"&gt;&lt;div class="fill" style="width:' + pct + '%"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
  });
  $('kbtGroups').innerHTML = g;
  var it = '';
  r.items.forEach(function(x){
    var cls = x.score &gt;= x.max ? 'ok' : (x.score &gt; 0 ? 'pt' : 'no');
    var lab = x.score &gt;= x.max ? 'Tercapai' : (x.score &gt; 0 ? 'Sebagian' : 'Belum');
    it += '&lt;div class="irow"&gt;&lt;span class="bd ' + cls + '"&gt;' + lab + '&lt;/span&gt;&lt;div class="tx"&gt;' + esc(x.label) + '&lt;small&gt;' + esc(x.detail) + (x.score &lt; x.max ? ' - Saran: ' + esc(x.tip) : '') + '&lt;/small&gt;&lt;/div&gt;&lt;span class="sc"&gt;' + x.score + ' / ' + x.max + '&lt;/span&gt;&lt;/div&gt;';
  });
  $('kbtItems').innerHTML = it;
  $('kbtHasil').classList.remove('hidden');
  $('kbtHasil').scrollIntoView({behavior:'smooth', block:'start'});
}

$('kbtBtnReset').addEventListener('click', function(){
  if(!window.confirm('Hapus seluruh isian dan hasil penilaian?')) return;
  ['kbtAbsen','kbtNama','kbtKelas','kbtJudul','kbtPem','kbtIsi','kbtPen'].forEach(function(id){ $(id).value = ''; });
  $('kbtHasil').classList.add('hidden');
  hideErr(); updateCounts(); last = null; downloaded = false;
});

/* ---- Kirim opsional ke Google Apps Script ---- */
var lastSentSig = '';
function sendToSheet(){
  if(!GAS_URL || !last) return;
  var r = last.res;
  var sig = last.absen + '|' + last.nama + '|' + r.total + '|' + (last.pem + last.isi + last.pen).length;
  if(sig === lastSentSig) return;
  lastSentSig = sig;
  try{
    var p = new URLSearchParams();
    var payload = {waktu:last.time.toISOString(), nama:last.nama, kelas:last.kelas, absen:last.absen, materi:'Menulis Teks Ceramah', judul:last.judul, nilaiAkhir:r.total, skorA:r.groups.A.score, skorB:r.groups.B.score, skorC:r.groups.C.score, skorD:r.groups.D.score, skorE:r.groups.E.score, jumlahKata:r.words, pembukaan:last.pem, isi:last.isi, penutup:last.pen};
    Object.keys(payload).forEach(function(k){ p.append(k, String(payload[k])); });
    fetch(GAS_URL, {method:'POST', mode:'no-cors', body:p}).catch(function(){});
  }catch(e){}
}

/* ---- PDF F4 (cetak) ---- */
var pa = document.getElementById('kbtPrint');
if(!pa){ pa = document.createElement('div'); pa.id = 'kbtPrint'; document.body.appendChild(pa); }

function buildPrint(){
  var d = last, r = d.res;
  var tgl = d.time.toLocaleDateString('id-ID', {day:'numeric', month:'long', year:'numeric'});
  var h = '';
  h += '&lt;div class="kop"&gt;&lt;h2&gt;Bukti Mengerjakan Tugas Individu - Menulis Teks Ceramah&lt;/h2&gt;&lt;p&gt;Mata Pelajaran Bahasa Indonesia - Kelas XI SMK - Kaidah Kebahasaan Teks Ceramah (Bagian 2) dan Menulis Teks Ceramah&lt;/p&gt;&lt;/div&gt;';
  h += '&lt;table class="idt"&gt;&lt;tr&gt;&lt;td class="l"&gt;Nama Lengkap&lt;/td&gt;&lt;td&gt;: ' + esc(d.nama) + '&lt;/td&gt;&lt;td class="l"&gt;No. Absen&lt;/td&gt;&lt;td&gt;: ' + esc(d.absen) + '&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="l"&gt;Kelas&lt;/td&gt;&lt;td&gt;: ' + esc(d.kelas) + '&lt;/td&gt;&lt;td class="l"&gt;Tanggal&lt;/td&gt;&lt;td&gt;: ' + esc(tgl) + '&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;';
  h += '&lt;div class="skor"&gt;&lt;div&gt;Nilai Akhir (otomatis)&lt;b&gt;' + r.total + ' / 100&lt;/b&gt;&lt;/div&gt;';
  Object.keys(GROUPS).forEach(function(k){ h += '&lt;div&gt;' + esc(GROUPS[k]) + '&lt;b&gt;' + r.groups[k].score + ' / ' + r.groups[k].max + '&lt;/b&gt;&lt;/div&gt;'; });
  h += '&lt;div&gt;Kategori&lt;b style="font-size:13px"&gt;' + esc(r.cat) + '&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;';
  h += '&lt;h3&gt;Naskah Ceramah Siswa&lt;/h3&gt;';
  h += '&lt;div class="jdl"&gt;' + esc(d.judul) + '&lt;/div&gt;';
  h += '&lt;div class="lb"&gt;PEMBUKAAN&lt;/div&gt;&lt;div class="tk"&gt;' + esc(d.pem) + '&lt;/div&gt;';
  h += '&lt;div class="lb"&gt;ISI&lt;/div&gt;&lt;div class="tk"&gt;' + esc(d.isi) + '&lt;/div&gt;';
  h += '&lt;div class="lb"&gt;PENUTUP&lt;/div&gt;&lt;div class="tk"&gt;' + esc(d.pen) + '&lt;/div&gt;';
  h += '&lt;div class="nt"&gt;Jumlah kata: ' + r.words + ' - Paragraf isi: ' + r.paras + ' - Kalimat: ' + r.sents + '&lt;/div&gt;';
  h += '&lt;h3&gt;Rincian Penilaian Otomatis&lt;/h3&gt;&lt;table class="rb"&gt;&lt;tr&gt;&lt;th style="width:26px"&gt;No&lt;/th&gt;&lt;th&gt;Indikator&lt;/th&gt;&lt;th style="width:60px"&gt;Skor&lt;/th&gt;&lt;th&gt;Keterangan&lt;/th&gt;&lt;/tr&gt;';
  var no = 0;
  Object.keys(GROUPS).forEach(function(k){
    h += '&lt;tr class="gh"&gt;&lt;td colspan="2"&gt;' + esc(GROUPS[k]) + '&lt;/td&gt;&lt;td&gt;' + r.groups[k].score + ' / ' + r.groups[k].max + '&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;';
    r.items.forEach(function(x){ if(x.g === k){ no++; h += '&lt;tr&gt;&lt;td&gt;' + no + '&lt;/td&gt;&lt;td&gt;' + esc(x.label) + '&lt;/td&gt;&lt;td&gt;' + x.score + ' / ' + x.max + '&lt;/td&gt;&lt;td&gt;' + esc(x.detail) + '&lt;/td&gt;&lt;/tr&gt;'; } });
  });
  h += '&lt;/table&gt;';
  h += '&lt;div class="ttd"&gt;&lt;div&gt;Peserta Didik&lt;div class="gr"&gt;' + esc(d.nama) + '&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Guru Mata Pelajaran&lt;div class="gr"&gt;.............................&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
  h += '&lt;p class="nt"&gt;&lt;b&gt;Dokumen ini wajib dicetak dan diserahkan kepada guru mata pelajaran Bahasa Indonesia&lt;/b&gt; sebagai bukti fisik pengerjaan tugas. Nilai dihitung otomatis berdasarkan rubrik 100 poin (KKM 75); guru dapat meninjau kembali kualitas isi naskah.&lt;/p&gt;';
  pa.innerHTML = h;
}

$('kbtBtnPdf').addEventListener('click', function(){
  if(!last){ window.alert('Tekan Nilai Tulisan Saya terlebih dahulu.'); return; }
  buildPrint();
  sendToSheet();
  downloaded = true;
  setTimeout(function(){ window.print(); }, 150);
});

window.addEventListener('beforeunload', function(e){
  var any = $('kbtPem').value || $('kbtIsi').value || $('kbtPen').value;
  if(any &amp;&amp; !downloaded){ e.preventDefault(); e.returnValue = ''; }
});

updateCounts();
})();
&lt;/script&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/kaidah-kebahasaan-teks-ceramah-bagian-2.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-3146553586869073198</guid><pubDate>Tue, 06 Oct 2026 00:37:56 +0000</pubDate><atom:updated>2026-10-07T15:32:31.276+07:00</atom:updated><title>Pengenalan Aplikasi Pengolah Angka (Spreadsheet)</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Pengenalan Aplikasi Pengolah Angka (Spreadsheet) - Informatika Kelas X SMK&lt;/title&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&amp;family=IBM+Plex+Sans:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/jspdf-autotable@3.8.2/dist/jspdf.plugin.autotable.min.js"&gt;&lt;/script&gt;
&lt;style&gt;
  :root{
    --brand:#15803D; --brand-dark:#0D5A2A; --surface:#EEF7F1; --paper:#FFFFFF;
    --ink:#16221B; --ink-soft:#4C5C52; --line:#D7E6DC;
    --amber:#D99A1B; --green:#15803D; --red:#D6412C;
  }
  *{box-sizing:border-box; margin:0; padding:0;}
  html{scroll-behavior:smooth;}
  body{font-family:'IBM Plex Sans',sans-serif; background:#F3F7F4; color:var(--ink); line-height:1.6;}
  .mono{font-family:'IBM Plex Mono',monospace;}

  .page-shell{max-width:1100px; margin:16px auto; background:#FFFFFF; border-radius:14px;
    box-shadow:0 8px 32px rgba(10,40,20,.14),0 1px 3px rgba(10,40,20,.08); border:1px solid var(--line);}

  header.hero{background:var(--brand); color:#fff; padding:34px 5% 30px; border-radius:14px 14px 0 0; position:relative; overflow:hidden;}
  header.hero::before{content:""; position:absolute; inset:0; background-image:
    linear-gradient(rgba(255,255,255,.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.07) 1px, transparent 1px);
    background-size:28px 28px; pointer-events:none;}
  header.hero &gt; *{position:relative; z-index:1;}
  .kop{text-align:center; font-family:'IBM Plex Mono',monospace; font-size:.72rem; letter-spacing:.12em; text-transform:uppercase; color:#BFEBCF; margin-bottom:8px;}
  h1.title{font-family:'IBM Plex Mono',monospace; font-weight:700; font-size:clamp(1.5rem,3.6vw,2.3rem); text-align:center; line-height:1.3;}
  .subtitle{text-align:center; margin-top:10px; color:#DFF3E6; font-size:.98rem; max-width:70ch; margin-left:auto; margin-right:auto;}
  .meta-strip{margin-top:26px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:1px; background:rgba(255,255,255,.18); border:1px solid rgba(255,255,255,.18);}
  .meta-cell{background:var(--brand-dark); padding:12px 14px; text-align:center;}
  .meta-cell .k{font-family:'IBM Plex Mono',monospace; font-size:.62rem; letter-spacing:.08em; text-transform:uppercase; color:#9FDFB5;}
  .meta-cell .v{margin-top:4px; font-size:.9rem; font-weight:700;}

  main{padding:0;}
  .section{padding:32px 5%; border-bottom:1px solid #ECEFF3;}
  .section:last-child{border-bottom:none;}
  .section-tag{font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-soft); margin-bottom:8px;}
  h2.section-title{font-family:'IBM Plex Mono',monospace; font-size:clamp(1.15rem,2vw,1.5rem); font-weight:700; color:var(--brand-dark); margin-bottom:14px;}
  h3.sub-title{font-family:'IBM Plex Mono',monospace; font-size:1rem; font-weight:600; color:var(--brand-dark); margin:20px 0 10px; display:flex; align-items:center; gap:9px;}
  h3.sub-title::before{content:""; width:8px; height:8px; background:var(--amber); display:inline-block; border-radius:1px;}
  p{color:#33403A; max-width:80ch; margin-bottom:10px;}
  ul,ol{margin:6px 0 14px 1.3em; max-width:80ch;}
  li{margin-bottom:6px; color:#33403A;}
  strong{color:var(--brand-dark);}

  .callout{border-left:3px solid var(--brand); background:var(--surface); padding:14px 18px; border-radius:0 8px 8px 0; margin:14px 0; max-width:80ch;}
  .callout .lbl{font-family:'IBM Plex Mono',monospace; font-size:.68rem; letter-spacing:.1em; text-transform:uppercase; color:var(--brand-dark); font-weight:700; margin-bottom:6px;}
  .callout.warn{border-left-color:var(--red); background:#FCEBE8;}
  .callout.warn .lbl{color:#9B3324;}

  .card-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:14px; margin:16px 0 6px;}
  .card{background:#FFFFFF; border:1.5px solid var(--line); border-radius:8px; padding:16px 18px; box-shadow:0 2px 8px rgba(10,40,20,.05);}
  .card .tag{font-family:'IBM Plex Mono',monospace; font-size:.64rem; letter-spacing:.1em; text-transform:uppercase; color:#fff; background:var(--brand); padding:3px 8px; border-radius:3px; display:inline-block; margin-bottom:10px;}
  .card h4{font-family:'IBM Plex Mono',monospace; font-size:.94rem; margin-bottom:6px; color:var(--brand-dark);}
  .card p{font-size:.87rem; margin-bottom:0;}
  .card code{font-family:'IBM Plex Mono',monospace; background:var(--surface); padding:1px 6px; border-radius:3px; font-size:.85em; color:var(--brand-dark);}

  .table-scroll{overflow-x:auto; margin:16px 0;}
  table.compare{width:100%; border-collapse:collapse; font-size:.88rem; min-width:460px;}
  table.compare th, table.compare td{border:1px solid var(--line); padding:10px 12px; text-align:left;}
  table.compare th{background:var(--brand); color:#fff; font-family:'IBM Plex Mono',monospace; font-size:.72rem; letter-spacing:.04em; text-transform:uppercase;}
  table.compare tr:nth-child(even) td{background:var(--surface);}

  .summary-box{background:var(--brand-dark); color:#fff; border-radius:8px; padding:22px 24px; margin-top:8px;}
  .summary-box h4{font-family:'IBM Plex Mono',monospace; color:#B8ECC9; font-size:.82rem; letter-spacing:.08em; text-transform:uppercase; margin-bottom:12px;}
  .summary-box ul{margin:0 0 0 1.1em; max-width:none;}
  .summary-box li{color:#E3F4E9; margin-bottom:7px;}

  .task-panel{border:1.5px dashed var(--brand); border-radius:8px; padding:24px 26px; background:repeating-linear-gradient(135deg,#FFFFFF,#FFFFFF 18px,var(--surface) 18px,var(--surface) 19px);}
  .task-head{display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:12px;}
  .task-head .badge{font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.08em; text-transform:uppercase; background:var(--amber); color:#3A2A00; padding:4px 10px; border-radius:3px; font-weight:700;}
  .task-panel h3{font-family:'IBM Plex Mono',monospace; font-size:1.2rem; color:var(--brand-dark);}
  .steps-num{counter-reset:step; margin:14px 0; max-width:80ch;}
  .steps-num li{counter-increment:step; list-style:none; position:relative; padding-left:32px; margin-bottom:12px;}
  .steps-num li::before{content:counter(step); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; font-weight:700; color:#fff; background:var(--brand); width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:.78rem;}
  table.rubrik{width:100%; border-collapse:collapse; margin-top:16px; font-size:.86rem;}
  table.rubrik th, table.rubrik td{border:1px solid #C9D6CC; padding:9px 11px; text-align:left;}
  table.rubrik th{background:var(--brand-dark); color:#fff; font-family:'IBM Plex Mono',monospace; font-size:.73rem; letter-spacing:.04em; text-transform:uppercase;}
  table.rubrik tr:nth-child(even) td{background:#EFF5F0;}

  .field label{display:block; font-family:'IBM Plex Mono',monospace; font-size:.68rem; letter-spacing:.06em; text-transform:uppercase; color:var(--brand-dark); margin-bottom:6px;}
  .field input,.field select{width:100%; padding:10px 12px; border-radius:6px; border:1px solid var(--line); background:#fff; font-size:.92rem;}
  .student-info{display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:14px; margin:14px 0;}

  .primary-btn{font-family:'IBM Plex Mono',monospace; font-weight:600; font-size:.9rem; padding:13px 26px; border-radius:7px; border:none; cursor:pointer; background:var(--brand); color:#fff;}
  .primary-btn:hover{filter:brightness(1.08);}
  .primary-btn.ghost{background:#fff; color:var(--brand); border:1.5px solid var(--brand);}
  .primary-btn:disabled{opacity:.5; cursor:not-allowed;}

  .q-card{background:#FBFCFB; border:1px solid var(--line); border-radius:8px; padding:16px 18px; margin-bottom:12px;}
  .q-num{font-family:'IBM Plex Mono',monospace; font-size:.68rem; color:var(--brand); letter-spacing:.06em; margin-bottom:6px;}
  .q-text{font-size:.95rem; color:var(--ink); margin-bottom:10px; font-weight:500;}
  .options{display:flex; flex-direction:column; gap:7px;}
  .opt{display:flex; gap:9px; padding:8px 11px; border:1px solid var(--line); border-radius:6px; cursor:pointer; background:#fff;}
  .opt:hover{border-color:var(--brand);}
  .opt.selected{border-color:var(--brand); background:#E9F7EE;}
  .opt input{margin-top:3px; accent-color:var(--brand);}
  .opt span{font-size:.88rem; color:#33403A;}

  .excel-grid{border-collapse:collapse; margin:10px 0 14px; font-family:'IBM Plex Mono',monospace; font-size:.82rem;}
  .excel-grid th, .excel-grid td{border:1px solid #BFD4C6; padding:6px 10px; text-align:center; min-width:70px;}
  .excel-grid th{background:#E3F0E7; color:var(--brand-dark); font-weight:700;}
  .excel-grid th.rownum{background:#E3F0E7; min-width:30px;}
  .excel-grid td.label-cell{text-align:left; background:#F7FAF8; color:var(--ink-soft); font-family:'IBM Plex Sans',sans-serif;}
  .excel-grid td.target-cell{background:#FFF6DC; border:2px dashed var(--amber); color:#8A6A1E; font-weight:700;}

  .sheet-grid td.g-label{text-align:left; background:#F7FAF8; color:var(--ink-soft); font-family:'IBM Plex Sans',sans-serif; font-size:.78rem; min-width:110px;}
  .sheet-input{width:100%; min-width:92px; border:1px solid var(--line); border-radius:4px; padding:5px 6px; font-family:'IBM Plex Mono',monospace; font-size:.8rem; text-align:right; background:#fff;}
  .sheet-input.formula{text-align:left; background:#FFFDF2;}
  .sheet-input:focus{outline:none; border-color:var(--brand); box-shadow:0 0 0 2px rgba(21,128,61,.18);}
  .sheet-input.err{border-color:var(--red); color:var(--red); background:#FCEBE8;}
  tr.hi-max td{background:#D9F2E1 !important; font-weight:700;}
  table.final-table td.cell-ok{background:#E3F7EA; color:#0D5A2A; font-weight:600;}
  table.final-table td.cell-bad{background:#FCE6E3; color:#9B3324; font-weight:600;}
  table.final-table td.cell-empty{background:#F3F3F3; color:#8A8A8A; font-style:italic;}
  .group-score-panel{display:none; margin-top:16px; background:#fff; border:1.5px solid var(--green); border-radius:8px; padding:20px 22px;}
  .group-actions{display:flex; gap:10px; flex-wrap:wrap; margin:14px 0;}

  .formula-input{width:100%; max-width:360px; padding:10px 12px; border-radius:6px; border:1.5px solid var(--line); font-family:'IBM Plex Mono',monospace; font-size:.92rem;}
  .formula-input:focus{outline:none; border-color:var(--brand); box-shadow:0 0 0 3px rgba(21,128,61,.14);}
  .formula-input.wrong-paste{border-color:var(--red);}

  .essay-box{width:100%; min-height:110px; border:1px solid var(--line); border-radius:6px; padding:12px 14px; font-family:'IBM Plex Sans',sans-serif; font-size:.94rem; resize:vertical;}
  .essay-box:focus{outline:none; border-color:var(--brand); box-shadow:0 0 0 3px rgba(21,128,61,.14);}

  .editor-meta{display:flex; justify-content:space-between; font-family:'IBM Plex Mono',monospace; font-size:.7rem; color:var(--ink-soft); margin-top:6px;}
  .flag{color:var(--red); font-weight:700; display:none;}
  .flag.show{display:inline;}

  .result-panel{display:none; margin-top:18px; background:#fff; border:1.5px solid var(--green); border-radius:8px; padding:22px 24px;}
  .score-lbl{font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-soft);}
  .score{font-family:'IBM Plex Mono',monospace; font-size:2.3rem; font-weight:700; color:var(--green);}
  #uploadMsg{font-family:'IBM Plex Mono',monospace; font-size:.82rem; color:#8A6A1E; margin-left:10px;}

  footer{background:var(--surface); text-align:center; padding:20px 5% 28px; font-family:'IBM Plex Mono',monospace; font-size:.72rem; color:var(--ink-soft); border-top:1px solid var(--line); border-radius:0 0 14px 14px;}

  @media(max-width:700px){header.hero{padding:26px 5% 24px;} .section{padding:24px 5%;}}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="page-shell"&gt;
&lt;header class="hero"&gt;
  &lt;div class="kop"&gt;Informatika - Fase E - Kelas X SMK&lt;/div&gt;
  &lt;h1 class="title"&gt;Pengenalan Aplikasi Pengolah Angka (Spreadsheet)&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Mengenal antarmuka spreadsheet, format sel, rumus dasar, fungsi matematika, logika, dan statistik sederhana untuk mengolah data secara cepat dan akurat.&lt;/p&gt;
  &lt;div class="meta-strip"&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Elemen CP&lt;/div&gt;&lt;div class="v"&gt;Pengolah Angka&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK Kelas X&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;1 Pertemuan - 4 JP&lt;/div&gt;&lt;/div&gt;
    &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Model&lt;/div&gt;&lt;div class="v"&gt;Penjelasan, Praktik &amp; Evaluasi&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;
  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="section-tag"&gt;Capaian &amp; Tujuan Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Ke Mana Materi Ini Menuju&lt;/h2&gt;
    &lt;p&gt;Murid mampu mengolah data menggunakan format sel, rumus dasar, fungsi matematika, logika, dan statistik sederhana pada aplikasi spreadsheet.&lt;/p&gt;
    &lt;h3 class="sub-title"&gt;Rincian Kegiatan (4 JP x 45 menit)&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Tahap&lt;/th&gt;&lt;th&gt;Kegiatan&lt;/th&gt;&lt;th&gt;Waktu&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pendahuluan&lt;/td&gt;&lt;td&gt;Apersepsi ("Bagaimana toko kecil menghitung ratusan transaksi tanpa kalkulator manual?"), penyampaian tujuan&lt;/td&gt;&lt;td&gt;15 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Eksplorasi Konsep&lt;/td&gt;&lt;td&gt;Penjelasan antarmuka spreadsheet, format sel, rumus dasar, fungsi matematika, logika, dan statistik&lt;/td&gt;&lt;td&gt;55 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Kerja Kelompok&lt;/td&gt;&lt;td&gt;Diskusi dan praktik membuat laporan data sederhana dengan spreadsheet&lt;/td&gt;&lt;td&gt;45 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Presentasi&lt;/td&gt;&lt;td&gt;Presentasi singkat tiap kelompok dan tanggapan guru&lt;/td&gt;&lt;td&gt;20 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Evaluasi Individu&lt;/td&gt;&lt;td&gt;Mengerjakan soal pilihan ganda, praktik rumus, dan esai secara mandiri&lt;/td&gt;&lt;td&gt;35 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Refleksi dan penguatan konsep&lt;/td&gt;&lt;td&gt;10 menit&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pengenalan"&gt;
    &lt;div class="section-tag"&gt;Konsep Dasar&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Apa Itu Aplikasi Pengolah Angka?&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Aplikasi pengolah angka (spreadsheet)&lt;/strong&gt; adalah perangkat lunak yang digunakan untuk mengolah, menghitung, menganalisis, dan menyajikan data berbentuk angka maupun teks dalam format tabel yang tersusun atas baris dan kolom. Contoh aplikasi: Microsoft Excel, Google Sheets, dan LibreOffice Calc.&lt;/p&gt;

    &lt;h3 class="sub-title"&gt;Istilah Dasar dalam Spreadsheet&lt;/h3&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Wadah Utama&lt;/span&gt;&lt;h4&gt;Workbook&lt;/h4&gt;&lt;p&gt;Satu file spreadsheet yang dapat berisi banyak sheet (lembar kerja) di dalamnya.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Lembar Kerja&lt;/span&gt;&lt;h4&gt;Sheet&lt;/h4&gt;&lt;p&gt;Satu halaman kerja berisi grid sel tempat data dimasukkan dan diolah.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Satuan Data&lt;/span&gt;&lt;h4&gt;Cell (Sel)&lt;/h4&gt;&lt;p&gt;Kotak perpotongan antara kolom dan baris, diberi alamat seperti A1, B2, C7.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Vertikal&lt;/span&gt;&lt;h4&gt;Kolom (Column)&lt;/h4&gt;&lt;p&gt;Diberi label huruf (A, B, C, ...) tersusun vertikal.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Horizontal&lt;/span&gt;&lt;h4&gt;Baris (Row)&lt;/h4&gt;&lt;p&gt;Diberi label angka (1, 2, 3, ...) tersusun horizontal.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Kumpulan Sel&lt;/span&gt;&lt;h4&gt;Range&lt;/h4&gt;&lt;p&gt;Sekumpulan sel yang dipilih bersama, ditulis dengan tanda titik dua, mis. B2:B6.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="format-sel"&gt;
    &lt;div class="section-tag"&gt;Tampilan Data&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Format Sel (Cell Formatting)&lt;/h2&gt;
    &lt;p&gt;Format sel mengatur bagaimana nilai di dalam sel ditampilkan, tanpa mengubah nilai aslinya. Pemformatan yang tepat membuat data lebih mudah dibaca dan dipahami.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Format&lt;/th&gt;&lt;th&gt;Contoh Tampilan&lt;/th&gt;&lt;th&gt;Kegunaan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;General/Number&lt;/td&gt;&lt;td&gt;12000&lt;/td&gt;&lt;td&gt;Angka standar tanpa pemformatan khusus.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Currency (Mata Uang)&lt;/td&gt;&lt;td&gt;Rp12.000&lt;/td&gt;&lt;td&gt;Menampilkan nilai sebagai nominal uang.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Percentage&lt;/td&gt;&lt;td&gt;75%&lt;/td&gt;&lt;td&gt;Menampilkan pecahan sebagai persentase (0,75 menjadi 75%).&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Comma Style (Pemisah Ribuan)&lt;/td&gt;&lt;td&gt;9.999&lt;/td&gt;&lt;td&gt;Menambahkan pemisah ribuan agar angka besar mudah dibaca.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Date&lt;/td&gt;&lt;td&gt;06/10/2026&lt;/td&gt;&lt;td&gt;Menampilkan nilai tanggal dalam format kalender.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Conditional Formatting&lt;/td&gt;&lt;td&gt;Warna/Data Bar otomatis&lt;/td&gt;&lt;td&gt;Memberi warna atau indikator visual otomatis berdasarkan nilai sel.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Catatan Penting&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Format sel hanya mengubah &lt;strong&gt;tampilan&lt;/strong&gt;, bukan nilai sebenarnya. Sel yang diformat Currency menjadi "Rp12.000" nilainya tetap 12000 saat dihitung dalam rumus.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="rumus-dasar"&gt;
    &lt;div class="section-tag"&gt;Perhitungan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rumus Dasar (Basic Formula)&lt;/h2&gt;
    &lt;p&gt;Setiap rumus di spreadsheet selalu diawali tanda sama dengan (&lt;span class="mono"&gt;=&lt;/span&gt;). Rumus dapat menggunakan angka langsung maupun &lt;strong&gt;referensi sel&lt;/strong&gt; (alamat sel seperti A1, B2) agar hasil otomatis diperbarui saat data sumber berubah.&lt;/p&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Operator&lt;/th&gt;&lt;th&gt;Fungsi&lt;/th&gt;&lt;th&gt;Contoh Rumus&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;+&lt;/td&gt;&lt;td&gt;Penjumlahan&lt;/td&gt;&lt;td&gt;=A1+A2&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;Pengurangan&lt;/td&gt;&lt;td&gt;=A1-A2&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;*&lt;/td&gt;&lt;td&gt;Perkalian&lt;/td&gt;&lt;td&gt;=A1*A2&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;/&lt;/td&gt;&lt;td&gt;Pembagian&lt;/td&gt;&lt;td&gt;=A1/A2&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;( )&lt;/td&gt;&lt;td&gt;Mengatur prioritas perhitungan&lt;/td&gt;&lt;td&gt;=(A1+A2)*B1&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
    &lt;h3 class="sub-title"&gt;Referensi Relatif vs Referensi Absolut&lt;/h3&gt;
    &lt;p&gt;Saat sebuah rumus disalin (copy) ke sel lain, &lt;strong&gt;referensi relatif&lt;/strong&gt; (mis. B2) akan otomatis bergeser mengikuti posisi baru, sedangkan &lt;strong&gt;referensi absolut&lt;/strong&gt; (mis. $B$7, dikunci dengan tanda dolar) akan selalu menunjuk ke sel yang sama meskipun disalin ke mana pun. Referensi absolut penting digunakan saat sebuah rumus perlu selalu merujuk ke satu sel tetap, misalnya sel total keseluruhan atau sel tarif pajak.&lt;/p&gt;
  &lt;/section&gt;

  &lt;section class="section" id="fungsi"&gt;
    &lt;div class="section-tag"&gt;Fungsi Spreadsheet&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fungsi Matematika, Logika, dan Statistik&lt;/h2&gt;

    &lt;h3 class="sub-title"&gt;Fungsi Matematika &amp; Statistik Dasar&lt;/h3&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;SUM&lt;/h4&gt;&lt;p&gt;Menjumlahkan seluruh nilai dalam suatu range. Contoh: &lt;code&gt;=SUM(B2:B6)&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;AVERAGE&lt;/h4&gt;&lt;p&gt;Menghitung nilai rata-rata dari suatu range. Contoh: &lt;code&gt;=AVERAGE(B2:B6)&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;MAX / MIN&lt;/h4&gt;&lt;p&gt;Mencari nilai tertinggi/terendah dalam suatu range. Contoh: &lt;code&gt;=MAX(B2:B6)&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;COUNT / COUNTA&lt;/h4&gt;&lt;p&gt;COUNT menghitung sel berisi angka; COUNTA menghitung seluruh sel yang terisi (termasuk teks).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;ROUND&lt;/h4&gt;&lt;p&gt;Membulatkan angka ke sejumlah desimal tertentu. Contoh: &lt;code&gt;=ROUND(A1,2)&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;h3 class="sub-title"&gt;Fungsi Logika&lt;/h3&gt;
    &lt;p&gt;Fungsi &lt;strong&gt;IF&lt;/strong&gt; digunakan untuk membuat keputusan otomatis berdasarkan suatu syarat (kondisi). Struktur dasarnya: &lt;code&gt;=IF(syarat, nilai_jika_benar, nilai_jika_salah)&lt;/code&gt;.&lt;/p&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Contoh Penerapan&lt;/div&gt;
      &lt;p style="margin:0;"&gt;&lt;code&gt;=IF(B2&gt;=75,"Lulus","Tidak Lulus")&lt;/code&gt; akan menampilkan "Lulus" jika nilai di B2 adalah 75 atau lebih, dan "Tidak Lulus" jika kurang dari itu. Fungsi IF dapat digabung dengan &lt;strong&gt;AND&lt;/strong&gt; (semua syarat harus benar) atau &lt;strong&gt;OR&lt;/strong&gt; (salah satu syarat benar) untuk kondisi yang lebih kompleks, misalnya &lt;code&gt;=IF(AND(B2&gt;=75,C2&gt;=80),"Lulus","Tidak Lulus")&lt;/code&gt;.&lt;/p&gt;
    &lt;/div&gt;

    &lt;h3 class="sub-title"&gt;Fungsi Statistik Bersyarat&lt;/h3&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;COUNTIF&lt;/h4&gt;&lt;p&gt;Menghitung banyak sel dalam suatu range yang memenuhi satu syarat. Contoh: &lt;code&gt;=COUNTIF(B2:B10,"&amp;gt;=80")&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Fungsi&lt;/span&gt;&lt;h4&gt;SUMIF&lt;/h4&gt;&lt;p&gt;Menjumlahkan nilai dalam suatu range yang memenuhi satu syarat tertentu. Contoh: &lt;code&gt;=SUMIF(A2:A10,"Elektronik",B2:B10)&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="fitur-lain"&gt;
    &lt;div class="section-tag"&gt;Produktivitas&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fitur Pendukung Lainnya&lt;/h2&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Otomasi&lt;/span&gt;&lt;h4&gt;Fill Handle (AutoFill)&lt;/h4&gt;&lt;p&gt;Menarik kotak kecil di sudut sel untuk mengisi data berpola (nomor urut, tanggal) secara otomatis tanpa mengetik satu per satu.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Penyajian&lt;/span&gt;&lt;h4&gt;Sorting (A-Z/Z-A)&lt;/h4&gt;&lt;p&gt;Mengurutkan data tabel berdasarkan satu kolom, dari terkecil ke terbesar atau sebaliknya.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Visualisasi&lt;/span&gt;&lt;h4&gt;Chart (Grafik)&lt;/h4&gt;&lt;p&gt;Mengubah data tabel menjadi grafik batang, garis, atau pie agar lebih mudah dianalisis secara visual.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Tampilan&lt;/span&gt;&lt;h4&gt;Merge &amp; Center&lt;/h4&gt;&lt;p&gt;Menggabungkan beberapa sel menjadi satu sekaligus menengahkan isinya, umum dipakai untuk judul tabel.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="section-tag"&gt;Penguatan Konsep&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rangkuman Materi&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;h4&gt;Poin-Poin Kunci&lt;/h4&gt;
      &lt;ul&gt;
        &lt;li&gt;Spreadsheet mengolah data dalam bentuk sel, baris, kolom, dan range di dalam sheet dan workbook.&lt;/li&gt;
        &lt;li&gt;Format sel mengubah tampilan data (Currency, Percentage, Comma Style) tanpa mengubah nilai aslinya.&lt;/li&gt;
        &lt;li&gt;Rumus selalu diawali tanda "=" dan dapat memakai referensi relatif maupun absolut ($).&lt;/li&gt;
        &lt;li&gt;Fungsi matematika dasar: SUM, AVERAGE, MAX, MIN, COUNT/COUNTA, ROUND.&lt;/li&gt;
        &lt;li&gt;Fungsi logika IF membuat keputusan otomatis, dapat digabung dengan AND/OR.&lt;/li&gt;
        &lt;li&gt;Fungsi statistik bersyarat COUNTIF dan SUMIF menghitung/menjumlahkan data berdasarkan kriteria tertentu.&lt;/li&gt;
        &lt;li&gt;Fitur pendukung seperti Fill Handle, Sorting, Chart, dan Merge &amp; Center meningkatkan efisiensi dan kualitas penyajian data.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="tugas-kelompok"&gt;
    &lt;div class="section-tag"&gt;Aktivitas Belajar&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Tugas Kelompok&lt;/h2&gt;
    &lt;div class="task-panel"&gt;
      &lt;div class="task-head"&gt;
        &lt;span class="badge"&gt;Kerja Kelompok - maksimal 6 orang&lt;/span&gt;
        &lt;span class="badge" style="background:var(--brand-dark); color:#fff;"&gt;45 menit praktik + 20 menit presentasi&lt;/span&gt;
      &lt;/div&gt;
      &lt;h3&gt;Mengerjakan Instruksi Soal Bernomor Sesuai Jurusan&lt;/h3&gt;
      &lt;p&gt;Setiap kelompok mengerjakan instruksi soal bernomor berikut &lt;strong&gt;secara langsung&lt;/strong&gt; dengan mengetikkan data dan rumus sesuai cell masing-masing pada Lembar Kerja Spreadsheet &lt;strong&gt;langsung di halaman web ini&lt;/strong&gt; - persis seperti latihan entri data yang pernah dikerjakan. Seluruh data dan rumus harus diketik sendiri (tidak bisa disalin-tempel) dan dihitung sungguhan oleh sistem, persis seperti bekerja di Excel/Google Sheets.&lt;/p&gt;

      &lt;div class="field" style="max-width:440px; margin:16px 0;"&gt;
        &lt;label&gt;Pilih Jurusan Kelompok&lt;/label&gt;
        &lt;select id="jurusanSelect"&gt;
          &lt;option value=""&gt;-- Pilih Jurusan --&lt;/option&gt;
          &lt;option value="TB"&gt;Tata Busana (TB)&lt;/option&gt;
          &lt;option value="TO"&gt;Teknik Otomotif (TO)&lt;/option&gt;
          &lt;option value="TJKT"&gt;Teknik Jaringan Komputer dan Telekomunikasi (TJKT)&lt;/option&gt;
        &lt;/select&gt;
      &lt;/div&gt;

      &lt;div id="groupTaskBody" style="display:none;"&gt;
        &lt;h3 class="sub-title"&gt;Nama Anggota Kelompok (maksimal 6 orang)&lt;/h3&gt;
        &lt;div class="student-info" id="groupMembersWrap"&gt;
          &lt;div class="field"&gt;&lt;label&gt;Anggota 1&lt;/label&gt;&lt;input type="text" class="group-member" data-n="1" placeholder="Nama anggota 1"&gt;&lt;/div&gt;
          &lt;div class="field"&gt;&lt;label&gt;Anggota 2&lt;/label&gt;&lt;input type="text" class="group-member" data-n="2" placeholder="Nama anggota 2"&gt;&lt;/div&gt;
          &lt;div class="field"&gt;&lt;label&gt;Anggota 3&lt;/label&gt;&lt;input type="text" class="group-member" data-n="3" placeholder="Nama anggota 3"&gt;&lt;/div&gt;
          &lt;div class="field"&gt;&lt;label&gt;Anggota 4&lt;/label&gt;&lt;input type="text" class="group-member" data-n="4" placeholder="Nama anggota 4 (opsional)"&gt;&lt;/div&gt;
          &lt;div class="field"&gt;&lt;label&gt;Anggota 5&lt;/label&gt;&lt;input type="text" class="group-member" data-n="5" placeholder="Nama anggota 5 (opsional)"&gt;&lt;/div&gt;
          &lt;div class="field"&gt;&lt;label&gt;Anggota 6&lt;/label&gt;&lt;input type="text" class="group-member" data-n="6" placeholder="Nama anggota 6 (opsional)"&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="callout"&gt;
          &lt;div class="lbl"&gt;Peran Kelompok&lt;/div&gt;
          &lt;p style="margin:0;"&gt;Kelompok berperan sebagai &lt;strong id="groupTaskPeran"&gt;&lt;/strong&gt; (&lt;span id="groupTaskTitleJurusan"&gt;&lt;/span&gt;) yang diminta menyelesaikan kasus berikut menggunakan spreadsheet.&lt;/p&gt;
        &lt;/div&gt;

        &lt;h3 class="sub-title" style="margin-top:22px;"&gt;Baca dan Kerjakan Instruksi Berikut&lt;/h3&gt;
        &lt;div class="card" style="background:var(--surface); border-style:dashed;"&gt;
          &lt;span class="tag"&gt;Instruksi Soal (Referensi - Jangan Disalin-Tempel)&lt;/span&gt;
          &lt;p id="groupInstruksiIntro" style="font-size:.88rem; color:var(--ink);"&gt;&lt;/p&gt;
          &lt;p style="font-size:.78rem; font-weight:600; color:var(--brand-dark); margin-top:10px;"&gt;Data Awal (Contoh - Ketikkan ke Lembar Kerja di Bawah)&lt;/p&gt;
          &lt;div class="table-scroll" id="groupDataAwalWrap" style="margin:8px 0 4px;"&gt;&lt;/div&gt;
          &lt;ol class="steps-num" id="groupInstruksiSteps" style="margin-top:14px;"&gt;&lt;/ol&gt;
        &lt;/div&gt;

        &lt;h3 class="sub-title" style="margin-top:22px;"&gt;Lembar Kerja Spreadsheet (Dihitung Langsung oleh Sistem)&lt;/h3&gt;
        &lt;p style="font-size:.86rem;"&gt;Kerjakan instruksi bernomor di atas langsung pada lembar kerja berikut. Ketikkan data pada kolom &lt;strong&gt;C&lt;/strong&gt; (Jumlah Terjual) dan &lt;strong&gt;D&lt;/strong&gt; (Harga Satuan), lalu ketikkan rumus pada kolom &lt;strong&gt;E&lt;/strong&gt; (Total) dan &lt;strong&gt;F&lt;/strong&gt; (Status), diawali tanda "=". Seluruh sel tidak menerima tempel (paste) - semua harus diketik sendiri.&lt;/p&gt;
        &lt;div class="table-scroll" id="groupGridWrap"&gt;&lt;/div&gt;
        &lt;p class="flag" id="groupPasteWarning" style="margin:6px 0 0;"&gt;[PERINGATAN] Percobaan tempel terdeteksi pada lembar kerja - seluruh data dan rumus harus diketik sendiri.&lt;/p&gt;
        &lt;div class="group-actions"&gt;
          &lt;button id="btnSortGrid" class="primary-btn ghost"&gt;Urutkan Berdasarkan Total&lt;/button&gt;
          &lt;button id="btnResetGrid" class="primary-btn ghost"&gt;Reset Lembar Kerja&lt;/button&gt;
        &lt;/div&gt;
        &lt;div id="sortedResult"&gt;&lt;/div&gt;

        &lt;h3 class="sub-title"&gt;Grafik Total Penjualan (Otomatis)&lt;/h3&gt;
        &lt;canvas id="groupChart" width="640" height="220" style="max-width:100%; background:#fff; border:1px solid var(--line); border-radius:8px; display:block;"&gt;&lt;/canvas&gt;

        &lt;div class="group-actions" style="margin-top:20px;"&gt;
          &lt;button id="btnNilaiKelompok" class="primary-btn"&gt;Hitung Penilaian &amp;amp; Tampilkan Hasil Akhir&lt;/button&gt;
        &lt;/div&gt;

        &lt;div class="group-score-panel" id="groupScorePanel"&gt;
          &lt;div class="score-lbl"&gt;Nilai Tugas Kelompok (Otomatis)&lt;/div&gt;
          &lt;div class="score" id="groupScoreValue"&gt;0&lt;/div&gt;
          &lt;p id="groupScoreDetail" style="font-size:.84rem; color:var(--ink-soft);"&gt;&lt;/p&gt;

          &lt;h3 class="sub-title" style="margin-top:18px;"&gt;Hasil Akhir Pengerjaan Tabel&lt;/h3&gt;
          &lt;p style="font-size:.8rem; color:var(--ink-soft);"&gt;Sel berwarna hijau = benar, merah = salah, abu-abu = belum diisi.&lt;/p&gt;
          &lt;div id="groupFinalTableWrap"&gt;&lt;/div&gt;

          &lt;div class="group-actions" style="margin-top:16px;"&gt;
            &lt;button id="btnUnduhTugasPDF" class="primary-btn ghost"&gt;Unduh Bukti (PDF - Tabel Hasil Akhir)&lt;/button&gt;
          &lt;/div&gt;
          &lt;span id="groupPdfMsg" class="mono" style="font-size:.78rem; color:var(--ink-soft);"&gt;&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;h3 class="sub-title" style="margin-top:22px;"&gt;Format Penilaian&lt;/h3&gt;
      &lt;p&gt;Pekerjaan dinilai dari ketepatan mengikuti instruksi bernomor dan hasil praktik pada lembar kerja (data, rumus Total, Status, ringkasan SUM/AVERAGE/MAX/MIN, hasil urutan, dan grafik), tanpa ada percobaan salin-tempel. Kelompok tetap mempresentasikan hasil dan alasannya di depan kelas.&lt;/p&gt;
      &lt;h3 class="sub-title"&gt;Rubrik Penilaian&lt;/h3&gt;
      &lt;table class="rubrik"&gt;
        &lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Indikator&lt;/th&gt;&lt;th&gt;Skor Maks.&lt;/th&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Ketepatan Mengikuti Instruksi&lt;/td&gt;&lt;td&gt;Data dan rumus dientri langsung sesuai instruksi bernomor, diketik sendiri tanpa salin-tempel&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Ketepatan Rumus Total&lt;/td&gt;&lt;td&gt;Kolom Total (E) terisi rumus perkalian yang benar pada seluruh baris&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Fungsi Logika&lt;/td&gt;&lt;td&gt;Kolom Status (F) menggunakan fungsi IF bertingkat (nested IF) tiga kategori dengan logika yang tepat&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Fungsi Ringkasan&lt;/td&gt;&lt;td&gt;SUM, AVERAGE, MAX, MIN pada baris ringkasan terisi dan bernilai benar&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Analisis Urutan &amp; Grafik&lt;/td&gt;&lt;td&gt;Kelompok mampu menjelaskan hasil urutan data dan membaca grafik dengan tepat&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Kerja Sama &amp; Presentasi&lt;/td&gt;&lt;td&gt;Seluruh anggota berkontribusi; presentasi jelas dan percaya diri&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td colspan="2" style="text-align:right; font-weight:600;"&gt;Total&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="identitas"&gt;
    &lt;div class="section-tag"&gt;Evaluasi Individu&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Isi Data Diri&lt;/h2&gt;
    &lt;p&gt;Kerjakan 10 soal pilihan ganda, 10 soal praktik rumus, dan 5 esai berikut secara mandiri. Skor dihitung otomatis dan langsung terkirim ke guru begitu tombol dikirim ditekan.&lt;/p&gt;
    &lt;div class="student-info"&gt;
      &lt;div class="field"&gt;&lt;label&gt;Kelas&lt;/label&gt;
        &lt;select id="kelas"&gt;
          &lt;option value=""&gt;-- Pilih Kelas --&lt;/option&gt;
          &lt;option value="X TB1"&gt;X TB1&lt;/option&gt;&lt;option value="X TB2"&gt;X TB2&lt;/option&gt;
          &lt;option value="X TO1"&gt;X TO1&lt;/option&gt;&lt;option value="X TO2"&gt;X TO2&lt;/option&gt;
          &lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;&lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;
        &lt;/select&gt;
      &lt;/div&gt;
      &lt;div class="field"&gt;&lt;label&gt;No. Absen&lt;/label&gt;&lt;input type="number" id="noAbsen" min="1" placeholder="- Pilih kelas dulu -" disabled&gt;&lt;/div&gt;
      &lt;div class="field"&gt;&lt;label&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="nama"&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="pg"&gt;
    &lt;div class="section-tag"&gt;Bagian A - Bobot 35%&lt;/div&gt;
    &lt;h2 class="section-title"&gt;10 Soal Pilihan Ganda (HOTS)&lt;/h2&gt;
    &lt;div id="pgContainer"&gt;&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="praktik"&gt;
    &lt;div class="section-tag"&gt;Bagian B - Bobot 40%&lt;/div&gt;
    &lt;h2 class="section-title"&gt;10 Soal Praktik Mengoperasikan Spreadsheet&lt;/h2&gt;
    &lt;p&gt;Perhatikan tabel data pada tiap soal, lalu tuliskan rumus yang tepat untuk sel yang ditandai (kotak kuning putus-putus). Tulis rumus lengkap diawali tanda "=".&lt;/p&gt;
    &lt;div id="practiceContainer"&gt;&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="esai"&gt;
    &lt;div class="section-tag"&gt;Bagian C - Bobot 25%&lt;/div&gt;
    &lt;h2 class="section-title"&gt;5 Soal Esai&lt;/h2&gt;
    &lt;p&gt;Jawab dengan kalimat sendiri berdasarkan pemahamanmu. Dilarang menyalin-tempel.&lt;/p&gt;
    &lt;div id="essayContainer"&gt;&lt;/div&gt;
  &lt;/section&gt;

  &lt;section class="section" id="submit"&gt;
    &lt;div class="section-tag"&gt;Pengumpulan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kirim Jawaban&lt;/h2&gt;
    &lt;button id="btnHitung" class="primary-btn"&gt;Hitung &amp; Kirim Nilai&lt;/button&gt;
    &lt;span id="uploadMsg"&gt;&lt;/span&gt;
    &lt;div class="result-panel" id="resultPanel"&gt;
      &lt;div class="score-lbl"&gt;Nilai Akhir&lt;/div&gt;
      &lt;div class="score" id="scoreValue"&gt;0&lt;/div&gt;
      &lt;p id="resultDetail"&gt;&lt;/p&gt;
      &lt;button id="btnUnduhPDF" class="primary-btn ghost" style="margin-top:14px;" disabled&gt;Unduh Bukti Mengerjakan (PDF)&lt;/button&gt;
    &lt;/div&gt;
  &lt;/section&gt;
&lt;/main&gt;
&lt;footer&gt;PENGENALAN APLIKASI PENGOLAH ANGKA (SPREADSHEET) - INFORMATIKA - FASE E KELAS X SMK&lt;/footer&gt;
&lt;/div&gt;

&lt;script&gt;
const GAS_ENDPOINT = "https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec";
const JUDUL_MATERI = "Pengenalan Aplikasi Pengolah Angka (Spreadsheet)";

document.getElementById('kelas').addEventListener('change', (e)=&gt;{
  const noAbsenInput=document.getElementById('noAbsen');
  if(e.target.value){ noAbsenInput.disabled=false; noAbsenInput.placeholder='mis. 12'; }
  else { noAbsenInput.disabled=true; noAbsenInput.value=''; noAbsenInput.placeholder='- Pilih kelas dulu -'; }
});

// ===================== TUGAS KELOMPOK: SPREADSHEET INTERAKTIF PER JURUSAN =====================
const JURUSAN_DATA = {
  TB: {
    label:"Tata Busana (TB)",
    peran:"staf administrasi sebuah usaha konveksi dan jasa jahit",
    kolomA:"Nama Bahan/Jasa", kolomB:"Kategori",
    instruksiIntro:"Sebuah usaha konveksi dan jasa jahit mencatat penjualan bahan serta jasa selama satu bulan. Kerjakan langsung instruksi bernomor berikut pada Lembar Kerja Spreadsheet di bawah, persis seperti latihan entri data Daftar Penjualan Barang yang pernah dikerjakan.",
    items:[
      ["Kain Katun (meter)","Bahan Dasar",40,35000],
      ["Kain Denim (meter)","Bahan Dasar",13,55000],
      ["Benang Jahit (gulung)","Aksesoris",60,8000],
      ["Kancing (pack)","Aksesoris",50,12000],
      ["Resleting (pcs)","Aksesoris",45,7000],
      ["Pola Baju (lembar)","Desain",20,15000],
      ["Jasa Jahit Baju","Jasa",32,85000],
      ["Jasa Bordir","Jasa",10,65000]
    ]
  },
  TO: {
    label:"Teknik Otomotif (TO)",
    peran:"staf administrasi sebuah bengkel kendaraan bermotor",
    kolomA:"Nama Sparepart/Jasa", kolomB:"Kategori",
    instruksiIntro:"Sebuah bengkel kendaraan bermotor mencatat penjualan sparepart serta jasa servis selama satu bulan. Kerjakan langsung instruksi bernomor berikut pada Lembar Kerja Spreadsheet di bawah, persis seperti latihan entri data Daftar Penjualan Barang yang pernah dikerjakan.",
    items:[
      ["Oli Mesin 1L","Pelumas",32,65000],
      ["Ban Luar Motor","Sparepart",14,320000],
      ["Aki Motor","Sparepart",9,410000],
      ["Busi","Sparepart",40,18000],
      ["Kampas Rem","Sparepart",26,55000],
      ["Filter Udara","Sparepart",20,45000],
      ["Lampu Depan LED","Aksesoris",15,95000],
      ["Jasa Servis Ringan","Jasa",38,75000]
    ]
  },
  TJKT: {
    label:"Teknik Jaringan Komputer dan Telekomunikasi (TJKT)",
    peran:"staf administrasi sebuah toko dan jasa instalasi jaringan",
    kolomA:"Nama Produk/Jasa", kolomB:"Kategori",
    instruksiIntro:"Sebuah toko sekaligus penyedia jasa instalasi jaringan mencatat penjualan produk serta jasa selama satu bulan. Kerjakan langsung instruksi bernomor berikut pada Lembar Kerja Spreadsheet di bawah, persis seperti latihan entri data Daftar Penjualan Barang yang pernah dikerjakan.",
    items:[
      ["Kabel UTP Cat6 (roll)","Perangkat Pasif",12,850000],
      ["Router Wi-Fi","Perangkat Aktif",18,320000],
      ["Switch 8 Port","Perangkat Aktif",10,275000],
      ["Access Point","Perangkat Aktif",14,410000],
      ["Paket Internet Bulanan","Jasa Layanan",45,250000],
      ["Modem ONT","Perangkat Aktif",16,180000],
      ["Konektor RJ45 (pack)","Perangkat Pasif",28,25000],
      ["Jasa Instalasi Jaringan","Jasa",20,150000]
    ]
  }
};

let currentJurusan = null;
let REG = {};
let groupGridPasteFlag = false;

function buildDataAwalTable(data){
  let html = `&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;Baris&lt;/th&gt;&lt;th&gt;${data.kolomA}&lt;/th&gt;&lt;th&gt;${data.kolomB}&lt;/th&gt;&lt;th&gt;Jumlah Terjual&lt;/th&gt;&lt;th&gt;Harga Satuan&lt;/th&gt;&lt;/tr&gt;`;
  data.items.forEach((item,i)=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${i+2}&lt;/td&gt;&lt;td&gt;${item[0]}&lt;/td&gt;&lt;td&gt;${item[1]}&lt;/td&gt;&lt;td&gt;${item[2]}&lt;/td&gt;&lt;td&gt;${item[3].toLocaleString('id-ID')}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;';
  return html;
}

function buildInstruksiSteps(data){
  return [
    `Ketikkan data &lt;strong&gt;${data.kolomA}&lt;/strong&gt;, &lt;strong&gt;${data.kolomB}&lt;/strong&gt;, &lt;strong&gt;Jumlah Terjual&lt;/strong&gt;, dan &lt;strong&gt;Harga Satuan&lt;/strong&gt; sesuai cell masing-masing pada kolom A sampai D (baris 2 sampai 9), seperti contoh pada tabel Data Awal di atas.`,
    `Hitunglah kolom &lt;strong&gt;TOTAL&lt;/strong&gt; (kolom E) dengan ketentuan Jumlah Terjual dikalikan Harga Satuan.&lt;br&gt;Cara: klik cell E2 &amp;gt; ketik &lt;span class="mono"&gt;=C2*D2&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk E3 sampai E9.`,
    `Isilah kolom &lt;strong&gt;STATUS&lt;/strong&gt; (kolom F) menggunakan rumus IF bertingkat (nested IF): jika Jumlah Terjual 30 kali atau lebih maka "Laris", jika 15 sampai 29 kali maka "Cukup Laris", selain itu "Kurang Laris".&lt;br&gt;Cara: klik cell F2 &amp;gt; ketik &lt;span class="mono"&gt;=IF(C2&amp;gt;=30,"Laris",IF(C2&amp;gt;=15,"Cukup Laris","Kurang Laris"))&lt;/span&gt; &amp;gt; tekan Enter, lalu ulangi untuk F3 sampai F9.`,
    `Hitunglah &lt;strong&gt;Total Keseluruhan&lt;/strong&gt; pada cell E11.&lt;br&gt;Cara: klik cell E11 &amp;gt; ketik &lt;span class="mono"&gt;=SUM(E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Hitunglah &lt;strong&gt;Rata-rata Penjualan&lt;/strong&gt; pada cell E12.&lt;br&gt;Cara: klik cell E12 &amp;gt; ketik &lt;span class="mono"&gt;=AVERAGE(E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Carilah penjualan &lt;strong&gt;Tertinggi&lt;/strong&gt; pada cell E13.&lt;br&gt;Cara: klik cell E13 &amp;gt; ketik &lt;span class="mono"&gt;=MAX(E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Carilah penjualan &lt;strong&gt;Terendah&lt;/strong&gt; pada cell E14.&lt;br&gt;Cara: klik cell E14 &amp;gt; ketik &lt;span class="mono"&gt;=MIN(E2:E9)&lt;/span&gt; &amp;gt; tekan Enter.`,
    `Amati baris dengan Total tertinggi yang otomatis tersorot warna hijau (simulasi Conditional Formatting), lalu klik tombol "Urutkan Berdasarkan Total" untuk melihat data terurut dari penjualan tertinggi ke terendah.`,
    `Amati grafik batang yang otomatis terbentuk dari kolom Total, lalu presentasikan hasil dan alasan kerja kelompok di depan kelas maksimal 5 menit.`
  ];
}

document.getElementById('jurusanSelect').addEventListener('change', (e)=&gt;{
  const val = e.target.value;
  const body = document.getElementById('groupTaskBody');
  if(!val){ body.style.display='none'; return; }
  body.style.display='block';
  const data = JURUSAN_DATA[val];
  document.getElementById('groupTaskTitleJurusan').textContent = data.label;
  document.getElementById('groupTaskPeran').textContent = data.peran;
  document.getElementById('groupInstruksiIntro').textContent = data.instruksiIntro;
  document.getElementById('groupDataAwalWrap').innerHTML = buildDataAwalTable(data);
  const stepsList = document.getElementById('groupInstruksiSteps');
  stepsList.innerHTML = buildInstruksiSteps(data).map(s=&gt;`&lt;li&gt;${s}&lt;/li&gt;`).join('');
  document.getElementById('sortedResult').innerHTML='';
  document.getElementById('groupScorePanel').style.display='none';
  lastGroupScore = null;
  renderGrid(val);
});

function renderGrid(key){
  currentJurusan = key;
  const data = JURUSAN_DATA[key];
  let rows = '';
  rows += `&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;th&gt;C&lt;/th&gt;&lt;th&gt;D&lt;/th&gt;&lt;th&gt;E&lt;/th&gt;&lt;th&gt;F&lt;/th&gt;&lt;/tr&gt;`;
  rows += `&lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="g-label"&gt;${data.kolomA}&lt;/td&gt;&lt;td class="g-label"&gt;${data.kolomB}&lt;/td&gt;&lt;td class="g-label"&gt;Jumlah Terjual&lt;/td&gt;&lt;td class="g-label"&gt;Harga Satuan&lt;/td&gt;&lt;td class="g-label"&gt;Total&lt;/td&gt;&lt;td class="g-label"&gt;Status&lt;/td&gt;&lt;/tr&gt;`;
  data.items.forEach((item,i)=&gt;{
    const r=i+2;
    rows += `&lt;tr&gt;
      &lt;th class="rownum"&gt;${r}&lt;/th&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input plain" data-addr="A${r}" value="" placeholder="Baris ${r} - lihat Data Awal"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input plain" data-addr="B${r}" value="" placeholder="Kategori"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="number" class="sheet-input plain" data-addr="C${r}" value="" placeholder="Jumlah"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="number" class="sheet-input plain" data-addr="D${r}" value="" placeholder="Harga"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="E${r}" data-raw="" placeholder="=C${r}*D${r}"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="F${r}" data-raw="" placeholder='=IF(C${r}&amp;gt;=30,"Laris",IF(C${r}&amp;gt;=15,"Cukup Laris","Kurang Laris"))'&gt;&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  rows += `&lt;tr&gt;&lt;td colspan="7" style="height:6px; border:none; background:transparent;"&gt;&lt;/td&gt;&lt;/tr&gt;`;
  const sumRows = [
    ['11','Total Keseluruhan','E11', `=SUM(E2:E9)`],
    ['12','Rata-rata Penjualan','E12', `=AVERAGE(E2:E9)`],
    ['13','Produk/Jasa Terlaris (Total Tertinggi)','E13', `=MAX(E2:E9)`],
    ['14','Produk/Jasa Kurang Laris (Total Terendah)','E14', `=MIN(E2:E9)`]
  ];
  sumRows.forEach(sr=&gt;{
    rows += `&lt;tr&gt;
      &lt;th class="rownum"&gt;${sr[0]}&lt;/th&gt;
      &lt;td class="g-label" colspan="4"&gt;${sr[1]}&lt;/td&gt;
      &lt;td&gt;&lt;input type="text" class="sheet-input formula" data-addr="${sr[2]}" data-raw="" placeholder="${sr[3]}"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  document.getElementById('groupGridWrap').innerHTML = `&lt;table class="excel-grid sheet-grid"&gt;${rows}&lt;/table&gt;`;
  groupGridPasteFlag = false;
  const warnEl = document.getElementById('groupPasteWarning');
  if(warnEl) warnEl.classList.remove('show');
  bindGridEvents();
  recalcAll();
}

function bindGridEvents(){
  document.querySelectorAll('.sheet-input').forEach(inp=&gt;{
    inp.addEventListener('paste', (e)=&gt;{
      e.preventDefault();
      groupGridPasteFlag = true;
      const warnEl = document.getElementById('groupPasteWarning');
      if(warnEl) warnEl.classList.add('show');
    });
    if(inp.classList.contains('formula')){
      inp.addEventListener('focus', ()=&gt;{ inp.value = inp.dataset.raw || ''; });
      inp.addEventListener('input', ()=&gt;{ inp.dataset.raw = inp.value; recalcAll(inp); });
    } else {
      inp.addEventListener('input', ()=&gt;{ recalcAll(); });
    }
    inp.addEventListener('blur', ()=&gt;{ recalcAll(); });
  });
}

function buildRegistry(){
  const reg = {};
  document.querySelectorAll('.sheet-input').forEach(inp=&gt;{ reg[inp.dataset.addr]=inp; });
  return reg;
}
function cellRawValue(addr){
  const el = REG[addr];
  if(!el) return '';
  if(el.classList.contains('formula')) return el.dataset.raw!==undefined ? el.dataset.raw : '';
  return el.value;
}
function evalAddr(addr, stack){
  addr = addr.toUpperCase().replace(/\$/g,'');
  if(stack.has(addr)) return 0;
  const raw = cellRawValue(addr);
  const s = String(raw===undefined||raw===null?'':raw).trim();
  if(s==='') return 0;
  if(s.startsWith('=')){
    stack.add(addr);
    const v = evalFormula(s.slice(1), stack);
    stack.delete(addr);
    return v;
  }
  const num = parseFloat(s);
  return isNaN(num) ? s : num;
}
function expandRange(a,b){
  const ma=a.match(/([A-Z]+)(\d+)/), mb=b.match(/([A-Z]+)(\d+)/);
  if(!ma||!mb) return [];
  const c1=ma[1].charCodeAt(0), c2=mb[1].charCodeAt(0);
  const r1=parseInt(ma[2]), r2=parseInt(mb[2]);
  const addrs=[];
  for(let c=Math.min(c1,c2); c&lt;=Math.max(c1,c2); c++){
    for(let r=Math.min(r1,r2); r&lt;=Math.max(r1,r2); r++) addrs.push(String.fromCharCode(c)+r);
  }
  return addrs;
}
function rangeValues(rangeStr, stack){
  const parts = rangeStr.split(':');
  if(parts.length!==2) return [];
  const addrs = expandRange(parts[0].replace(/\$/g,''), parts[1].replace(/\$/g,''));
  return addrs.map(addr=&gt;{ const v=evalAddr(addr, stack); const n=typeof v==='number'?v:parseFloat(v); return isNaN(n)?0:n; });
}
function resolveRefsInExpr(s, stack){
  return s.replace(/\$?([A-Z]{1,2})\$?(\d{1,3})/g, (m,col,row)=&gt;{
    const v=evalAddr(col+row, stack);
    return typeof v==='number' ? v : JSON.stringify(v);
  });
}
function evalAddr0(tok, stack){
  const t=tok.trim();
  if(/^\$?[A-Z]{1,2}\$?\d{1,3}$/.test(t)) return evalAddr(t.replace(/\$/g,''), stack);
  const n=parseFloat(t);
  return isNaN(n)?0:n;
}
function splitArgs(s){
  const args=[]; let depth=0, cur='', inQ=false;
  for(let i=0;i&lt;s.length;i++){
    const ch=s[i];
    if(ch==='"') inQ=!inQ;
    if(ch===',' &amp;&amp; !inQ &amp;&amp; depth===0){ args.push(cur); cur=''; continue; }
    if(ch==='(') depth++;
    if(ch===')') depth--;
    cur+=ch;
  }
  if(cur.trim().length || args.length) args.push(cur);
  return args.map(a=&gt;a.trim());
}
function computeFunc(fn, argsStr, stack){
  const args = splitArgs(argsStr);
  if(['SUM','AVERAGE','MAX','MIN','COUNT','COUNTA'].includes(fn)){
    let vals=[];
    args.forEach(a=&gt;{
      if(/^\$?[A-Z]{1,2}\$?\d{1,3}:\$?[A-Z]{1,2}\$?\d{1,3}$/.test(a)) vals=vals.concat(rangeValues(a, stack));
      else vals.push(evalAddr0(a, stack));
    });
    if(fn==='SUM') return String(vals.reduce((x,y)=&gt;x+y,0));
    if(fn==='AVERAGE') return String(vals.length?vals.reduce((x,y)=&gt;x+y,0)/vals.length:0);
    if(fn==='MAX') return String(vals.length?Math.max(...vals):0);
    if(fn==='MIN') return String(vals.length?Math.min(...vals):0);
    if(fn==='COUNT') return String(vals.filter(v=&gt;!isNaN(v)).length);
    if(fn==='COUNTA') return String(vals.length);
  }
  if(fn==='ROUND'){
    const resolved = resolveRefsInExpr(args[0], stack);
    const val = parseFloat(resolved)||0;
    const digits = parseInt(args[1])||0;
    return String(Math.round(val*Math.pow(10,digits))/Math.pow(10,digits));
  }
  if(fn==='AND'||fn==='OR'){
    const bools = args.map(a=&gt;{
      const resolved = resolveRefsInExpr(a, stack);
      try{ return !!Function('"use strict"; return ('+resolved+')')(); }catch(e){ return false; }
    });
    const result = fn==='AND' ? bools.every(Boolean) : bools.some(Boolean);
    return String(result);
  }
  if(fn==='IF'){
    const condResolved = resolveRefsInExpr(args[0], stack);
    let condVal=false;
    try{ condVal = !!Function('"use strict"; return ('+condResolved+')')(); }catch(e){ condVal=false; }
    return condVal ? args[1] : args[2];
  }
  return '0';
}
function evalFormula(expr, stack){
  expr = expr.trim();
  let changed=true, safety=0;
  while(changed &amp;&amp; safety&lt;40){
    changed=false; safety++;
    const next = expr.replace(/\b(SUM|AVERAGE|MAX|MIN|COUNT|COUNTA|ROUND|AND|OR|IF)\s*\(([^()]*)\)/i, (m, fn, argsStr)=&gt;{
      changed=true;
      return computeFunc(fn.toUpperCase(), argsStr, stack);
    });
    expr = next;
  }
  expr = expr.replace(/\$?([A-Z]{1,2})\$?(\d{1,3})/g, (m, col, row)=&gt;{
    const v = evalAddr(col+row, stack);
    return typeof v==='number' ? v : JSON.stringify(v);
  });
  try{
    return Function('"use strict"; return ('+expr+')')();
  }catch(e){ return '#ERROR'; }
}
function formatNum(n){
  if(Number.isInteger(n)) return String(n);
  return (Math.round(n*100)/100).toString();
}
function recalcAll(activeInput){
  REG = buildRegistry();
  document.querySelectorAll('.sheet-input.formula').forEach(inp=&gt;{
    if(inp===activeInput || inp===document.activeElement) return;
    const addr = inp.dataset.addr;
    const raw = (inp.dataset.raw!==undefined ? inp.dataset.raw : '').trim();
    if(raw===''){ inp.value=''; inp.classList.remove('err'); return; }
    let val;
    try{ val = raw.startsWith('=') ? evalFormula(raw.slice(1), new Set()) : raw; }
    catch(e){ val='#ERROR'; }
    if(val==='#ERROR' || (typeof val==='number' &amp;&amp; Number.isNaN(val))){
      inp.value='#ERROR'; inp.classList.add('err');
    } else {
      inp.classList.remove('err');
      inp.value = (typeof val==='number') ? formatNum(val) : val;
    }
  });
  highlightTopRow();
  drawChart();
}
function highlightTopRow(){
  let maxVal=-Infinity, maxRow=null;
  for(let r=2;r&lt;=9;r++){
    const el=REG['E'+r];
    if(!el) continue;
    const v=parseFloat(el.value);
    if(!isNaN(v) &amp;&amp; v&gt;maxVal){ maxVal=v; maxRow=r; }
  }
  document.querySelectorAll('#groupGridWrap tr').forEach(tr=&gt;tr.classList.remove('hi-max'));
  if(maxRow){
    const marker=REG['E'+maxRow];
    if(marker){ const tr=marker.closest('tr'); if(tr) tr.classList.add('hi-max'); }
  }
}
function drawChart(){
  const canvas=document.getElementById('groupChart');
  if(!canvas || !currentJurusan) return;
  const ctx=canvas.getContext('2d');
  const w=canvas.width, h=canvas.height;
  ctx.clearRect(0,0,w,h);
  const data=JURUSAN_DATA[currentJurusan];
  const vals=[];
  for(let r=2;r&lt;=9;r++){
    const el=REG['E'+r];
    const v=el?parseFloat(el.value):NaN;
    vals.push(isNaN(v)?0:v);
  }
  const maxV=Math.max(...vals,1);
  const padding=30, barGap=10;
  const barW=(w-padding*2-barGap*(vals.length-1))/vals.length;
  ctx.textAlign='center'; ctx.font='9px IBM Plex Mono, monospace';
  vals.forEach((v,i)=&gt;{
    const barH = maxV&gt;0 ? (v/maxV)*(h-60) : 0;
    const x = padding + i*(barW+barGap);
    const y = h-30-barH;
    ctx.fillStyle='#15803D';
    ctx.fillRect(x,y,barW,barH);
    ctx.fillStyle='#16221B';
    const aEl = REG['A'+(i+2)];
    const label = (aEl &amp;&amp; aEl.value.trim()) ? aEl.value.trim() : data.items[i][0];
    ctx.fillText(label.split(' ')[0], x+barW/2, h-14);
  });
}
function formatRupiah(n){
  if(isNaN(n)) return '-';
  return 'Rp' + Math.round(n).toLocaleString('id-ID');
}
document.getElementById('btnSortGrid').addEventListener('click', ()=&gt;{
  if(!currentJurusan) return;
  const data = JURUSAN_DATA[currentJurusan];
  const rows = data.items.map((item,i)=&gt;{
    const r=i+2;
    const aEl=REG['A'+r], bEl=REG['B'+r], eEl=REG['E'+r], fEl=REG['F'+r];
    const nama = (aEl &amp;&amp; aEl.value.trim()) ? aEl.value.trim() : '(belum diisi)';
    const kategori = (bEl &amp;&amp; bEl.value.trim()) ? bEl.value.trim() : '-';
    const total = eEl?parseFloat(eEl.value):NaN;
    const status = fEl?fEl.value:'';
    return {nama, kategori, total:isNaN(total)?0:total, status};
  });
  rows.sort((a,b)=&gt;b.total-a.total);
  let html = `&lt;div class="table-scroll"&gt;&lt;table class="compare"&gt;&lt;tr&gt;&lt;th&gt;Peringkat&lt;/th&gt;&lt;th&gt;${data.kolomA}&lt;/th&gt;&lt;th&gt;${data.kolomB}&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;`;
  rows.forEach((row,i)=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${i+1}&lt;/td&gt;&lt;td&gt;${row.nama}&lt;/td&gt;&lt;td&gt;${row.kategori}&lt;/td&gt;&lt;td&gt;${formatRupiah(row.total)}&lt;/td&gt;&lt;td&gt;${row.status &amp;&amp; row.status!=='#ERROR' ? row.status : '-'}&lt;/td&gt;&lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  document.getElementById('sortedResult').innerHTML = html;
});
document.getElementById('btnResetGrid').addEventListener('click', ()=&gt;{
  if(!currentJurusan) return;
  if(confirm('Reset seluruh rumus pada lembar kerja kelompok ini?')){
    document.getElementById('sortedResult').innerHTML='';
    document.getElementById('groupScorePanel').style.display='none';
    lastGroupScore = null;
    renderGrid(currentJurusan);
  }
});

function getGroupMembers(){
  return Array.from(document.querySelectorAll('.group-member'))
    .map(inp=&gt;inp.value.trim())
    .filter(v=&gt;v.length&gt;0);
}

function evaluateGroupWork(){
  if(!currentJurusan) return null;
  REG = buildRegistry();
  const data = JURUSAN_DATA[currentJurusan];
  const rows = data.items.map((item,i)=&gt;{
    const r=i+2;
    const nama = REG['A'+r] ? REG['A'+r].value.trim() : '';
    const kategori = REG['B'+r] ? REG['B'+r].value.trim() : '';
    const jumlahRaw = REG['C'+r] ? REG['C'+r].value.trim() : '';
    const hargaRaw = REG['D'+r] ? REG['D'+r].value.trim() : '';
    const totalRaw = REG['E'+r] ? REG['E'+r].value : '';
    const statusRaw = REG['F'+r] ? REG['F'+r].value.trim() : '';

    const namaOk = nama.length&gt;0 &amp;&amp; nama.toLowerCase()===item[0].toLowerCase();
    const kategoriOk = kategori.length&gt;0 &amp;&amp; kategori.toLowerCase()===item[1].toLowerCase();
    const jumlahOk = jumlahRaw!=='' &amp;&amp; parseFloat(jumlahRaw)===item[2];
    const hargaOk = hargaRaw!=='' &amp;&amp; parseFloat(hargaRaw)===item[3];

    const expectedTotal = evalFormula(`C${r}*D${r}`, new Set());
    const totalOk = totalRaw!=='' &amp;&amp; totalRaw!=='#ERROR' &amp;&amp; !isNaN(parseFloat(totalRaw)) &amp;&amp; Math.round(parseFloat(totalRaw))===Math.round(expectedTotal);

    const expectedStatus = evalFormula(`IF(C${r}&gt;=30,"Laris",IF(C${r}&gt;=15,"Cukup Laris","Kurang Laris"))`, new Set());
    const statusOk = statusRaw!=='' &amp;&amp; statusRaw!=='#ERROR' &amp;&amp; statusRaw.toLowerCase()===String(expectedStatus).toLowerCase();

    return {no:i+1, r, nama, kategori, jumlah:jumlahRaw, harga:hargaRaw, total:totalRaw, status:statusRaw,
      namaOk, kategoriOk, jumlahOk, hargaOk, totalOk, statusOk};
  });

  const expectedSum = evalFormula('SUM(E2:E9)', new Set());
  const expectedAvg = evalFormula('AVERAGE(E2:E9)', new Set());
  const expectedMax = evalFormula('MAX(E2:E9)', new Set());
  const expectedMin = evalFormula('MIN(E2:E9)', new Set());

  const sumRaw = REG['E11']?REG['E11'].value:'';
  const avgRaw = REG['E12']?REG['E12'].value:'';
  const maxRaw = REG['E13']?REG['E13'].value:'';
  const minRaw = REG['E14']?REG['E14'].value:'';

  const closeEnough=(raw,expected)=&gt;{ const x=parseFloat(raw); return raw!=='' &amp;&amp; raw!=='#ERROR' &amp;&amp; !isNaN(x) &amp;&amp; Math.abs(x-expected)&lt;1; };

  const summary = {
    sum:{raw:sumRaw, ok:closeEnough(sumRaw, expectedSum)},
    avg:{raw:avgRaw, ok:closeEnough(avgRaw, expectedAvg)},
    max:{raw:maxRaw, ok:closeEnough(maxRaw, expectedMax)},
    min:{raw:minRaw, ok:closeEnough(minRaw, expectedMin)}
  };

  return {label:data.label, kolomA:data.kolomA, kolomB:data.kolomB, rows, summary};
}

function scoreGroupWork(){
  const ev = evaluateGroupWork();
  if(!ev) return null;
  const n = ev.rows.length;
  let dataCorrect=0, totalCorrect=0, statusCorrect=0;
  ev.rows.forEach(row=&gt;{
    if(row.namaOk &amp;&amp; row.kategoriOk &amp;&amp; row.jumlahOk &amp;&amp; row.hargaOk) dataCorrect++;
    if(row.totalOk) totalCorrect++;
    if(row.statusOk) statusCorrect++;
  });
  const dataScore = Math.round(dataCorrect/n*100);
  const totalScore = Math.round(totalCorrect/n*100);
  const statusScore = Math.round(statusCorrect/n*100);
  const summaryCorrect = [ev.summary.sum.ok, ev.summary.avg.ok, ev.summary.max.ok, ev.summary.min.ok].filter(Boolean).length;
  const summaryScore = Math.round(summaryCorrect/4*100);

  let finalScore = Math.round(dataScore*0.20 + totalScore*0.25 + statusScore*0.25 + summaryScore*0.30);
  const pastePenalty = groupGridPasteFlag;
  if(pastePenalty) finalScore = Math.round(finalScore*0.5);

  return {ev, dataScore, totalScore, statusScore, summaryScore, finalScore, pastePenalty};
}

let lastGroupScore = null;

function cellClass(val, ok){
  if(!val || String(val).trim()==='') return 'cell-empty';
  return ok ? 'cell-ok' : 'cell-bad';
}

function renderGroupAssessment(){
  if(!currentJurusan){ alert('Pilih jurusan terlebih dahulu.'); return; }
  const members = getGroupMembers();
  if(members.length===0){ alert('Isi minimal satu nama anggota kelompok terlebih dahulu.'); return; }

  const result = scoreGroupWork();
  lastGroupScore = result;
  const data = JURUSAN_DATA[currentJurusan];

  document.getElementById('groupScoreValue').textContent = result.finalScore;
  document.getElementById('groupScoreDetail').innerHTML =
    `Data Entri (20%): &lt;strong&gt;${result.dataScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Rumus Total (25%): &lt;strong&gt;${result.totalScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Rumus Status (25%): &lt;strong&gt;${result.statusScore}&lt;/strong&gt; &amp;nbsp;|&amp;nbsp; ` +
    `Rumus Ringkasan (30%): &lt;strong&gt;${result.summaryScore}&lt;/strong&gt;` +
    (result.pastePenalty ? '&lt;br&gt;&lt;span style="color:var(--red); font-weight:700;"&gt;[PERINGATAN] Terdeteksi percobaan tempel pada lembar kerja - nilai dipotong 50%&lt;/span&gt;' : '');

  const ev = result.ev;
  let html = `&lt;div class="table-scroll"&gt;&lt;table class="compare final-table"&gt;
    &lt;tr&gt;&lt;th&gt;No&lt;/th&gt;&lt;th&gt;${data.kolomA}&lt;/th&gt;&lt;th&gt;${data.kolomB}&lt;/th&gt;&lt;th&gt;Jumlah Terjual&lt;/th&gt;&lt;th&gt;Harga Satuan&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;`;
  ev.rows.forEach(row=&gt;{
    html += `&lt;tr&gt;&lt;td&gt;${row.no}&lt;/td&gt;
      &lt;td class="${cellClass(row.nama,row.namaOk)}"&gt;${row.nama||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.kategori,row.kategoriOk)}"&gt;${row.kategori||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.jumlah,row.jumlahOk)}"&gt;${row.jumlah||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.harga,row.hargaOk)}"&gt;${row.harga||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.total,row.totalOk)}"&gt;${row.total||'-'}&lt;/td&gt;
      &lt;td class="${cellClass(row.status,row.statusOk)}"&gt;${row.status||'-'}&lt;/td&gt;
    &lt;/tr&gt;`;
  });
  html += '&lt;/table&gt;&lt;/div&gt;';
  html += `&lt;div class="table-scroll" style="margin-top:10px;"&gt;&lt;table class="compare final-table"&gt;
    &lt;tr&gt;&lt;th&gt;Ringkasan&lt;/th&gt;&lt;th&gt;Nilai&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Total Keseluruhan (SUM)&lt;/td&gt;&lt;td class="${cellClass(ev.summary.sum.raw, ev.summary.sum.ok)}"&gt;${ev.summary.sum.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Rata-rata (AVERAGE)&lt;/td&gt;&lt;td class="${cellClass(ev.summary.avg.raw, ev.summary.avg.ok)}"&gt;${ev.summary.avg.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Tertinggi (MAX)&lt;/td&gt;&lt;td class="${cellClass(ev.summary.max.raw, ev.summary.max.ok)}"&gt;${ev.summary.max.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Terendah (MIN)&lt;/td&gt;&lt;td class="${cellClass(ev.summary.min.raw, ev.summary.min.ok)}"&gt;${ev.summary.min.raw||'-'}&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/div&gt;`;
  document.getElementById('groupFinalTableWrap').innerHTML = html;

  document.getElementById('groupScorePanel').style.display='block';
  document.getElementById('groupPdfMsg').textContent='';
}

document.getElementById('btnNilaiKelompok').addEventListener('click', renderGroupAssessment);
document.getElementById('btnUnduhTugasPDF').addEventListener('click', downloadTugasKelompokPDF);

function downloadTugasKelompokPDF(){
  const msg = document.getElementById('groupPdfMsg');
  if(!lastGroupScore){ msg.textContent='Klik "Hitung Penilaian &amp; Tampilkan Hasil Akhir" terlebih dahulu.'; return; }
  if(!window.jspdf){ alert('Pustaka pembuat PDF gagal dimuat. Pastikan koneksi internet aktif lalu muat ulang halaman.'); return; }
  const members = getGroupMembers();
  const data = JURUSAN_DATA[currentJurusan];
  const ev = lastGroupScore.ev;

  const { jsPDF } = window.jspdf;
  const doc = new jsPDF({ unit:'mm', format:[215,330] }); // Ukuran F4
  const pageW=215, marginX=16;
  let y = 16;

  doc.setFont('helvetica','bold'); doc.setFontSize(13); doc.setTextColor(20,30,25);
  doc.text('HASIL AKHIR TUGAS KELOMPOK', marginX, y); y+=6;
  doc.setFontSize(10); doc.setTextColor(21,128,61);
  doc.text(JUDUL_MATERI, marginX, y); y+=6;
  doc.setFont('helvetica','normal'); doc.setFontSize(9.5); doc.setTextColor(50,60,55);
  doc.text(`Jurusan: ${ev.label}   |   Anggota: ${members.join(', ')}`, marginX, y); y+=6;
  doc.setFont('helvetica','bold'); doc.setFontSize(11); doc.setTextColor(13,90,42);
  doc.text(`Nilai Tugas Kelompok (Otomatis): ${lastGroupScore.finalScore}`, marginX, y); y+=8;

  doc.autoTable({
    startY: y,
    head: [[ 'No', data.kolomA, data.kolomB, 'Jumlah Terjual', 'Harga Satuan', 'Total', 'Status' ]],
    body: ev.rows.map(row=&gt;[row.no, row.nama||'-', row.kategori||'-', row.jumlah||'-', row.harga||'-', row.total||'-', row.status||'-']),
    theme: 'grid',
    headStyles: { fillColor: [21,128,61], textColor: 255, fontStyle: 'bold', fontSize: 8.5 },
    bodyStyles: { fontSize: 8.5 },
    margin: { left: marginX, right: marginX },
    didParseCell: function(d){
      if(d.section==='body'){
        const row = ev.rows[d.row.index];
        const okMap = [true, row.namaOk, row.kategoriOk, row.jumlahOk, row.hargaOk, row.totalOk, row.statusOk];
        if(d.column.index&gt;0){
          d.cell.styles.fillColor = okMap[d.column.index] ? [227,247,234] : [252,230,227];
          d.cell.styles.textColor = okMap[d.column.index] ? [13,90,42] : [155,51,36];
        }
      }
    }
  });

  let y2 = doc.lastAutoTable.finalY + 8;
  doc.autoTable({
    startY: y2,
    head: [['Ringkasan','Nilai']],
    body: [
      ['Total Keseluruhan (SUM)', ev.summary.sum.raw||'-'],
      ['Rata-rata (AVERAGE)', ev.summary.avg.raw||'-'],
      ['Tertinggi (MAX)', ev.summary.max.raw||'-'],
      ['Terendah (MIN)', ev.summary.min.raw||'-']
    ],
    theme: 'grid',
    headStyles: { fillColor: [13,90,42], textColor: 255, fontStyle: 'bold', fontSize: 8.5 },
    bodyStyles: { fontSize: 8.5 },
    margin: { left: marginX, right: marginX },
    didParseCell: function(d){
      if(d.section==='body' &amp;&amp; d.column.index===1){
        const keys=['sum','avg','max','min'];
        const ok = ev.summary[keys[d.row.index]].ok;
        d.cell.styles.fillColor = ok ? [227,247,234] : [252,230,227];
        d.cell.styles.textColor = ok ? [13,90,42] : [155,51,36];
      }
    }
  });

  let y3 = doc.lastAutoTable.finalY + 10;
  doc.setFont('helvetica','normal'); doc.setFontSize(8); doc.setTextColor(90,100,95);
  doc.text('Dokumen ini dicetak otomatis oleh sistem sebagai bukti hasil akhir pengerjaan Tugas Kelompok.', marginX, y3); y3+=14;
  doc.setFontSize(10); doc.setTextColor(20,30,25);
  const todayStr = new Date().toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric'});
  doc.text('......................, ' + todayStr, marginX, y3); y3+=16;
  doc.text('( ' + (members[0]||'...........................') + ' )', marginX, y3); y3+=5;
  doc.setFontSize(9); doc.setTextColor(90,100,95);
  doc.text('Perwakilan Kelompok', marginX, y3);

  msg.textContent='';
  doc.save('Hasil-Tugas-Kelompok-' + currentJurusan + '-' + Date.now() + '.pdf');
}

const LETTERS=["A","B","C","D","E"];
let lastPGDetail=[], lastPracticeDetail=[], lastEssayDetail=[], lastSubmission=null;

// ===================== 10 SOAL PG (HOTS) =====================
const PG = [
 {q:"Seorang siswa membuat rumus pajak 10% dari harga barang dengan menulis tarif pajak di sel B1, lalu rumus =A2*B1 disalin ke A3, A4, dst. Namun hasilnya salah karena B1 ikut bergeser. Perbaikan yang tepat adalah mengubah rumus menjadi...",o:["=A2*B2","=A2*$B$1","=A2+B1","=$A2*B1","=A2*B1$"],a:1},
 {q:"Seorang kasir mengetik total belanja dengan mengetik langsung angka hasil penjumlahan manual di setiap transaksi, bukan dengan rumus. Risiko utama dari kebiasaan ini adalah...",o:["Data akan otomatis diperbarui jika harga berubah","Total tidak akan ikut berubah otomatis saat data sumber diubah","Spreadsheet akan berjalan lebih cepat","Format Currency tidak akan berfungsi","Tidak ada risiko karena hasilnya tetap akurat"],a:1},
 {q:"Sebuah sel menampilkan 12000 sebagai Rp12.000 setelah diformat Currency. Saat sel tersebut dipakai dalam rumus SUM, nilai yang dihitung sistem adalah...",o:["12000 (nilai asli)","Rp12.000 (termasuk simbol Rp)","0 karena formatnya berubah","12.000 sebagai teks","Error karena format tidak dikenali"],a:0},
 {q:"Seorang siswa ingin mengisi nomor urut 1 sampai 50 di kolom A tanpa mengetik satu per satu. Cara paling efisien yang sebaiknya digunakan adalah...",o:["Mengetik manual seluruh angka","Menggunakan Fill Handle (AutoFill) dari dua angka awal yang membentuk pola","Menyalin angka 1 ke seluruh sel","Menggunakan fungsi SUM","Mengubah format sel menjadi Number"],a:1},
 {q:"Sebuah tabel gaji menampilkan angka 2500000 tanpa pemisah ribuan sehingga sulit dibaca dengan cepat. Format yang paling tepat diterapkan adalah...",o:["Percentage","Date","Comma Style (pemisah ribuan)","General","Text"],a:2},
 {q:"Seorang guru ingin mengurutkan data nilai siswa dari yang tertinggi ke terendah agar mudah menentukan peringkat kelas. Fitur yang paling tepat digunakan adalah...",o:["Conditional Formatting","Merge &amp; Center","Sorting (urutkan Z-A)","Fill Handle","Chart"],a:2},
 {q:"Sebuah tabel penjualan ingin menyorot otomatis sel dengan nilai di atas rata-rata menggunakan warna berbeda tanpa menandai manual satu per satu. Fitur yang tepat digunakan adalah...",o:["Sorting","Conditional Formatting","Merge &amp; Center","Freeze Panes","Text Wrap"],a:1},
 {q:"Seorang siswa ingin membuat grafik yang membandingkan hanya kolom Nama Produk dan Total Penjualan, sementara kolom Kategori di antara keduanya tidak ikut disertakan. Cara memilih kedua kolom yang tidak berdampingan tersebut adalah...",o:["Blok semua kolom lalu hapus kolom Kategori","Tahan tombol CTRL sambil mengklik kedua kolom yang diinginkan","Hanya bisa memilih kolom yang berdampingan","Menggunakan fungsi SUM terlebih dahulu","Mengubah format sel menjadi Date"],a:1},
 {q:"Judul laporan \"LAPORAN PENJUALAN BULAN OKTOBER\" perlu ditampilkan memanjang dan berada tepat di tengah atas tabel yang terdiri dari 5 kolom. Fitur yang paling tepat digunakan adalah...",o:["Merge &amp; Center","Conditional Formatting","Sorting","Fill Handle","Freeze Panes"],a:0},
 {q:"Setelah data penjualan bulan lalu direvisi, laporan yang memakai rumus SUM(B2:B6) otomatis memperbarui hasil totalnya tanpa perlu dihitung ulang manual. Hal ini terjadi karena...",o:["Spreadsheet menyimpan nilai lama secara permanen","Rumus merujuk ke referensi sel, bukan nilai tetap, sehingga hasil otomatis mengikuti perubahan data sumber","Fitur Conditional Formatting bekerja otomatis","Format Currency memperbarui nilai secara otomatis","Fill Handle menghitung ulang semua sel"],a:1}
];

pgC_render();
function pgC_render(){
  const pgC=document.getElementById('pgContainer');
  PG.forEach((item,idx)=&gt;{
    const card=document.createElement('div'); card.className='q-card';
    card.innerHTML=`&lt;div class="q-num"&gt;SOAL ${idx+1} / ${PG.length}&lt;/div&gt;&lt;div class="q-text"&gt;${idx+1}. ${item.q}&lt;/div&gt;&lt;div class="options"&gt;&lt;/div&gt;`;
    const opts=card.querySelector('.options');
    item.o.forEach((optText,oi)=&gt;{
      const label=document.createElement('label'); label.className='opt';
      label.innerHTML=`&lt;input type="radio" name="pg${idx}" value="${oi}"&gt;&lt;span&gt;${LETTERS[oi]}. ${optText}&lt;/span&gt;`;
      label.querySelector('input').addEventListener('change',()=&gt;{
        opts.querySelectorAll('.opt').forEach(o=&gt;o.classList.remove('selected'));
        label.classList.add('selected');
      });
      opts.appendChild(label);
    });
    pgC.appendChild(card);
  });
}

function scorePG(){
  let correct=0; lastPGDetail=[];
  PG.forEach((item,idx)=&gt;{
    const picked=document.querySelector(`input[name="pg${idx}"]:checked`);
    const pickedIdx = picked ? parseInt(picked.value) : null;
    const isCorrect = pickedIdx!==null &amp;&amp; pickedIdx===item.a;
    if(isCorrect) correct++;
    lastPGDetail.push({
      no: idx+1,
      q: item.q,
      picked: pickedIdx!==null ? (LETTERS[pickedIdx]+'. '+item.o[pickedIdx]) : '(tidak dijawab)',
      correct: LETTERS[item.a]+'. '+item.o[item.a],
      isCorrect
    });
  });
  return {score:Math.round(correct/PG.length*100), correct, total:PG.length};
}

// ===================== 10 SOAL PRAKTIK (HOTS) =====================
const PRACTICE = [
 {
   title:"Menjumlahkan Total Penjualan",
   instruksi:"Sebuah toko mencatat penjualan 5 bulan terakhir. Tuliskan rumus yang tepat di sel B7 untuk menghitung Total Penjualan.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Bulan&lt;/td&gt;&lt;td class="label-cell"&gt;Penjualan&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Januari&lt;/td&gt;&lt;td&gt;12000000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;3&lt;/th&gt;&lt;td class="label-cell"&gt;Februari&lt;/td&gt;&lt;td&gt;15000000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;4&lt;/th&gt;&lt;td class="label-cell"&gt;Maret&lt;/td&gt;&lt;td&gt;9000000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;5&lt;/th&gt;&lt;td class="label-cell"&gt;April&lt;/td&gt;&lt;td&gt;18000000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;6&lt;/th&gt;&lt;td class="label-cell"&gt;Mei&lt;/td&gt;&lt;td&gt;21000000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;7&lt;/th&gt;&lt;td class="label-cell"&gt;Total&lt;/td&gt;&lt;td class="target-cell"&gt;B7 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"SUM", refs:["B2","B6"], exact:/^=?SUM\(B2:B6\)$/, contoh:"=SUM(B2:B6)"
 },
 {
   title:"Menghitung Rata-Rata Nilai",
   instruksi:"Tuliskan rumus yang tepat di sel B7 untuk menghitung rata-rata nilai 5 siswa berikut.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Nama&lt;/td&gt;&lt;td class="label-cell"&gt;Nilai&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Andi&lt;/td&gt;&lt;td&gt;80&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;3&lt;/th&gt;&lt;td class="label-cell"&gt;Budi&lt;/td&gt;&lt;td&gt;75&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;4&lt;/th&gt;&lt;td class="label-cell"&gt;Citra&lt;/td&gt;&lt;td&gt;90&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;5&lt;/th&gt;&lt;td class="label-cell"&gt;Dewi&lt;/td&gt;&lt;td&gt;65&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;6&lt;/th&gt;&lt;td class="label-cell"&gt;Eka&lt;/td&gt;&lt;td&gt;85&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;7&lt;/th&gt;&lt;td class="label-cell"&gt;Rata-rata&lt;/td&gt;&lt;td class="target-cell"&gt;B7 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"AVERAGE", refs:["B2","B6"], exact:/^=?AVERAGE\(B2:B6\)$/, contoh:"=AVERAGE(B2:B6)"
 },
 {
   title:"Mencari Nilai Tertinggi",
   instruksi:"Dari data penjualan 5 sales berikut, tuliskan rumus di sel B7 untuk menemukan nilai penjualan TERTINGGI.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Sales&lt;/td&gt;&lt;td class="label-cell"&gt;Penjualan&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Rini&lt;/td&gt;&lt;td&gt;3200000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;3&lt;/th&gt;&lt;td class="label-cell"&gt;Doni&lt;/td&gt;&lt;td&gt;4100000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;4&lt;/th&gt;&lt;td class="label-cell"&gt;Sari&lt;/td&gt;&lt;td&gt;2800000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;5&lt;/th&gt;&lt;td class="label-cell"&gt;Tono&lt;/td&gt;&lt;td&gt;3900000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;6&lt;/th&gt;&lt;td class="label-cell"&gt;Wati&lt;/td&gt;&lt;td&gt;4500000&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;7&lt;/th&gt;&lt;td class="label-cell"&gt;Tertinggi&lt;/td&gt;&lt;td class="target-cell"&gt;B7 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"MAX", refs:["B2","B6"], exact:/^=?MAX\(B2:B6\)$/, contoh:"=MAX(B2:B6)"
 },
 {
   title:"Mencari Nilai Terendah",
   instruksi:"Dari data suhu harian berikut, tuliskan rumus di sel B7 untuk menemukan suhu TERENDAH.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Hari&lt;/td&gt;&lt;td class="label-cell"&gt;Suhu (°C)&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Senin&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;3&lt;/th&gt;&lt;td class="label-cell"&gt;Selasa&lt;/td&gt;&lt;td&gt;26&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;4&lt;/th&gt;&lt;td class="label-cell"&gt;Rabu&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;5&lt;/th&gt;&lt;td class="label-cell"&gt;Kamis&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;6&lt;/th&gt;&lt;td class="label-cell"&gt;Jumat&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;7&lt;/th&gt;&lt;td class="label-cell"&gt;Terendah&lt;/td&gt;&lt;td class="target-cell"&gt;B7 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"MIN", refs:["B2","B6"], exact:/^=?MIN\(B2:B6\)$/, contoh:"=MIN(B2:B6)"
 },
 {
   title:"Menghitung Jumlah Data Terisi",
   instruksi:"Kolom Nama berisi campuran teks, dan baris ke-9 kosong (belum diisi). Tuliskan rumus di sel A9 untuk menghitung BERAPA BANYAK data nama yang sudah terisi dari A2 sampai A8 (tanpa menghitung sel kosong).",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Nama Peserta&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Arka&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;3&lt;/th&gt;&lt;td class="label-cell"&gt;Bela&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;4&lt;/th&gt;&lt;td class="label-cell"&gt;Cici&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;5&lt;/th&gt;&lt;td class="label-cell"&gt;(kosong)&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;6&lt;/th&gt;&lt;td class="label-cell"&gt;Dini&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;7&lt;/th&gt;&lt;td class="label-cell"&gt;Eno&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;8&lt;/th&gt;&lt;td class="label-cell"&gt;Fira&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;9&lt;/th&gt;&lt;td class="target-cell"&gt;A9 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"COUNTA", refs:["A2","A8"], exact:/^=?COUNTA\(A2:A8\)$/, contoh:"=COUNTA(A2:A8)"
 },
 {
   title:"Menentukan Status Kelulusan (IF)",
   instruksi:"Nilai siswa ada di sel B2. Tuliskan rumus di sel C2 agar menampilkan \"Lulus\" jika nilai 75 atau lebih, dan \"Tidak Lulus\" jika kurang dari itu.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;th&gt;C&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Nama&lt;/td&gt;&lt;td class="label-cell"&gt;Nilai&lt;/td&gt;&lt;td class="label-cell"&gt;Status&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Hadi&lt;/td&gt;&lt;td&gt;80&lt;/td&gt;&lt;td class="target-cell"&gt;C2 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"IF", refs:["B2","75","LULUS"], exact:/^=?IF\(B2&gt;=75,"LULUS","TIDAKLULUS"\)$/, contoh:'=IF(B2&gt;=75,"Lulus","Tidak Lulus")'
 },
 {
   title:"Kelulusan dengan Dua Syarat (IF + AND)",
   instruksi:"Siswa dinyatakan Lulus hanya jika Nilai (B2) minimal 75 DAN Kehadiran (C2) minimal 80. Tuliskan rumus yang tepat di sel D2.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;th&gt;C&lt;/th&gt;&lt;th&gt;D&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Nama&lt;/td&gt;&lt;td class="label-cell"&gt;Nilai&lt;/td&gt;&lt;td class="label-cell"&gt;Kehadiran(%)&lt;/td&gt;&lt;td class="label-cell"&gt;Status&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Rara&lt;/td&gt;&lt;td&gt;80&lt;/td&gt;&lt;td&gt;85&lt;/td&gt;&lt;td class="target-cell"&gt;D2 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"IF", refs:["AND","B2","C2"], exact:/^=?IF\(AND\(B2&gt;=75,C2&gt;=80\),"LULUS","TIDAKLULUS"\)$/, contoh:'=IF(AND(B2&gt;=75,C2&gt;=80),"Lulus","Tidak Lulus")'
 },
 {
   title:"Menghitung Banyak Siswa Bernilai Tinggi (COUNTIF)",
   instruksi:"Data nilai 9 siswa ada di B2 sampai B10. Tuliskan rumus untuk menghitung BERAPA BANYAK siswa yang nilainya 80 atau lebih.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Nama&lt;/td&gt;&lt;td class="label-cell"&gt;Nilai&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2-10&lt;/th&gt;&lt;td class="label-cell"&gt;(9 siswa)&lt;/td&gt;&lt;td class="label-cell"&gt;60 - 95&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;11&lt;/th&gt;&lt;td class="label-cell"&gt;Jumlah Nilai Tinggi&lt;/td&gt;&lt;td class="target-cell"&gt;B11 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"COUNTIF", refs:["B2","B10","&gt;=80"], exact:/^=?COUNTIF\(B2:B10,"&gt;=80"\)$/, contoh:'=COUNTIF(B2:B10,"&gt;=80")'
 },
 {
   title:"Menjumlahkan Penjualan per Kategori (SUMIF)",
   instruksi:"Kolom A berisi Kategori, kolom B berisi Jumlah Terjual. Tuliskan rumus untuk menjumlahkan HANYA penjualan dengan kategori \"Elektronik\" dari A2:A10 dan B2:B10.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Kategori&lt;/td&gt;&lt;td class="label-cell"&gt;Jumlah&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2-10&lt;/th&gt;&lt;td class="label-cell"&gt;Elektronik/Fashion/dll&lt;/td&gt;&lt;td class="label-cell"&gt;Angka&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;11&lt;/th&gt;&lt;td class="label-cell"&gt;Total Elektronik&lt;/td&gt;&lt;td class="target-cell"&gt;B11 = ?&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:"SUMIF", refs:["A2","A10","ELEKTRONIK","B2","B10"], exact:/^=?SUMIF\(A2:A10,"ELEKTRONIK",B2:B10\)$/, contoh:'=SUMIF(A2:A10,"Elektronik",B2:B10)'
 },
 {
   title:"Menghitung Persentase Kontribusi (Referensi Absolut)",
   instruksi:"B7 berisi Total keseluruhan. Tuliskan rumus di sel C2 untuk menghitung persentase kontribusi B2 terhadap Total, dengan referensi ke B7 DIKUNCI (absolut) agar tetap benar saat rumus disalin ke C3, C4, dst.",
   grid:`&lt;table class="excel-grid"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;th&gt;C&lt;/th&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;1&lt;/th&gt;&lt;td class="label-cell"&gt;Bulan&lt;/td&gt;&lt;td class="label-cell"&gt;Penjualan&lt;/td&gt;&lt;td class="label-cell"&gt;% Kontribusi&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;2&lt;/th&gt;&lt;td class="label-cell"&gt;Januari&lt;/td&gt;&lt;td&gt;12000000&lt;/td&gt;&lt;td class="target-cell"&gt;C2 = ?&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&gt;&lt;th class="rownum"&gt;7&lt;/th&gt;&lt;td class="label-cell"&gt;Total&lt;/td&gt;&lt;td&gt;75000000&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`,
   func:null, refs:["B2","B7","$"], exact:/^=?B2\/\$B\$7$/, contoh:"=B2/$B$7"
 }
];

practice_render();
function practice_render(){
  const pc=document.getElementById('practiceContainer');
  PRACTICE.forEach((item,idx)=&gt;{
    const card=document.createElement('div'); card.className='q-card';
    card.innerHTML=`&lt;div class="q-num"&gt;PRAKTIK ${idx+1} / ${PRACTICE.length} - ${item.title}&lt;/div&gt;
      &lt;div class="q-text" style="font-weight:400; font-size:.88rem; color:var(--ink-soft);"&gt;${item.instruksi}&lt;/div&gt;
      &lt;div class="table-scroll"&gt;${item.grid}&lt;/div&gt;
      &lt;input type="text" class="formula-input practice-input" data-idx="${idx}" placeholder="Ketik rumus di sini, contoh: =SUM(A1:A5)" autocomplete="off" autocapitalize="off" spellcheck="false"&gt;
      &lt;div class="editor-meta"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="flag"&gt;[PERINGATAN] Percobaan tempel terdeteksi - bernilai 0&lt;/span&gt;&lt;/div&gt;`;
    const input=card.querySelector('.practice-input');
    const flag=card.querySelector('.flag');
    input.addEventListener('paste',(e)=&gt;{ e.preventDefault(); practicePasteFlags[idx]=true; flag.classList.add('show'); input.classList.add('wrong-paste'); });
    pc.appendChild(card);
  });
}
const practicePasteFlags=new Array(PRACTICE.length).fill(false);

function normFormula(s){
  return s.trim().toUpperCase().replace(/\s+/g,'').replace(/;/g,',');
}
function scorePractice(){
  let total=0; lastPracticeDetail=[];
  const inputs=document.querySelectorAll('.practice-input');
  PRACTICE.forEach((item,idx)=&gt;{
    const raw=inputs[idx].value;
    let s=0;
    if(!practicePasteFlags[idx] &amp;&amp; raw.trim().length&gt;0){
      let norm=normFormula(raw);
      const normWithEq = norm.startsWith('=') ? norm : ('='+norm);
      const normNoEq = norm.startsWith('=') ? norm.slice(1) : norm;
      if(item.exact.test(normWithEq) || item.exact.test(normNoEq) || item.exact.test(norm)){
        s=100;
      } else {
        const funcOk = item.func ? norm.includes(item.func) : norm.includes('$');
        const refsOk = item.refs.every(r=&gt;norm.includes(r.toUpperCase().replace(/\s+/g,'')));
        if(funcOk &amp;&amp; refsOk) s=60;
        else if(funcOk || refsOk) s=30;
        else s=10;
      }
    }
    total+=s;
    lastPracticeDetail.push({
      no: idx+1,
      title: item.title,
      userFormula: practicePasteFlags[idx] ? '(terdeteksi tempel - bernilai 0)' : (raw.trim() || '(tidak diisi)'),
      contoh: item.contoh,
      skor: s
    });
  });
  return Math.round(total/PRACTICE.length);
}

// ===================== 5 SOAL ESAI (HOTS) =====================
const ESSAY = [
 {q:"Sebuah rumus pajak yang disalin ke banyak baris menghasilkan nilai salah karena sel tarif pajaknya ikut bergeser mengikuti baris tujuan. Jelaskan konsep referensi relatif dan absolut, serta bagaimana referensi absolut mengatasi masalah tersebut!",kw:["relatif","absolut","$","mengunci","referensi","bergeser"],min:60},
 {q:"Sebuah toko kelontong mencatat ratusan transaksi secara manual di buku dan sering salah menjumlahkan total harian. Jelaskan bagaimana spreadsheet dengan fungsi SUM dan format Currency dapat membantu mengatasi masalah tersebut!",kw:["sum","currency","otomatis","akurat","rumus","total"],min:60},
 {q:"Seorang guru ingin sistem otomatis menampilkan \"Lulus\" atau \"Tidak Lulus\" berdasarkan nilai siswa tanpa memeriksa satu per satu secara manual. Jelaskan cara kerja fungsi IF dan berikan contoh rumusnya untuk skenario ini!",kw:["if","syarat","kondisi","lulus","benar","salah"],min:60},
 {q:"Sebuah data penjualan memiliki 500 baris dan perlu diketahui berapa transaksi dengan nilai di atas 1 juta, serta total penjualan untuk kategori tertentu saja. Jelaskan mengapa COUNTIF dan SUMIF lebih efisien dibanding menghitung manual satu per satu pada data sebesar ini!",kw:["countif","sumif","kriteria","syarat","efisien","data besar"],min:60},
 {q:"Sebuah laporan penjualan berbentuk angka mentah sulit dipahami manajer dengan cepat saat rapat. Jelaskan manfaat conditional formatting dan chart (grafik) dalam membantu penyajian data spreadsheet agar lebih mudah dipahami!",kw:["conditional formatting","chart","grafik","visual","mudah dipahami","warna"],min:60}
];

essay_render();
const essayPasteFlags=new Array(ESSAY.length).fill(false);
function essay_render(){
  const ec=document.getElementById('essayContainer');
  ESSAY.forEach((item,idx)=&gt;{
    const card=document.createElement('div'); card.className='q-card';
    card.innerHTML=`&lt;div class="q-num"&gt;ESAI ${idx+1} / ${ESSAY.length}&lt;/div&gt;&lt;div class="q-text"&gt;${idx+1}. ${item.q}&lt;/div&gt;
      &lt;textarea class="essay-box" data-idx="${idx}" placeholder="Ketik jawabanmu di sini..."&gt;&lt;/textarea&gt;
      &lt;div class="editor-meta"&gt;&lt;span class="cc"&gt;0 karakter&lt;/span&gt;&lt;span class="flag"&gt;[PERINGATAN] Percobaan tempel terdeteksi - bernilai 0&lt;/span&gt;&lt;/div&gt;`;
    const box=card.querySelector('.essay-box'); const meta=card.querySelector('.cc'); const flag=card.querySelector('.flag');
    box.addEventListener('input',()=&gt;{meta.textContent=box.value.trim().length+' karakter';});
    box.addEventListener('paste',e=&gt;{e.preventDefault(); essayPasteFlags[idx]=true; flag.classList.add('show');});
    ec.appendChild(card);
  });
}
function normWords(t){return Array.from(new Set(t.toLowerCase().replace(/[^a-z0-9\s]/gi,' ').split(/\s+/).filter(w=&gt;w.length&gt;=3)));}
function scoreEssay(){
  let total=0; lastEssayDetail=[];
  const boxes=document.querySelectorAll('#essayContainer .essay-box');
  ESSAY.forEach((item,idx)=&gt;{
    const typed=boxes[idx].value.trim();
    let s=0;
    if(!essayPasteFlags[idx] &amp;&amp; typed.length&gt;0){
      const lengthRatio=Math.min(1,typed.length/item.min);
      const typedLower=typed.toLowerCase();
      let matched=0; item.kw.forEach(k=&gt;{if(typedLower.indexOf(k)!==-1)matched++;});
      const kwRatio=matched/item.kw.length;
      s=Math.min(100,Math.round(100*(0.3*lengthRatio+0.7*kwRatio)));
    }
    total+=s;
    lastEssayDetail.push({
      no: idx+1,
      q: item.q,
      answer: essayPasteFlags[idx] ? '(terdeteksi tempel - bernilai 0)' : (typed || '(tidak diisi)'),
      skor: s
    });
  });
  return Math.round(total/ESSAY.length);
}

// ===================== SUBMIT =====================
document.getElementById('btnHitung').addEventListener('click', ()=&gt;{
  const noAbsen=document.getElementById('noAbsen').value.trim();
  const nama=document.getElementById('nama').value.trim();
  const kelas=document.getElementById('kelas').value.trim();
  const msg=document.getElementById('uploadMsg');
  if(!noAbsen||!nama||!kelas){ msg.textContent='Lengkapi Kelas, No. Absen, dan Nama terlebih dahulu.'; return; }

  const pg=scorePG(); const practice=scorePractice(); const essay=scoreEssay();
  const finalScore=Math.round(pg.score*0.35 + practice*0.40 + essay*0.25);

  lastSubmission={noAbsen,nama,kelas,pg,practice,essay,finalScore};
  document.getElementById('btnUnduhPDF').disabled=false;

  msg.textContent='';
  const panel=document.getElementById('resultPanel');
  const scoreEl=document.getElementById('scoreValue');
  const detailEl=document.getElementById('resultDetail');
  panel.style.display='block';
  scoreEl.textContent=finalScore;
  detailEl.textContent=`Pilihan Ganda: ${pg.correct}/${pg.total} benar (${pg.score}) | Praktik Rumus: ${practice} | Esai: ${essay} - Mengirim ke guru...`;

  fetch(GAS_ENDPOINT,{method:'POST',mode:'no-cors',headers:{'Content-Type':'application/json'},
    body:JSON.stringify({noAbsen,nama,kelas,skor:finalScore,judulMateri:JUDUL_MATERI})
  }).then(()=&gt;{
    detailEl.textContent=`Pilihan Ganda: ${pg.correct}/${pg.total} benar (${pg.score}) | Praktik Rumus: ${practice} | Esai: ${essay} - Nilai sudah terkirim ke guru.`;
  }).catch(()=&gt;{
    detailEl.textContent=`Pilihan Ganda: ${pg.correct}/${pg.total} benar (${pg.score}) | Praktik Rumus: ${practice} | Esai: ${essay} - Gagal terkirim otomatis, tunjukkan skor ini ke guru.`;
  });
});

// ===================== UNDUH BUKTI MENGERJAKAN (PDF F4) =====================
document.getElementById('btnUnduhPDF').addEventListener('click', downloadBuktiPDF);

function downloadBuktiPDF(){
  if(!lastSubmission){ alert('Klik tombol "Hitung &amp; Kirim Nilai" terlebih dahulu sebelum mengunduh bukti.'); return; }
  if(!window.jspdf){ alert('Pustaka pembuat PDF gagal dimuat. Pastikan koneksi internet aktif lalu muat ulang halaman.'); return; }
  const { jsPDF } = window.jspdf;
  const doc = new jsPDF({ unit:'mm', format:[215,330] }); // Ukuran F4
  const pageW=215, pageH=330, marginX=16, marginTop=18, marginBottom=16, contentW=pageW-marginX*2;
  let y = marginTop;

  function ensureSpace(h){ if(y+h&gt;pageH-marginBottom){ doc.addPage([pageW,pageH]); y=marginTop; } }
  function divider(){ ensureSpace(4); doc.setDrawColor(190,200,195); doc.line(marginX,y,pageW-marginX,y); y+=6; }
  function text(str, opts={}){
    const size=opts.size||10; const bold=opts.bold||false; const color=opts.color||[20,30,25];
    doc.setFont('helvetica', bold?'bold':'normal'); doc.setFontSize(size); doc.setTextColor(color[0],color[1],color[2]);
    const lines = doc.splitTextToSize(String(str), opts.width||contentW);
    lines.forEach(line=&gt;{
      ensureSpace(size*0.55);
      doc.text(line, marginX, y);
      y += size*0.5;
    });
    y += (opts.gap!==undefined ? opts.gap : 2);
  }

  text('BUKTI MENGERJAKAN EVALUASI INDIVIDU', {size:14, bold:true, gap:1});
  text(JUDUL_MATERI, {size:11, bold:true, color:[21,128,61], gap:4});
  divider();

  text('IDENTITAS PESERTA', {size:10, bold:true, gap:3});
  text('Kelas       : ' + lastSubmission.kelas, {size:10});
  text('No. Absen   : ' + lastSubmission.noAbsen, {size:10});
  text('Nama        : ' + lastSubmission.nama, {size:10, gap:4});
  divider();

  text('RINGKASAN NILAI', {size:10, bold:true, gap:3});
  text(`Pilihan Ganda (bobot 35%)  : ${lastSubmission.pg.correct}/${lastSubmission.pg.total} benar  -&gt;  skor ${lastSubmission.pg.score}`, {size:10});
  text(`Praktik Rumus (bobot 40%)  : skor ${lastSubmission.practice}`, {size:10});
  text(`Esai (bobot 25%)           : skor ${lastSubmission.essay}`, {size:10, gap:2});
  text(`NILAI AKHIR                : ${lastSubmission.finalScore}`, {size:12, bold:true, color:[13,90,42], gap:4});
  divider();

  text('RINCIAN JAWABAN PILIHAN GANDA', {size:10, bold:true, gap:3});
  lastPGDetail.forEach(item=&gt;{
    text(`${item.no}. ${item.q}`, {size:9, gap:1});
    text(`   Jawaban dipilih : ${item.picked}`, {size:9});
    text(`   Jawaban benar   : ${item.correct}  [${item.isCorrect?'BENAR':'SALAH'}]`, {size:9, gap:3});
  });
  divider();

  text('RINCIAN JAWABAN PRAKTIK RUMUS', {size:10, bold:true, gap:3});
  lastPracticeDetail.forEach(item=&gt;{
    text(`${item.no}. ${item.title}`, {size:9, bold:true, gap:1});
    text(`   Rumus ditulis   : ${item.userFormula}`, {size:9});
    text(`   Contoh benar    : ${item.contoh}`, {size:9});
    text(`   Skor            : ${item.skor}`, {size:9, gap:3});
  });
  divider();

  text('RINCIAN JAWABAN ESAI', {size:10, bold:true, gap:3});
  lastEssayDetail.forEach(item=&gt;{
    text(`${item.no}. ${item.q}`, {size:9, bold:true, gap:1});
    text(`   Jawaban : ${item.answer}`, {size:9});
    text(`   Skor    : ${item.skor}`, {size:9, gap:3});
  });

  ensureSpace(40);
  y += 8;
  text('Dokumen ini dicetak otomatis oleh sistem sebagai bukti telah mengerjakan evaluasi.', {size:8, color:[90,100,95], gap:10});
  const todayStr = new Date().toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric'});
  text('......................, ' + todayStr, {size:10, gap:18});
  text('( ' + lastSubmission.nama + ' )', {size:10, gap:2});
  text('Siswa', {size:9, color:[90,100,95]});

  const safeNama = lastSubmission.nama.replace(/[^a-z0-9]+/gi,'_');
  doc.save('Bukti-Spreadsheet-' + safeNama + '.pdf');
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/pengenalan-aplikasi-pengolah-angka.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-5344647207214984129</guid><pubDate>Thu, 01 Oct 2026 02:38:15 +0000</pubDate><atom:updated>2026-10-01T09:38:15.125+07:00</atom:updated><title>Kaidah Kebahasaan Teks Ceramah (Bagian 1)</title><description>&lt;style&gt;
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&amp;family=Source+Sans+3:wght@400;500;600;700&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;display=swap');
.epb{
  --blueprint:#0F2A3D;
  --blueprint-deep:#0A1E2C;
  --panel:#4A2E12;
  --paper:#F3F0E6;
  --ink:#132029;
  --amber:#F5A623;
  --amber-deep:#C97F12;
  --mint:#6EC6CA;
  --line:rgba(234,242,245,0.16);
  --off-white:#EAF2F5;
  --muted:#9FB4C2;
}
.epb *{box-sizing:border-box;}
.epb{scroll-behavior:smooth; background:#0A1F2E;}
.epb{
  margin:0;
  position:relative;
  overflow:hidden;
  background:var(--blueprint);
  color:var(--off-white);
  font-family:'Source Sans 3',sans-serif;
  line-height:1.65;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 34px 34px;
}
.epb h1, .epb h2, .epb h3, .epb h4{font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.02em; margin:0 0 0.5em;}
.epb .mono{font-family:'IBM Plex Mono',monospace;}
.epb a{color:var(--amber);}
.epb .crop{position:absolute; width:22px; height:22px; z-index:5; opacity:.55; pointer-events:none;}
.epb .crop::before, .epb .crop::after{content:"";position:absolute;background:var(--mint);}
.epb .crop::before{width:100%;height:1px;top:50%;left:0;}
.epb .crop::after{width:1px;height:100%;left:50%;top:0;}
.epb .crop.tl{top:14px;left:14px;}
.epb .crop.tr{top:14px;right:14px;}
.epb .crop.bl{bottom:14px;left:14px;}
.epb .crop.br{bottom:14px;right:14px;}
.epb header.cover{
  position:relative;
  padding:40px 24px 36px;
  border-bottom:1px solid var(--line);
  overflow:hidden;
}
.epb .doc-code{font-family:'IBM Plex Mono',monospace; color:var(--amber); letter-spacing:0.15em; font-size:0.78rem; text-transform:uppercase; margin-bottom:18px; display:flex; gap:10px; align-items:center;}
.epb .doc-code::before{content:"◆"; color:var(--amber);}
.epb .cover h1{font-size:clamp(1.6rem,7vw,2.5rem); color:var(--off-white); max-width:16ch; line-height:1.05;}
.epb .cover .subtitle{color:var(--muted); font-size:1.05rem; max-width:60ch; margin-top:14px;}
.epb .meta-strip{
  margin-top:38px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px dashed var(--line); border-bottom:1px dashed var(--line);
}
.epb .meta-strip div{padding:14px 16px; border-right:1px dashed var(--line);}
.epb .meta-strip div:last-child{border-right:none;}
.epb .meta-strip .k{font-family:'IBM Plex Mono',monospace; font-size:0.68rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.1em;}
.epb .meta-strip .v{font-size:0.98rem; color:var(--off-white); margin-top:4px; font-weight:600;}
.epb .stamp{
  position:absolute; top:40px; right:20px; width:104px; height:104px;
  border:2.5px solid var(--amber); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  transform:rotate(-11deg);
  color:var(--amber); font-family:'IBM Plex Mono',monospace;
  text-align:center; font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase;
  opacity:0.9;
}
.epb .stamp .ring{position:absolute; inset:8px; border:1px dashed var(--amber); border-radius:50%;}
.epb .stamp b{display:block; font-family:'Oswald',sans-serif; font-size:1.05rem; letter-spacing:0.06em; margin-top:4px;}

@media (max-width:760px){.epb .stamp{display:none;}
.epb }


.epb main{max-width:920px; margin:0 auto; padding:0 20px 60px;}
.epb .section{padding-top:64px;}
.epb .eyebrow{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; margin-bottom:10px; display:flex; align-items:center; gap:10px;}
.epb .eyebrow::after{content:""; flex:1; height:1px; background:var(--line);}
.epb h2.section-title{font-size:1.7rem; color:var(--off-white);}
.epb p{color:#DDE7EC;}
.epb .lead{color:var(--muted); font-size:1.02rem; max-width:72ch;}
.epb .panel{
  background:var(--panel); border:1px solid var(--line); border-radius:2px;
  padding:26px 28px; margin:22px 0;
}
.epb .panel.tp{border-left:4px solid var(--mint);}
.epb .panel.warn{border-left:4px solid var(--amber);}
.epb .tp-box{display:grid; gap:14px; margin-top:18px;}
.epb .tp-item{display:flex; gap:14px; align-items:flex-start;}
.epb .tp-item .idx{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; min-width:34px;}
.epb .flow{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; margin-top:20px;}
.epb .flow .step{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px; position:relative;}
.epb .flow .step .tag{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase;}
.epb .flow .step h4{font-size:1rem; margin-top:8px; color:var(--off-white);}
.epb .flow .step .time{position:absolute; top:16px; right:16px; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; color:var(--amber);}
.epb .grid-3{display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:14px; margin-top:16px;}
.epb .card{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px;}
.epb .card h4{color:var(--amber); font-size:0.95rem;}
.epb .criteria{display:grid; gap:16px; margin-top:20px;}
.epb .crit{
  background:var(--blueprint-deep); border:1px solid var(--line);
  display:grid; grid-template-columns:64px 1fr; gap:0;
}
.epb .crit .num{
  background:var(--amber); color:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace;
  font-weight:700; display:flex; align-items:center; justify-content:center; font-size:1.05rem;
}
.epb .crit .body{padding:18px 20px;}
.epb .crit h4{color:var(--off-white); font-size:1.02rem; margin-bottom:6px;}
.epb .crit .q{margin-top:10px; padding-top:10px; border-top:1px dashed var(--line); font-family:'IBM Plex Mono',monospace; font-size:0.86rem; color:var(--amber);}
.epb .timeline{margin-top:24px; position:relative; padding-left:34px;}
.epb .timeline::before{content:""; position:absolute; left:11px; top:6px; bottom:6px; width:2px; background:var(--line);}
.epb .tl-item{position:relative; padding-bottom:26px;}
.epb .tl-item:last-child{padding-bottom:0;}
.epb .tl-item::before{content:""; position:absolute; left:-34px; top:2px; width:22px; height:22px; border-radius:50%; background:var(--blueprint); border:2px solid var(--amber);}
.epb .tl-item h4{color:var(--off-white); font-size:1rem; margin-bottom:4px;}
.epb .doc-sample{
  background:var(--paper); color:var(--ink); border-radius:2px; padding:0; margin-top:22px;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .doc-sample .doc-head{background:var(--ink); color:var(--paper); padding:12px 22px; font-family:'IBM Plex Mono',monospace; font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; display:flex; justify-content:space-between;}
.epb .doc-sample .doc-body{padding:26px 28px 30px;}
.epb .doc-sample h3{font-family:'Oswald',sans-serif; color:var(--ink); font-size:1.25rem; text-transform:none; letter-spacing:0;}
.epb .doc-sample ol{padding-left:22px;}
.epb .doc-sample li{margin-bottom:9px;}
.epb .flag{
  display:inline-block; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; font-weight:600;
  background:#D94E2A; color:#fff; padding:1px 7px; border-radius:2px; margin-left:6px; vertical-align:middle;
}
.epb .doc-sample .note-margin{margin-top:6px; font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:#8A3A1F; padding-left:8px; border-left:2px solid #D94E2A;}
.epb .legend{display:flex; flex-wrap:wrap; gap:10px 20px; margin-top:16px;}
.epb .legend span{font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:var(--muted); display:flex; align-items:center; gap:8px;}
.epb .legend .dot{width:10px; height:10px; border-radius:50%; display:inline-block;}
.epb table{width:100%; border-collapse:collapse; margin-top:18px; font-size:0.94rem;}
.epb th, .epb td{border:1px solid var(--line); padding:10px 12px; text-align:left; vertical-align:top;}
.epb th{background:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.06em; color:var(--amber);}
.epb .summary-box{
  border:1px solid var(--amber); background:linear-gradient(180deg, rgba(245,166,35,0.08), transparent);
  padding:24px 26px; margin-top:24px;
}
.epb .summary-box ul{margin:10px 0 0; padding-left:20px;}
.epb .summary-box li{margin-bottom:8px;}
.epb .task-header{
  background:var(--amber); color:var(--blueprint-deep); padding:22px 26px; margin-top:20px;
  display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;
}
.epb .task-header h2{color:var(--blueprint-deep); font-size:1.4rem; margin:0;}
.epb .task-header .badge{font-family:'IBM Plex Mono',monospace; font-size:0.8rem; background:var(--blueprint-deep); color:var(--amber); padding:6px 12px; border-radius:2px;}
.epb .steps-num{counter-reset:step; margin-top:18px; list-style:none; padding:0;}
.epb .steps-num li{counter-increment:step; position:relative; padding-left:44px; margin-bottom:16px;}
.epb .steps-num li::before{content:counter(step,decimal-leading-zero); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; font-size:0.95rem; border:1px solid var(--amber); width:30px; height:26px; display:flex; align-items:center; justify-content:center;}
.epb .rubric-scale{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:16px;}
.epb .rubric-scale div{background:var(--blueprint-deep); border:1px solid var(--line); padding:12px; text-align:center;}
.epb .rubric-scale .n{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:1.3rem; font-weight:700;}
.epb .rubric-scale .l{font-size:0.78rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.05em;}
.epb .notebook{
  background:repeating-linear-gradient(var(--paper), var(--paper) 33px, #D8D3C2 34px);
  color:var(--ink); border-radius:2px; padding:28px 30px; margin-top:20px; position:relative;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .notebook::before{content:""; position:absolute; left:46px; top:0; bottom:0; width:1.5px; background:#C0453A; opacity:0.5;}
.epb .notebook .content{padding-left:24px;}
.epb .notebook h3{color:var(--ink); font-family:'Oswald',sans-serif; letter-spacing:0.02em; margin-bottom:10px;}
.epb .notebook ul{padding-left:20px;}
.epb footer{border-top:1px solid var(--line); padding:24px 20px 40px; color:var(--muted); font-size:0.86rem;}
.epb footer .doc-code{margin-bottom:10px;}
.epb .divider-dash{border:none; border-top:1px dashed var(--line); margin:44px 0 0;}
.epb::selection{background:var(--amber); color:var(--blueprint-deep);}

&lt;/style&gt;
&lt;div class="epb"&gt;
&lt;div class="crop tl"&gt;&lt;/div&gt;&lt;div class="crop tr"&gt;&lt;/div&gt;&lt;div class="crop bl"&gt;&lt;/div&gt;&lt;div class="crop br"&gt;&lt;/div&gt;

&lt;header class="cover"&gt;
  &lt;div class="stamp"&gt;&lt;div class="ring"&gt;&lt;/div&gt;&lt;span&gt;Modul Ajar&lt;b&gt;Terverifikasi&lt;/b&gt;Fase F&lt;/span&gt;&lt;/div&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/CERAMAH-KAIDAH-1 — 3 JP · 1 Pertemuan&lt;/div&gt;
  &lt;h1&gt;Kaidah Kebahasaan Teks Ceramah (Bagian 1)&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Materi Bahasa Indonesia Kelas XI SMK/MAK — melatih murid mengidentifikasi ciri kebahasaan teks ceramah: kata ganti orang, kata teknis, dan kata persuasif.&lt;/p&gt;

  &lt;div class="meta-strip"&gt;
    &lt;div&gt;&lt;div class="k"&gt;Mata Pelajaran&lt;/div&gt;&lt;div class="v"&gt;Bahasa Indonesia&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK/MAK — Kelas XI&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Elemen&lt;/div&gt;&lt;div class="v"&gt;Membaca &amp;amp; Memirsa&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;3 JP (3 × 45 menit)&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;

  &lt;!-- ============ TUJUAN PEMBELAJARAN ============ --&gt;
  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="eyebrow"&gt;01 — Identitas Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Capaian &amp;amp; Tujuan Pembelajaran&lt;/h2&gt;

    &lt;div class="panel tp"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Capaian Pembelajaran (Elemen Membaca–Memirsa)&lt;/div&gt;
      &lt;p&gt;Murid mampu mengidentifikasi dan menganalisis ciri kebahasaan pada berbagai jenis teks, termasuk teks ceramah, untuk memahami bagaimana bahasa digunakan secara efektif dalam menyampaikan pesan kepada pendengar.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Tujuan Pembelajaran&lt;/div&gt;
      &lt;p style="font-size:1.05rem; color:var(--off-white); font-weight:600;"&gt;Murid mampu mengidentifikasi ciri kebahasaan teks ceramah, seperti kata ganti orang, kata teknis, dan kata persuasif.&lt;/p&gt;

      &lt;div class="tp-box"&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.1&lt;/span&gt;&lt;p&gt;Murid dapat menjelaskan jenis dan fungsi kata ganti orang dalam teks ceramah.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.2&lt;/span&gt;&lt;p&gt;Murid dapat menjelaskan pengertian dan fungsi kata teknis dalam teks ceramah.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.3&lt;/span&gt;&lt;p&gt;Murid dapat menjelaskan pengertian dan fungsi kata persuasif dalam teks ceramah.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.4&lt;/span&gt;&lt;p&gt;Murid dapat menemukan ketiga ciri kebahasaan tersebut dalam sebuah teks ceramah.&lt;/p&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="eyebrow" style="margin-top:26px; margin-bottom:6px;"&gt;8 Profil Lulusan yang Dikembangkan&lt;/div&gt;
    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Penalaran Kritis&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menganalisis fungsi kata ganti orang, kata teknis, dan kata persuasif dalam teks.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kemandirian&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menyelesaikan identifikasi dan reviu materi secara mandiri.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kolaborasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Bekerja sama dalam kelompok saat menganalisis ciri kebahasaan teks ceramah.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Komunikasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menyampaikan hasil identifikasi ciri kebahasaan secara jelas dan runtut.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI A: RECAP TEKS CERAMAH ============ --&gt;
  &lt;section class="section" id="materi-a"&gt;
    &lt;div class="eyebrow"&gt;02 — Materi Inti A&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Mengingat Kembali Teks Ceramah&lt;/h2&gt;
    &lt;p class="lead"&gt;Teks ceramah adalah teks berisi pidato seseorang di hadapan pendengar, bertujuan memberi nasihat, wejangan, atau pengetahuan tentang suatu hal, dengan struktur pembukaan, isi, dan penutup. Pada pertemuan ini, murid akan mempelajari tiga ciri kebahasaan utama teks ceramah.&lt;/p&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;1. Kata Ganti Orang&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Menunjukkan posisi penceramah dan pendengar dalam teks.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;2. Kata Teknis&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Istilah khusus sesuai tema ceramah yang memberi kedalaman isi.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;3. Kata Persuasif&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Kata ajakan/anjuran yang mendorong pendengar bertindak.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI B: KATA GANTI ORANG ============ --&gt;
  &lt;section class="section" id="materi-b"&gt;
    &lt;div class="eyebrow"&gt;03 — Materi Inti B&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kata Ganti Orang dalam Teks Ceramah&lt;/h2&gt;

    &lt;div class="panel"&gt;
      &lt;p&gt;&lt;b style="color:var(--off-white);"&gt;Kata ganti orang&lt;/b&gt; dalam teks ceramah digunakan untuk menunjukkan posisi penceramah dan pendengar, sekaligus membangun kedekatan di antara keduanya selama penyampaian pesan.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="criteria"&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;1&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Kata Ganti Orang Pertama Tunggal&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Merujuk pada diri penceramah sendiri.&lt;/p&gt;
          &lt;div class="q"&gt;Contoh: "Izinkan saya menyampaikan sedikit renungan."&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;2&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Kata Ganti Orang Pertama Jamak&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Membangun kebersamaan antara penceramah dan pendengar.&lt;/p&gt;
          &lt;div class="q"&gt;Contoh: "Marilah kita renungkan bersama."&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;3&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Kata Ganti/Sapaan Orang Kedua&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Menyapa pendengar secara langsung.&lt;/p&gt;
          &lt;div class="q"&gt;Contoh: "Hadirin yang saya hormati..."&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI C: KATA TEKNIS ============ --&gt;
  &lt;section class="section" id="materi-c"&gt;
    &lt;div class="eyebrow"&gt;04 — Materi Inti C&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kata Teknis dalam Teks Ceramah&lt;/h2&gt;

    &lt;div class="panel"&gt;
      &lt;p&gt;&lt;b style="color:var(--off-white);"&gt;Kata teknis&lt;/b&gt; adalah istilah khusus sesuai tema ceramah yang memberikan kedalaman dan kredibilitas pada isi yang disampaikan.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Tema Keagamaan&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;ibadah, akhlak, syukur, taqwa&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Tema Kesehatan&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;imunitas, gizi seimbang, imunisasi&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Tema Pendidikan&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;kurikulum, literasi, karakter&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;p class="lead" style="margin-top:14px;"&gt;&lt;b style="color:var(--off-white);"&gt;Fungsi:&lt;/b&gt; memperkuat kredibilitas penceramah dan membantu pendengar memahami isu secara lebih mendalam sesuai tema yang dibahas.&lt;/p&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI D: KATA PERSUASIF ============ --&gt;
  &lt;section class="section" id="materi-d"&gt;
    &lt;div class="eyebrow"&gt;05 — Materi Inti D&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kata Persuasif dalam Teks Ceramah&lt;/h2&gt;

    &lt;div class="panel"&gt;
      &lt;p&gt;&lt;b style="color:var(--off-white);"&gt;Kata persuasif&lt;/b&gt; adalah kata yang bertujuan mengajak atau menganjurkan pendengar agar bertindak sesuai pesan yang disampaikan dalam ceramah.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kata Ajakan&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;mari, marilah, ayo&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kata Anjuran&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;hendaknya, sebaiknya, seyogyanya&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Contoh Kalimat&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;"Marilah kita membiasakan pola hidup sehat."&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI E: TABEL RANGKUMAN ============ --&gt;
  &lt;section class="section" id="materi-e"&gt;
    &lt;div class="eyebrow"&gt;06 — Materi Inti E&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rangkuman Ciri Kebahasaan Teks Ceramah&lt;/h2&gt;

    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:22%;"&gt;Ciri Kebahasaan&lt;/th&gt;&lt;th style="width:34%;"&gt;Fungsi&lt;/th&gt;&lt;th&gt;Contoh&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Ganti Orang&lt;/td&gt;&lt;td&gt;Menunjukkan posisi penceramah &amp;amp; pendengar, membangun kedekatan&lt;/td&gt;&lt;td&gt;"saya", "kita", "hadirin"&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Teknis&lt;/td&gt;&lt;td&gt;Memberi kedalaman dan kredibilitas sesuai tema ceramah&lt;/td&gt;&lt;td&gt;"imunitas", "akhlak", "kurikulum"&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Persuasif&lt;/td&gt;&lt;td&gt;Mengajak/menganjurkan pendengar bertindak sesuai pesan&lt;/td&gt;&lt;td&gt;"marilah", "hendaknya"&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ PEMODELAN ============ --&gt;
  &lt;section class="section" id="contoh"&gt;
    &lt;div class="eyebrow"&gt;07 — Pemodelan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Contoh Penerapan: Mengidentifikasi Ciri Kebahasaan&lt;/h2&gt;
    &lt;p class="lead"&gt;Guru membacakan dan membahas bersama contoh berikut sebagai model identifikasi sebelum murid mengerjakan tugas kelompok.&lt;/p&gt;

    &lt;div class="doc-sample"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks Ceramah Contoh&lt;/span&gt;&lt;span&gt;Dokumen-01&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Pentingnya Menjaga Kesehatan di Musim Pancaroba&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Assalamu'alaikum warahmatullahi wabarakatuh. Bapak/Ibu dan hadirin&lt;span class="flag"&gt;SAPAAN/ORG.2&lt;/span&gt; yang saya hormati, izinkan saya&lt;span class="flag"&gt;ORG.1 TUNGGAL&lt;/span&gt; menyampaikan sedikit pesan tentang pentingnya menjaga kesehatan di musim pancaroba. Musim pancaroba sering menyebabkan daya tahan tubuh menurun, sehingga kita&lt;span class="flag"&gt;ORG.1 JAMAK&lt;/span&gt; rentan terserang flu dan demam. Oleh karena itu, marilah kita&lt;span class="flag"&gt;PERSUASIF&lt;/span&gt; membiasakan pola hidup sehat dengan mengonsumsi makanan bergizi seimbang&lt;span class="flag"&gt;KATA TEKNIS&lt;/span&gt; dan cukup istirahat. Selain itu, hendaknya&lt;span class="flag"&gt;PERSUASIF&lt;/span&gt; kita juga rutin berolahraga agar imunitas&lt;span class="flag"&gt;KATA TEKNIS&lt;/span&gt; tubuh tetap terjaga. Demikian yang dapat saya sampaikan, semoga bermanfaat bagi kita semua. Wassalamu'alaikum warahmatullahi wabarakatuh.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="panel" style="margin-top:22px;"&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Simpulan model identifikasi:&lt;/b&gt; Teks di atas memakai kata ganti orang kedua untuk menyapa ("hadirin"), orang pertama tunggal saat memperkenalkan diri ("saya"), dan orang pertama jamak untuk membangun kebersamaan ("kita"). Kata teknis bidang kesehatan ("gizi seimbang", "imunitas") memperkuat kredibilitas isi, sementara kata persuasif ("marilah", "hendaknya") mengajak pendengar menerapkan pola hidup sehat.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ RANGKUMAN ============ --&gt;
  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="eyebrow"&gt;08 — Ringkasan Materi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Poin-Poin Penting&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;ul&gt;
        &lt;li&gt;Kata ganti orang dalam teks ceramah terdiri atas orang pertama tunggal ("saya"), orang pertama jamak ("kita/kami"), dan sapaan orang kedua ("hadirin", "Bapak/Ibu").&lt;/li&gt;
        &lt;li&gt;Kata teknis adalah istilah khusus sesuai tema ceramah yang memberi kedalaman dan kredibilitas pada isi.&lt;/li&gt;
        &lt;li&gt;Kata persuasif berupa kata ajakan ("marilah") dan anjuran ("hendaknya") yang mengajak pendengar bertindak sesuai pesan.&lt;/li&gt;
        &lt;li&gt;Ketiga ciri kebahasaan ini saling melengkapi untuk membuat ceramah lebih dekat, meyakinkan, dan mudah diterima pendengar.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ TUGAS KELOMPOK ============ --&gt;
  &lt;section class="section" id="tugas-kelompok"&gt;
    &lt;div class="eyebrow"&gt;09 — Lembar Kerja&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Kelompok: Mengidentifikasi Ciri Kebahasaan Teks Ceramah&lt;/h2&gt;
      &lt;span class="badge"&gt;Durasi ± 60 menit&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Tujuan tugas:&lt;/b&gt; Murid secara berkelompok mengidentifikasi kata ganti orang, kata teknis, dan kata persuasif pada teks ceramah berikut, lalu mempresentasikan hasil temuannya.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks Ceramah untuk Diidentifikasi&lt;/span&gt;&lt;span&gt;Dokumen-02&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Pentingnya Pendidikan Karakter bagi Generasi Muda&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Assalamu'alaikum warahmatullahi wabarakatuh. Bapak/Ibu guru dan adik-adik yang saya banggakan, pada kesempatan ini saya ingin mengajak kita semua merenungkan pentingnya pendidikan karakter bagi generasi muda. Pendidikan karakter berperan penting dalam membentuk akhlak dan moral peserta didik, tidak hanya kecerdasan intelektual semata. Oleh karena itu, hendaknya sekolah dan keluarga bekerja sama menanamkan nilai-nilai kejujuran, tanggung jawab, dan kedisiplinan sejak dini. Marilah kita bersama-sama menjadi teladan yang baik bagi generasi penerus bangsa. Demikian yang dapat saya sampaikan. Wassalamu'alaikum warahmatullahi wabarakatuh.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;p class="lead" style="margin-top:14px;"&gt;Teks di atas &lt;b style="color:var(--off-white);"&gt;belum ditandai&lt;/b&gt; — tugas kelompok adalah menemukan sendiri kata ganti orang, kata teknis, dan kata persuasif yang terdapat di dalamnya.&lt;/p&gt;

    &lt;h4 style="color:var(--off-white); margin-top:24px;"&gt;Petunjuk Pengerjaan&lt;/h4&gt;
    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;b&gt;Bentuk kelompok&lt;/b&gt; beranggotakan 4–5 orang sesuai pembagian dari guru.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Baca teks ceramah&lt;/b&gt; di atas secara bersama-sama dalam kelompok.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Tandai setiap kata/frasa&lt;/b&gt; sesuai jenisnya (kata ganti orang, kata teknis, atau kata persuasif).&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Isi Tabel Identifikasi&lt;/b&gt; di bawah ini untuk setiap ciri kebahasaan.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Presentasikan hasil&lt;/b&gt; di depan kelas selama maksimal 5 menit per kelompok, dilanjutkan tanya jawab singkat.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Format Tabel Identifikasi Kelompok&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Ciri Kebahasaan&lt;/th&gt;&lt;th style="width:36%;"&gt;Contoh Kata/Frasa yang Ditemukan&lt;/th&gt;&lt;th style="width:12%;"&gt;Jumlah&lt;/th&gt;&lt;th&gt;Fungsi dalam Teks&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Ganti Orang Pertama&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Ganti/Sapaan Orang Kedua&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Teknis&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Persuasif&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Rubrik Penilaian Kelompok&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Aspek Penilaian&lt;/th&gt;&lt;th&gt;Skor 4 (Sangat Baik)&lt;/th&gt;&lt;th&gt;Skor 3 (Baik)&lt;/th&gt;&lt;th&gt;Skor 2 (Cukup)&lt;/th&gt;&lt;th&gt;Skor 1 (Kurang)&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan identifikasi kata ganti orang&lt;/td&gt;&lt;td&gt;Seluruh jenis teridentifikasi tepat&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Identifikasi dangkal, sebagian keliru&lt;/td&gt;&lt;td&gt;Identifikasi tidak sesuai&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan identifikasi kata teknis &amp;amp; persuasif&lt;/td&gt;&lt;td&gt;Seluruh kata ditemukan dengan tepat&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Sebagian kata terlewat&lt;/td&gt;&lt;td&gt;Tidak ditemukan dengan tepat&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kerja sama kelompok&lt;/td&gt;&lt;td&gt;Seluruh anggota aktif berkontribusi&lt;/td&gt;&lt;td&gt;Sebagian besar anggota aktif&lt;/td&gt;&lt;td&gt;Hanya sebagian kecil anggota aktif&lt;/td&gt;&lt;td&gt;Didominasi satu orang&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Presentasi&lt;/td&gt;&lt;td&gt;Jelas, percaya diri, menjawab pertanyaan dengan baik&lt;/td&gt;&lt;td&gt;Jelas namun kurang percaya diri&lt;/td&gt;&lt;td&gt;Kurang jelas/terbata-bata&lt;/td&gt;&lt;td&gt;Tidak dapat menjelaskan hasil&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ TUGAS INDIVIDU: KUIS 25 PERTANYAAN ============ --&gt;
  &lt;section class="section" id="tugas-individu"&gt;
    &lt;div class="eyebrow"&gt;10 — Penutup Pembelajaran&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Individu: Menjawab 25 Pertanyaan tentang Materi&lt;/h2&gt;
      &lt;span class="badge"&gt;Dikerjakan Mandiri · Otomatis Bernilai&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Petunjuk:&lt;/b&gt; Isi No. Absen dan Nama, lalu jawab 25 pertanyaan pilihan ganda (5 opsi) berikut. Soal-soal ini berbentuk &lt;b style="color:var(--off-white);"&gt;HOTS (Higher Order Thinking Skills)&lt;/b&gt; — menuntut analisis kutipan, perbandingan kalimat, dan penilaian efektivitas, bukan sekadar hafalan. Skor akan muncul otomatis setelah menekan tombol "Kirim Jawaban", dan hasilnya akan tercatat otomatis.&lt;/p&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div style="display:grid; grid-template-columns:1fr 2fr; gap:14px; margin-bottom:22px;"&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;No. Absen&lt;/label&gt;
          &lt;input id="kq-absen" type="text" inputmode="numeric" placeholder="Contoh: 12" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Nama Lengkap&lt;/label&gt;
          &lt;input id="kq-nama" type="text" placeholder="Tulis nama lengkap" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;1. Perhatikan kutipan ceramah berikut: “Izinkan saya mengucapkan terima kasih atas kehadiran Bapak/Ibu sekalian. Pada kesempatan ini, marilah kita bersama-sama merenungkan arti pentingnya toleransi.” Perubahan kata ganti dari “saya” ke “kita” pada kutipan tersebut menunjukkan pergeseran fungsi dari...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq0" value="0" style="margin-right:8px;"&gt;A. memperkenalkan diri ke mengajak keterlibatan bersama pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq0" value="1" style="margin-right:8px;"&gt;B. menyapa ke menyindir&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq0" value="2" style="margin-right:8px;"&gt;C. menjelaskan ke melarang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq0" value="3" style="margin-right:8px;"&gt;D. bertanya ke menjawab&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq0" value="4" style="margin-right:8px;"&gt;E. mengeluh ke bersyukur&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;2. Seorang penceramah bertema kesehatan menyampaikan: “Pola makan yang buruk dapat meningkatkan risiko penyakit degeneratif.” Penggunaan istilah “penyakit degeneratif” dalam kutipan tersebut berfungsi untuk...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq1" value="0" style="margin-right:8px;"&gt;A. membuat ceramah lebih sulit dipahami&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq1" value="1" style="margin-right:8px;"&gt;B. menunjukkan kedalaman wawasan dan kredibilitas penceramah terhadap tema yang dibahas&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq1" value="2" style="margin-right:8px;"&gt;C. mengganti fungsi kata ganti orang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq1" value="3" style="margin-right:8px;"&gt;D. menghindari penggunaan kalimat persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq1" value="4" style="margin-right:8px;"&gt;E. menyingkat isi ceramah&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;3. Bandingkan dua kutipan berikut. Kutipan 1: “Anda harus mulai menabung sejak sekarang.” Kutipan 2: “Marilah kita mulai menabung sejak sekarang.” Perbedaan kesan yang ditimbulkan oleh kedua kutipan tersebut adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq2" value="0" style="margin-right:8px;"&gt;A. Kutipan 1 terkesan lebih menggurui/berjarak, kutipan 2 terkesan lebih mengajak dan melibatkan diri penceramah&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq2" value="1" style="margin-right:8px;"&gt;B. Kedua kutipan memberi kesan yang identik&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq2" value="2" style="margin-right:8px;"&gt;C. Kutipan 1 lebih persuasif daripada kutipan 2&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq2" value="3" style="margin-right:8px;"&gt;D. Kutipan 2 tidak termasuk kalimat persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq2" value="4" style="margin-right:8px;"&gt;E. Kutipan 1 menggunakan kata teknis, kutipan 2 tidak&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;4. “Hendaknya kita menjaga pola makan sehat demi imunitas tubuh yang optimal.” Jika kata “hendaknya” pada kalimat tersebut dihapus, dampak apa yang paling mungkin terjadi terhadap kalimat?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq3" value="0" style="margin-right:8px;"&gt;A. Kalimat kehilangan unsur ajakan/anjuran dan berubah menjadi pernyataan biasa&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq3" value="1" style="margin-right:8px;"&gt;B. Kalimat kehilangan seluruh maknanya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq3" value="2" style="margin-right:8px;"&gt;C. Kalimat menjadi kalimat tanya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq3" value="3" style="margin-right:8px;"&gt;D. Kalimat berubah menjadi kalimat larangan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq3" value="4" style="margin-right:8px;"&gt;E. Tidak ada perubahan apa pun&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;5. Seorang murid diminta menyusun potongan ceramah bertema lingkungan. Manakah susunan kalimat yang paling tepat menggabungkan kata ganti orang pertama jamak dan kata persuasif secara bersamaan?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq4" value="0" style="margin-right:8px;"&gt;A. “Sampah plastik sulit terurai di alam.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq4" value="1" style="margin-right:8px;"&gt;B. “Marilah kita bersama-sama mengurangi penggunaan plastik sekali pakai.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq4" value="2" style="margin-right:8px;"&gt;C. “Data menunjukkan peningkatan sampah plastik setiap tahun.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq4" value="3" style="margin-right:8px;"&gt;D. “Plastik adalah bahan yang terbuat dari minyak bumi.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq4" value="4" style="margin-right:8px;"&gt;E. “Pencemaran laut disebabkan berbagai faktor.”&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;6. Perhatikan kutipan: “Bapak/Ibu dan hadirin yang saya hormati, pendidikan karakter berperan penting membentuk akhlak generasi muda.” Jika sapaan “Bapak/Ibu dan hadirin” diubah menjadi “kalian”, perubahan apa yang paling mungkin terjadi pada kesan ceramah tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq5" value="0" style="margin-right:8px;"&gt;A. Kesan menjadi lebih formal dan menghormati pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq5" value="1" style="margin-right:8px;"&gt;B. Kesan menjadi lebih santai namun berpotensi kurang sopan dalam forum resmi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq5" value="2" style="margin-right:8px;"&gt;C. Tidak ada perubahan kesan sama sekali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq5" value="3" style="margin-right:8px;"&gt;D. Ceramah berubah menjadi teks prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq5" value="4" style="margin-right:8px;"&gt;E. Kalimat menjadi kalimat tanya&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;7. Mengapa penggunaan kata teknis yang berlebihan tanpa penjelasan dapat menjadi kelemahan dalam sebuah ceramah?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq6" value="0" style="margin-right:8px;"&gt;A. Karena dapat membuat pendengar awam kesulitan memahami isi pesan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq6" value="1" style="margin-right:8px;"&gt;B. Karena kata teknis selalu salah digunakan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq6" value="2" style="margin-right:8px;"&gt;C. Karena kata teknis tidak diperbolehkan dalam ceramah&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq6" value="3" style="margin-right:8px;"&gt;D. Karena ceramah tidak boleh membahas tema spesifik&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq6" value="4" style="margin-right:8px;"&gt;E. Karena kata teknis mengubah struktur ceramah&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;8. Perhatikan dua kalimat berikut. (1) “Kita semua tahu bahwa membaca itu penting.” (2) “Konon membaca itu penting, entahlah.” Manakah kalimat yang lebih efektif membangun keyakinan pendengar, dan mengapa?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq7" value="0" style="margin-right:8px;"&gt;A. Kalimat (1), karena menggunakan kata ganti “kita” yang melibatkan pendengar secara langsung&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq7" value="1" style="margin-right:8px;"&gt;B. Kalimat (2), karena terdengar lebih santai&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq7" value="2" style="margin-right:8px;"&gt;C. Keduanya sama efektifnya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq7" value="3" style="margin-right:8px;"&gt;D. Kalimat (2), karena menggunakan kata “konon” sebagai penguat&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq7" value="4" style="margin-right:8px;"&gt;E. Tidak ada yang efektif karena keduanya tidak persuasif&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;9. Seorang penceramah ingin membahas tema “pentingnya olahraga” namun menghindari seluruh istilah kesehatan seperti “metabolisme” atau “kardiovaskular”. Apa kemungkinan konsekuensi dari pilihan tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq8" value="0" style="margin-right:8px;"&gt;A. Ceramah akan lebih mudah dipahami pendengar awam, namun berpotensi kurang mendalam secara keilmuan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq8" value="1" style="margin-right:8px;"&gt;B. Ceramah otomatis menjadi tidak persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq8" value="2" style="margin-right:8px;"&gt;C. Ceramah akan berubah menjadi teks prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq8" value="3" style="margin-right:8px;"&gt;D. Ceramah tidak dapat menggunakan kata ganti orang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq8" value="4" style="margin-right:8px;"&gt;E. Tidak ada konsekuensi apa pun&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;10. Perhatikan kutipan: “Izinkan saya menutup ceramah ini dengan sebuah ajakan: marilah kita jadikan kejujuran sebagai kebiasaan sehari-hari.” Kombinasi kata ganti dan kata persuasif pada kalimat penutup tersebut berfungsi untuk...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq9" value="0" style="margin-right:8px;"&gt;A. Menegaskan kembali kehadiran penceramah sekaligus mengajak pendengar bertindak bersama sebagai penutup yang berkesan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq9" value="1" style="margin-right:8px;"&gt;B. Menyampaikan data statistik terakhir&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq9" value="2" style="margin-right:8px;"&gt;C. Menjelaskan proses ilmiah secara rinci&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq9" value="3" style="margin-right:8px;"&gt;D. Menyapa pendengar untuk pertama kalinya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq9" value="4" style="margin-right:8px;"&gt;E. Menunjukkan urutan waktu kejadian&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;11. Dalam sebuah ceramah bertema pendidikan, penceramah berkata: “Kurikulum yang adaptif terhadap kebutuhan zaman akan mencetak generasi yang kompetitif.” Jika kalimat ini diucapkan di hadapan audiens awam tanpa latar belakang pendidikan, tantangan apa yang paling mungkin dihadapi penceramah?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq10" value="0" style="margin-right:8px;"&gt;A. Audiens mungkin kesulitan memahami istilah “kurikulum adaptif” tanpa penjelasan tambahan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq10" value="1" style="margin-right:8px;"&gt;B. Audiens akan langsung memahami tanpa kendala&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq10" value="2" style="margin-right:8px;"&gt;C. Kalimat tersebut tidak mengandung kata teknis sama sekali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq10" value="3" style="margin-right:8px;"&gt;D. Audiens akan menganggap ceramah tersebut sebagai teks prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq10" value="4" style="margin-right:8px;"&gt;E. Tidak ada tantangan apa pun&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;12. Manakah revisi kalimat yang paling tepat mengubah kalimat formal “Anda diharapkan menjaga kebersihan lingkungan” menjadi kalimat ceramah yang lebih persuasif dan membangun kebersamaan?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq11" value="0" style="margin-right:8px;"&gt;A. “Marilah kita bersama-sama menjaga kebersihan lingkungan demi kenyamanan bersama.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq11" value="1" style="margin-right:8px;"&gt;B. “Lingkungan harus bersih.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq11" value="2" style="margin-right:8px;"&gt;C. “Kebersihan lingkungan adalah tanggung jawab pemerintah.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq11" value="3" style="margin-right:8px;"&gt;D. “Sampah menyebabkan banjir.”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq11" value="4" style="margin-right:8px;"&gt;E. “Lingkungan kotor tidak baik.”&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;13. Perhatikan kutipan: “Kita tentu sepakat bahwa generasi muda adalah penentu masa depan bangsa.” Kata “kita” dalam kalimat tersebut berfungsi untuk menciptakan kesan bahwa...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq12" value="0" style="margin-right:8px;"&gt;A. Pendapat yang disampaikan adalah pendapat bersama, bukan sekadar opini pribadi penceramah&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq12" value="1" style="margin-right:8px;"&gt;B. Penceramah sedang bertanya kepada pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq12" value="2" style="margin-right:8px;"&gt;C. Penceramah sedang menyapa untuk pertama kali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq12" value="3" style="margin-right:8px;"&gt;D. Kalimat tersebut adalah kalimat prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq12" value="4" style="margin-right:8px;"&gt;E. Kalimat tersebut tidak memiliki makna persuasif&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;14. Jika sebuah ceramah bertema keagamaan sama sekali tidak menggunakan istilah keagamaan (seperti “ibadah” atau “syukur”) dan hanya menggunakan bahasa umum, apa dampak paling mungkin terhadap kredibilitas ceramah tersebut di hadapan audiens yang memahami istilah tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq13" value="0" style="margin-right:8px;"&gt;A. Kredibilitas dapat menurun karena ceramah terkesan kurang mendalam membahas tema keagamaan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq13" value="1" style="margin-right:8px;"&gt;B. Kredibilitas pasti meningkat&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq13" value="2" style="margin-right:8px;"&gt;C. Tidak ada pengaruh terhadap kredibilitas&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq13" value="3" style="margin-right:8px;"&gt;D. Ceramah otomatis menjadi lebih persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq13" value="4" style="margin-right:8px;"&gt;E. Ceramah berubah menjadi artikel opini&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;15. Perhatikan kalimat: “Sebaiknya kita mulai membiasakan hidup hemat energi mulai dari rumah masing-masing.” Jika kata “sebaiknya” diganti dengan “wajib”, perubahan nuansa apa yang paling mungkin terjadi?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq14" value="0" style="margin-right:8px;"&gt;A. Nuansa anjuran halus berubah menjadi kesan perintah yang lebih tegas/memaksa&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq14" value="1" style="margin-right:8px;"&gt;B. Tidak ada perubahan nuansa sama sekali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq14" value="2" style="margin-right:8px;"&gt;C. Kalimat menjadi kalimat tanya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq14" value="3" style="margin-right:8px;"&gt;D. Kalimat kehilangan makna persuasif sepenuhnya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq14" value="4" style="margin-right:8px;"&gt;E. Kalimat berubah menjadi kalimat deklaratif netral&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;16. Seorang penceramah ingin ceramahnya terdengar lebih dekat secara emosional dengan audiens generasi muda. Manakah pilihan kata ganti yang paling sesuai untuk tujuan tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq15" value="0" style="margin-right:8px;"&gt;A. “Kita”, karena melibatkan audiens sebagai bagian dari pembicaraan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq15" value="1" style="margin-right:8px;"&gt;B. “Mereka”, karena lebih formal&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq15" value="2" style="margin-right:8px;"&gt;C. “Beliau”, karena lebih sopan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq15" value="3" style="margin-right:8px;"&gt;D. “Ia”, karena lebih netral&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq15" value="4" style="margin-right:8px;"&gt;E. “Saya”, karena lebih fokus pada penceramah saja&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;17. Perhatikan dua kutipan berikut. Kutipan A: “Hadirin sekalian, marilah kita syukuri nikmat kesehatan ini.” Kutipan B: “Masyarakat diimbau untuk menjaga kesehatan.” Manakah kutipan yang lebih efektif membangun kedekatan emosional dengan pendengar, dan mengapa?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq16" value="0" style="margin-right:8px;"&gt;A. Kutipan A, karena menggunakan sapaan langsung dan kata ganti “kita” yang melibatkan pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq16" value="1" style="margin-right:8px;"&gt;B. Kutipan B, karena lebih formal&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq16" value="2" style="margin-right:8px;"&gt;C. Keduanya sama efektifnya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq16" value="3" style="margin-right:8px;"&gt;D. Kutipan B, karena menggunakan kata “diimbau”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq16" value="4" style="margin-right:8px;"&gt;E. Tidak ada yang efektif&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;18. Mengapa kombinasi kata ganti orang pertama jamak dan kata persuasif sering muncul bersamaan pada bagian penutup ceramah?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq17" value="0" style="margin-right:8px;"&gt;A. Untuk memperkuat ajakan bertindak bersama sebagai kesan terakhir yang ditinggalkan kepada pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq17" value="1" style="margin-right:8px;"&gt;B. Karena aturan tata bahasa baku mengharuskannya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq17" value="2" style="margin-right:8px;"&gt;C. Karena bagian penutup tidak boleh mengandung kata ganti&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq17" value="3" style="margin-right:8px;"&gt;D. Agar ceramah terdengar lebih panjang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq17" value="4" style="margin-right:8px;"&gt;E. Tidak ada alasan khusus&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;19. Sebuah ceramah menggunakan kalimat: “Riset menunjukkan bahwa remaja yang gemar membaca memiliki kemampuan berpikir kritis lebih baik.” Fungsi penyebutan “riset” dalam kalimat tersebut adalah untuk...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq18" value="0" style="margin-right:8px;"&gt;A. Memperkuat kredibilitas pernyataan dengan merujuk pada bukti/fakta, mirip fungsi kata teknis dalam memperdalam isi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq18" value="1" style="margin-right:8px;"&gt;B. Menyapa pendengar secara langsung&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq18" value="2" style="margin-right:8px;"&gt;C. Menunjukkan kata ganti orang pertama&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq18" value="3" style="margin-right:8px;"&gt;D. Mengganti fungsi kata persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq18" value="4" style="margin-right:8px;"&gt;E. Menandai urutan waktu&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;20. Perhatikan kutipan ceramah: “Izinkan saya mengajak Bapak/Ibu sekalian untuk turut merenungkan pentingnya gotong royong di tengah masyarakat yang kian individualis.” Manakah ciri kebahasaan yang TIDAK terdapat dalam kutipan tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq19" value="0" style="margin-right:8px;"&gt;A. Kata ganti orang pertama tunggal&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq19" value="1" style="margin-right:8px;"&gt;B. Sapaan kepada pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq19" value="2" style="margin-right:8px;"&gt;C. Kata ajakan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq19" value="3" style="margin-right:8px;"&gt;D. Konjungsi temporal urutan langkah seperti “kemudian”&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq19" value="4" style="margin-right:8px;"&gt;E. Kata persuasif&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;21. Dalam konteks ceramah bertema sosial, manakah fungsi paling tepat dari penggunaan kata teknis seperti “individualis” atau “gotong royong”?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq20" value="0" style="margin-right:8px;"&gt;A. Memberi kerangka konsep yang memperjelas dan memperdalam gagasan yang disampaikan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq20" value="1" style="margin-right:8px;"&gt;B. Membuat ceramah terdengar lebih santai&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq20" value="2" style="margin-right:8px;"&gt;C. Menghindari penggunaan kata persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq20" value="3" style="margin-right:8px;"&gt;D. Mengganti fungsi kata ganti orang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq20" value="4" style="margin-right:8px;"&gt;E. Mempersingkat durasi ceramah&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;22. Seorang murid merevisi kalimat ceramah dari “Kalian harus peduli terhadap sesama” menjadi “Marilah kita peduli terhadap sesama”. Perubahan apa yang terjadi pada kalimat tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq21" value="0" style="margin-right:8px;"&gt;A. Kata ganti berubah dari orang kedua menjadi orang pertama jamak, dan nuansa perintah berubah menjadi ajakan bersama&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq21" value="1" style="margin-right:8px;"&gt;B. Tidak ada perubahan makna maupun struktur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq21" value="2" style="margin-right:8px;"&gt;C. Kalimat kehilangan unsur persuasif sepenuhnya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq21" value="3" style="margin-right:8px;"&gt;D. Kalimat berubah menjadi kalimat tanya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq21" value="4" style="margin-right:8px;"&gt;E. Kata teknis ditambahkan pada kalimat tersebut&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;23. Mengapa pemilihan kata ganti dan kata persuasif yang tepat penting diperhatikan saat menyusun teks ceramah untuk forum resmi dibandingkan forum santai?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq22" value="0" style="margin-right:8px;"&gt;A. Karena tingkat formalitas memengaruhi kesan hormat dan efektivitas penyampaian pesan kepada audiens&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq22" value="1" style="margin-right:8px;"&gt;B. Karena forum resmi tidak boleh menggunakan kata ganti sama sekali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq22" value="2" style="margin-right:8px;"&gt;C. Karena forum santai tidak membutuhkan kata persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq22" value="3" style="margin-right:8px;"&gt;D. Karena keduanya tidak memiliki perbedaan sama sekali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq22" value="4" style="margin-right:8px;"&gt;E. Karena kata teknis hanya digunakan di forum santai&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;24. Perhatikan kutipan: “Kita sering mendengar pepatah ‘buah jatuh tidak jauh dari pohonnya’. Oleh karena itu, marilah kita menjadi teladan yang baik bagi anak-anak kita.” Apa hubungan logis antara dua kalimat tersebut dalam membangun argumen ceramah?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq23" value="0" style="margin-right:8px;"&gt;A. Kalimat pertama menjadi dasar analogi yang memperkuat ajakan pada kalimat kedua&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq23" value="1" style="margin-right:8px;"&gt;B. Kedua kalimat tidak memiliki keterkaitan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq23" value="2" style="margin-right:8px;"&gt;C. Kalimat kedua membantah kalimat pertama&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq23" value="3" style="margin-right:8px;"&gt;D. Kalimat pertama adalah kata teknis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq23" value="4" style="margin-right:8px;"&gt;E. Kalimat kedua adalah sapaan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:0 0 8px;"&gt;25. Jika Anda diminta menilai efektivitas sebuah teks ceramah, manakah kombinasi ciri kebahasaan yang paling menunjukkan ceramah tersebut disusun dengan baik?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq24" value="0" style="margin-right:8px;"&gt;A. Penggunaan kata ganti yang membangun kedekatan, kata teknis yang memperdalam isi sesuai tema, dan kata persuasif yang mengajak secara halus&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq24" value="1" style="margin-right:8px;"&gt;B. Hanya menggunakan kalimat tanya di seluruh bagian&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq24" value="2" style="margin-right:8px;"&gt;C. Tidak menggunakan kata ganti maupun kata persuasif sama sekali&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq24" value="3" style="margin-right:8px;"&gt;D. Menggunakan bahasa asing tanpa terjemahan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="kq24" value="4" style="margin-right:8px;"&gt;E. Mengulang kata yang sama di setiap kalimat&lt;/label&gt;
      &lt;/div&gt;

&lt;div style="margin-top:24px; display:flex; align-items:center; gap:16px; flex-wrap:wrap;"&gt;
        &lt;button id="kq-submit" type="button" style="background:var(--amber); color:var(--blueprint-deep); border:none; padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Kirim Jawaban&lt;/button&gt;
        &lt;span id="kq-status" class="mono" style="font-size:0.85rem; color:var(--muted);"&gt;&lt;/span&gt;
      &lt;/div&gt;

      &lt;div id="kq-result" style="display:none; margin-top:20px; padding:18px 20px; border:1px solid var(--amber); background:rgba(245,166,35,0.08);"&gt;
        &lt;p style="margin:0; font-size:1.05rem;"&gt;Skor kamu: &lt;b id="kq-score" style="color:var(--amber); font-size:1.3rem;"&gt;&lt;/b&gt; / 100&lt;/p&gt;
        &lt;p id="kq-detail" style="margin:8px 0 0; font-size:0.9rem; color:var(--muted);"&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/CERAMAH-KAIDAH-1 — Fokus Tujuan Pembelajaran, Materi Inti, Tugas Kelompok &amp;amp; Tugas Individu, 1× pertemuan (3 JP)&lt;/div&gt;
  &lt;p style="margin:0;"&gt;Referensi: Kurikulum Merdeka — Capaian Pembelajaran Bahasa Indonesia Fase F (Elemen Membaca dan Memirsa); Buku Teks Bahasa Indonesia Kelas XI SMK/MAK Kemendikbudristek.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
(function(){
  var KQ_ENDPOINT = "https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
  var KQ_MATERI = "Kaidah Kebahasaan Teks Ceramah (Bagian 1)";
  var correctAnswers = [0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0];

  document.getElementById("kq-submit").addEventListener("click", function(){
    var absen = document.getElementById("kq-absen").value.trim();
    var nama = document.getElementById("kq-nama").value.trim();
    var statusEl = document.getElementById("kq-status");

    if(!absen || !nama){
      statusEl.textContent = "Isi No. Absen dan Nama terlebih dahulu.";
      statusEl.style.setProperty("color", "#F5A623", "important");
      return;
    }

    var belumDijawab = [];
    var benar = 0;
    correctAnswers.forEach(function(correct, idx){
      var checked = document.querySelector('input[name="kq' + idx + '"]:checked');
      if(!checked){
        belumDijawab.push(idx+1);
      } else if(parseInt(checked.value,10) === correct){
        benar++;
      }
    });

    if(belumDijawab.length &gt; 0){
      statusEl.textContent = "Masih ada " + belumDijawab.length + " soal yang belum dijawab (nomor " + belumDijawab.slice(0,5).join(", ") + (belumDijawab.length&gt;5? ", ...":"") + ").";
      statusEl.style.setProperty("color", "#F5A623", "important");
      return;
    }

    var skor = benar * 4;
    document.getElementById("kq-score").textContent = skor;
    document.getElementById("kq-detail").textContent = "Menjawab benar " + benar + " dari 25 pertanyaan.";
    document.getElementById("kq-result").style.display = "block";
    statusEl.textContent = "Mengirim hasil...";
    statusEl.style.setProperty("color", "#9FB4C2", "important");

    var payload = new URLSearchParams();
    payload.append("noAbsen", absen);
    payload.append("nama", nama);
    payload.append("skor", skor);
    payload.append("materi", KQ_MATERI);
    payload.append("waktu", new Date().toISOString());

    fetch(KQ_ENDPOINT, {
      method: "POST",
      mode: "no-cors",
      headers: {"Content-Type":"application/x-www-form-urlencoded"},
      body: payload.toString()
    }).then(function(){
      statusEl.textContent = "Hasil berhasil dikirim ke guru.";
      statusEl.style.setProperty("color", "#6EC6CA", "important");
    }).catch(function(){
      statusEl.textContent = "Skor tercatat di halaman ini, namun pengiriman gagal. Tunjukkan skor ini ke guru.";
      statusEl.style.setProperty("color", "#F5A623", "important");
    });
  });
})();
&lt;/script&gt;
&lt;/div&gt;</description><link>http://kitab-investasi.blogspot.com/2026/10/kaidah-kebahasaan-teks-ceramah-bagian-1.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-944626694112424463</guid><pubDate>Tue, 29 Sep 2026 01:52:49 +0000</pubDate><atom:updated>2026-10-15T08:03:10.568+07:00</atom:updated><title>Ujian Tengah Semester Informatika</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Ujian Tengah Semester Informatika Kelas X SMK&lt;/title&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;style&gt;
  :root{
    --navy:#0b1b3a; --navy2:#1b3a8a; --blue:#1d4ed8; --blue-soft:#e8efff;
    --amber:#f59e0b; --amber-dark:#b45309; --amber-soft:#fff6de;
    --teal:#0f766e; --teal-soft:#dff5f1;
    --ink:#0f172a; --muted:#475569; --line:#d6deeb; --bg:#f1f5fb; --card:#ffffff;
    --ok:#15803d; --okbg:#e2f6e9; --bad:#b91c1c; --badbg:#fde8e8;
  }
  *{box-sizing:border-box;}
  body{margin:0; background:var(--bg); color:var(--ink); font-family:'Inter', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif; line-height:1.55; padding-bottom:70px;}
  .hero{background:linear-gradient(135deg, var(--navy), var(--navy2)); color:#fff; padding:38px 20px 34px; text-align:center;}
  .hero .eyebrow{font-size:12.5px; letter-spacing:.09em; font-weight:700; color:#fbd38d;}
  .hero h1{margin:10px auto 8px; font-size:clamp(24px,4.4vw,36px); font-weight:800; line-height:1.2; max-width:780px;}
  .hero p{margin:0 auto; max-width:660px; color:#dbe6ff; font-size:15px;}
  .chips{display:flex; flex-wrap:wrap; justify-content:center; gap:8px; margin-top:20px;}
  .chip{background:rgba(255,255,255,.14); border:1px solid rgba(255,255,255,.28); padding:6px 14px; border-radius:999px; font-size:13px; font-weight:600;}
  .chip b{color:#fbd38d;}
  .wrap{max-width:920px; margin:0 auto; padding:0 18px;}
  .card{background:var(--card); border:1px solid var(--line); border-radius:14px; padding:24px 26px; margin-top:20px; box-shadow:0 2px 8px rgba(15,23,42,.05);}
  .card h2{margin:0 0 12px; font-size:19px; font-weight:800; color:var(--navy);}
  .card p, .card li{font-size:15px; color:#1e293b;}
  .card ul, .card ol{padding-left:20px; margin:8px 0;}
  table.tbl{width:100%; border-collapse:collapse; font-size:14.5px; margin-top:6px;}
  table.tbl th, table.tbl td{border:1px solid var(--line); padding:9px 12px; text-align:left;}
  table.tbl th{background:var(--blue-soft); color:var(--navy); font-weight:700;}
  table.tbl td.num, table.tbl th.num{text-align:center;}
  .notice{background:var(--amber-soft); border:1px solid #f3d68a; border-left:5px solid var(--amber); border-radius:10px; padding:14px 16px; font-size:14.5px; color:#5b3a06; margin-top:14px;}
  .notice b{color:#7a4a05;}
  .field-grid{display:grid; grid-template-columns:1.1fr 1fr 2fr 1.3fr; gap:14px;}
  @media (max-width:760px){ .field-grid{grid-template-columns:1fr 1fr;} .card{padding:20px 18px;} }
  @media (max-width:460px){ .field-grid{grid-template-columns:1fr;} }
  label.fl{display:block; font-size:13.5px; font-weight:700; margin-bottom:6px; color:var(--navy);}
  input[type=text], input[type=number], select, textarea{width:100%; padding:11px 12px; font-size:15px; border:1.5px solid #b6c2d6; border-radius:9px; font-family:inherit; background:#fff; color:var(--ink);}
  input:focus, select:focus, textarea:focus{outline:3px solid #bfd3ff; border-color:var(--blue);}
  input[readonly]{background:#f1f5f9; color:#334155; font-weight:600;}
  select:disabled{background:#f1f5f9; color:#94a3b8; cursor:not-allowed;}
  textarea{min-height:88px; resize:vertical; line-height:1.6;}
  .btn{display:inline-flex; align-items:center; justify-content:center; gap:8px; border:none; border-radius:10px; padding:13px 22px; font-size:15.5px; font-weight:700; cursor:pointer; font-family:inherit;}
  .btn.primary{background:var(--blue); color:#fff;}
  .btn.primary:hover{background:#1a43b8;}
  .btn.amber{background:var(--amber); color:#1c1300;}
  .btn.amber:hover{background:#e08d00;}
  .btn.ghost{background:#fff; color:var(--navy); border:1.5px solid #b6c2d6;}
  .btn.ghost:hover{background:var(--blue-soft);}
  .btn.block{width:100%;}
  .btn:disabled{opacity:.5; cursor:not-allowed;}
  .msg{padding:12px 14px; border-radius:10px; font-size:14.5px; margin-top:14px; display:none; font-weight:500;}
  .msg.show{display:block;}
  .msg.err{background:var(--badbg); color:var(--bad); border:1px solid #f1b8b8;}
  .msg.ok{background:var(--okbg); color:var(--ok); border:1px solid #a7dbb8;}
  .msg.info{background:var(--blue-soft); color:var(--navy); border:1px solid #bfd3ff;}
  .hidden{display:none !important;}

  .progress-bar{position:sticky; top:0; z-index:30; background:rgba(255,255,255,.97); border-bottom:1px solid var(--line); box-shadow:0 3px 10px rgba(15,23,42,.08); padding:10px 18px;}
  .progress-inner{max-width:920px; margin:0 auto; display:flex; align-items:center; gap:14px; flex-wrap:wrap;}
  .progress-inner .who{font-size:13px; font-weight:700; color:var(--navy);}
  .progress-inner .att{font-size:12.5px; font-weight:700; background:var(--amber-soft); color:var(--amber-dark); padding:3px 10px; border-radius:999px;}
  .pbar{flex:1; min-width:140px; height:10px; background:#e2e8f0; border-radius:999px; overflow:hidden;}
  .pbar &gt; div{height:100%; width:0; background:linear-gradient(90deg, var(--blue), #38bdf8); transition:width .25s;}
  .pcount{font-size:13px; font-weight:700; color:var(--navy); white-space:nowrap;}

  .sec-head{margin-top:26px; padding:16px 20px; border-radius:12px; color:#fff;}
  .sec-head h2{margin:0; font-size:18px; font-weight:800;}
  .sec-head p{margin:4px 0 0; font-size:13.5px; opacity:.95;}
  .sec-a{background:linear-gradient(135deg, #1d4ed8, #2563eb);}
  .sec-b{background:linear-gradient(135deg, #b45309, #d97706);}
  .sec-c{background:linear-gradient(135deg, #0f766e, #14958a);}

  .q{background:var(--card); border:1px solid var(--line); border-left:6px solid var(--blue); border-radius:12px; padding:18px 20px; margin-top:14px;}
  .q.b{border-left-color:var(--amber);}
  .q.c{border-left-color:var(--teal);}
  .q-top{display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:8px;}
  .q-no{font-weight:800; color:var(--navy); font-size:14px;}
  .q-tag{font-size:11.5px; font-weight:700; background:#eef2f9; color:var(--muted); padding:3px 10px; border-radius:999px;}
  .q-text{margin:0 0 12px; font-size:15.5px; font-weight:600; color:var(--ink);}
  .opt{display:flex; align-items:flex-start; gap:11px; padding:10px 12px; border:1.5px solid var(--line); border-radius:10px; margin-bottom:8px; cursor:pointer; font-size:14.8px; background:#fff;}
  .opt:hover{background:var(--blue-soft); border-color:#9db8f5;}
  .opt.sel{background:var(--blue-soft); border-color:var(--blue);}
  .opt input{margin-top:3px; accent-color:var(--blue);}
  .opt .lt{font-weight:800; color:var(--blue); min-width:18px;}
  .ref{background:var(--amber-soft); border:1.5px dashed #e0b34e; border-radius:10px; padding:14px 16px; font-size:15.5px; line-height:1.75; color:#3b2a05; margin-bottom:10px; user-select:none; -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; cursor:default; font-weight:500;}
  .ref-label{font-size:12px; font-weight:800; letter-spacing:.06em; color:var(--amber-dark); margin-bottom:4px;}
  .fmt-instr{font-size:13.5px; color:var(--amber-dark); font-weight:700; margin:0 0 10px; background:var(--amber-soft); padding:8px 11px; border-radius:8px; border:1px solid #f0d18f;}
  .fmt-toolbar{display:flex; flex-wrap:wrap; gap:10px; align-items:center; background:#0f172a; border-radius:12px; padding:10px 14px; margin-top:6px; position:sticky; top:54px; z-index:25; box-shadow:0 4px 12px rgba(15,23,42,.18);}
  .fmt-group{display:flex; gap:3px; background:rgba(255,255,255,.07); border-radius:8px; padding:4px;}
  .fmt-btn{background:transparent; border:none; color:#e2e8f0; padding:7px 11px; border-radius:6px; font-size:13.5px; font-weight:700; cursor:pointer; font-family:inherit;}
  .fmt-btn:hover{background:rgba(255,255,255,.16);}
  .fmt-select{background:#1e293b; color:#e2e8f0; border:1px solid #334155; border-radius:6px; padding:6px 8px; font-size:12.5px;}
  .fmt-hint{font-size:12px; color:#94a3b8; margin-left:auto;}
  .type-editable{min-height:78px; border:1.5px solid #b6c2d6; border-radius:9px; padding:11px 12px; font-size:15.5px; line-height:1.65; background:#fff; color:var(--ink); outline:none;}
  .type-editable:focus{border-color:var(--blue); outline:3px solid #bfd3ff;}
  .type-editable:empty:before{content:'Ketik di sini...'; color:#9aa5a1;}
  .type-editable ul, .type-editable ol{margin:6px 0; padding-left:24px;}
  .cnt{font-size:12.5px; color:var(--muted); margin-top:5px; display:flex; justify-content:space-between; gap:10px;}
  .cnt .warn{color:var(--bad); font-weight:700;}
  .missing{outline:3px solid #f5a3a3; outline-offset:2px;}

  .result-top{display:flex; gap:26px; align-items:center; flex-wrap:wrap;}
  .ring{--p:0; width:170px; height:170px; border-radius:50%; background:conic-gradient(var(--ringc,#1d4ed8) calc(var(--p) * 1%), #e2e8f0 0); display:flex; align-items:center; justify-content:center; flex:none;}
  .ring &gt; div{width:130px; height:130px; border-radius:50%; background:#fff; display:flex; flex-direction:column; align-items:center; justify-content:center;}
  .ring .val{font-size:44px; font-weight:800; color:var(--navy); line-height:1;}
  .ring .of{font-size:12.5px; color:var(--muted); font-weight:600; margin-top:2px;}
  .status-pill{display:inline-block; padding:6px 16px; border-radius:999px; font-weight:800; font-size:14px; margin-bottom:8px;}
  .status-pill.ok{background:var(--okbg); color:var(--ok);}
  .status-pill.no{background:var(--badbg); color:var(--bad);}
  .bars{margin-top:18px;}
  .bar-row{margin-bottom:12px;}
  .bar-row .lbl{display:flex; justify-content:space-between; font-size:13.5px; font-weight:700; color:var(--navy); margin-bottom:4px;}
  .bar-row .track{height:12px; background:#e2e8f0; border-radius:999px; overflow:hidden;}
  .bar-row .fill{height:100%; border-radius:999px;}
  .btn-row{display:flex; gap:12px; flex-wrap:wrap; margin-top:18px;}
  footer{max-width:920px; margin:38px auto 0; padding:0 18px; text-align:center; font-size:12.5px; color:#64748b;}

  .resetbox{margin-top:14px; border:1px dashed #b6c2d6; border-radius:10px; padding:10px 14px; background:#f8fafc;}
  .resetbox summary{cursor:pointer; font-size:14px; font-weight:600; color:var(--navy);}
  .resetbox .rrow{display:flex; gap:10px; flex-wrap:wrap; margin-top:10px; align-items:center;}
  .resetbox input[type=text]{flex:1; min-width:180px;}
  .guru-code{font-family:ui-monospace,Consolas,monospace; font-size:26px; font-weight:800; letter-spacing:.14em; color:var(--navy); background:var(--blue-soft); border-radius:10px; padding:10px 18px; display:inline-block; margin-top:6px;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="hero"&gt;
  &lt;div class="eyebrow"&gt;UJIAN TENGAH SEMESTER - INFORMATIKA - KELAS X SMK&lt;/div&gt;
  &lt;h1&gt;Ujian Tengah Semester Informatika&lt;/h1&gt;
  &lt;p&gt;Mencakup delapan materi: pengantar informatika, sistem komputer, sistem operasi, jaringan, pencarian informasi, dan pengolah kata.&lt;/p&gt;
  &lt;div class="chips"&gt;
    &lt;span class="chip"&gt;&lt;b&gt;35&lt;/b&gt; Pilihan Ganda&lt;/span&gt;
    &lt;span class="chip"&gt;&lt;b&gt;10&lt;/b&gt; Praktik Mengetik&lt;/span&gt;
    &lt;span class="chip"&gt;&lt;b&gt;5&lt;/b&gt; Esai&lt;/span&gt;
    &lt;span class="chip"&gt;KKM &lt;b&gt;75&lt;/b&gt;&lt;/span&gt;
    &lt;span class="chip"&gt;&lt;b&gt;3x&lt;/b&gt; Kesempatan&lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ============ LAYAR 1: ATURAN &amp; IDENTITAS ============ --&gt;
&lt;div id="screenStart" class="wrap"&gt;
  &lt;div class="card"&gt;
    &lt;h2&gt;Sebelum Mengerjakan&lt;/h2&gt;
    &lt;p&gt;Ujian terdiri atas &lt;b&gt;35 soal pilihan ganda (5 opsi)&lt;/b&gt;, &lt;b&gt;10 soal praktik mengetik&lt;/b&gt;, dan &lt;b&gt;5 soal esai&lt;/b&gt;. Semua dinilai &lt;b&gt;otomatis oleh sistem&lt;/b&gt;. Urutan soal dan urutan opsi jawaban diacak, jadi milikmu bisa berbeda dari teman sebangku.&lt;/p&gt;
    &lt;table class="tbl"&gt;
      &lt;tr&gt;&lt;th&gt;Bagian&lt;/th&gt;&lt;th class="num"&gt;Jumlah&lt;/th&gt;&lt;th class="num"&gt;Bobot per soal&lt;/th&gt;&lt;th class="num"&gt;Bobot total&lt;/th&gt;&lt;th&gt;Cara penilaian&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;A. Pilihan Ganda&lt;/td&gt;&lt;td class="num"&gt;35&lt;/td&gt;&lt;td class="num"&gt;~1,43&lt;/td&gt;&lt;td class="num"&gt;50&lt;/td&gt;&lt;td&gt;Jawaban benar mendapat skor penuh&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;B. Praktik Mengetik&lt;/td&gt;&lt;td class="num"&gt;10&lt;/td&gt;&lt;td class="num"&gt;2,5&lt;/td&gt;&lt;td class="num"&gt;25&lt;/td&gt;&lt;td&gt;Sesuai tingkat kesamaan ketikan dengan naskah&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;C. Esai&lt;/td&gt;&lt;td class="num"&gt;5&lt;/td&gt;&lt;td class="num"&gt;5&lt;/td&gt;&lt;td class="num"&gt;25&lt;/td&gt;&lt;td&gt;Sesuai konsep kunci yang tercakup dalam jawaban&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Total&lt;/b&gt;&lt;/td&gt;&lt;td class="num"&gt;&lt;b&gt;50&lt;/b&gt;&lt;/td&gt;&lt;td class="num"&gt;-&lt;/td&gt;&lt;td class="num"&gt;&lt;b&gt;100&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;div class="notice"&gt;
      &lt;b&gt;Aturan penting:&lt;/b&gt;
      &lt;ul style="margin:6px 0 0;"&gt;
        &lt;li&gt;&lt;b&gt;KKM = 75.&lt;/b&gt; Begitu nilai akhirmu mencapai atau melebihi KKM, nilai itu langsung terkirim ke guru.&lt;/li&gt;
        &lt;li&gt;Kamu punya &lt;b&gt;3 kali kesempatan&lt;/b&gt;. Jika sampai percobaan ke-3 belum mencapai KKM, sistem mengirim &lt;b&gt;nilai tertinggi&lt;/b&gt; dari ketiga percobaan.&lt;/li&gt;
        &lt;li&gt;Pilih &lt;b&gt;Kelas&lt;/b&gt;, lalu &lt;b&gt;No. Absen&lt;/b&gt; - namamu akan terisi otomatis dari data presensi. Pastikan No. Absen yang dipilih benar-benar milikmu.&lt;/li&gt;
        &lt;li&gt;Masukkan &lt;b&gt;Token Kelas&lt;/b&gt; yang diberikan guru sebelum bisa memulai ujian. Token berbeda untuk setiap kelas.&lt;/li&gt;
        &lt;li id="deadlineNotice"&gt;Ujian wajib diselesaikan dalam batas waktu yang ditentukan guru. &lt;b&gt;Jika belum mengerjakan sama sekali setelah batas waktu berakhir, nilai yang tercatat otomatis 0.&lt;/b&gt;&lt;/li&gt;
        &lt;li&gt;Naskah pada soal mengetik &lt;b&gt;tidak bisa disalin&lt;/b&gt;, dan kolom jawaban &lt;b&gt;menolak tempel (paste)&lt;/b&gt;, seret-lepas teks, serta klik kanan. Semua jawaban wajib diketik sendiri.&lt;/li&gt;
        &lt;li&gt;Setelah selesai, &lt;b&gt;unduh bukti mengerjakan (PDF F4)&lt;/b&gt;, cetak, lalu serahkan ke guru mata pelajaran.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="card"&gt;
    &lt;h2&gt;Identitas Peserta&lt;/h2&gt;
    &lt;div class="field-grid"&gt;
      &lt;div&gt;&lt;label class="fl" for="fKelas"&gt;Kelas&lt;/label&gt;
        &lt;select id="fKelas"&gt;
          &lt;option value="" disabled selected&gt;- Pilih kelas -&lt;/option&gt;
          &lt;option value="X TB1"&gt;X TB1&lt;/option&gt;
          &lt;option value="X TB2"&gt;X TB2&lt;/option&gt;
          &lt;option value="X TO1"&gt;X TO1&lt;/option&gt;
          &lt;option value="X TO2"&gt;X TO2&lt;/option&gt;
          &lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;
          &lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;
        &lt;/select&gt;&lt;/div&gt;
      &lt;div&gt;&lt;label class="fl" for="fAbsen"&gt;No. Absen&lt;/label&gt;
        &lt;select id="fAbsen" disabled&gt;&lt;option value=""&gt;- Pilih kelas dulu -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
      &lt;div&gt;&lt;label class="fl" for="fNama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="fNama" placeholder="Otomatis setelah No. Absen dipilih" readonly&gt;&lt;/div&gt;
      &lt;div&gt;&lt;label class="fl" for="fToken"&gt;Token Kelas&lt;/label&gt;&lt;input type="text" id="fToken" placeholder="Token dari guru" autocomplete="off"&gt;&lt;/div&gt;
    &lt;/div&gt;
    
    &lt;details class="resetbox" id="resetBox"&gt;
      &lt;summary&gt;Punya Kode Reset dari guru?&lt;/summary&gt;
      &lt;div class="rrow"&gt;
        &lt;input type="text" id="fResetCode" placeholder="Kode reset dari guru" autocomplete="off" maxlength="12"&gt;
        &lt;button class="btn ghost" id="btnUseReset" type="button"&gt;Gunakan Kode&lt;/button&gt;
      &lt;/div&gt;
      &lt;div class="msg" id="resetMsg"&gt;&lt;/div&gt;
    &lt;/details&gt;
    &lt;div class="msg err" id="startErr"&gt;&lt;/div&gt;
    &lt;div style="margin-top:18px;"&gt;&lt;button class="btn primary block" id="btnStart" type="button"&gt;Mulai Mengerjakan&lt;/button&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="card hidden" id="guruCard"&gt;
    &lt;h2&gt;Panel Guru - Reset Percobaan&lt;/h2&gt;
    &lt;div id="guruLogin"&gt;
      &lt;label class="fl" for="rsPin"&gt;PIN Guru&lt;/label&gt;
      &lt;div class="rrow" style="display:flex; gap:10px; flex-wrap:wrap;"&gt;
        &lt;input type="password" id="rsPin" placeholder="Masukkan PIN guru" autocomplete="off" style="flex:1; min-width:200px;"&gt;
        &lt;button class="btn primary" id="btnGuruLogin" type="button"&gt;Buka Panel&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div id="guruTools" class="hidden"&gt;
      &lt;div class="field-grid" style="grid-template-columns:1fr 2fr 1fr;"&gt;
        &lt;div&gt;&lt;label class="fl" for="rsKelas"&gt;Kelas&lt;/label&gt;
          &lt;select id="rsKelas"&gt;&lt;option value="" disabled selected&gt;- Pilih kelas -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
        &lt;div&gt;&lt;label class="fl" for="rsAbsen"&gt;Siswa (No. Absen)&lt;/label&gt;
          &lt;select id="rsAbsen" disabled&gt;&lt;option value=""&gt;- Pilih kelas dulu -&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;
        &lt;div&gt;&lt;label class="fl" for="rsNo"&gt;Reset ke-&lt;/label&gt;
          &lt;input type="number" id="rsNo" min="1" max="9" value="1"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="btn-row"&gt;
        &lt;button class="btn primary" id="btnRsCode" type="button"&gt;Buat Kode Reset&lt;/button&gt;
        &lt;button class="btn amber" id="btnRsHere" type="button"&gt;Reset di Perangkat Ini&lt;/button&gt;
      &lt;/div&gt;
      &lt;div id="rsOut" style="margin-top:12px;"&gt;&lt;/div&gt;
      &lt;p style="font-size:13px; color:#64748b; margin:12px 0 0;"&gt;
        &lt;b&gt;Kode reset&lt;/b&gt;: berikan ke siswa, lalu siswa memilih Kelas &amp;amp; No. Absen dan memasukkannya di kotak "Punya Kode Reset". Kode hanya berlaku untuk siswa tersebut dan sekali pakai per perangkat.
        Untuk reset kedua siswa yang sama, naikkan "Reset ke-" menjadi 2, dan seterusnya.
        &lt;b&gt;Reset di Perangkat Ini&lt;/b&gt; dipakai bila siswa mengerjakan di perangkat yang sedang Anda pegang.
      &lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ============ LAYAR 2: UJIAN ============ --&gt;
&lt;div id="screenExam" class="hidden"&gt;
  &lt;div class="progress-bar"&gt;
    &lt;div class="progress-inner"&gt;
      &lt;span class="who" id="whoLbl"&gt;&lt;/span&gt;
      &lt;span class="att" id="attLbl"&gt;&lt;/span&gt;
      &lt;div class="pbar"&gt;&lt;div id="pbarFill"&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;span class="pcount" id="pcount"&gt;0 / 50 terjawab&lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="wrap"&gt;
    &lt;div class="sec-head sec-a"&gt;&lt;h2&gt;A. Pilihan Ganda (35 soal - 50 poin)&lt;/h2&gt;&lt;p&gt;Pilih satu jawaban paling tepat dari lima opsi.&lt;/p&gt;&lt;/div&gt;
    &lt;div id="bodyA"&gt;&lt;/div&gt;

    &lt;div class="sec-head sec-b"&gt;&lt;h2&gt;B. Praktik Mengetik (10 soal - 25 poin)&lt;/h2&gt;&lt;p&gt;Ketik ulang naskah sesuai instruksi, lalu terapkan format yang diminta memakai toolbar di bawah ini. Tempel (paste) dinonaktifkan.&lt;/p&gt;&lt;/div&gt;
    &lt;div class="fmt-toolbar" id="fmtToolbar"&gt;
      &lt;div class="fmt-group"&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="bold" title="Bold"&gt;&lt;b&gt;B&lt;/b&gt;&lt;/button&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="italic" title="Italic"&gt;&lt;i&gt;I&lt;/i&gt;&lt;/button&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="underline" title="Underline"&gt;&lt;u&gt;U&lt;/u&gt;&lt;/button&gt;
      &lt;/div&gt;
      &lt;div class="fmt-group"&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="justifyLeft"&gt;Kiri&lt;/button&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="justifyCenter"&gt;Tengah&lt;/button&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="justifyRight"&gt;Kanan&lt;/button&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="justifyFull"&gt;Rata&lt;/button&gt;
      &lt;/div&gt;
      &lt;div class="fmt-group"&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="insertUnorderedList"&gt;List Bullet&lt;/button&gt;
        &lt;button type="button" class="fmt-btn" data-cmd="insertOrderedList"&gt;1. List&lt;/button&gt;
      &lt;/div&gt;
      &lt;div class="fmt-group"&gt;
        &lt;select id="fmtSize" class="fmt-select" title="Ukuran huruf (pt)"&gt;
          &lt;option value=""&gt;Ukuran Huruf&lt;/option&gt;
          &lt;option value="8"&gt;8&lt;/option&gt;&lt;option value="10"&gt;10&lt;/option&gt;&lt;option value="11"&gt;11&lt;/option&gt;
          &lt;option value="12"&gt;12&lt;/option&gt;&lt;option value="14"&gt;14&lt;/option&gt;&lt;option value="16"&gt;16&lt;/option&gt;
          &lt;option value="18"&gt;18&lt;/option&gt;&lt;option value="20"&gt;20&lt;/option&gt;&lt;option value="24"&gt;24&lt;/option&gt;
          &lt;option value="28"&gt;28&lt;/option&gt;&lt;option value="36"&gt;36&lt;/option&gt;
        &lt;/select&gt;
        &lt;select id="fmtFont" class="fmt-select" title="Jenis huruf"&gt;
          &lt;option value=""&gt;Jenis Huruf&lt;/option&gt;
          &lt;option value="Times New Roman"&gt;Times New Roman&lt;/option&gt;
          &lt;option value="Arial"&gt;Arial&lt;/option&gt;
          &lt;option value="Calibri"&gt;Calibri&lt;/option&gt;
          &lt;option value="Verdana"&gt;Verdana&lt;/option&gt;
          &lt;option value="Georgia"&gt;Georgia&lt;/option&gt;
          &lt;option value="Tahoma"&gt;Tahoma&lt;/option&gt;
          &lt;option value="Courier New"&gt;Courier New&lt;/option&gt;
          &lt;option value="Comic Sans MS"&gt;Comic Sans MS&lt;/option&gt;
        &lt;/select&gt;
      &lt;/div&gt;
      &lt;span class="fmt-hint"&gt;Klik di kotak jawaban dulu, lalu gunakan tombol ini&lt;/span&gt;
    &lt;/div&gt;
    &lt;div id="bodyB"&gt;&lt;/div&gt;

    &lt;div class="sec-head sec-c"&gt;&lt;h2&gt;C. Esai (5 soal - 25 poin)&lt;/h2&gt;&lt;p&gt;Jawab dengan kalimatmu sendiri (minimal 40 karakter). Jawaban dinilai dari konsep kunci pada materi. Tempel (paste) dinonaktifkan.&lt;/p&gt;&lt;/div&gt;
    &lt;div id="bodyC"&gt;&lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;div class="msg err" id="examErr"&gt;&lt;/div&gt;
      &lt;button class="btn primary block" id="btnSubmit" type="button"&gt;Kirim Jawaban&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ============ LAYAR 3: HASIL ============ --&gt;
&lt;div id="screenResult" class="wrap hidden"&gt;
  &lt;div class="card"&gt;
    &lt;div class="result-top"&gt;
      &lt;div class="ring" id="ring"&gt;&lt;div&gt;&lt;span class="val" id="rVal"&gt;0&lt;/span&gt;&lt;span class="of"&gt;dari 100&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div style="flex:1; min-width:240px;"&gt;
        &lt;span class="status-pill" id="rPill"&gt;&lt;/span&gt;
        &lt;h2 style="margin:0 0 6px;" id="rTitle"&gt;&lt;/h2&gt;
        &lt;p style="margin:0; color:#334155;" id="rDesc"&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="bars" id="rBars"&gt;&lt;/div&gt;
    &lt;div class="notice" id="rHist"&gt;&lt;/div&gt;
    &lt;div class="msg info" id="rSend"&gt;&lt;/div&gt;
    &lt;div class="btn-row"&gt;
      &lt;button class="btn amber" id="btnPdf" type="button"&gt;Unduh Bukti Mengerjakan (PDF F4)&lt;/button&gt;
      &lt;button class="btn primary hidden" id="btnRetry" type="button"&gt;Coba Lagi&lt;/button&gt;
      &lt;button class="btn ghost" id="btnHome" type="button"&gt;Kembali ke Awal&lt;/button&gt;
    &lt;/div&gt;
    &lt;p style="font-size:13px; color:#64748b; margin:14px 0 0;"&gt;&lt;b&gt;Wajib:&lt;/b&gt; setelah nilai tersimpan, unduh PDF, cetak pada kertas F4 (maks. 3 lembar), dan serahkan ke guru mata pelajaran sebagai bukti pengerjaan.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;footer&gt;Ujian Tengah Semester Informatika Kelas X SMK - DOK/INFORMATIKA/X/UTS&lt;/footer&gt;

&lt;iframe name="hidden_iframe" id="hidden_iframe" style="display:none;"&gt;&lt;/iframe&gt;
&lt;form id="gform" action="https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec" method="POST" target="hidden_iframe" style="display:none;"&gt;
  &lt;input type="hidden" name="no absen" id="gAbsen"&gt;
  &lt;input type="hidden" name="nama" id="gNama"&gt;
  &lt;input type="hidden" name="kelas" id="gKelas"&gt;
  &lt;input type="hidden" name="nilai evaluasi" id="gNilai"&gt;
  &lt;input type="hidden" name="skor" id="gSkor"&gt;
  &lt;input type="hidden" name="Nilai STS" id="gNilaiSTS"&gt;
  &lt;input type="hidden" name="judul materi" id="gJudul"&gt;
&lt;/form&gt;

&lt;script&gt;
(function(){
"use strict";

var KKM = 75, MAX_ATT = 3;
var JUDUL = "Ujian Tengah Semester Informatika Kelas X";
var DOC_CODE = "DOK/INFORMATIKA/X/UTS";

/* TODO GURU: sesuaikan tanggal &amp; jam ujian dibuka di sini (format ISO, zona waktu WIB +07:00) */
var EXAM_OPEN = new Date('2026-10-15T07:00:00+07:00');
var DEADLINE_DAYS = 7;
var EXAM_DEADLINE = new Date(EXAM_OPEN.getTime() + DEADLINE_DAYS*24*60*60*1000);
/* Versi ujian - diperbarui otomatis tiap kali EXAM_OPEN diubah, supaya percobaan (attempt) siswa dari periode ujian sebelumnya tidak ikut terhitung saat ujian dibuka ulang */
var EXAM_VERSION = 'v-' + EXAM_OPEN.toISOString().slice(0,10);

var ROSTER = {
  'X TB1': [[1,'ADHA MIRAHAYU NINGSIH'],[2,'ANGELINA AGNES NOVIOLA'],[3,'ANISSA VIRA FIRMANDA'],[4,'Awinda Rizqi Amru Anjani'],[5,'CITRA AULIA BAHARIZQI'],[6,'DHIRA CAHYA OKTAVIANINGRUM'],[7,'Ema Deny Setyaningrum'],[8,'EVITA APRILIANI'],[9,'FAIR ROCHMATUL ISHMAH'],[10,'FATMA NABILA'],[11,'FATMAWATI'],[12,'Fika Setya Wulandari'],[13,'Gelsi Zalika Thufaila'],[14,'HIDAYATUL HUSNA MAULIDA'],[15,'IZZATUL LUTHFA IMANDA'],[16,'MEISHA AYU BERLIA'],[17,'MEYLISA PUTRI MURDIANI'],[18,'MITA TRI OKTAVIA'],[19,'MUTIARA SYABILLA'],[20,'NABILA SASKIA'],[21,'NADYA VEGA RAHMADANI'],[22,'Nafisa Nailal Husna'],[23,'NAWA SYIFFA MARYAM'],[24,'NIRIN FADILLAH'],[25,'RENI RESTIA PRAMESTI'],[26,'RIZKIYA MAULIDA'],[27,'SHINTA AULIA MUKTI'],[28,'SHINTIA AMELIA PUTRI'],[29,'SUCI NUR AZAHRA'],[30,'SYAH UITTA JIHAN MAKAELA'],[31,'VARISCA INDRI ARIANI'],[32,'VILSA SAFA AZZAHRA'],[33,'YUANITA ROBBITOTUN NAJA'],[34,'ZUNI ARIFAH']],
  'X TB2': [[1,'ADINDA EKA NUR AINI'],[2,'Adinda Permata Sari'],[3,'AMANDA PUSPA MAHARANI'],[4,'ANA AMELLIYA'],[5,'AULIYA NATASYA DEWI'],[6,'AYUB MAULANA'],[7,'BILQIS NGIZZATUN NISA\''],[8,'CEYSA ELVINA AMANDA'],[9,'CHAUDLI MAULIDINA AZRA'],[10,'DESTA AURA AZALIA SANI'],[11,'DINDA NURUL AMALIA'],[12,'Dwi Mayangsari'],[13,'EFRIDA DEWI ANANDA'],[14,'Emi Rara Pramudita'],[15,'FIRMAN ARYA DINAIA'],[16,'KEISYA ALISYA PUTRI ALI'],[17,'Kusnul Kusuma Wardhani'],[18,'LIBY ARUMBIYA'],[19,'Lutfia Putri'],[20,'MARISKA NUR AIRA'],[21,'NADIA NUR AZIZAH IRYSATI'],[22,'NAHLA AULIA SALSABILLA'],[23,'NAURA IZA AZAHRA'],[24,'NAZWA AKAYLA SAPUTRI'],[25,'NOVITA ADHA RIZKI ANGGRAINI'],[26,'PEMBAYUN ANGGER WIDJAYA'],[27,'REVALIA NOVIANTI TAMARA'],[28,'SAHAL HANIF YUSUF'],[29,'SEKAR WANGI'],[30,'Silvi Dwi Noviyanti'],[31,'SIRVI KHUNAIFIYAH LINA AINI NISA'],[32,'VITO ADI CANDRA'],[33,'ZAHWA'],[34,'ZIDANNAJ MAROSAT']],
  'X TO1': [[1,'ACHMAT SAPTA ARYA'],[2,'ADE FASIYAN NANDO'],[3,'ALBERTUS ADELIO RONALD FAGUNDEZ'],[4,'ALLAN FADLI MUHAMMAD JIBRANNI'],[5,'ARGA SYARUL SETIYAWAN'],[6,'DEDDY SETIYAWAN'],[7,'DENNIZ PRADIVTA ALVIANDRA'],[8,'DIMAS PUTRA ERLANGGA'],[9,'DIYAS PRASETYO'],[10,'DZAKIE AZ ZAAHII'],[11,'FADHIL DIANTORO'],[12,'FAQIH UBAIDILLAH'],[13,'FAQIYUDDIN ASYRAF'],[14,'HANIF TAUFIQULHAQIM'],[15,'IQBAL HAIDAR ARIQ'],[16,'IRFAN BAGAS VEBYANTINO'],[17,'MARCELINO ADI SANTOSO'],[18,'MUHAMAD AFTAH FARKHAN NAKA'],[19,'MUHAMAD AMAR MA\'RUF'],[20,'MUHAMAD ILHAM PRASETYO'],[21,'MUHAMMAD AFWAN MAULANA AZIDAN'],[22,'MUHAMMAD KAFA BIHI FADLURROZAG'],[23,'MUHAMMAD SONY TAFRIKHAN'],[24,'MUHAMMAD SYAHID SYAHPUTRA'],[25,'MUHAMMAD ZAHRUL ULUM AL BUSTHOMI'],[26,'NANDO KURNIA EKA PUTRA'],[27,'NAUFAL ZAKY MUTTAQIN'],[28,'Nizham Zafir Hariz Saputra'],[29,'NUR FATHUR RAHMATULLAH'],[30,'RADITYA PUTRA PERDANA'],[31,'RAFKA MAULANA PUTRA'],[32,'RAKA RAFIF AZMI'],[33,'REYHAN YUNAZTA PUTRA HANY'],[34,'RIZKI IBADALLAH ALBAROYA'],[35,'SEPTIAN DANANG PRASETYO'],[36,'Yehuda Bayu Seno Abimanyu']],
  'X TO2': [[1,'ADHAM DZAKI IBRAHIM MUFID'],[2,'AFLAH RAPHAEL WINDRA RADITYA'],[3,'AHMAD DAVI PRIA UTAMA'],[4,'AHMAD MAULANA ABDUL MALIK'],[5,'Ahmad Shovi Maola'],[6,'ALBARASYA HERMAN'],[7,'ANDIK FEBRI FIRMANSYAH'],[8,'DIMAS SAPUTRA'],[9,'DISKA INDRA FATA WIDAKDO'],[10,'FAREL ANDRIAN PRATAMA'],[11,'Ferdita Fatma Kusuma'],[12,'GALIH GINANJAR SAPUTRO'],[13,'LEVI FASTABIKHUL KHOIR'],[14,'LIONEL NATHAN KURNIAWAN'],[15,'MUHAMAD FIRDIANSYAH'],[16,'MUHAMAD KHOIRUL ANWAR'],[17,'MUHAMAD RIJAL ALIF IBRAHIM'],[18,'MUHAMAD TANFEBRO PERMANA'],[19,'MUHAMMAD AKMAL FARIZKY'],[20,'MUHAMMAD ALWI SIHAB'],[21,'MUHAMMAD \'AQILA AZKA'],[22,'MUHAMMAD FATIH'],[23,'MUHAMMAD FAUZI NOVANTYO'],[24,'Muhammad Rafka Adi Pratama'],[25,'MUHAMMAD TONI SUPRASETYO'],[26,'NOVAN ANANDA RIZKY'],[27,'PRAKASA TEGAR ANDRIYANTO'],[28,'RAFAEL BAYU PRATAMA'],[29,'RANDI KHOIRUL AZZAM'],[30,'RIDHO AMRU BAIN'],[31,'SABRI RADHITYA GANESWARA'],[32,'SATRIA FIRMAN WIBOWO'],[33,'SYARIF HIDAYAD'],[34,'WILDAN RIZQI ARRUMMAN'],[35,'VICKY ADIKA CHANDRA']],
  'X TJKT1': [[1,'ADLY SURYA HERNAWAN'],[2,'ADRIANUS FERDINAND BARRA ARDIONA'],[3,'AISYAH KHOIRUN NISA'],[4,'ALIF EVAN SANJAYA'],[5,'Alif Maulana Ashidiqie'],[6,'Alif Rizqullah Ramadhan'],[7,'ANDIKA BILLIE FAUSTINO'],[8,'ARVIN ARYASETA ATMAJA'],[9,'BRYLIANA JIHAN AMIR'],[10,'DANDRY KORNELIUS ARISANDI'],[11,'DANIEL REVELINO FEIVEL WIDJANARKO'],[12,'DWI APRIELLA NUR RAHMA'],[13,'HANAR FAIRUS NADHIR AMRULLAH'],[14,'HANI DWI RAHMAWATI'],[15,'IKHSAN ABDUL ROZAK'],[16,'JAVIEN PUTRA MAHENDRA'],[17,'Linda Putri Darmawan'],[18,'LINGGA MAHARDIKA SUSATYO'],[19,'MUAZARAH FARRAS AMELIA'],[20,'MUHAMMAD FA\'DLIL ASYHARI'],[21,'MUHAMMAD FAQIH ANWARI'],[22,'MUHAMMAD SIDIQ FATHUL MU\'IZ'],[23,'NAILATUSSAKINAH'],[24,'NAJMA AULYA'],[25,'Nazila Karrolina Aishi Azzahra'],[26,'RAKHUL SURYA PERMANA'],[27,'RAYA GHANIA PUTRI'],[28,'REVALINA AULIA ZAHRA'],[29,'Salma Anggun Aurellia'],[30,'SANREZ REKSI KUSUMA'],[31,'SETIYA RIZKY MAULANA'],[32,'SHARA FATIKA ADDAHLANI'],[33,'SINTA PUSPA SARI'],[34,'ZULFA IBNUL KARIM']],
  'X TJKT2': [[1,'AKHMAD FAKHRI HIDAYAT'],[2,'ALIA NUR AISYAH'],[3,'ANDIKA MAULANA MUHAIMIN'],[4,'ANDRIANSYAH MAULANA'],[5,'ANUM MANURAGA'],[6,'BRIANDA OCTA PRATAMA'],[7,'DAVID LUQMAN'],[8,'DEVANDRI KUSUMA'],[9,'DIMAS SALMAN KHAFI'],[10,'DIVANI AULIA NAJMI'],[11,'DWI AYU SASKIYA'],[12,'DYLLAN DEEP YOSHAN'],[13,'DZIKRI MAULANA'],[14,'FAREL RADHITYA PRADANA'],[15,'GHIFARI SAHILA DZAKI'],[16,'INDAH NAZIFA'],[17,'JATI WEKO ADI'],[18,'JERIKO PRANATA'],[19,'KHOIRUL MUNA'],[20,'MAYLINDA QHONITA PUTRI'],[21,'MIFTAKHUL ADIYANSYAH'],[22,'MUHAMAD RESTU FAUZI'],[23,'MUHAMMAD ABADI'],[24,'MUHAMMAD FAIZ FIRMANSYAH'],[25,'MUHAMMAD IRFAN SAPUTRA'],[26,'MUHAMMAD REVA IBRAHIMMUVIQ'],[27,'NAJA ADKAR KHAIR'],[28,'NIZAM JATI WARDOYO'],[29,'NUR FIDYAH'],[30,'OKFAL NOVA PUJI SATRIAWAN'],[31,'QUINSHA VIROSYA PRICILLIA'],[32,'REZA HAFID ARIYANTO'],[33,'RICKY ARDIANSYAH'],[34,'SITI ERNAWATI'],[35,'SYAHRINA LAILA FARHANA'],[36,'WILDAN SETIO FIRMANSYAH']],
};
var CLASS_TOKENS = {
  'X TB1': 'TB1-4821',
  'X TB2': 'TB2-7093',
  'X TO1': 'TO1-2264',
  'X TO2': 'TO2-5518',
  'X TJKT1': 'TJKT1-3390',
  'X TJKT2': 'TJKT2-9147',
};
var PT_PG = 50/35, PT_TYPE = 2.5, PT_ESAI = 5;
var MAX_PG = 50, MAX_TYPE = 25, MAX_ESAI = 25;
var TOTAL_Q = 50;

/* ================= BANK SOAL ================= */

var PG = [
 {m:"Pengantar Informatika", q:"Seorang siswa diminta menyusun langkah-langkah menemukan lokasi kelas yang berpindah menjadi instruksi berurutan yang bisa diikuti siapa pun. Kemampuan yang paling tepat digunakan siswa tersebut adalah...", o:["Machine learning", "Computational thinking", "Cloud computing", "Big data", "Artificial intelligence"], a:1, lvl:"mudah"},
 {m:"Pengantar Informatika", q:"Sebuah perusahaan logistik memasang sensor pada truk untuk memantau lokasi real-time, menganalisis pola pengiriman dengan big data, lalu mengotomatisasi rute lewat AI. Transformasi ini paling tepat menggambarkan ciri Revolusi Industri...", o:["1.0", "2.0", "3.0", "4.0", "5.0"], a:3, lvl:"sedang"},
 {m:"Pengantar Informatika", q:"Seorang karyawan baru terbiasa bekerja manual dan menyimpan file hanya di komputer pribadi. Setelah pindah ke tim yang tersebar di berbagai kota dan bekerja jarak jauh, langkah paling mendesak yang perlu ia lakukan adalah...", o:["Tetap bekerja seperti sebelumnya karena sudah terbiasa", "Mengadopsi kolaborasi daring dan penyimpanan cloud sebagai bagian budaya kerja digital", "Meminta seluruh tim kembali bekerja manual", "Menolak menggunakan aplikasi apa pun", "Hanya mengandalkan komunikasi tatap muka"], a:1, lvl:"sedang"},
 {m:"Pengantar Informatika", q:"Sebuah sekolah ingin memakai software edukasi gratis, namun tim IT-nya juga berencana memodifikasi kode programnya agar sesuai kebutuhan lokal dan membagikannya kembali ke sekolah lain. Jenis lisensi yang paling sesuai adalah...", o:["Proprietary", "Freeware", "Shareware", "Open Source", "Trial"], a:3, lvl:"sulit"},
 {m:"Pengantar Informatika", q:"Sebuah instansi mengalami kebocoran data pelanggan akibat serangan siber, dan manajemen ingin merekrut seseorang untuk merancang sistem pertahanan serta menganalisis celah keamanan secara berkelanjutan. Profesi yang paling tepat direkrut adalah...", o:["UI/UX Designer", "Data Analyst", "Cyber Security Specialist", "Cloud Engineer", "Game Developer"], a:2, lvl:"sulit"},
 {m:"Sistem Komputer", q:"Saat sebuah kalkulator menjumlahkan dua angka, komponen CPU yang secara langsung memproses operasi hitung tersebut adalah...", o:["Control Unit", "ALU", "Register", "Memori Utama", "Bus data"], a:1, lvl:"mudah"},
 {m:"Sistem Komputer", q:"Seorang teknisi menjelaskan bahwa pada komputer modern, program dan data yang diolahnya sama-sama disimpan di lokasi memori yang sama sehingga CPU dapat mengaksesnya secara fleksibel. Prinsip yang dijelaskan teknisi tersebut adalah...", o:["Bottleneck", "Stored program concept", "Fetch cycle", "Cache memory", "Virtual memory"], a:1, lvl:"sedang"},
 {m:"Sistem Komputer", q:"Saat listrik padam mendadak tanpa sempat menyimpan pekerjaan, dokumen yang sedang dikerjakan di komputer hilang total. Jenis penyimpanan yang menjelaskan mengapa hal ini terjadi adalah...", o:["HDD", "SSD", "RAM", "Flashdisk", "CD-ROM"], a:2, lvl:"sedang"},
 {m:"Sistem Komputer", q:"Seorang siswa mengamati bahwa CPU selalu mengambil instruksi dari memori terlebih dahulu, menerjemahkannya, baru menjalankannya, secara berulang jutaan kali per detik. Urutan tahapan yang sedang diamati siswa tersebut adalah...", o:["Execute-Fetch-Decode", "Decode-Fetch-Execute", "Fetch-Decode-Execute", "Fetch-Execute-Decode", "Execute-Decode-Fetch"], a:2, lvl:"sulit"},
 {m:"Sistem Operasi &amp; File", q:"Ketika laptop menjalankan Word, browser, dan pemutar musik secara bersamaan tanpa saling mengganggu, fungsi sistem operasi yang sedang bekerja adalah...", o:["Manajemen memori", "Manajemen proses", "Manajemen file", "User interface", "Manajemen perangkat"], a:1, lvl:"mudah"},
 {m:"Sistem Operasi &amp; File", q:"Seorang siswa kehilangan laptopnya, namun tetap dapat membuka seluruh tugasnya dari HP karena filenya otomatis tersinkronisasi ke sebuah layanan. Layanan yang dimaksud adalah...", o:["Hard disk internal", "Flashdisk", "Cloud storage", "Memory card", "CD-ROM"], a:2, lvl:"sedang"},
 {m:"Sistem Operasi &amp; File", q:"Seorang guru meminta tugas dikumpulkan dalam format yang dapat dibuka dan diedit ulang di Microsoft Word versi terbaru tanpa kehilangan format aslinya. Ekstensi file yang paling tepat diminta adalah...", o:[".txt", ".pdf", ".docx", ".jpg", ".exe"], a:2, lvl:"sedang"},
 {m:"Sistem Operasi &amp; File", q:"Sebuah pabrik smartphone menginginkan sistem operasi yang bersifat open source, ringan, dan dapat dimodifikasi bebas oleh berbagai produsen di dunia. Sistem operasi yang paling sesuai kebutuhan tersebut adalah...", o:["iOS", "Windows", "Android", "Chrome OS", "macOS"], a:2, lvl:"sulit"},
 {m:"Jaringan &amp; Keamanan", q:"Sebuah laboratorium komputer sekolah menghubungkan 20 unit komputer dalam satu ruangan menggunakan satu switch. Jenis jaringan yang diterapkan adalah...", o:["WAN", "MAN", "LAN", "PAN", "Internet"], a:2, lvl:"mudah"},
 {m:"Jaringan &amp; Keamanan", q:"Saat mengetikkan alamat sebuah situs di peramban, komputer perlu mengetahui alamat IP server tujuan sebelum dapat terhubung. Sistem yang menerjemahkan nama domain menjadi alamat IP tersebut adalah...", o:["HTTP", "DNS", "FTP", "ISP", "TCP"], a:1, lvl:"sedang"},
 {m:"Jaringan &amp; Keamanan", q:"Seorang siswa menerima pesan yang mengaku dari bank, meminta segera mengklik tautan dan memasukkan PIN karena akunnya \"akan diblokir\". Tindakan siber yang sedang dialami siswa tersebut adalah...", o:["Malware", "DDoS", "Phishing", "Firewall", "Enkripsi"], a:2, lvl:"sedang"},
 {m:"Jaringan &amp; Keamanan", q:"Sebuah aplikasi perbankan mengubah seluruh data transaksi pengguna menjadi kode acak yang hanya bisa dibaca kembali dengan kunci tertentu sebelum dikirim melalui jaringan. Proses yang diterapkan aplikasi tersebut adalah...", o:["Kompresi", "Enkripsi", "Kalibrasi", "Sinkronisasi", "Virtualisasi"], a:1, lvl:"sulit"},
 {m:"Pencarian &amp; Kolaborasi", q:"Seorang siswa ingin hasil pencariannya hanya menampilkan artikel dari situs resmi kemdikbud.go.id. Operator pencarian yang paling tepat digunakan adalah...", o:["Tanda kutip", "Tanda minus", "site:", "filetype:", "OR"], a:2, lvl:"mudah"},
 {m:"Pencarian &amp; Kolaborasi", q:"Saat meminjam laptop teman untuk login akun pribadi sebentar, seorang siswa ingin memastikan riwayat pencarian dan kata sandinya tidak tersimpan di perangkat tersebut. Fitur peramban yang sebaiknya digunakan adalah...", o:["Mode offline", "Mode incognito", "Mode hemat data", "Mode baca", "Mode pengembang"], a:1, lvl:"sedang"},
 {m:"Pencarian &amp; Kolaborasi", q:"Sebuah kelompok mengerjakan proposal bersama di Google Docs, namun salah satu anggota tidak sengaja menghapus dua paragraf penting. Fitur yang paling tepat digunakan untuk mengembalikan paragraf tersebut adalah...", o:["Bookmark", "Version history", "Cache", "Mode incognito", "Filter pencarian"], a:1, lvl:"sedang"},
 {m:"Pencarian &amp; Kolaborasi", q:"Sebuah judul artikel yang sangat provokatif tersebar cepat di media sosial tanpa mencantumkan sumber data yang jelas, dan setelah ditelusuri ternyata isinya tidak sesuai fakta. Fenomena ini paling tepat disebut...", o:["Opini", "Hoaks", "Editorial", "Testimoni", "Riset ilmiah"], a:1, lvl:"sulit"},
 {m:"Pencarian &amp; Kolaborasi", q:"Tiga siswa ingin menyunting satu dokumen laporan secara bersamaan dari rumah masing-masing pada waktu yang sama. Aplikasi pengolah kata yang paling sesuai untuk kebutuhan tersebut adalah...", o:["Microsoft Word (offline)", "Google Docs", "Notepad", "WordPad", "Adobe Reader"], a:1, lvl:"mudah"},
 {m:"Pengolah Kata Dasar", q:"Sebuah dokumen resmi mengharuskan seluruh paragraf terlihat rapi dengan sisi kiri dan kanan sama-sama lurus, tidak bergerigi. Pengaturan perataan yang perlu diterapkan adalah...", o:["Align Left", "Center", "Align Right", "Justify", "Indent"], a:3, lvl:"sedang"},
 {m:"Pengolah Kata Dasar", q:"Dalam dokumen sepanjang 50 halaman, seorang siswa perlu segera menemukan seluruh kemunculan kata \"revisi\" tanpa membaca ulang seluruh dokumen. Cara tercepat adalah menggunakan...", o:["Ctrl+S", "Ctrl+P", "Ctrl+F", "Ctrl+Z", "Ctrl+Y"], a:2, lvl:"sedang"},
 {m:"Pengolah Kata Dasar", q:"Seorang penulis menyadari ia salah mengeja nama tokoh di 40 tempat berbeda sepanjang naskah novelnya, dan ingin memperbaikinya sekaligus tanpa mencari satu per satu secara manual. Fitur paling efisien untuk kebutuhan tersebut adalah...", o:["Copy-Paste", "Find &amp; Replace", "Cut-Paste", "Undo-Redo", "Spell Check"], a:1, lvl:"sulit"},
 {m:"Fitur Lanjut Pengolah Kata", q:"Agar seluruh judul bab pada laporan dapat langsung dikenali sistem dan disusun otomatis menjadi daftar isi, judul-judul tersebut perlu diberi format...", o:["Normal", "Heading", "Caption", "Watermark", "Footnote"], a:1, lvl:"mudah"},
 {m:"Fitur Lanjut Pengolah Kata", q:"Panitia sebuah acara perlu mengirim 200 undangan dengan nama dan alamat penerima berbeda-beda, namun format suratnya identik. Fitur paling efisien untuk kebutuhan ini adalah...", o:["Table of Contents", "Mail Merge", "Cross-reference", "Citation", "Track Changes"], a:1, lvl:"sedang"},
 {m:"Fitur Lanjut Pengolah Kata", q:"Setelah menambahkan tiga subbab baru di tengah laporan, seorang siswa ingin daftar isi dan nomor halamannya diperbarui otomatis tanpa mengetik ulang. Hal ini dimungkinkan karena daftar isi otomatis dibangun berdasarkan...", o:["Warna teks", "Style Heading", "Ukuran font", "Jenis huruf", "Nomor halaman manual"], a:1, lvl:"sedang"},
 {m:"Fitur Lanjut Pengolah Kata", q:"Sebuah laporan berisi banyak gambar dan tabel yang perlu diberi keterangan bernomor otomatis seperti \"Gambar 1.1\" agar mudah dirujuk di dalam teks. Fitur yang tepat digunakan adalah...", o:["Watermark", "Caption", "Header", "Footer", "Page Break"], a:1, lvl:"sulit"},
 {m:"Integrasi Dokumen", q:"Sebuah laporan PKL perlu memakai angka romawi (i, ii, iii) untuk halaman kata pengantar dan angka biasa (1, 2, 3) untuk bab isi, dalam satu dokumen yang sama. Fitur yang memungkinkan dua format nomor halaman berbeda ini adalah...", o:["Page Number saja", "Section Break", "Column Break", "Mail Merge", "Caption"], a:1, lvl:"mudah"},
 {m:"Fitur Lanjut Pengolah Kata", q:"Seorang siswa merancang formulir biodata (nama, NIS, kelas) yang perlu terlihat rapi dan sejajar, namun tidak boleh menampilkan garis kotak apa pun saat dicetak. Teknik paling efisien untuk kebutuhan ini adalah...", o:["Tab manual berulang", "Spasi berulang", "Tabel dengan border disembunyikan (No Border)", "Bullet list", "Drop cap"], a:2, lvl:"sedang"},
 {m:"Integrasi Dokumen", q:"Sekolah menetapkan seluruh Laporan PKL siswa harus dicetak menggunakan ukuran kertas yang sedikit lebih panjang dari A4 sesuai pedoman resmi laporan dinas. Ukuran kertas yang dimaksud adalah...", o:["A4", "Letter", "F4", "Legal", "B5"], a:2, lvl:"mudah"},
 {m:"Integrasi Dokumen", q:"Seorang siswa menyusun Laporan PKL dan bingung di bagian mana sebaiknya menuliskan kesimpulan hasil pelaksanaan serta saran untuk perusahaan. Berdasarkan sistematika umum laporan, bagian yang tepat adalah...", o:["BAB I", "BAB II", "BAB III", "BAB IV", "Bagian Akhir"], a:3, lvl:"sedang"},
 {m:"Fitur Lanjut Pengolah Kata", q:"Sebuah laporan organisasi perlu menampilkan hierarki jabatan dari Ketua hingga Anggota secara visual dan mudah dipahami tanpa harus menuliskan penjelasan panjang. Fitur yang paling tepat digunakan adalah...", o:["Grafik data penjualan", "SmartArt bagan struktur organisasi", "Daftar pustaka", "Nomor halaman", "Watermark"], a:1, lvl:"sulit"},
 {m:"Integrasi Dokumen", q:"Sebuah karya tulis mengutip lima orang penulis sekaligus dari satu sumber jurnal, dan siswa ingin menuliskan sitasinya secara ringkas sesuai kaidah tanpa menyebutkan seluruh nama. Format sitasi yang tepat adalah...", o:["Menuliskan nama lengkap seluruh penulis", "Nama penulis pertama diikuti \"dkk.\"", "Hanya mencantumkan tahun", "Nama penulis terakhir saja", "Tanpa nama, hanya judul"], a:1, lvl:"sulit"},
];

var TYPING = [
 {m:'Fitur Lanjut Pengolah Kata', t:'STYLE DAN HEADING', instr:'Ketik judul berikut, lalu jadikan Bold dan rata Tengah.', fmt:['bold','center']},
 {m:'Fitur Lanjut Pengolah Kata', t:'Heading menandai struktur dan hierarki dokumen secara konsisten.', instr:'Ketik kalimat berikut, lalu jadikan huruf miring (Italic).', fmt:['italic']},
 {m:'Fitur Lanjut Pengolah Kata', t:'Update Table memperbarui daftar isi otomatis tanpa mengetik ulang.', instr:'Ketik kalimat berikut, lalu garis bawahi (Underline) seluruhnya.', fmt:['underline']},
 {m:'Fitur Lanjut Pengolah Kata', t:'Menyisipkan tabel\nMengatur text wrapping gambar\nMenambahkan caption otomatis', instr:'Ketik ketiga baris berikut sebagai daftar bertitik (Bullet List).', fmt:['ul'], multiline:true},
 {m:'Fitur Lanjut Pengolah Kata', t:'Buat dokumen utama\nSiapkan sumber data\nGabungkan dengan mail merge', instr:'Ketik ketiga baris berikut sebagai daftar bernomor (Numbered List).', fmt:['ol'], multiline:true},
 {m:'Integrasi Dokumen', t:'Jakarta, 8 September 2026', instr:'Ketik naskah berikut, lalu buat rata kanan (Align Right), seperti penulisan tanggal surat.', fmt:['right']},
 {m:'Fitur Lanjut Pengolah Kata', t:'Daftar pustaka disusun otomatis dari seluruh sitasi yang sudah dimasukkan ke dalam dokumen.', instr:'Ketik kalimat berikut, lalu ratakan kiri-kanan (Justify).', fmt:['justify']},
 {m:'Fitur Lanjut Pengolah Kata', t:'DAFTAR PUSTAKA', instr:'Ketik judul berikut, blok seluruh teksnya, lalu jadikan Bold dan pilih ukuran huruf 16 pt pada menu Ukuran Huruf.', fmt:['bold','size:16']},
 {m:'Integrasi Dokumen', t:'Section break memisahkan format nomor halaman romawi dan angka arab.', instr:'Ketik kalimat berikut, blok seluruh teksnya, lalu pilih jenis huruf Times New Roman dan ukuran huruf 12 pt pada menu yang tersedia.', fmt:['font:Times New Roman','size:12']},
 {m:'Fitur Lanjut Pengolah Kata', t:'SPELLING DAN AUTOCORRECT', instr:'Ketik judul berikut, lalu jadikan Bold, Underline, dan rata Tengah sekaligus.', fmt:['bold','underline','center']}
];
var FMT_LABELS = {bold:'Bold', italic:'Italic', underline:'Underline', center:'Rata Tengah', right:'Rata Kanan', justify:'Rata Kiri-Kanan', ul:'Bullet List', ol:'Numbered List', big:'Ukuran Besar', times:'Times New Roman'};
function fmtLabel(arr){ return (arr||[]).map(function(k){
  if(k.indexOf('size:')===0){ return 'Ukuran huruf '+k.slice(5)+' pt'; }
  if(k.indexOf('font:')===0){ return 'Jenis huruf '+k.slice(5); }
  return FMT_LABELS[k]||k; }).join(', '); }

var ESAI = [
 {m:"Sistem Komputer",
  q:"Sebuah komputer lama terasa sangat lambat meski menjalankan instruksi sederhana, karena data dan instruksi harus bergantian melalui jalur yang sama menuju satu memori. Jelaskan konsep arsitektur Von Neumann yang menjelaskan situasi ini beserta komponen utamanya!",
  g:[["von neumann", "neumann"], ["stored program", "program tersimpan", "instruksi dan data", "satu memori", "memori yang sama"], ["bus", "jalur"], ["cpu", "prosesor", "alu", "control unit", "unit kendali"], ["memori", "ram", "memory"], ["bottleneck", "kemacetan", "bergantian", "hambatan", "lambat", "antre"]]},
 {m:"Jaringan &amp; Keamanan",
  q:"Akun media sosial seorang siswa diretas setelah ia mengklik tautan hadiah palsu dan memakai kata sandi yang sama di semua akunnya. Jelaskan tiga langkah keamanan digital dasar yang seharusnya diterapkan untuk mencegah kejadian tersebut!",
  g:[["kata sandi", "password", "sandi"], ["unik", "berbeda", "kuat", "kombinasi", "panjang", "tidak sama"], ["autentikasi dua faktor", "2fa", "dua faktor", "otp", "verifikasi dua langkah"], ["phishing", "tautan", "link", "mencurigakan", "waspada", "tidak mengklik", "jangan klik"], ["backup", "cadangan", "antivirus", "firewall", "perbarui", "update", "enkripsi"], ["akun", "diretas", "peretasan", "data pribadi", "keamanan digital"]]},
 {m:"Pencarian &amp; Kolaborasi",
  q:"Seorang siswa mencari informasi tentang \"dampak AI\" namun hasil pencariannya penuh iklan dan artikel yang tidak relevan. Jelaskan cara kerja mesin pencari dan strategi yang bisa dilakukan siswa tersebut agar hasil pencarian lebih relevan!",
  g:[["crawling", "menjelajah", "spider", "bot"], ["indexing", "indeks", "mengindeks"], ["ranking", "peringkat", "mengurutkan", "urutan", "algoritma"], ["operator", "site:", "filetype", "tanda kutip", "tanda minus", "intitle"], ["kata kunci", "keyword", "spesifik"], ["relevan", "sesuai", "tepat", "tepercaya", "kredibel", "filter"]]},
 {m:"Fitur Lanjut Pengolah Kata",
  q:"Sebuah laporan 60 halaman kehilangan struktur karena seluruh judul bab hanya diberi format Bold manual tanpa Heading. Jelaskan mengapa hal ini menyulitkan pembuatan daftar isi otomatis dan bagaimana solusinya!",
  g:[["heading", "style", "gaya judul"], ["daftar isi", "table of contents"], ["otomatis", "langsung dibuat", "dibuat sendiri"], ["bold", "tebal", "manual", "tidak dikenali", "tidak terdeteksi", "hanya tampilan"], ["struktur", "hierarki", "level", "bab"], ["nomor halaman", "diperbarui", "update", "memperbarui", "navigasi", "rapi", "konsisten"]]},
 {m:"Integrasi Dokumen",
  q:"Laporan PKL seorang siswa menampilkan angka romawi di seluruh halaman termasuk bab isi, karena hanya satu jenis format nomor halaman diterapkan ke seluruh dokumen. Jelaskan mengapa section break diperlukan dan bagaimana cara kerjanya untuk mengatasi masalah ini!",
  g:[["section break", "section", "pemisah bagian"], ["nomor halaman", "penomoran"], ["romawi"], ["arab", "angka biasa", "1, 2, 3"], ["bagian", "terpisah", "memisahkan", "berbeda", "independen", "mandiri"], ["link to previous", "tautan", "format", "pengaturan", "mulai dari", "mulai lagi"]]}
];

/* ================= UTIL ================= */

function $(id){ return document.getElementById(id); }
function esc(s){ var d=document.createElement('div'); d.textContent=String(s); return d.innerHTML; }
function range(n){ var a=[],i; for(i=0;i&lt;n;i++){a.push(i);} return a; }
function shuffle(a){ var b=a.slice(),i,j,t; for(i=b.length-1;i&gt;0;i--){ j=Math.floor(Math.random()*(i+1)); t=b[i]; b[i]=b[j]; b[j]=t; } return b; }
function norm(s){ return String(s).replace(/\r/g,'').replace(/\s+/g,' ').trim(); }
function r1(x){ return Math.round(x*10)/10; }
function r2(x){ return Math.round(x*100)/100; }
function fmt(x){ return String(r1(x)).replace('.',','); }
function lev(a,b){
  var m=a.length, n=b.length, i, j, prev, cur, t, cost;
  if(m===0){ return n; } if(n===0){ return m; }
  prev=new Array(n+1); cur=new Array(n+1);
  for(j=0;j&lt;=n;j++){ prev[j]=j; }
  for(i=1;i&lt;=m;i++){
    cur[0]=i;
    for(j=1;j&lt;=n;j++){
      cost = a.charAt(i-1)===b.charAt(j-1) ? 0 : 1;
      cur[j] = Math.min(prev[j]+1, cur[j-1]+1, prev[j-1]+cost);
    }
    t=prev; prev=cur; cur=t;
  }
  return prev[n];
}
function editableText(el){
  var clone=el.cloneNode(true), i;
  var brs=clone.querySelectorAll('br');
  for(i=0;i&lt;brs.length;i++){ brs[i].parentNode.replaceChild(document.createTextNode('\n'), brs[i]); }
  var blocks=clone.querySelectorAll('li,div,p');
  for(i=0;i&lt;blocks.length;i++){ blocks[i].appendChild(document.createTextNode('\n')); }
  return clone.textContent;
}
function detectFormats(html){
  var f={};
  f.bold = /&lt;b[\s&gt;]|&lt;strong[\s&gt;]|font-weight:\s*(bold|[6-9]00)/i.test(html);
  f.italic = /&lt;i[\s&gt;]|&lt;em[\s&gt;]|font-style:\s*italic/i.test(html);
  f.underline = /&lt;u[\s&gt;]|text-decoration[^;"]*underline/i.test(html);
  f.center = /text-align:\s*center/i.test(html);
  f.right = /text-align:\s*right/i.test(html);
  f.justify = /text-align:\s*justify/i.test(html);
  f.ul = /&lt;ul[\s&gt;]/i.test(html);
  f.ol = /&lt;ol[\s&gt;]/i.test(html);
  f.big = /size=["']?[5-7]["']?|font-size:\s*(1[8-9]|[2-9][0-9])px|font-size:\s*x-?large/i.test(html);
  f.times = /times new roman/i.test(html);
  return f;
}
function detectSizes(html){
  var out=[], m, re;
  re=/font-size:\s*([0-9.]+)\s*(pt|px)/ig;
  while((m=re.exec(html))){ out.push(m[2].toLowerCase()==='px' ? parseFloat(m[1])*0.75 : parseFloat(m[1])); }
  re=/&lt;font[^&gt;]*\ssize=["']?([1-7])["']?/ig;
  var map={1:8,2:10,3:12,4:14,5:18,6:24,7:36};
  while((m=re.exec(html))){ out.push(map[m[1]]); }
  return out;
}
function hasFontName(html, name){
  var n=name.replace(/[.*+?^${}()|[\]\\]/g,'\\$&amp;');
  return new RegExp('(face=["\']?|font-family:\\s*(&amp;quot;|["\'])?)'+n, 'i').test(html);
}
function formatScoreRatio(required, html){
  if(!required || !required.length){ return 1; }
  var det=detectFormats(html), sizes=detectSizes(html), hit=0, i, k, ok, j;
  for(i=0;i&lt;required.length;i++){
    k=required[i]; ok=false;
    if(k.indexOf('size:')===0){
      var want=parseFloat(k.slice(5));
      for(j=0;j&lt;sizes.length;j++){ if(Math.abs(sizes[j]-want)&lt;0.5){ ok=true; break; } }
    } else if(k.indexOf('font:')===0){
      ok=hasFontName(html, k.slice(5));
    } else { ok=!!det[k]; }
    if(ok){ hit++; }
  }
  return hit/required.length;
}
function placeCaretAtEnd(el){
  el.focus();
  if(window.getSelection &amp;&amp; document.createRange){
    var range=document.createRange();
    range.selectNodeContents(el);
    range.collapse(false);
    var sel=window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
  }
}
function similarity(ref, typed){
  var r=norm(ref), t=norm(typed);
  if(r.length===0){ return 100; }
  var d=lev(r,t), mx=Math.max(r.length,t.length);
  return Math.max(0, Math.round((1 - d/mx)*1000)/10);
}
function kwMatch(text, k){
  if(k.length&lt;=4){
    var re=new RegExp('(^|[^a-z0-9])'+k.replace(/[.*+?^${}()|[\]\\]/g,'\\$&amp;')+'($|[^a-z0-9])');
    return re.test(text);
  }
  return text.indexOf(k)!==-1;
}
function scoreEssay(idx, answer){
  var t=norm(answer).toLowerCase();
  var groups=ESAI[idx].g, matched=0;
  if(t.length&lt;40){ return {matched:0,total:groups.length,score:0}; }
  groups.forEach(function(g){
    var hit=false, i;
    for(i=0;i&lt;g.length;i++){ if(kwMatch(t,g[i])){ hit=true; break; } }
    if(hit){ matched++; }
  });
  var need=Math.ceil(groups.length*0.7);
  var ratio=Math.min(1, matched/need);
  return {matched:matched,total:groups.length,score:r2(ratio*PT_ESAI)};
}

/* ================= PENYIMPANAN PERCOBAAN ================= */

var memStore={};
function keyOf(kelas, absen){ return 'uts_inf_x|'+EXAM_VERSION+'|'+kelas+'|'+absen; }
function loadState(key){
  var s=null;
  try{ s=localStorage.getItem(key); }catch(e){}
  if(s){ try{ return JSON.parse(s); }catch(e2){} }
  return memStore[key] || {attempts:[], best:null, sent:false, sentValue:null};
}
function saveState(key, st){
  memStore[key]=st;
  try{ localStorage.setItem(key, JSON.stringify(st)); }catch(e){}
}

/* ================= STATE SESI ================= */

var ID=null, STATE=null, CUR=null;

/* ================= TAMPILAN ================= */

function show(which){
  $('screenStart').classList.toggle('hidden', which!=='start');
  $('screenExam').classList.toggle('hidden', which!=='exam');
  $('screenResult').classList.toggle('hidden', which!=='result');
  window.scrollTo(0,0);
}
function setMsg(el, text){
  if(text){ el.textContent=text; el.classList.add('show'); } else { el.classList.remove('show'); el.textContent=''; }
}

function buildExam(){
  var pg = shuffle(range(PG.length)).map(function(i){ return {i:i, order:shuffle([0,1,2,3,4])}; });
  var ty = shuffle(range(TYPING.length));
  return {pg:pg, ty:ty};
}

function renderExam(){
  var A=$('bodyA'), B=$('bodyB'), C=$('bodyC'), html='';
  CUR.exam.pg.forEach(function(item, n){
    var q=PG[item.i];
    html += '&lt;div class="q" id="qa'+n+'"&gt;&lt;div class="q-top"&gt;&lt;span class="q-no"&gt;Soal '+(n+1)+' dari 35&lt;/span&gt;&lt;span class="q-tag"&gt;'+esc(q.m)+'&lt;/span&gt;&lt;/div&gt;';
    html += '&lt;p class="q-text"&gt;'+esc(q.q)+'&lt;/p&gt;';
    item.order.forEach(function(oi, pos){
      html += '&lt;label class="opt"&gt;&lt;input type="radio" name="pg'+n+'" value="'+oi+'"&gt;&lt;span class="lt"&gt;'+String.fromCharCode(65+pos)+'.&lt;/span&gt;&lt;span&gt;'+esc(q.o[oi])+'&lt;/span&gt;&lt;/label&gt;';
    });
    html += '&lt;/div&gt;';
  });
  A.innerHTML=html;

  html='';
  CUR.exam.ty.forEach(function(ti, n){
    var t=TYPING[ti];
    var refDisplay = t.multiline ? esc(t.t).replace(/\n/g,'&lt;br&gt;') : esc(t.t);
    var refLen = t.t.replace(/\n/g,' ').length;
    html += '&lt;div class="q b" id="qb'+n+'"&gt;&lt;div class="q-top"&gt;&lt;span class="q-no"&gt;Soal '+(n+1)+' dari 10&lt;/span&gt;&lt;span class="q-tag"&gt;'+esc(t.m)+'&lt;/span&gt;&lt;/div&gt;';
    html += '&lt;p class="fmt-instr"&gt;'+esc(t.instr)+'&lt;/p&gt;';
    html += '&lt;div class="ref-label"&gt;NASKAH (TIDAK BISA DISALIN)&lt;/div&gt;';
    html += '&lt;div class="ref" id="ref'+n+'"&gt;'+refDisplay+'&lt;/div&gt;';
    html += '&lt;div class="type-editable" id="ty'+n+'" data-n="'+n+'" contenteditable="true" spellcheck="false"&gt;&lt;/div&gt;';
    html += '&lt;div class="cnt"&gt;&lt;span id="tyc'+n+'"&gt;0 / '+refLen+' karakter - format wajib: '+esc(fmtLabel(t.fmt))+'&lt;/span&gt;&lt;span class="warn" id="tyw'+n+'"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;';
  });
  B.innerHTML=html;

  html='';
  ESAI.forEach(function(e, n){
    html += '&lt;div class="q c" id="qc'+n+'"&gt;&lt;div class="q-top"&gt;&lt;span class="q-no"&gt;Soal '+(n+1)+' dari 5&lt;/span&gt;&lt;span class="q-tag"&gt;'+esc(e.m)+'&lt;/span&gt;&lt;/div&gt;';
    html += '&lt;p class="q-text"&gt;'+esc(e.q)+'&lt;/p&gt;';
    html += '&lt;textarea id="es'+n+'" data-n="'+n+'" style="min-height:130px;" placeholder="Ketik jawabanmu di sini (minimal 40 karakter)..." autocomplete="off" spellcheck="false"&gt;&lt;/textarea&gt;';
    html += '&lt;div class="cnt"&gt;&lt;span id="esc'+n+'"&gt;0 karakter&lt;/span&gt;&lt;span class="warn" id="esw'+n+'"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;';
  });
  C.innerHTML=html;

  bindExamEvents();
  updateProgress();
}

function flashWarn(id, text){
  var el=$(id); if(!el){ return; }
  el.textContent=text;
  setTimeout(function(){ if(el.textContent===text){ el.textContent=''; } }, 2600);
}

function guardTextarea(ta, warnId){
  var last='';
  var block=function(e){ e.preventDefault(); CUR.paste++; flashWarn(warnId, 'Tempel (paste) dinonaktifkan - ketik manual.'); };
  ta.addEventListener('paste', block);
  ta.addEventListener('drop', block);
  ta.addEventListener('contextmenu', function(e){ e.preventDefault(); });
  ta.addEventListener('beforeinput', function(e){
    var t=e.inputType||'';
    if(t==='insertFromPaste' || t==='insertFromDrop' || t==='insertFromPasteAsQuotation' || t==='insertFromYank'){ block(e); }
  });
  ta.addEventListener('input', function(){
    var v=ta.value;
    if(v.length - last.length &gt; 30){
      ta.value=last; CUR.paste++;
      flashWarn(warnId, 'Teks yang masuk sekaligus ditolak - ketik manual.');
    } else { last=v; }
  });
}

var activeEditable=null;
function guardEditable(el, warnId){
  el.__lastHtml='';
  var lastLen=0;
  el.addEventListener('paste', function(e){ e.preventDefault(); CUR.paste++; flashWarn(warnId, 'Tempel (paste) dinonaktifkan - ketik manual.'); });
  el.addEventListener('drop', function(e){ e.preventDefault(); CUR.paste++; flashWarn(warnId, 'Seret-lepas teks dinonaktifkan - ketik manual.'); });
  el.addEventListener('contextmenu', function(e){ e.preventDefault(); });
  el.addEventListener('focus', function(){ activeEditable=el; });
  el.addEventListener('input', function(){
    var len=el.textContent.length;
    if(len - lastLen &gt; 30){
      el.innerHTML = el.__lastHtml || '';
      CUR.paste++;
      flashWarn(warnId, 'Teks yang masuk sekaligus ditolak - ketik manual.');
      placeCaretAtEnd(el);
    } else {
      el.__lastHtml = el.innerHTML;
    }
    lastLen = el.textContent.length;
  });
}
var savedRange=null;
function trackSelection(){
  document.addEventListener('selectionchange', function(){
    if(!activeEditable){ return; }
    var sel=window.getSelection();
    if(sel &amp;&amp; sel.rangeCount&gt;0){
      var r=sel.getRangeAt(0);
      if(activeEditable.contains(r.commonAncestorContainer)){ savedRange=r.cloneRange(); }
    }
  });
}
function restoreSelection(){
  if(!activeEditable){ return false; }
  activeEditable.focus();
  if(savedRange &amp;&amp; activeEditable.contains(savedRange.commonAncestorContainer)){
    var sel=window.getSelection();
    sel.removeAllRanges();
    sel.addRange(savedRange);
  }
  return true;
}
function applyFontSizePt(pt){
  if(!restoreSelection()){ return; }
  try{ document.execCommand('styleWithCSS', false, false); }catch(e0){}
  try{ document.execCommand('fontSize', false, '7'); }catch(e){}
  var fonts=activeEditable.querySelectorAll('font[size="7"]'), i, sp;
  for(i=0;i&lt;fonts.length;i++){
    sp=document.createElement('span');
    sp.style.fontSize=pt+'pt';
    while(fonts[i].firstChild){ sp.appendChild(fonts[i].firstChild); }
    fonts[i].parentNode.replaceChild(sp, fonts[i]);
  }
  var spans=activeEditable.querySelectorAll('span[style*="xxx-large"]');
  for(i=0;i&lt;spans.length;i++){ spans[i].style.fontSize=pt+'pt'; }
}
function bindFormatToolbar(){
  var toolbar=$('fmtToolbar');
  if(!toolbar || toolbar.__bound){ return; }
  toolbar.__bound=true;
  trackSelection();
  var btns=toolbar.querySelectorAll('.fmt-btn'), i;
  for(i=0;i&lt;btns.length;i++){
    btns[i].addEventListener('mousedown', function(e){ e.preventDefault(); });
    btns[i].addEventListener('click', function(){
      if(!activeEditable){ return; }
      restoreSelection();
      try{ document.execCommand(this.getAttribute('data-cmd'), false, null); }catch(e){}
      activeEditable.dispatchEvent(new Event('input', {bubbles:true}));
    });
  }
  /* Dropdown TIDAK boleh memakai preventDefault pada mousedown (menyebabkan daftar tidak bisa dibuka). */
  $('fmtSize').addEventListener('change', function(){
    var v=parseFloat(this.value);
    if(!activeEditable){ alert('Klik di dalam kotak jawaban dulu, blok teks yang ingin diubah, lalu pilih ukuran huruf.'); this.value=''; return; }
    if(v&gt;0){ applyFontSizePt(v); activeEditable.dispatchEvent(new Event('input', {bubbles:true})); }
    this.value='';
  });
  $('fmtFont').addEventListener('change', function(){
    var v=this.value;
    if(!activeEditable){ alert('Klik di dalam kotak jawaban dulu, blok teks yang ingin diubah, lalu pilih jenis huruf.'); this.value=''; return; }
    if(v){
      restoreSelection();
      try{ document.execCommand('styleWithCSS', false, false); }catch(e0){}
      try{ document.execCommand('fontName', false, v); }catch(e){}
      activeEditable.dispatchEvent(new Event('input', {bubbles:true}));
    }
    this.value='';
  });
}

function bindExamEvents(){
  var n;
  CUR.exam.pg.forEach(function(_, i){
    var radios=document.getElementsByName('pg'+i), k;
    for(k=0;k&lt;radios.length;k++){
      radios[k].addEventListener('change', function(){
        var group=document.getElementsByName(this.name), z;
        for(z=0;z&lt;group.length;z++){ group[z].parentNode.classList.remove('sel'); }
        this.parentNode.classList.add('sel');
        $('qa'+i).classList.remove('missing');
        updateProgress();
      });
    }
  });
  CUR.exam.ty.forEach(function(ti, i){
    var el=$('ty'+i);
    guardEditable(el, 'tyw'+i);
    el.addEventListener('input', function(){
      var len=editableText(el).replace(/\n/g,' ').trim().length;
      var refLen=TYPING[ti].t.replace(/\n/g,' ').length;
      $('tyc'+i).textContent = len+' / '+refLen+' karakter - format wajib: '+fmtLabel(TYPING[ti].fmt);
      $('qb'+i).classList.remove('missing');
      updateProgress();
    });
    var ref=$('ref'+i);
    ['copy','cut','contextmenu','dragstart','selectstart'].forEach(function(ev){
      ref.addEventListener(ev, function(e){ e.preventDefault(); });
    });
  });
  bindFormatToolbar();
  ESAI.forEach(function(_, i){
    var ta=$('es'+i);
    guardTextarea(ta, 'esw'+i);
    ta.addEventListener('input', function(){
      $('esc'+i).textContent = ta.value.length+' karakter';
      $('qc'+i).classList.remove('missing');
      updateProgress();
    });
  });
}

function countAnswered(){
  var a=0, b=0, c=0, i;
  for(i=0;i&lt;PG.length;i++){ if(document.querySelector('input[name="pg'+i+'"]:checked')){ a++; } }
  for(i=0;i&lt;TYPING.length;i++){ if(editableText($('ty'+i)).replace(/\n/g,' ').trim().length&gt;=5){ b++; } }
  for(i=0;i&lt;ESAI.length;i++){ if($('es'+i).value.trim().length&gt;=40){ c++; } }
  return {a:a,b:b,c:c,all:a+b+c};
}
function updateProgress(){
  var c=countAnswered();
  $('pcount').textContent = c.all+' / '+TOTAL_Q+' terjawab';
  $('pbarFill').style.width = (c.all/TOTAL_Q*100)+'%';
}

/* ================= ALUR UJIAN ================= */

function attemptsLabel(){ return 'Percobaan ke-'+(STATE.attempts.length+1)+' dari '+MAX_ATT; }

function populateAbsenOptions(){
  var kelas=$('fKelas').value;
  var sel=$('fAbsen');
  sel.innerHTML='';
  $('fNama').value='';
  if(!kelas || !ROSTER[kelas]){
    sel.disabled=true;
    sel.innerHTML='&lt;option value=""&gt;- Pilih kelas dulu -&lt;/option&gt;';
    return;
  }
  sel.disabled=false;
  var opt0=document.createElement('option');
  opt0.value=''; opt0.textContent='- Pilih No. Absen -'; opt0.disabled=true; opt0.selected=true;
  sel.appendChild(opt0);
  ROSTER[kelas].forEach(function(pair){
    var o=document.createElement('option');
    o.value=String(pair[0]); o.textContent=pair[0]+' - '+pair[1];
    sel.appendChild(o);
  });
}
function populateNamaFromRoster(){
  var kelas=$('fKelas').value, absen=$('fAbsen').value, nama='', i, list;
  if(kelas &amp;&amp; absen &amp;&amp; ROSTER[kelas]){
    list=ROSTER[kelas];
    for(i=0;i&lt;list.length;i++){ if(String(list[i][0])===String(absen)){ nama=list[i][1]; break; } }
  }
  $('fNama').value=nama;
}
function initDeadlineNotice(){
  var opts={day:'numeric', month:'long', year:'numeric', hour:'2-digit', minute:'2-digit'};
  var openStr=EXAM_OPEN.toLocaleString('id-ID', opts);
  var closeStr=EXAM_DEADLINE.toLocaleString('id-ID', opts);
  var el=$('deadlineNotice');
  if(el){ el.innerHTML='Ujian dibuka mulai &lt;b&gt;'+openStr+'&lt;/b&gt; dan wajib diselesaikan sebelum &lt;b&gt;'+closeStr+'&lt;/b&gt; ('+DEADLINE_DAYS+' hari sejak dibuka). &lt;b&gt;Jika belum mengerjakan sama sekali setelah batas waktu berakhir, nilai yang tercatat otomatis 0.&lt;/b&gt;'; }
}

function startAttempt(){
  CUR={exam:buildExam(), paste:0, start:Date.now()};
  $('whoLbl').textContent = ID.nama+' - '+ID.kelas+' / No. '+ID.absen;
  $('attLbl').textContent = attemptsLabel();
  setMsg($('examErr'), '');
  $('btnSubmit').disabled=false;
  $('btnSubmit').textContent='Kirim Jawaban';
  renderExam();
  show('exam');
}

function onStart(){
  var kelas=$('fKelas').value, absen=$('fAbsen').value, nama=$('fNama').value.trim(), token=$('fToken').value.trim();
  setMsg($('startErr'), '');
  if(!kelas){ setMsg($('startErr'), 'Pilih Kelas terlebih dahulu.'); return; }
  if(!absen){ setMsg($('startErr'), 'Pilih No. Absen terlebih dahulu.'); return; }
  if(!nama){ setMsg($('startErr'), 'Nama tidak ditemukan untuk No. Absen ini. Hubungi guru.'); return; }
  if(!token){ setMsg($('startErr'), 'Masukkan Token Kelas yang diberikan guru.'); return; }
  if(!CLASS_TOKENS[kelas] || token.toUpperCase()!==CLASS_TOKENS[kelas].toUpperCase()){
    setMsg($('startErr'), 'Token Kelas salah. Minta token yang benar kepada guru.');
    return;
  }
  ID={absen:String(parseInt(absen,10)), nama:nama, kelas:kelas};
  ID.key=keyOf(ID.kelas, ID.absen);
  STATE=loadState(ID.key);
  STATE.nama=nama;

  if(!STATE.sent &amp;&amp; Date.now()&gt;EXAM_DEADLINE.getTime()){
    var val = STATE.best ? STATE.best.total : 0;
    if(!STATE.best){
      STATE.best={total:0, pg:0, type:0, esai:0, waktu:new Date().toISOString(), paste:0, no:0, detail:{pg:[],ty:[],es:[]}};
    }
    STATE.sent=true; STATE.sentValue=val;
    saveState(ID.key, STATE);
    sendScore(val);
    showResult(true, true);
    return;
  }

  if(STATE.sent || STATE.attempts.length&gt;=MAX_ATT){
    saveState(ID.key, STATE);
    showResult(false);
    return;
  }
  saveState(ID.key, STATE);
  startAttempt();
}

function collect(){
  var pg=[], ty=[], es=[], i, sPG=0, sTY=0, sES=0;
  CUR.exam.pg.forEach(function(item, n){
    var q=PG[item.i];
    var chosen=document.querySelector('input[name="pg'+n+'"]:checked');
    var val=parseInt(chosen.value,10);
    var ok=(val===q.a);
    if(ok){ sPG+=PT_PG; }
    pg.push({n:n+1, chosen:q.o[val], ok:ok});
  });
  CUR.exam.ty.forEach(function(ti, n){
    var el=$('ty'+n);
    var html=el.innerHTML;
    var typedText=editableText(el).replace(/\n/g,' ');
    var refText=TYPING[ti].t.replace(/\n/g,' ');
    var sim=similarity(refText, typedText);
    var fmtRatio=formatScoreRatio(TYPING[ti].fmt, html);
    var sc=r2((sim/100*0.7 + fmtRatio*0.3) * PT_TYPE);
    sTY+=sc;
    ty.push({n:n+1, typed:norm(typedText), sim:sim, fmtRatio:Math.round(fmtRatio*100), fmtLabel:fmtLabel(TYPING[ti].fmt), instr:TYPING[ti].instr, score:sc});
  });
  ESAI.forEach(function(e, n){
    var ans=$('es'+n).value;
    var r=scoreEssay(n, ans);
    sES+=r.score;
    es.push({n:n+1, q:e.q, answer:norm(ans), score:r.score, matched:r.matched, total:r.total});
  });
  sPG=r2(sPG); sTY=r2(sTY); sES=r2(sES);
  var total=Math.round(Math.min(100, sPG+sTY+sES));
  return {pg:sPG, type:sTY, esai:sES, total:total, waktu:new Date().toISOString(), paste:CUR.paste, detail:{pg:pg, ty:ty, es:es}};
}

function onSubmit(){
  setMsg($('examErr'), '');
  var c=countAnswered(), i, first=null, miss=[];
  for(i=0;i&lt;PG.length;i++){ if(!document.querySelector('input[name="pg'+i+'"]:checked')){ miss.push('A'+(i+1)); if(!first){first=$('qa'+i);} $('qa'+i).classList.add('missing'); } }
  for(i=0;i&lt;TYPING.length;i++){ if(editableText($('ty'+i)).replace(/\n/g,' ').trim().length&lt;5){ miss.push('B'+(i+1)); if(!first){first=$('qb'+i);} $('qb'+i).classList.add('missing'); } }
  for(i=0;i&lt;ESAI.length;i++){ if($('es'+i).value.trim().length&lt;40){ miss.push('C'+(i+1)); if(!first){first=$('qc'+i);} $('qc'+i).classList.add('missing'); } }
  if(miss.length){
    setMsg($('examErr'), 'Masih ada '+miss.length+' soal yang belum lengkap ('+miss.slice(0,12).join(', ')+(miss.length&gt;12?', ...':'')+'). Pilihan ganda wajib dijawab, soal mengetik wajib diisi dan diformat sesuai instruksi, esai minimal 40 karakter.');
    if(first){ first.scrollIntoView({behavior:'smooth', block:'center'}); }
    return;
  }
  $('btnSubmit').disabled=true;
  $('btnSubmit').textContent='Memproses...';

  var res=collect();
  res.no=STATE.attempts.length+1;
  STATE.attempts.push({no:res.no, total:res.total, pg:res.pg, type:res.type, esai:res.esai, waktu:res.waktu});
  if(!STATE.best || res.total&gt;STATE.best.total){ STATE.best=res; }

  var justSent=false;
  if(res.total&gt;=KKM){
    STATE.sent=true; STATE.sentValue=res.total; justSent=true;
  } else if(STATE.attempts.length&gt;=MAX_ATT){
    STATE.sent=true; STATE.sentValue=STATE.best.total; justSent=true;
  }
  saveState(ID.key, STATE);
  if(justSent){ sendScore(STATE.sentValue); }
  showResult(justSent);
}

function sendScore(value){
  $('gAbsen').value=ID.absen;
  $('gNama').value=ID.nama;
  $('gKelas').value=ID.kelas;
  $('gNilai').value=value;
  $('gSkor').value=value;
  $('gNilaiSTS').value=value;
  $('gJudul').value=JUDUL;
  $('gform').submit();
}

function showResult(justSent, isLate){
  var best=STATE.best;
  var att=STATE.attempts.length;
  var left=MAX_ATT-att;
  var last=STATE.attempts[att-1];
  var shown=best ? best.total : 0;
  var reached = best &amp;&amp; best.total&gt;=KKM;

  $('rVal').textContent = shown;
  $('ring').style.setProperty('--p', shown);
  $('ring').style.setProperty('--ringc', reached ? '#15803d' : '#d97706');

  var pill=$('rPill');
  if(isLate){
    pill.textContent='WAKTU HABIS - NILAI OTOMATIS TERCATAT';
    pill.className='status-pill no';
  } else if(STATE.sent){
    pill.textContent = reached ? 'TUNTAS - KKM TERCAPAI' : 'BELUM TUNTAS - NILAI TERTINGGI DIKIRIM';
    pill.className='status-pill '+(reached?'ok':'no');
  } else {
    pill.textContent='BELUM MENCAPAI KKM ('+KKM+')';
    pill.className='status-pill no';
  }

  if(isLate){
    $('rTitle').textContent = att===0 ? 'Batas waktu ujian sudah berakhir' : 'Batas waktu berakhir - nilai terakhir dikirim';
    $('rDesc').textContent = att===0
      ? 'Kamu tidak mengerjakan ujian ini sebelum batas waktu berakhir, sehingga nilai yang tercatat dan dikirim ke guru adalah 0.'
      : 'Batas waktu ujian sudah berakhir. Nilai tertinggi yang sempat kamu capai ('+STATE.sentValue+') telah dikirim ke guru.';
  } else if(att===0 || !best){
    $('rTitle').textContent='Belum ada data';
    $('rDesc').textContent='';
  } else if(STATE.sent &amp;&amp; !justSent){
    $('rTitle').textContent='Nilai kamu sudah tercatat';
    $('rDesc').textContent='Kesempatan mengerjakan sudah selesai untuk No. Absen ini. Nilai yang tercatat: '+STATE.sentValue+'.';
  } else if(STATE.sent){
    $('rTitle').textContent = reached ? 'Selamat, kamu mencapai KKM!' : 'Kesempatan ketiga selesai';
    $('rDesc').textContent = reached ? 'Nilai akhirmu '+best.total+' dan sudah dikirim ke guru.' : 'KKM belum tercapai. Nilai tertinggi dari '+att+' percobaan ('+best.total+') dikirim ke guru.';
  } else {
    $('rTitle').textContent='Nilaimu belum mencapai KKM';
    $('rDesc').textContent='Nilai percobaan ini '+last.total+' (tertinggi sementara '+best.total+'). Kamu masih punya '+left+' kesempatan. Nilai akan dikirim setelah KKM tercapai atau setelah percobaan ke-'+MAX_ATT+'.';
  }

  var bars='';
  if(best){
    bars += barRow('A. Pilihan Ganda', best.pg, MAX_PG, '#1d4ed8');
    bars += barRow('B. Praktik Mengetik', best.type, MAX_TYPE, '#d97706');
    bars += barRow('C. Esai', best.esai, MAX_ESAI, '#0f766e');
  }
  $('rBars').innerHTML=bars;

  var hist='&lt;b&gt;Riwayat percobaan:&lt;/b&gt; ';
  if(att===0){ hist+='-'; } else {
    hist += STATE.attempts.map(function(a){ return 'Ke-'+a.no+': &lt;b&gt;'+a.total+'&lt;/b&gt;'; }).join(' &amp;nbsp;|&amp;nbsp; ');
    hist += ' &amp;nbsp;-&amp;nbsp; Kesempatan tersisa: &lt;b&gt;'+(STATE.sent?0:left)+'&lt;/b&gt;';
  }
  $('rHist').innerHTML=hist;

  var sendEl=$('rSend');
  if(STATE.sent){
    sendEl.textContent='Nilai '+STATE.sentValue+' tercatat dan dikirim ke guru'+(justSent?'':' (pada sesi sebelumnya)')+'.';
    sendEl.className='msg info show';
  } else {
    sendEl.textContent='Nilai belum dikirim ke guru. Nilai dikirim otomatis begitu KKM tercapai atau setelah percobaan ke-'+MAX_ATT+'.';
    sendEl.className='msg info show';
  }

  $('btnRetry').classList.toggle('hidden', STATE.sent || left&lt;=0);
  $('btnPdf').disabled = !best;
  show('result');
}
function barRow(label, val, max, color){
  var pct=Math.max(0, Math.min(100, val/max*100));
  return '&lt;div class="bar-row"&gt;&lt;div class="lbl"&gt;&lt;span&gt;'+label+'&lt;/span&gt;&lt;span&gt;'+fmt(val)+' / '+max+'&lt;/span&gt;&lt;/div&gt;&lt;div class="track"&gt;&lt;div class="fill" style="width:'+pct+'%; background:'+color+';"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;';
}

/* ================= PDF F4 ================= */

function pdfClean(s){ return String(s==null?'':s).replace(/[^\x20-\x7E\xA0-\xFF\n]/g,'?'); }

function downloadPdf(){
  if(!STATE || !STATE.best){ return; }
  if(!window.jspdf){ alert('Pustaka PDF belum termuat. Periksa koneksi internet lalu coba lagi.'); return; }
  var best=STATE.best;
  var doc=new window.jspdf.jsPDF({unit:'mm', format:[215,330]});
  var W=215, H=330, ML=15, MR=15, CW=W-ML-MR, YMAX=H-18, y=20;

  function need(h){ if(y+h&gt;YMAX){ doc.addPage([215,330]); y=22; } }
  function txt(s,x,yy,o){ doc.text(pdfClean(s), x, yy, o); }
  function bar(title, r,g,b){
    need(34);
    doc.setFillColor(r,g,b); doc.rect(ML,y,CW,7,'F');
    doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(10);
    txt(title, ML+3, y+4.9);
    doc.setTextColor(0,0,0); y+=10;
  }

  doc.setFont('helvetica','bold'); doc.setFontSize(15);
  txt('BUKTI MENGERJAKAN UJIAN TENGAH SEMESTER', W/2, y, {align:'center'}); y+=6.5;
  doc.setFontSize(11); txt('Informatika Kelas X SMK', W/2, y, {align:'center'}); y+=5.2;
  doc.setFont('helvetica','normal'); doc.setFontSize(9);
  txt(DOC_CODE+' - 35 Pilihan Ganda, 10 Praktik Mengetik, 5 Esai - KKM '+KKM, W/2, y, {align:'center'}); y+=4;
  doc.setLineWidth(0.6); doc.line(ML,y,W-MR,y); y+=7;

  var rows=[
    ['No. Absen', ID.absen],
    ['Nama Lengkap', ID.nama],
    ['Kelas', ID.kelas],
    ['Waktu Pengerjaan', new Date(best.waktu).toLocaleString('id-ID')+' (percobaan ke-'+best.no+')']
  ];
  doc.setFontSize(10.5);
  rows.forEach(function(r){
    doc.setFont('helvetica','bold'); txt(r[0], ML, y);
    doc.setFont('helvetica','normal'); txt(': '+r[1], ML+38, y);
    y+=6;
  });
  y+=2;

  var reached=best.total&gt;=KKM;
  doc.setDrawColor(29,78,216); doc.setLineWidth(0.5); doc.rect(ML,y,CW,30);
  doc.setFont('helvetica','bold'); doc.setFontSize(9); txt('NILAI AKHIR', ML+5, y+7);
  doc.setFontSize(30); txt(String(best.total), ML+5, y+21);
  doc.setFontSize(10); txt('/ 100', ML+30, y+21);
  doc.setFontSize(9.5);
  txt('Pilihan Ganda   : '+fmt(best.pg)+' / '+MAX_PG, ML+62, y+8);
  txt('Praktik Mengetik: '+fmt(best.type)+' / '+MAX_TYPE, ML+62, y+14);
  txt('Esai            : '+fmt(best.esai)+' / '+MAX_ESAI, ML+62, y+20);
  if(reached){ doc.setTextColor(21,128,61); } else { doc.setTextColor(185,28,28); }
  doc.setFontSize(10.5); txt(reached?'TUNTAS (KKM '+KKM+' tercapai)':'BELUM TUNTAS (KKM '+KKM+')', ML+62, y+27);
  doc.setTextColor(0,0,0);
  y+=36;

  doc.setFont('helvetica','normal'); doc.setFontSize(9);
  var hist='Riwayat percobaan: '+STATE.attempts.map(function(a){ return 'Ke-'+a.no+' = '+a.total; }).join('  |  ');
  var hl=doc.splitTextToSize(pdfClean(hist), CW); doc.text(hl, ML, y); y+=hl.length*4;
  var sendTxt = STATE.sent ? 'Status: nilai '+STATE.sentValue+' telah dikirim ke guru.' : 'Status: nilai belum dikirim (masih ada kesempatan mengulang).';
  txt(sendTxt+'   Percobaan tempel yang ditolak sistem pada pengerjaan ini: '+(best.paste||0)+'.', ML, y); y+=8;

  /* A. PG */
  bar('A. PILIHAN GANDA - jawaban peserta', 29,78,216);
  doc.setFontSize(8.5);
  best.detail.pg.forEach(function(p){
    var lines=doc.splitTextToSize(pdfClean(p.n+'. '+p.chosen), CW-24);
    var h=lines.length*3.9+1.6;
    need(h);
    doc.setFont('helvetica','normal'); doc.setTextColor(0,0,0);
    doc.text(lines, ML, y+3);
    doc.setFont('helvetica','bold');
    if(p.ok){ doc.setTextColor(21,128,61); txt('BENAR', W-MR, y+3, {align:'right'}); }
    else { doc.setTextColor(185,28,28); txt('SALAH', W-MR, y+3, {align:'right'}); }
    doc.setTextColor(0,0,0);
    doc.setDrawColor(220,226,236); doc.setLineWidth(0.15); doc.line(ML, y+h-0.6, W-MR, y+h-0.6);
    y+=h;
  });
  y+=4;

  /* B. Mengetik */
  bar('B. PRAKTIK MENGETIK - ketikan peserta', 180,83,9);
  best.detail.ty.forEach(function(t){
    doc.setFontSize(8);
    var il=doc.splitTextToSize(pdfClean('Instruksi: '+t.instr), CW-4);
    var lines=doc.splitTextToSize(pdfClean(t.typed), CW-4);
    var h=il.length*3.6+lines.length*3.6+11;
    need(h);
    doc.setFont('helvetica','bold'); doc.setFontSize(8.5);
    txt('No. '+t.n+'  |  Kesamaan teks: '+String(t.sim).replace('.',',')+'%  |  Format diminta: '+t.fmtLabel+'  |  Format terpenuhi: '+t.fmtRatio+'%  |  Skor: '+fmt(t.score)+' / '+PT_TYPE, ML, y+3);
    doc.setFont('helvetica','italic'); doc.setFontSize(7.8);
    doc.text(il, ML+2, y+7);
    doc.setFont('helvetica','normal'); doc.setFontSize(8);
    doc.text(lines, ML+2, y+7+il.length*3.6);
    y+=h;
  });
  y+=4;

  /* C. Esai */
  bar('C. ESAI - jawaban peserta', 15,118,110);
  best.detail.es.forEach(function(e){
    doc.setFontSize(8.5);
    var ql=doc.splitTextToSize(pdfClean('Soal '+e.n+': '+e.q), CW);
    var al=doc.splitTextToSize(pdfClean('Jawaban: '+(e.answer||'-')), CW-4);
    var h=ql.length*3.9+al.length*3.7+11;
    need(h);
    doc.setFont('helvetica','bold'); doc.text(ql, ML, y+3);
    var yy=y+3+ql.length*3.9;
    doc.setFont('helvetica','normal'); doc.setFontSize(8.2); doc.text(al, ML+2, yy+1);
    yy+=al.length*3.7+2.5;
    doc.setFont('helvetica','bold'); doc.setFontSize(8.5);
    txt('Skor: '+fmt(e.score)+' / '+PT_ESAI, ML, yy+1);
    y+=h;
  });
  y+=6;

  /* Penutup */
  need(48);
  doc.setFont('helvetica','normal'); doc.setFontSize(9);
  var note='Dokumen ini adalah bukti otomatis bahwa peserta telah mengerjakan Ujian Tengah Semester Informatika Kelas X. Cetak pada kertas F4 dan serahkan kepada guru mata pelajaran sebagai bukti fisik pengerjaan.';
  var nl=doc.splitTextToSize(pdfClean(note), CW); doc.text(nl, ML, y); y+=nl.length*4.2+8;
  txt('Peserta,', ML+8, y); txt('Guru Mata Pelajaran,', W-MR-52, y);
  y+=22;
  txt('( '+ID.nama+' )', ML+8, y); txt('( ................................ )', W-MR-52, y);

  var total=doc.getNumberOfPages(), p;
  for(p=1;p&lt;=total;p++){
    doc.setPage(p);
    doc.setFont('helvetica','normal'); doc.setFontSize(8); doc.setTextColor(110,110,110);
    if(p&gt;1){ txt('UTS Informatika X - '+ID.nama+' ('+ID.kelas+' / No. '+ID.absen+')', ML, 12); }
    txt(DOC_CODE, ML, H-8);
    txt('Halaman '+p+' dari '+total, W-MR, H-8, {align:'right'});
  }
  doc.setTextColor(0,0,0);
  var safe=ID.nama.replace(/[^a-zA-Z0-9]+/g,'_');
  doc.save('Bukti_UTS_Informatika_X_'+safe+'_'+ID.absen+'.pdf');
}


/* ================= RESET PERCOBAAN OLEH GURU ================= */
/* TODO GURU: ganti PIN di bawah ini sebelum ujian dibagikan. Panel guru dibuka dengan menambahkan #guru di akhir alamat halaman. */
var GURU_PIN = 'GURU-UTS-X';

function resetCode(kelas, absen, n){
  var s=GURU_PIN+'|'+kelas+'|'+String(parseInt(absen,10))+'|reset|'+n, h=5381, i;
  for(i=0;i&lt;s.length;i++){ h=((h*33)^s.charCodeAt(i))&gt;&gt;&gt;0; }
  return ('000000'+h.toString(36).toUpperCase()).slice(-6);
}
function getUsedCodes(){
  try{ return JSON.parse(localStorage.getItem('uts_inf_x_used|'+EXAM_VERSION)||'[]'); }catch(e){ return memStore.__used||[]; }
}
function addUsedCode(c){
  var u=getUsedCodes(); u.push(c); memStore.__used=u;
  try{ localStorage.setItem('uts_inf_x_used|'+EXAM_VERSION, JSON.stringify(u)); }catch(e){}
}
function clearAttempts(kelas, absen){
  var key=keyOf(kelas, String(parseInt(absen,10)));
  delete memStore[key];
  try{ localStorage.removeItem(key); }catch(e){}
}
function onUseResetCode(){
  var kelas=$('fKelas').value, absen=$('fAbsen').value, code=$('fResetCode').value.trim().toUpperCase(), msg=$('resetMsg'), n, ok=false;
  msg.className='msg';
  function say(t, cls){ msg.textContent=t; msg.className='msg show '+cls; }
  if(!kelas || !absen){ say('Pilih Kelas dan No. Absen terlebih dahulu, lalu masukkan kode.', 'err'); return; }
  if(!code){ say('Masukkan kode reset dari guru.', 'err'); return; }
  if(getUsedCodes().indexOf(kelas+'|'+absen+'|'+code)!==-1){ say('Kode ini sudah pernah dipakai. Minta kode baru kepada guru.', 'err'); return; }
  for(n=1;n&lt;=9;n++){ if(resetCode(kelas, absen, n)===code){ ok=true; break; } }
  if(!ok){ say('Kode reset tidak cocok untuk Kelas dan No. Absen ini.', 'err'); return; }
  addUsedCode(kelas+'|'+absen+'|'+code);
  clearAttempts(kelas, absen);
  $('fResetCode').value='';
  say('Percobaan berhasil direset. Klik "Mulai Mengerjakan" untuk memulai lagi (3 kesempatan baru).', 'ok');
}
function fillGuruKelas(){
  var sel=$('rsKelas'), k;
  if(sel.options.length&gt;1){ return; }
  for(k in ROSTER){ if(Object.prototype.hasOwnProperty.call(ROSTER,k)){ var o=document.createElement('option'); o.value=k; o.textContent=k; sel.appendChild(o); } }
}
function fillGuruAbsen(){
  var kelas=$('rsKelas').value, sel=$('rsAbsen');
  sel.innerHTML='';
  if(!kelas || !ROSTER[kelas]){ sel.disabled=true; sel.innerHTML='&lt;option value=""&gt;- Pilih kelas dulu -&lt;/option&gt;'; return; }
  sel.disabled=false;
  var o0=document.createElement('option'); o0.value=''; o0.textContent='- Pilih siswa -'; o0.disabled=true; o0.selected=true; sel.appendChild(o0);
  ROSTER[kelas].forEach(function(p){ var o=document.createElement('option'); o.value=String(p[0]); o.textContent=p[0]+' - '+p[1]; sel.appendChild(o); });
}
function guruPick(){
  var kelas=$('rsKelas').value, absen=$('rsAbsen').value;
  if(!kelas || !absen){ $('rsOut').innerHTML='&lt;div class="msg err show"&gt;Pilih Kelas dan siswa terlebih dahulu.&lt;/div&gt;'; return null; }
  return {kelas:kelas, absen:absen};
}
function onGuruLogin(){
  if($('rsPin').value===GURU_PIN){
    $('guruLogin').classList.add('hidden'); $('guruTools').classList.remove('hidden'); fillGuruKelas();
  } else {
    alert('PIN guru salah.');
  }
}
function onGuruCode(){
  var p=guruPick(); if(!p){ return; }
  var n=parseInt($('rsNo').value,10); if(!(n&gt;=1 &amp;&amp; n&lt;=9)){ n=1; $('rsNo').value=1; }
  $('rsOut').innerHTML='&lt;div&gt;Kode reset untuk &lt;b&gt;'+esc(p.kelas)+' / No. '+esc(p.absen)+'&lt;/b&gt; (reset ke-'+n+'):&lt;/div&gt;&lt;div class="guru-code"&gt;'+resetCode(p.kelas,p.absen,n)+'&lt;/div&gt;';
}
function onGuruHere(){
  var p=guruPick(); if(!p){ return; }
  clearAttempts(p.kelas, p.absen);
  $('rsOut').innerHTML='&lt;div class="msg ok show"&gt;Percobaan '+esc(p.kelas)+' / No. '+esc(p.absen)+' pada perangkat ini sudah direset.&lt;/div&gt;';
}
function syncGuruCard(){
  $('guruCard').classList.toggle('hidden', window.location.hash!=='#guru');
}

/* ================= EVENT ================= */

$('btnStart').addEventListener('click', onStart);
$('fKelas').addEventListener('change', populateAbsenOptions);
$('fAbsen').addEventListener('change', populateNamaFromRoster);
initDeadlineNotice();
$('btnSubmit').addEventListener('click', onSubmit);
$('btnPdf').addEventListener('click', downloadPdf);
$('btnRetry').addEventListener('click', function(){ startAttempt(); });
$('btnHome').addEventListener('click', function(){ show('start'); });

$('btnUseReset').addEventListener('click', onUseResetCode);
$('btnGuruLogin').addEventListener('click', onGuruLogin);
$('rsKelas').addEventListener('change', fillGuruAbsen);
$('btnRsCode').addEventListener('click', onGuruCode);
$('btnRsHere').addEventListener('click', onGuruHere);
window.addEventListener('hashchange', syncGuruCard);
syncGuruCard();

/* Hook uji (tidak mengganggu pemakaian normal) */
window.__uts = {formatScoreRatio:formatScoreRatio, fmtLabel:fmtLabel, resetCode:resetCode, similarity:similarity, scoreEssay:scoreEssay, PG:PG, TYPING:TYPING, ESAI:ESAI, ROSTER:ROSTER, CLASS_TOKENS:CLASS_TOKENS, EXAM_OPEN:EXAM_OPEN, EXAM_DEADLINE:EXAM_DEADLINE};

})();
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/ujian-tengah-semester-informatika.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-4838622872826116034</guid><pubDate>Tue, 29 Sep 2026 01:01:34 +0000</pubDate><atom:updated>2026-09-29T08:57:01.877+07:00</atom:updated><title>SERTIFIKAT KOMBEL</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Generator Sertifikat&lt;/title&gt;

&lt;link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@700&amp;display=swap" rel="stylesheet"&gt;

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;

&lt;style&gt;

body{
    font-family:Arial,sans-serif;
    background:#eef2f7;
    padding:20px;
    max-width:700px;
    margin:auto;
}

.card{
    background:white;
    padding:24px;
    border-radius:20px;
    box-shadow:0 10px 20px rgba(0,0,0,.1);
}

h2{
    text-align:center;
    color:#1e4668;
}

.confirm-box{
    background:#f0f6ff;
    border-left:5px solid #2c7da0;
    padding:16px;
    border-radius:16px;
    margin-bottom:20px;
    text-align:center;
}

.confirm-box p{
    margin-top:0;
    font-weight:bold;
    color:#1e4668;
}

.confirm-buttons{
    display:flex;
    gap:10px;
    margin-top:10px;
}

.confirm-buttons button{
    flex:1;
}

.yes-btn{
    background:#1f8a4c;
}

.yes-btn:hover{
    background:#166638;
}

.no-btn{
    background:#c0392b;
}

.no-btn:hover{
    background:#962d22;
}

input,select,button{
    width:100%;
    padding:12px;
    margin-top:10px;
    border-radius:12px;
    border:1px solid #ccc;
    font-size:16px;
}

button{
    background:#2c7da0;
    color:white;
    border:none;
    cursor:pointer;
    font-weight:bold;
}

button:hover{
    background:#1f5e7a;
}

button:disabled{
    background:#94a3b8;
    cursor:not-allowed;
}

input:disabled,
select:disabled{
    background:#f1f5f9;
}

#preview{
    width:100%;
    border-radius:12px;
    margin-top:10px;
}

.loading{
    display:none;
    margin-top:15px;
    color:#1e4668;
    font-weight:bold;
    text-align:center;
}

.status{
    margin-top:12px;
    font-weight:bold;
}

.success{
    color:#1f8a4c;
}

.error{
    color:#c0392b;
}

&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="card"&gt;

&lt;h2&gt;&#128220; Generator Sertifikat&lt;/h2&gt;

&lt;!-- ================= KONFIRMASI ================= --&gt;

&lt;div class="confirm-box"&gt;

&lt;p&gt;&#128269; Apakah Anda mengikuti pelatihan?&lt;/p&gt;

&lt;div class="confirm-buttons"&gt;

&lt;button class="yes-btn" onclick="confirmIkut()"&gt;
✅ YA, IKUT
&lt;/button&gt;

&lt;button class="no-btn" onclick="confirmTidakIkut()"&gt;
❌ TIDAK
&lt;/button&gt;

&lt;/div&gt;

&lt;div id="statusKonfirmasi" class="status"&gt;&lt;/div&gt;

&lt;/div&gt;

&lt;!-- ================= FORM ================= --&gt;

&lt;label&gt;Nama Lengkap&lt;/label&gt;

&lt;input
type="text"
id="nama"
placeholder="Contoh: Ahmad Fauzi"
disabled&gt;

&lt;label&gt;Peran&lt;/label&gt;

&lt;select id="peran" disabled&gt;
    &lt;option&gt;Peserta&lt;/option&gt;
    &lt;option&gt;Narasumber&lt;/option&gt;
&lt;/select&gt;

&lt;img
id="preview"
src="https://lh3.googleusercontent.com/d/18EtT4MIeCpdBOvq7KWwjtm9ucFXmVGbj"
alt="Template"&gt;

&lt;button
id="downloadBtn"
onclick="generatePDF()"
disabled&gt;

⬇️ UNDUH PDF

&lt;/button&gt;

&lt;div class="loading" id="loading"&gt;
Memproses PDF...
&lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;

// ================= TEMPLATE (DIUBAH) =================

const FILE_ID =
'18EtT4MIeCpdBOvq7KWwjtm9ucFXmVGbj';  // &lt;-- ID baru

const TEMPLATE_URL =
`https://lh3.googleusercontent.com/d/${FILE_ID}`;

// ================= UTIL =================

function showLoading(show){

    document.getElementById('loading').style.display =
        show ? 'block' : 'none';

}

function getRomanMonth(monthIndex){

    const romawi = [
        "I","II","III","IV","V","VI",
        "VII","VIII","IX","X","XI","XII"
    ];

    return romawi[monthIndex];

}

function capitalizeEachWord(text){

    return text
        .toLowerCase()
        .replace(/\b\w/g,c=&gt;c.toUpperCase());

}

// ================= KONFIRMASI =================

function confirmIkut(){

    document.getElementById('nama').disabled = false;

    document.getElementById('peran').disabled = false;

    document.getElementById('downloadBtn').disabled = false;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="success"&gt;✅ Konfirmasi diterima. Anda dapat mengunduh sertifikat.&lt;/span&gt;';

}

function confirmTidakIkut(){

    document.getElementById('nama').disabled = true;

    document.getElementById('peran').disabled = true;

    document.getElementById('downloadBtn').disabled = true;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="error"&gt;❌ Anda tidak dapat mengunduh sertifikat.&lt;/span&gt;';

}

// ================= LOAD IMAGE =================

function loadImage(url){

    return new Promise((resolve,reject)=&gt;{

        const img = new Image();

        img.crossOrigin = "anonymous";

        img.onload = ()=&gt;resolve(img);

        img.onerror = ()=&gt;{

            reject(
                new Error(
                    'Template Google Drive gagal dimuat'
                )
            );

        };

        img.src = url + '?t=' + Date.now();

    });

}

// ================= NOMOR =================

async function generateNomor(){

    const now = new Date();

    const bulan =
        getRomanMonth(now.getMonth());

    const tahun =
        now.getFullYear();

    const nomor =
        Math.floor(Math.random()*900)+100;

    return `${nomor}/K/${bulan}/${tahun}`;

}

// ================= PDF =================

async function generatePDF(){

    const namaInput =
        document.getElementById('nama');

    const peranSelect =
        document.getElementById('peran');

    let nama =
        namaInput.value.trim();

    const peran =
        peranSelect.value;

    if(!nama){

        alert('Nama wajib diisi');

        return;

    }

    nama =
        capitalizeEachWord(nama);

    showLoading(true);

    try{

        await document.fonts.ready;

        const img =
            await loadImage(TEMPLATE_URL);

        const canvas =
            document.createElement('canvas');

        canvas.width = img.width;
        canvas.height = img.height;

        const ctx =
            canvas.getContext('2d');

        ctx.drawImage(
            img,
            0,
            0,
            canvas.width,
            canvas.height
        );

        // ================= POSISI =================

        const nomorY =
            canvas.height * 0.235;

        const namaY =
            canvas.height * 0.3990;

        const peranY =
            canvas.height * 0.5420;

        // ================= STYLE =================

        ctx.textAlign = 'center';

        ctx.textBaseline = 'middle';

        ctx.fillStyle = '#000';

        // nomor
        const nomorSertifikat =
            await generateNomor();

        ctx.font =
            `bold ${canvas.width*0.0125}px Montserrat`;

        ctx.fillText(
            nomorSertifikat,
            canvas.width/2,
            nomorY
        );

        // nama auto shrink
        let fontSize =
            canvas.width * 0.031;

        ctx.font =
            `700 ${fontSize}px Montserrat`;

        let textWidth =
            ctx.measureText(nama).width;

        const maxWidth =
            canvas.width - 180;

        while(
            textWidth &gt; maxWidth &amp;&amp;
            fontSize &gt; 20
        ){

            fontSize -= 2;

            ctx.font =
                `700 ${fontSize}px Montserrat`;

            textWidth =
                ctx.measureText(nama).width;

        }

        ctx.fillText(
            nama,
            canvas.width/2,
            namaY
        );

        // peran
        ctx.font =
            `bold ${canvas.width*0.024}px Montserrat`;

        ctx.fillText(
            peran,
            canvas.width/2,
            peranY
        );

        // ================= PDF =================

        const imageData =
            canvas.toDataURL(
                'image/jpeg',
                1.0
            );

        const { jsPDF } =
            window.jspdf;

        const pdf =
            new jsPDF({

                orientation:
                    canvas.width &gt; canvas.height
                    ? 'landscape'
                    : 'portrait',

                unit:'px',

                format:[
                    canvas.width,
                    canvas.height
                ]

            });

        pdf.addImage(
            imageData,
            'JPEG',
            0,
            0,
            canvas.width,
            canvas.height
        );

        const safeName =
            nama.replace(/[^a-z0-9]/gi,'_');

        pdf.save(
            `Sertifikat_${safeName}.pdf`
        );

    }catch(error){

        console.error(error);

        alert(
            '❌ Gagal membuat PDF\n\n' +
            error.message +
            '\n\nPastikan file Google Drive bersifat PUBLIC.'
        );

    }finally{

        showLoading(false);

    }

}

&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/sertifikat-kombel_0210708163.html</link><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://lh3.googleusercontent.com/d/18EtT4MIeCpdBOvq7KWwjtm9ucFXmVGbj=s72-c" width="72"/><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-8866540713710335007</guid><pubDate>Wed, 23 Sep 2026 06:03:35 +0000</pubDate><atom:updated>2026-09-23T13:03:35.896+07:00</atom:updated><title>Membandingkan Isi Teks Ceramah dengan Teks Lain</title><description>&lt;style&gt;
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&amp;family=Source+Sans+3:wght@400;500;600;700&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;display=swap');
.epb{
  --blueprint:#0F2A3D;
  --blueprint-deep:#0A1E2C;
  --panel:#4A2E12;
  --paper:#F3F0E6;
  --ink:#132029;
  --amber:#F5A623;
  --amber-deep:#C97F12;
  --mint:#6EC6CA;
  --line:rgba(234,242,245,0.16);
  --off-white:#EAF2F5;
  --muted:#9FB4C2;
}
.epb *{box-sizing:border-box;}
.epb{scroll-behavior:smooth; background:#0A1F2E;}
.epb{
  margin:0;
  position:relative;
  overflow:hidden;
  background:var(--blueprint);
  color:var(--off-white);
  font-family:'Source Sans 3',sans-serif;
  line-height:1.65;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 34px 34px;
}
.epb h1, .epb h2, .epb h3, .epb h4{font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.02em; margin:0 0 0.5em;}
.epb .mono{font-family:'IBM Plex Mono',monospace;}
.epb a{color:var(--amber);}
.epb .crop{position:absolute; width:22px; height:22px; z-index:5; opacity:.55; pointer-events:none;}
.epb .crop::before, .epb .crop::after{content:"";position:absolute;background:var(--mint);}
.epb .crop::before{width:100%;height:1px;top:50%;left:0;}
.epb .crop::after{width:1px;height:100%;left:50%;top:0;}
.epb .crop.tl{top:14px;left:14px;}
.epb .crop.tr{top:14px;right:14px;}
.epb .crop.bl{bottom:14px;left:14px;}
.epb .crop.br{bottom:14px;right:14px;}
.epb header.cover{
  position:relative;
  padding:40px 24px 36px;
  border-bottom:1px solid var(--line);
  overflow:hidden;
}
.epb .doc-code{font-family:'IBM Plex Mono',monospace; color:var(--amber); letter-spacing:0.15em; font-size:0.78rem; text-transform:uppercase; margin-bottom:18px; display:flex; gap:10px; align-items:center;}
.epb .doc-code::before{content:"◆"; color:var(--amber);}
.epb .cover h1{font-size:clamp(1.6rem,7vw,2.5rem); color:var(--off-white); max-width:16ch; line-height:1.05;}
.epb .cover .subtitle{color:var(--muted); font-size:1.05rem; max-width:60ch; margin-top:14px;}
.epb .meta-strip{
  margin-top:38px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px dashed var(--line); border-bottom:1px dashed var(--line);
}
.epb .meta-strip div{padding:14px 16px; border-right:1px dashed var(--line);}
.epb .meta-strip div:last-child{border-right:none;}
.epb .meta-strip .k{font-family:'IBM Plex Mono',monospace; font-size:0.68rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.1em;}
.epb .meta-strip .v{font-size:0.98rem; color:var(--off-white); margin-top:4px; font-weight:600;}
.epb .stamp{
  position:absolute; top:40px; right:20px; width:104px; height:104px;
  border:2.5px solid var(--amber); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  transform:rotate(-11deg);
  color:var(--amber); font-family:'IBM Plex Mono',monospace;
  text-align:center; font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase;
  opacity:0.9;
}
.epb .stamp .ring{position:absolute; inset:8px; border:1px dashed var(--amber); border-radius:50%;}
.epb .stamp b{display:block; font-family:'Oswald',sans-serif; font-size:1.05rem; letter-spacing:0.06em; margin-top:4px;}

@media (max-width:760px){.epb .stamp{display:none;}
.epb }


.epb main{max-width:920px; margin:0 auto; padding:0 20px 60px;}
.epb .section{padding-top:64px;}
.epb .eyebrow{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; margin-bottom:10px; display:flex; align-items:center; gap:10px;}
.epb .eyebrow::after{content:""; flex:1; height:1px; background:var(--line);}
.epb h2.section-title{font-size:1.7rem; color:var(--off-white);}
.epb p{color:#DDE7EC;}
.epb .lead{color:var(--muted); font-size:1.02rem; max-width:72ch;}
.epb .panel{
  background:var(--panel); border:1px solid var(--line); border-radius:2px;
  padding:26px 28px; margin:22px 0;
}
.epb .panel.tp{border-left:4px solid var(--mint);}
.epb .panel.warn{border-left:4px solid var(--amber);}
.epb .tp-box{display:grid; gap:14px; margin-top:18px;}
.epb .tp-item{display:flex; gap:14px; align-items:flex-start;}
.epb .tp-item .idx{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; min-width:34px;}
.epb .flow{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; margin-top:20px;}
.epb .flow .step{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px; position:relative;}
.epb .flow .step .tag{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase;}
.epb .flow .step h4{font-size:1rem; margin-top:8px; color:var(--off-white);}
.epb .flow .step .time{position:absolute; top:16px; right:16px; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; color:var(--amber);}
.epb .grid-3{display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:14px; margin-top:16px;}
.epb .card{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px;}
.epb .card h4{color:var(--amber); font-size:0.95rem;}
.epb .criteria{display:grid; gap:16px; margin-top:20px;}
.epb .crit{
  background:var(--blueprint-deep); border:1px solid var(--line);
  display:grid; grid-template-columns:64px 1fr; gap:0;
}
.epb .crit .num{
  background:var(--amber); color:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace;
  font-weight:700; display:flex; align-items:center; justify-content:center; font-size:1.05rem;
}
.epb .crit .body{padding:18px 20px;}
.epb .crit h4{color:var(--off-white); font-size:1.02rem; margin-bottom:6px;}
.epb .crit .q{margin-top:10px; padding-top:10px; border-top:1px dashed var(--line); font-family:'IBM Plex Mono',monospace; font-size:0.86rem; color:var(--amber);}
.epb .timeline{margin-top:24px; position:relative; padding-left:34px;}
.epb .timeline::before{content:""; position:absolute; left:11px; top:6px; bottom:6px; width:2px; background:var(--line);}
.epb .tl-item{position:relative; padding-bottom:26px;}
.epb .tl-item:last-child{padding-bottom:0;}
.epb .tl-item::before{content:""; position:absolute; left:-34px; top:2px; width:22px; height:22px; border-radius:50%; background:var(--blueprint); border:2px solid var(--amber);}
.epb .tl-item h4{color:var(--off-white); font-size:1rem; margin-bottom:4px;}
.epb .doc-sample{
  background:var(--paper); color:var(--ink); border-radius:2px; padding:0; margin-top:22px;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .doc-sample .doc-head{background:var(--ink); color:var(--paper); padding:12px 22px; font-family:'IBM Plex Mono',monospace; font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; display:flex; justify-content:space-between;}
.epb .doc-sample .doc-body{padding:26px 28px 30px;}
.epb .doc-sample h3{font-family:'Oswald',sans-serif; color:var(--ink); font-size:1.25rem; text-transform:none; letter-spacing:0;}
.epb .doc-sample ol{padding-left:22px;}
.epb .doc-sample li{margin-bottom:9px;}
.epb .flag{
  display:inline-block; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; font-weight:600;
  background:#D94E2A; color:#fff; padding:1px 7px; border-radius:2px; margin-left:6px; vertical-align:middle;
}
.epb .doc-sample .note-margin{margin-top:6px; font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:#8A3A1F; padding-left:8px; border-left:2px solid #D94E2A;}
.epb .legend{display:flex; flex-wrap:wrap; gap:10px 20px; margin-top:16px;}
.epb .legend span{font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:var(--muted); display:flex; align-items:center; gap:8px;}
.epb .legend .dot{width:10px; height:10px; border-radius:50%; display:inline-block;}
.epb table{width:100%; border-collapse:collapse; margin-top:18px; font-size:0.94rem;}
.epb th, .epb td{border:1px solid var(--line); padding:10px 12px; text-align:left; vertical-align:top;}
.epb th{background:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.06em; color:var(--amber);}
.epb .summary-box{
  border:1px solid var(--amber); background:linear-gradient(180deg, rgba(245,166,35,0.08), transparent);
  padding:24px 26px; margin-top:24px;
}
.epb .summary-box ul{margin:10px 0 0; padding-left:20px;}
.epb .summary-box li{margin-bottom:8px;}
.epb .task-header{
  background:var(--amber); color:var(--blueprint-deep); padding:22px 26px; margin-top:20px;
  display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;
}
.epb .task-header h2{color:var(--blueprint-deep); font-size:1.4rem; margin:0;}
.epb .task-header .badge{font-family:'IBM Plex Mono',monospace; font-size:0.8rem; background:var(--blueprint-deep); color:var(--amber); padding:6px 12px; border-radius:2px;}
.epb .steps-num{counter-reset:step; margin-top:18px; list-style:none; padding:0;}
.epb .steps-num li{counter-increment:step; position:relative; padding-left:44px; margin-bottom:16px;}
.epb .steps-num li::before{content:counter(step,decimal-leading-zero); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; font-size:0.95rem; border:1px solid var(--amber); width:30px; height:26px; display:flex; align-items:center; justify-content:center;}
.epb .rubric-scale{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:16px;}
.epb .rubric-scale div{background:var(--blueprint-deep); border:1px solid var(--line); padding:12px; text-align:center;}
.epb .rubric-scale .n{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:1.3rem; font-weight:700;}
.epb .rubric-scale .l{font-size:0.78rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.05em;}
.epb .notebook{
  background:repeating-linear-gradient(var(--paper), var(--paper) 33px, #D8D3C2 34px);
  color:var(--ink); border-radius:2px; padding:28px 30px; margin-top:20px; position:relative;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .notebook::before{content:""; position:absolute; left:46px; top:0; bottom:0; width:1.5px; background:#C0453A; opacity:0.5;}
.epb .notebook .content{padding-left:24px;}
.epb .notebook h3{color:var(--ink); font-family:'Oswald',sans-serif; letter-spacing:0.02em; margin-bottom:10px;}
.epb .notebook ul{padding-left:20px;}
.epb footer{border-top:1px solid var(--line); padding:24px 20px 40px; color:var(--muted); font-size:0.86rem;}
.epb footer .doc-code{margin-bottom:10px;}
.epb .divider-dash{border:none; border-top:1px dashed var(--line); margin:44px 0 0;}
.epb::selection{background:var(--amber); color:var(--blueprint-deep);}

&lt;/style&gt;
&lt;div class="epb"&gt;
&lt;div class="crop tl"&gt;&lt;/div&gt;&lt;div class="crop tr"&gt;&lt;/div&gt;&lt;div class="crop bl"&gt;&lt;/div&gt;&lt;div class="crop br"&gt;&lt;/div&gt;

&lt;header class="cover"&gt;
  &lt;div class="stamp"&gt;&lt;div class="ring"&gt;&lt;/div&gt;&lt;span&gt;Modul Ajar&lt;b&gt;Terverifikasi&lt;/b&gt;Fase F&lt;/span&gt;&lt;/div&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/CERAMAH-BANDING — 3 JP · 1 Pertemuan&lt;/div&gt;
  &lt;h1&gt;Membandingkan Isi Teks Ceramah dengan Teks Lain&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Materi Bahasa Indonesia Kelas XI SMK/MAK — melatih murid membandingkan isi teks ceramah dengan teks artikel opini serta menemukan kelebihan dan kekurangan masing-masing.&lt;/p&gt;

  &lt;div class="meta-strip"&gt;
    &lt;div&gt;&lt;div class="k"&gt;Mata Pelajaran&lt;/div&gt;&lt;div class="v"&gt;Bahasa Indonesia&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK/MAK — Kelas XI&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Elemen&lt;/div&gt;&lt;div class="v"&gt;Membaca &amp;amp; Memirsa&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;3 JP (3 × 45 menit)&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;

  &lt;!-- ============ TUJUAN PEMBELAJARAN ============ --&gt;
  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="eyebrow"&gt;01 — Identitas Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Capaian &amp;amp; Tujuan Pembelajaran&lt;/h2&gt;

    &lt;div class="panel tp"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Capaian Pembelajaran (Elemen Membaca–Memirsa)&lt;/div&gt;
      &lt;p&gt;Murid mampu mengevaluasi informasi dari berbagai jenis teks, termasuk teks ceramah, serta membandingkannya dengan teks lain untuk menemukan persamaan, perbedaan, kelebihan, dan kekurangan masing-masing secara kritis.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="panel"&gt;
      &lt;div class="eyebrow" style="margin-bottom:6px;"&gt;Tujuan Pembelajaran&lt;/div&gt;
      &lt;p style="font-size:1.05rem; color:var(--off-white); font-weight:600;"&gt;Murid mampu membandingkan isi teks ceramah dengan teks lainnya serta menemukan kelebihan dan kekurangan masing-masing.&lt;/p&gt;

      &lt;div class="tp-box"&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.1&lt;/span&gt;&lt;p&gt;Murid dapat menjelaskan struktur dan ciri kebahasaan teks ceramah.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.2&lt;/span&gt;&lt;p&gt;Murid dapat menjelaskan struktur dan ciri kebahasaan teks artikel opini sebagai teks pembanding.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.3&lt;/span&gt;&lt;p&gt;Murid dapat menemukan persamaan dan perbedaan antara teks ceramah dan teks artikel opini.&lt;/p&gt;&lt;/div&gt;
        &lt;div class="tp-item"&gt;&lt;span class="idx mono"&gt;TP.4&lt;/span&gt;&lt;p&gt;Murid dapat menemukan kelebihan dan kekurangan teks ceramah maupun teks artikel opini berdasarkan bukti dari kedua teks.&lt;/p&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="eyebrow" style="margin-top:26px; margin-bottom:6px;"&gt;8 Profil Lulusan yang Dikembangkan&lt;/div&gt;
    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Penalaran Kritis&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menganalisis kelebihan dan kekurangan dua jenis teks berdasarkan bukti tekstual.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kemandirian&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menyelesaikan reviu dan simpulan perbandingan secara mandiri.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kolaborasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Bekerja sama dalam kelompok saat membandingkan dua teks.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Komunikasi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Menyampaikan hasil perbandingan teks secara jelas dan runtut.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI A: TEKS CERAMAH ============ --&gt;
  &lt;section class="section" id="materi-a"&gt;
    &lt;div class="eyebrow"&gt;02 — Materi Inti A&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Mengenal Teks Ceramah&lt;/h2&gt;
    &lt;p class="lead"&gt;Teks ceramah dipelajari kembali sebagai salah satu teks yang akan dibandingkan pada pertemuan ini.&lt;/p&gt;

    &lt;div class="panel"&gt;
      &lt;p&gt;&lt;b style="color:var(--off-white);"&gt;Teks ceramah&lt;/b&gt; adalah teks berisi pidato seseorang di hadapan pendengar, bertujuan memberi nasihat, wejangan, atau pengetahuan tentang suatu hal, biasanya bertema moral, agama, atau sosial, dan disampaikan secara lisan.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Struktur&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Pembukaan (salam &amp;amp; sapaan) → isi (paparan gagasan) → penutup (simpulan &amp;amp; salam).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Kata Ganti &amp;amp; Sapaan&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;"Kita", "kami", serta sapaan seperti "hadirin", "saudara-saudara".&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Ciri Kebahasaan Lain&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Kalimat persuasif, konjungsi kausalitas (karena, oleh karena itu), kalimat ajakan ("mari", "marilah").&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI B: ARTIKEL OPINI ============ --&gt;
  &lt;section class="section" id="materi-b"&gt;
    &lt;div class="eyebrow"&gt;03 — Materi Inti B&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Mengenal Teks Artikel Opini sebagai Teks Pembanding&lt;/h2&gt;
    &lt;p class="lead"&gt;Teks artikel opini dipilih sebagai teks pembanding karena sama-sama bersifat persuasif, namun disampaikan melalui tulisan.&lt;/p&gt;

    &lt;div class="panel"&gt;
      &lt;p&gt;&lt;b style="color:var(--off-white);"&gt;Teks artikel opini&lt;/b&gt; adalah teks tulisan yang berisi pandangan atau pendapat penulis terhadap suatu isu, didukung fakta dan data, bertujuan meyakinkan pembaca terhadap sudut pandang tertentu.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Struktur&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Pengantar/tesis (isu diangkat) → argumentasi (alasan &amp;amp; bukti) → penegasan ulang/simpulan.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Sifat Penulisan&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Lebih objektif, jarang memakai sapaan langsung, didukung data/fakta.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;Ciri Kebahasaan Lain&lt;/h4&gt;&lt;p style="font-size:0.92rem;"&gt;Kalimat argumentatif, konjungsi kausalitas &amp;amp; pertentangan, rujukan pendapat ahli/data.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI C: PERSAMAAN ============ --&gt;
  &lt;section class="section" id="materi-c"&gt;
    &lt;div class="eyebrow"&gt;04 — Materi Inti C&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Persamaan Teks Ceramah dan Artikel Opini&lt;/h2&gt;

    &lt;div class="criteria"&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;1&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Sama-Sama Bersifat Persuasif&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Keduanya berusaha memengaruhi atau meyakinkan pembaca/pendengar terhadap suatu pandangan.&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;2&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Menyajikan Argumen Berdasarkan Fakta&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Keduanya mendukung gagasan dengan alasan, contoh, atau data agar lebih meyakinkan.&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;3&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Memiliki Struktur Pembuka-Isi-Penutup&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Keduanya tersusun dari bagian pembuka, pemaparan gagasan, dan penutup/simpulan.&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="crit"&gt;
        &lt;div class="num mono"&gt;4&lt;/div&gt;
        &lt;div class="body"&gt;
          &lt;h4&gt;Membahas Suatu Isu secara Mendalam&lt;/h4&gt;
          &lt;p style="margin:0;"&gt;Keduanya mengangkat satu topik tertentu dan membahasnya secara terfokus.&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI D: PERBEDAAN ============ --&gt;
  &lt;section class="section" id="materi-d"&gt;
    &lt;div class="eyebrow"&gt;05 — Materi Inti D&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Perbedaan Teks Ceramah dan Artikel Opini&lt;/h2&gt;

    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:22%;"&gt;Aspek&lt;/th&gt;&lt;th&gt;Teks Ceramah&lt;/th&gt;&lt;th&gt;Teks Artikel Opini&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Media&lt;/td&gt;&lt;td&gt;Disampaikan secara lisan&lt;/td&gt;&lt;td&gt;Disampaikan secara tulisan&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Sifat Interaksi&lt;/td&gt;&lt;td&gt;Interaktif, ada kontak langsung dengan pendengar&lt;/td&gt;&lt;td&gt;Satu arah, dibaca sendiri oleh pembaca&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kata Ganti/Sapaan&lt;/td&gt;&lt;td&gt;Sering memakai "kita/kami" dan sapaan "hadirin"&lt;/td&gt;&lt;td&gt;Lebih objektif, jarang memakai sapaan langsung&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Tema Umum&lt;/td&gt;&lt;td&gt;Sering bertema moral, agama, atau sosial&lt;/td&gt;&lt;td&gt;Lintas tema: politik, ekonomi, sosial, dan lainnya&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Cara Penyampaian&lt;/td&gt;&lt;td&gt;Intonasi, ekspresi, dan bahasa tubuh&lt;/td&gt;&lt;td&gt;Pilihan kata dan struktur kalimat tertulis&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI E: KELEBIHAN &amp; KEKURANGAN ============ --&gt;
  &lt;section class="section" id="materi-e"&gt;
    &lt;div class="eyebrow"&gt;06 — Materi Inti E&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kelebihan dan Kekurangan Masing-Masing Teks&lt;/h2&gt;

    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:22%;"&gt;Jenis Teks&lt;/th&gt;&lt;th&gt;Kelebihan&lt;/th&gt;&lt;th&gt;Kekurangan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Teks Ceramah&lt;/td&gt;&lt;td&gt;Dapat menyentuh emosi pendengar secara langsung melalui intonasi dan ekspresi; ada interaksi langsung (tanya jawab)&lt;/td&gt;&lt;td&gt;Sulit diulang kecuali direkam; pesan mudah terlupakan jika tidak dicatat&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Teks Artikel Opini&lt;/td&gt;&lt;td&gt;Dapat dibaca ulang kapan saja; dapat disebarluaskan lebih luas dan cepat melalui media cetak/daring&lt;/td&gt;&lt;td&gt;Tidak ada interaksi langsung dengan pembaca; kurang menyentuh emosi secara langsung&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ MATERI F: LANGKAH MEMBANDINGKAN ============ --&gt;
  &lt;section class="section" id="materi-f"&gt;
    &lt;div class="eyebrow"&gt;07 — Materi Inti F&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Langkah-Langkah Membandingkan Isi Dua Teks&lt;/h2&gt;

    &lt;div class="timeline"&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;01 · Membaca Kedua Teks&lt;/h4&gt;&lt;p&gt;Baca teks ceramah dan artikel opini secara menyeluruh untuk memahami isi masing-masing.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;02 · Mengidentifikasi Topik/Isu&lt;/h4&gt;&lt;p&gt;Tentukan isu atau topik yang dibahas pada masing-masing teks.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;03 · Membandingkan Struktur &amp;amp; Cara Penyampaian&lt;/h4&gt;&lt;p&gt;Bandingkan bagian-bagian pembangun teks serta gaya penyampaian argumennya.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;04 · Menganalisis Kelebihan &amp;amp; Kekurangan&lt;/h4&gt;&lt;p&gt;Tentukan sisi unggul dan sisi lemah masing-masing teks berdasarkan bukti dari teks.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="tl-item"&gt;&lt;h4 class="mono"&gt;05 · Menyusun Simpulan Perbandingan&lt;/h4&gt;&lt;p&gt;Tuliskan simpulan perbandingan secara ringkas dan berdasarkan bukti dari kedua teks.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ PEMODELAN ============ --&gt;
  &lt;section class="section" id="contoh"&gt;
    &lt;div class="eyebrow"&gt;08 — Pemodelan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Contoh Penerapan: Membandingkan Dua Teks&lt;/h2&gt;
    &lt;p class="lead"&gt;Guru membacakan dan membahas bersama sepasang teks berikut sebagai model penerapan sebelum murid mengerjakan tugas kelompok.&lt;/p&gt;

    &lt;div class="doc-sample"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks Ceramah Contoh&lt;/span&gt;&lt;span&gt;Dokumen-01&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Pentingnya Menjaga Lingkungan&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Assalamu'alaikum warahmatullahi wabarakatuh. Hadirin yang berbahagia, pada kesempatan ini marilah kita bersama-sama merenungkan pentingnya menjaga lingkungan. Kita sering melihat sampah berserakan di sekitar kita. Oleh karena itu, mari kita mulai membiasakan diri membuang sampah pada tempatnya. Dengan menjaga lingkungan, kita turut menjaga kesehatan diri dan generasi mendatang. Demikian yang dapat saya sampaikan, semoga bermanfaat. Wassalamu'alaikum warahmatullahi wabarakatuh.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="doc-sample" style="margin-top:22px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks Artikel Opini Contoh&lt;/span&gt;&lt;span&gt;Dokumen-02&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Pentingnya Menjaga Lingkungan bagi Generasi Mendatang&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Masalah sampah masih menjadi persoalan serius di berbagai daerah. Berdasarkan data Kementerian Lingkungan Hidup, volume sampah nasional terus meningkat setiap tahun. Kondisi ini menunjukkan bahwa kesadaran masyarakat dalam mengelola sampah masih rendah. Oleh sebab itu, diperlukan edukasi berkelanjutan mengenai pengelolaan sampah sejak dini agar generasi mendatang dapat hidup di lingkungan yang lebih bersih dan sehat.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="panel" style="margin-top:22px;"&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Simpulan model perbandingan:&lt;/b&gt; Kedua teks membahas isu yang sama, yaitu menjaga lingkungan. Teks ceramah menggunakan sapaan religius dan kalimat ajakan langsung ("mari kita..."), sedangkan artikel opini menggunakan data resmi sebagai penguat argumen dengan bahasa yang lebih formal dan objektif. Kelebihan ceramah terletak pada kedekatan emosional melalui sapaan langsung, sementara kelebihan artikel opini terletak pada kekuatan argumen berbasis data yang dapat diverifikasi.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ RANGKUMAN ============ --&gt;
  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="eyebrow"&gt;09 — Ringkasan Materi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Poin-Poin Penting&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;ul&gt;
        &lt;li&gt;Teks ceramah disampaikan secara lisan dan interaktif, sedangkan artikel opini disampaikan secara tulisan dan satu arah.&lt;/li&gt;
        &lt;li&gt;Persamaan keduanya: bersifat persuasif, menyajikan argumen berdasarkan fakta, dan memiliki struktur pembuka-isi-penutup.&lt;/li&gt;
        &lt;li&gt;Kelebihan ceramah adalah kedekatan emosional langsung; kekurangannya sulit diulang. Kelebihan artikel opini adalah dapat dibaca ulang dan disebarluaskan luas; kekurangannya tidak ada interaksi langsung.&lt;/li&gt;
        &lt;li&gt;Membandingkan dilakukan bertahap: membaca kedua teks → mengidentifikasi topik → membandingkan struktur → menganalisis kelebihan-kekurangan → menyusun simpulan.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ TUGAS KELOMPOK ============ --&gt;
  &lt;section class="section" id="tugas-kelompok"&gt;
    &lt;div class="eyebrow"&gt;10 — Lembar Kerja&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Kelompok: Membandingkan Teks Ceramah dan Artikel Opini&lt;/h2&gt;
      &lt;span class="badge"&gt;Durasi ± 60 menit&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Tujuan tugas:&lt;/b&gt; Murid secara berkelompok membandingkan sepasang teks bertema sama berikut, lalu menemukan persamaan, perbedaan, kelebihan, dan kekurangan masing-masing.&lt;/p&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks Ceramah untuk Dibandingkan&lt;/span&gt;&lt;span&gt;Dokumen-03&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Pentingnya Membaca Buku&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Assalamu'alaikum warahmatullahi wabarakatuh. Bapak/Ibu dan saudara sekalian yang saya hormati, pada hari ini izinkan saya menyampaikan sedikit renungan tentang pentingnya membaca buku. Di era digital ini, banyak dari kita lebih memilih menghabiskan waktu dengan gawai dibandingkan membaca buku. Padahal, buku adalah jendela ilmu yang dapat membuka wawasan kita seluas-luasnya. Oleh karena itu, marilah kita mulai membiasakan diri membaca buku minimal beberapa menit setiap hari. Dengan membiasakan membaca, kita akan menjadi pribadi yang lebih bijaksana dalam bertindak. Demikian yang dapat saya sampaikan. Wassalamu'alaikum warahmatullahi wabarakatuh.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="doc-sample" style="margin-top:20px;"&gt;
      &lt;div class="doc-head"&gt;&lt;span&gt;Lampiran · Teks Artikel Opini untuk Dibandingkan&lt;/span&gt;&lt;span&gt;Dokumen-04&lt;/span&gt;&lt;/div&gt;
      &lt;div class="doc-body"&gt;
        &lt;h3 style="color:#132029 !important;"&gt;Minat Baca Rendah, Tantangan Generasi Digital&lt;/h3&gt;
        &lt;p style="margin:10px 0; color:#132029 !important;"&gt;Data dari sebuah survei literasi menunjukkan bahwa minat baca masyarakat Indonesia masih tergolong rendah dibandingkan negara lain. Fenomena ini erat kaitannya dengan maraknya penggunaan gawai untuk hiburan singkat seperti media sosial. Akibatnya, kemampuan berpikir kritis dan mendalam masyarakat berpotensi menurun. Oleh sebab itu, diperlukan upaya bersama, mulai dari keluarga hingga sekolah, untuk membangun kembali kebiasaan membaca buku sejak usia dini.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Petunjuk Pengerjaan&lt;/h4&gt;
    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;b&gt;Bentuk kelompok&lt;/b&gt; beranggotakan 4–5 orang sesuai pembagian dari guru.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Baca kedua teks&lt;/b&gt; di atas secara bersama-sama dalam kelompok.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Identifikasi persamaan dan perbedaan&lt;/b&gt; kedua teks berdasarkan struktur, kata ganti/sapaan, dan cara penyampaian.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Isi Tabel Kelebihan dan Kekurangan&lt;/b&gt; di bawah ini untuk masing-masing teks.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Presentasikan hasil&lt;/b&gt; di depan kelas selama maksimal 5 menit per kelompok, dilanjutkan tanya jawab singkat.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Format Tabel Kelebihan dan Kekurangan&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:22%;"&gt;Jenis Teks&lt;/th&gt;&lt;th&gt;Kelebihan&lt;/th&gt;&lt;th&gt;Kekurangan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Teks Ceramah&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Teks Artikel Opini&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h4 style="color:var(--off-white); margin-top:26px;"&gt;Rubrik Penilaian Kelompok&lt;/h4&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th style="width:26%;"&gt;Aspek Penilaian&lt;/th&gt;&lt;th&gt;Skor 4 (Sangat Baik)&lt;/th&gt;&lt;th&gt;Skor 3 (Baik)&lt;/th&gt;&lt;th&gt;Skor 2 (Cukup)&lt;/th&gt;&lt;th&gt;Skor 1 (Kurang)&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan identifikasi persamaan &amp;amp; perbedaan&lt;/td&gt;&lt;td&gt;Seluruh aspek teridentifikasi tepat dengan bukti jelas&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Identifikasi dangkal&lt;/td&gt;&lt;td&gt;Tidak sesuai&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan analisis kelebihan &amp;amp; kekurangan&lt;/td&gt;&lt;td&gt;Analisis tepat dan didukung bukti dari teks&lt;/td&gt;&lt;td&gt;Sebagian besar tepat&lt;/td&gt;&lt;td&gt;Analisis kurang didukung bukti&lt;/td&gt;&lt;td&gt;Tidak tepat&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kerja sama kelompok&lt;/td&gt;&lt;td&gt;Seluruh anggota aktif berkontribusi&lt;/td&gt;&lt;td&gt;Sebagian besar anggota aktif&lt;/td&gt;&lt;td&gt;Hanya sebagian kecil anggota aktif&lt;/td&gt;&lt;td&gt;Didominasi satu orang&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Presentasi&lt;/td&gt;&lt;td&gt;Jelas, percaya diri, menjawab pertanyaan dengan baik&lt;/td&gt;&lt;td&gt;Jelas namun kurang percaya diri&lt;/td&gt;&lt;td&gt;Kurang jelas/terbata-bata&lt;/td&gt;&lt;td&gt;Tidak dapat menjelaskan hasil&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ TUGAS INDIVIDU: KUIS 25 PERTANYAAN ============ --&gt;
  &lt;section class="section" id="tugas-individu"&gt;
    &lt;div class="eyebrow"&gt;11 — Penutup Pembelajaran&lt;/div&gt;
    &lt;div class="task-header"&gt;
      &lt;h2&gt;Tugas Individu: Menjawab 25 Pertanyaan tentang Materi&lt;/h2&gt;
      &lt;span class="badge"&gt;Dikerjakan Mandiri · Otomatis Bernilai&lt;/span&gt;
    &lt;/div&gt;

    &lt;p style="margin-top:18px;"&gt;&lt;b style="color:var(--off-white);"&gt;Petunjuk:&lt;/b&gt; Isi No. Absen dan Nama, lalu jawab 25 pertanyaan pilihan ganda (5 opsi) berikut berdasarkan materi yang telah dipelajari hari ini — terdiri atas 10 soal mudah, 10 soal sedang, dan 5 soal sulit. Skor akan muncul otomatis setelah menekan tombol "Kirim Jawaban", dan hasilnya akan tercatat otomatis.&lt;/p&gt;

    &lt;div class="panel" style="margin-top:20px;"&gt;
      &lt;div style="display:grid; grid-template-columns:1fr 2fr; gap:14px; margin-bottom:22px;"&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;No. Absen&lt;/label&gt;
          &lt;input id="cq-absen" type="text" inputmode="numeric" placeholder="Contoh: 12" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Nama Lengkap&lt;/label&gt;
          &lt;input id="cq-nama" type="text" placeholder="Tulis nama lengkap" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;1. Teks yang berisi pidato bertujuan memberi nasihat atau wejangan kepada pendengar disebut teks...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq0" value="0" style="margin-right:8px;"&gt;A. Artikel&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq0" value="1" style="margin-right:8px;"&gt;B. Ceramah&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq0" value="2" style="margin-right:8px;"&gt;C. Prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq0" value="3" style="margin-right:8px;"&gt;D. Eksplanasi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq0" value="4" style="margin-right:8px;"&gt;E. Anekdot&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;2. Bagian struktur teks ceramah yang berisi salam dan sapaan disebut...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq1" value="0" style="margin-right:8px;"&gt;A. Isi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq1" value="1" style="margin-right:8px;"&gt;B. Penutup&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq1" value="2" style="margin-right:8px;"&gt;C. Pembukaan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq1" value="3" style="margin-right:8px;"&gt;D. Simpulan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq1" value="4" style="margin-right:8px;"&gt;E. Argumen&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;3. Kata ganti yang sering digunakan dalam teks ceramah untuk menyapa pendengar adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq2" value="0" style="margin-right:8px;"&gt;A. Saya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq2" value="1" style="margin-right:8px;"&gt;B. Kalian&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq2" value="2" style="margin-right:8px;"&gt;C. Hadirin&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq2" value="3" style="margin-right:8px;"&gt;D. Mereka&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq2" value="4" style="margin-right:8px;"&gt;E. Dia&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;4. Teks tulisan yang berisi pendapat penulis terhadap suatu isu disebut teks...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq3" value="0" style="margin-right:8px;"&gt;A. Ceramah&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq3" value="1" style="margin-right:8px;"&gt;B. Artikel opini&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq3" value="2" style="margin-right:8px;"&gt;C. Prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq3" value="3" style="margin-right:8px;"&gt;D. Deskripsi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq3" value="4" style="margin-right:8px;"&gt;E. Anekdot&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;5. Bagian struktur artikel opini yang berisi isu yang diangkat disebut...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq4" value="0" style="margin-right:8px;"&gt;A. Argumentasi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq4" value="1" style="margin-right:8px;"&gt;B. Pengantar/tesis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq4" value="2" style="margin-right:8px;"&gt;C. Penutup&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq4" value="3" style="margin-right:8px;"&gt;D. Interpretasi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq4" value="4" style="margin-right:8px;"&gt;E. Orientasi&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;6. Manakah media penyampaian teks ceramah?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq5" value="0" style="margin-right:8px;"&gt;A. Tulisan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq5" value="1" style="margin-right:8px;"&gt;B. Lisan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq5" value="2" style="margin-right:8px;"&gt;C. Gambar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq5" value="3" style="margin-right:8px;"&gt;D. Video tanpa suara&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq5" value="4" style="margin-right:8px;"&gt;E. Simbol&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;7. Manakah media penyampaian teks artikel opini?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq6" value="0" style="margin-right:8px;"&gt;A. Lisan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq6" value="1" style="margin-right:8px;"&gt;B. Tulisan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq6" value="2" style="margin-right:8px;"&gt;C. Gerakan tubuh&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq6" value="3" style="margin-right:8px;"&gt;D. Suara tanpa kata&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq6" value="4" style="margin-right:8px;"&gt;E. Simbol&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;8. Kalimat “Marilah kita menjaga lingkungan bersama-sama” termasuk kalimat...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq7" value="0" style="margin-right:8px;"&gt;A. Deklaratif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq7" value="1" style="margin-right:8px;"&gt;B. Interogatif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq7" value="2" style="margin-right:8px;"&gt;C. Persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq7" value="3" style="margin-right:8px;"&gt;D. Naratif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq7" value="4" style="margin-right:8px;"&gt;E. Deskriptif&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;9. Konjungsi yang menunjukkan sebab-akibat disebut konjungsi...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq8" value="0" style="margin-right:8px;"&gt;A. Temporal&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq8" value="1" style="margin-right:8px;"&gt;B. Kausalitas&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq8" value="2" style="margin-right:8px;"&gt;C. Pertentangan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq8" value="3" style="margin-right:8px;"&gt;D. Pilihan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq8" value="4" style="margin-right:8px;"&gt;E. Penjumlahan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#6EC6CA !important; border:1px solid #6EC6CA; padding:2px 8px; margin-bottom:6px;"&gt;Mudah&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;10. Apa tujuan utama dari kedua teks ceramah dan artikel opini?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq9" value="0" style="margin-right:8px;"&gt;A. Menghibur pembaca&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq9" value="1" style="margin-right:8px;"&gt;B. Memengaruhi/meyakinkan pembaca atau pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq9" value="2" style="margin-right:8px;"&gt;C. Menceritakan pengalaman pribadi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq9" value="3" style="margin-right:8px;"&gt;D. Menjelaskan langkah kerja&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq9" value="4" style="margin-right:8px;"&gt;E. Menjual produk&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;11. Manakah persamaan antara teks ceramah dan artikel opini?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq10" value="0" style="margin-right:8px;"&gt;A. Keduanya bersifat naratif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq10" value="1" style="margin-right:8px;"&gt;B. Keduanya bersifat persuasif dan menyajikan argumen&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq10" value="2" style="margin-right:8px;"&gt;C. Keduanya tidak memiliki struktur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq10" value="3" style="margin-right:8px;"&gt;D. Keduanya tidak menggunakan konjungsi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq10" value="4" style="margin-right:8px;"&gt;E. Keduanya tidak membahas isu tertentu&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;12. Perbedaan utama cara penyampaian pesan antara ceramah dan artikel opini adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq11" value="0" style="margin-right:8px;"&gt;A. Ceramah menggunakan intonasi dan ekspresi, artikel opini menggunakan pilihan kata tertulis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq11" value="1" style="margin-right:8px;"&gt;B. Keduanya sama-sama menggunakan gambar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq11" value="2" style="margin-right:8px;"&gt;C. Ceramah tidak memiliki pesan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq11" value="3" style="margin-right:8px;"&gt;D. Artikel opini disampaikan secara lisan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq11" value="4" style="margin-right:8px;"&gt;E. Keduanya tidak memiliki tujuan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;13. Salah satu kelebihan teks ceramah dibandingkan artikel opini adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq12" value="0" style="margin-right:8px;"&gt;A. Dapat dibaca ulang kapan saja&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq12" value="1" style="margin-right:8px;"&gt;B. Dapat menyentuh emosi pendengar secara langsung&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq12" value="2" style="margin-right:8px;"&gt;C. Lebih mudah disebarluaskan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq12" value="3" style="margin-right:8px;"&gt;D. Lebih objektif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq12" value="4" style="margin-right:8px;"&gt;E. Tidak memerlukan persiapan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;14. Salah satu kekurangan teks ceramah dibandingkan artikel opini adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq13" value="0" style="margin-right:8px;"&gt;A. Sulit diulang kecuali direkam&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq13" value="1" style="margin-right:8px;"&gt;B. Tidak dapat menyentuh emosi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq13" value="2" style="margin-right:8px;"&gt;C. Tidak memiliki struktur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq13" value="3" style="margin-right:8px;"&gt;D. Tidak bisa disampaikan di depan umum&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq13" value="4" style="margin-right:8px;"&gt;E. Tidak memiliki tujuan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;15. Salah satu kelebihan artikel opini dibandingkan ceramah adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq14" value="0" style="margin-right:8px;"&gt;A. Lebih menyentuh emosi secara langsung&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq14" value="1" style="margin-right:8px;"&gt;B. Dapat dibaca ulang dan disebarluaskan lebih luas&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq14" value="2" style="margin-right:8px;"&gt;C. Selalu lebih pendek&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq14" value="3" style="margin-right:8px;"&gt;D. Tidak memerlukan data&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq14" value="4" style="margin-right:8px;"&gt;E. Tidak memiliki penulis&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;16. Mengapa teks ceramah sering menggunakan kata ganti “kita” atau “kami”?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq15" value="0" style="margin-right:8px;"&gt;A. Untuk membangun kedekatan dengan pendengar&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq15" value="1" style="margin-right:8px;"&gt;B. Karena aturan tata bahasa baku&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq15" value="2" style="margin-right:8px;"&gt;C. Agar teks lebih panjang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq15" value="3" style="margin-right:8px;"&gt;D. Untuk menghindari kesalahan tata bahasa&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq15" value="4" style="margin-right:8px;"&gt;E. Tidak ada alasan khusus&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;17. Artikel opini cenderung lebih objektif dibanding ceramah karena...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq16" value="0" style="margin-right:8px;"&gt;A. Menggunakan data dan fakta sebagai penguat argumen&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq16" value="1" style="margin-right:8px;"&gt;B. Tidak memiliki penulis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq16" value="2" style="margin-right:8px;"&gt;C. Tidak membahas isu apa pun&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq16" value="3" style="margin-right:8px;"&gt;D. Selalu ditulis oleh banyak orang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq16" value="4" style="margin-right:8px;"&gt;E. Tidak memiliki struktur&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;18. Langkah pertama dalam membandingkan isi dua teks adalah...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq17" value="0" style="margin-right:8px;"&gt;A. Menyusun simpulan langsung&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq17" value="1" style="margin-right:8px;"&gt;B. Membaca kedua teks secara menyeluruh&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq17" value="2" style="margin-right:8px;"&gt;C. Menilai kelebihan tanpa membaca&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq17" value="3" style="margin-right:8px;"&gt;D. Menghapus salah satu teks&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq17" value="4" style="margin-right:8px;"&gt;E. Menyajikan secara lisan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;19. Konjungsi “oleh karena itu” dalam teks ceramah maupun artikel opini umumnya berfungsi untuk...&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq18" value="0" style="margin-right:8px;"&gt;A. Menandai urutan waktu&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq18" value="1" style="margin-right:8px;"&gt;B. Menyimpulkan atau menegaskan alasan sebelumnya&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq18" value="2" style="margin-right:8px;"&gt;C. Menyatakan pertentangan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq18" value="3" style="margin-right:8px;"&gt;D. Menyatakan pilihan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq18" value="4" style="margin-right:8px;"&gt;E. Menambahkan informasi tanpa kaitan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#F5A623 !important; border:1px solid #F5A623; padding:2px 8px; margin-bottom:6px;"&gt;Sedang&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;20. Manakah pernyataan yang tepat tentang struktur kedua teks?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq19" value="0" style="margin-right:8px;"&gt;A. Ceramah tidak memiliki struktur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq19" value="1" style="margin-right:8px;"&gt;B. Keduanya sama-sama memiliki bagian pembuka, isi, dan penutup meski istilahnya berbeda&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq19" value="2" style="margin-right:8px;"&gt;C. Artikel opini tidak memiliki penutup&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq19" value="3" style="margin-right:8px;"&gt;D. Keduanya identik tanpa perbedaan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq19" value="4" style="margin-right:8px;"&gt;E. Ceramah tidak memiliki bagian isi&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#D94E2A !important; border:1px solid #D94E2A; padding:2px 8px; margin-bottom:6px;"&gt;Sulit&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;21. Sebuah ceramah dan artikel opini membahas isu yang sama, yaitu pentingnya menabung. Manakah simpulan yang paling tepat menggambarkan perbedaan pendekatan keduanya?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq20" value="0" style="margin-right:8px;"&gt;A. Ceramah cenderung mengandalkan kedekatan emosional dan ajakan langsung, sedangkan artikel opini mengandalkan data dan argumentasi tertulis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq20" value="1" style="margin-right:8px;"&gt;B. Keduanya menggunakan pendekatan yang sama persis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq20" value="2" style="margin-right:8px;"&gt;C. Ceramah tidak memiliki pendekatan persuasif&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq20" value="3" style="margin-right:8px;"&gt;D. Artikel opini tidak membahas data&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq20" value="4" style="margin-right:8px;"&gt;E. Keduanya tidak dapat dibandingkan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#D94E2A !important; border:1px solid #D94E2A; padding:2px 8px; margin-bottom:6px;"&gt;Sulit&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;22. Jika sebuah artikel opini ingin dijadikan bahan ceramah, penyesuaian apa yang paling penting dilakukan?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq21" value="0" style="margin-right:8px;"&gt;A. Menambahkan sapaan, kata ganti “kita”, dan kalimat ajakan langsung yang sesuai penyampaian lisan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq21" value="1" style="margin-right:8px;"&gt;B. Menghapus seluruh data pendukung&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq21" value="2" style="margin-right:8px;"&gt;C. Mengubahnya menjadi teks prosedur&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq21" value="3" style="margin-right:8px;"&gt;D. Menghapus argumen utama&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq21" value="4" style="margin-right:8px;"&gt;E. Tidak perlu penyesuaian apa pun&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#D94E2A !important; border:1px solid #D94E2A; padding:2px 8px; margin-bottom:6px;"&gt;Sulit&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;23. Manakah kondisi yang paling menunjukkan kekurangan teks ceramah dibandingkan artikel opini dalam konteks penyebaran informasi jangka panjang?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq22" value="0" style="margin-right:8px;"&gt;A. Ceramah dapat langsung direkam otomatis&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq22" value="1" style="margin-right:8px;"&gt;B. Pesan ceramah berisiko hilang/terlupakan jika tidak didokumentasikan, sementara artikel opini tetap tersimpan sebagai teks&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq22" value="2" style="margin-right:8px;"&gt;C. Ceramah selalu lebih singkat&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq22" value="3" style="margin-right:8px;"&gt;D. Artikel opini tidak dapat disimpan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq22" value="4" style="margin-right:8px;"&gt;E. Keduanya sama-sama tidak dapat didokumentasikan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#D94E2A !important; border:1px solid #D94E2A; padding:2px 8px; margin-bottom:6px;"&gt;Sulit&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;24. Seorang penceramah menyampaikan argumen tanpa didukung data, sementara sebuah artikel opini pada topik yang sama menyertakan data resmi. Berdasarkan hal tersebut, manakah kelemahan yang paling menonjol dari ceramah tersebut?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq23" value="0" style="margin-right:8px;"&gt;A. Kurangnya penguat argumen berupa fakta/data yang dapat diverifikasi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq23" value="1" style="margin-right:8px;"&gt;B. Ceramah tersebut terlalu panjang&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq23" value="2" style="margin-right:8px;"&gt;C. Ceramah tersebut tidak memiliki penutup&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq23" value="3" style="margin-right:8px;"&gt;D. Ceramah tersebut tidak memiliki pembukaan&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq23" value="4" style="margin-right:8px;"&gt;E. Ceramah tersebut tidak disampaikan secara lisan&lt;/label&gt;
      &lt;/div&gt;
      &lt;div style="margin-bottom:20px; padding-bottom:16px; border-bottom:1px dashed rgba(234,242,245,0.16);"&gt;
        &lt;span class="mono" style="display:inline-block; font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase; color:#D94E2A !important; border:1px solid #D94E2A; padding:2px 8px; margin-bottom:6px;"&gt;Sulit&lt;/span&gt;
        &lt;p style="color:#EAF2F5 !important; font-weight:600; margin:6px 0 8px;"&gt;25. Manakah simpulan paling tepat tentang kapan sebaiknya menggunakan teks ceramah dibandingkan artikel opini untuk menyampaikan suatu pesan?&lt;/p&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq24" value="0" style="margin-right:8px;"&gt;A. Ceramah lebih tepat digunakan ketika ingin membangun kedekatan emosional secara langsung dengan audiens dalam suatu forum tertentu&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq24" value="1" style="margin-right:8px;"&gt;B. Artikel opini selalu lebih baik dalam segala situasi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq24" value="2" style="margin-right:8px;"&gt;C. Ceramah tidak pernah digunakan untuk pesan penting&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq24" value="3" style="margin-right:8px;"&gt;D. Keduanya tidak memiliki perbedaan fungsi&lt;/label&gt;
        &lt;label style="display:block; color:#DDE7EC !important; font-size:0.92rem; margin:4px 0; cursor:pointer;"&gt;&lt;input type="radio" name="cq24" value="4" style="margin-right:8px;"&gt;E. Artikel opini tidak dapat memengaruhi pembaca&lt;/label&gt;
      &lt;/div&gt;

      &lt;div style="margin-top:24px; display:flex; align-items:center; gap:16px; flex-wrap:wrap;"&gt;
        &lt;button id="cq-submit" type="button" style="background:var(--amber); color:var(--blueprint-deep); border:none; padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Kirim Jawaban&lt;/button&gt;
        &lt;span id="cq-status" class="mono" style="font-size:0.85rem; color:var(--muted);"&gt;&lt;/span&gt;
      &lt;/div&gt;

      &lt;div id="cq-result" style="display:none; margin-top:20px; padding:18px 20px; border:1px solid var(--amber); background:rgba(245,166,35,0.08);"&gt;
        &lt;p style="margin:0; font-size:1.05rem;"&gt;Skor kamu: &lt;b id="cq-score" style="color:var(--amber); font-size:1.3rem;"&gt;&lt;/b&gt; / 100&lt;/p&gt;
        &lt;p id="cq-detail" style="margin:8px 0 0; font-size:0.9rem; color:var(--muted);"&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/CERAMAH-BANDING — Fokus Tujuan Pembelajaran, Materi Inti, Tugas Kelompok &amp;amp; Tugas Individu, 1× pertemuan (3 JP)&lt;/div&gt;
  &lt;p style="margin:0;"&gt;Referensi: Kurikulum Merdeka — Capaian Pembelajaran Bahasa Indonesia Fase F (Elemen Membaca dan Memirsa); Buku Teks Bahasa Indonesia Kelas XI SMK/MAK Kemendikbudristek.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
(function(){
  var CQ_ENDPOINT = "https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
  var CQ_MATERI = "Membandingkan Isi Teks Ceramah dengan Teks Lain";
  var correctAnswers = [1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0];

  document.getElementById("cq-submit").addEventListener("click", function(){
    var absen = document.getElementById("cq-absen").value.trim();
    var nama = document.getElementById("cq-nama").value.trim();
    var statusEl = document.getElementById("cq-status");

    if(!absen || !nama){
      statusEl.textContent = "Isi No. Absen dan Nama terlebih dahulu.";
      statusEl.style.setProperty("color", "#F5A623", "important");
      return;
    }

    var belumDijawab = [];
    var benar = 0;
    correctAnswers.forEach(function(correct, idx){
      var checked = document.querySelector('input[name="cq' + idx + '"]:checked');
      if(!checked){
        belumDijawab.push(idx+1);
      } else if(parseInt(checked.value,10) === correct){
        benar++;
      }
    });

    if(belumDijawab.length &gt; 0){
      statusEl.textContent = "Masih ada " + belumDijawab.length + " soal yang belum dijawab (nomor " + belumDijawab.slice(0,5).join(", ") + (belumDijawab.length&gt;5? ", ...":"") + ").";
      statusEl.style.setProperty("color", "#F5A623", "important");
      return;
    }

    var skor = benar * 4;
    document.getElementById("cq-score").textContent = skor;
    document.getElementById("cq-detail").textContent = "Menjawab benar " + benar + " dari 25 pertanyaan.";
    document.getElementById("cq-result").style.display = "block";
    statusEl.textContent = "Mengirim hasil...";
    statusEl.style.setProperty("color", "#9FB4C2", "important");

    var payload = new URLSearchParams();
    payload.append("noAbsen", absen);
    payload.append("nama", nama);
    payload.append("skor", skor);
    payload.append("materi", CQ_MATERI);
    payload.append("waktu", new Date().toISOString());

    fetch(CQ_ENDPOINT, {
      method: "POST",
      mode: "no-cors",
      headers: {"Content-Type":"application/x-www-form-urlencoded"},
      body: payload.toString()
    }).then(function(){
      statusEl.textContent = "Hasil berhasil dikirim ke guru.";
      statusEl.style.setProperty("color", "#6EC6CA", "important");
    }).catch(function(){
      statusEl.textContent = "Skor tercatat di halaman ini, namun pengiriman gagal. Tunjukkan skor ini ke guru.";
      statusEl.style.setProperty("color", "#F5A623", "important");
    });
  });
})();
&lt;/script&gt;
&lt;/div&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/membandingkan-isi-teks-ceramah-dengan.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-8515984388765739015</guid><pubDate>Fri, 18 Sep 2026 06:32:06 +0000</pubDate><atom:updated>2026-09-18T13:32:06.488+07:00</atom:updated><title>SERTIFIKAT KOMBEL</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Generator Sertifikat&lt;/title&gt;

&lt;link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@700&amp;display=swap" rel="stylesheet"&gt;

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;

&lt;style&gt;

body{
    font-family:Arial,sans-serif;
    background:#eef2f7;
    padding:20px;
    max-width:700px;
    margin:auto;
}

.card{
    background:white;
    padding:24px;
    border-radius:20px;
    box-shadow:0 10px 20px rgba(0,0,0,.1);
}

h2{
    text-align:center;
    color:#1e4668;
}

.confirm-box{
    background:#f0f6ff;
    border-left:5px solid #2c7da0;
    padding:16px;
    border-radius:16px;
    margin-bottom:20px;
    text-align:center;
}

.confirm-box p{
    margin-top:0;
    font-weight:bold;
    color:#1e4668;
}

.confirm-buttons{
    display:flex;
    gap:10px;
    margin-top:10px;
}

.confirm-buttons button{
    flex:1;
}

.yes-btn{
    background:#1f8a4c;
}

.yes-btn:hover{
    background:#166638;
}

.no-btn{
    background:#c0392b;
}

.no-btn:hover{
    background:#962d22;
}

input,select,button{
    width:100%;
    padding:12px;
    margin-top:10px;
    border-radius:12px;
    border:1px solid #ccc;
    font-size:16px;
}

button{
    background:#2c7da0;
    color:white;
    border:none;
    cursor:pointer;
    font-weight:bold;
}

button:hover{
    background:#1f5e7a;
}

button:disabled{
    background:#94a3b8;
    cursor:not-allowed;
}

input:disabled,
select:disabled{
    background:#f1f5f9;
}

#preview{
    width:100%;
    border-radius:12px;
    margin-top:10px;
}

.loading{
    display:none;
    margin-top:15px;
    color:#1e4668;
    font-weight:bold;
    text-align:center;
}

.status{
    margin-top:12px;
    font-weight:bold;
}

.success{
    color:#1f8a4c;
}

.error{
    color:#c0392b;
}

&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="card"&gt;

&lt;h2&gt;&#128220; Generator Sertifikat&lt;/h2&gt;

&lt;!-- ================= KONFIRMASI ================= --&gt;

&lt;div class="confirm-box"&gt;

&lt;p&gt;&#128269; Apakah Anda mengikuti pelatihan?&lt;/p&gt;

&lt;div class="confirm-buttons"&gt;

&lt;button class="yes-btn" onclick="confirmIkut()"&gt;
✅ YA, IKUT
&lt;/button&gt;

&lt;button class="no-btn" onclick="confirmTidakIkut()"&gt;
❌ TIDAK
&lt;/button&gt;

&lt;/div&gt;

&lt;div id="statusKonfirmasi" class="status"&gt;&lt;/div&gt;

&lt;/div&gt;

&lt;!-- ================= FORM ================= --&gt;

&lt;label&gt;Nama Lengkap&lt;/label&gt;

&lt;input
type="text"
id="nama"
placeholder="Contoh: Ahmad Fauzi"
disabled&gt;

&lt;label&gt;Peran&lt;/label&gt;

&lt;select id="peran" disabled&gt;
    &lt;option&gt;Peserta&lt;/option&gt;
    &lt;option&gt;Narasumber&lt;/option&gt;
&lt;/select&gt;

&lt;img
id="preview"
src="https://lh3.googleusercontent.com/d/1L7TuHgvpP30JNm5lwNSrqSLsTPDxdDbH"
alt="Template"&gt;

&lt;button
id="downloadBtn"
onclick="generatePDF()"
disabled&gt;

⬇️ UNDUH PDF

&lt;/button&gt;

&lt;div class="loading" id="loading"&gt;
Memproses PDF...
&lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;

// ================= TEMPLATE (DIUBAH) =================

const FILE_ID =
'1L7TuHgvpP30JNm5lwNSrqSLsTPDxdDbH';  // &lt;-- ID baru

const TEMPLATE_URL =
`https://lh3.googleusercontent.com/d/${FILE_ID}`;

// ================= UTIL =================

function showLoading(show){

    document.getElementById('loading').style.display =
        show ? 'block' : 'none';

}

function getRomanMonth(monthIndex){

    const romawi = [
        "I","II","III","IV","V","VI",
        "VII","VIII","IX","X","XI","XII"
    ];

    return romawi[monthIndex];

}

function capitalizeEachWord(text){

    return text
        .toLowerCase()
        .replace(/\b\w/g,c=&gt;c.toUpperCase());

}

// ================= KONFIRMASI =================

function confirmIkut(){

    document.getElementById('nama').disabled = false;

    document.getElementById('peran').disabled = false;

    document.getElementById('downloadBtn').disabled = false;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="success"&gt;✅ Konfirmasi diterima. Anda dapat mengunduh sertifikat.&lt;/span&gt;';

}

function confirmTidakIkut(){

    document.getElementById('nama').disabled = true;

    document.getElementById('peran').disabled = true;

    document.getElementById('downloadBtn').disabled = true;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="error"&gt;❌ Anda tidak dapat mengunduh sertifikat.&lt;/span&gt;';

}

// ================= LOAD IMAGE =================

function loadImage(url){

    return new Promise((resolve,reject)=&gt;{

        const img = new Image();

        img.crossOrigin = "anonymous";

        img.onload = ()=&gt;resolve(img);

        img.onerror = ()=&gt;{

            reject(
                new Error(
                    'Template Google Drive gagal dimuat'
                )
            );

        };

        img.src = url + '?t=' + Date.now();

    });

}

// ================= NOMOR =================

async function generateNomor(){

    const now = new Date();

    const bulan =
        getRomanMonth(now.getMonth());

    const tahun =
        now.getFullYear();

    const nomor =
        Math.floor(Math.random()*900)+100;

    return `${nomor}/K/${bulan}/${tahun}`;

}

// ================= PDF =================

async function generatePDF(){

    const namaInput =
        document.getElementById('nama');

    const peranSelect =
        document.getElementById('peran');

    let nama =
        namaInput.value.trim();

    const peran =
        peranSelect.value;

    if(!nama){

        alert('Nama wajib diisi');

        return;

    }

    nama =
        capitalizeEachWord(nama);

    showLoading(true);

    try{

        await document.fonts.ready;

        const img =
            await loadImage(TEMPLATE_URL);

        const canvas =
            document.createElement('canvas');

        canvas.width = img.width;
        canvas.height = img.height;

        const ctx =
            canvas.getContext('2d');

        ctx.drawImage(
            img,
            0,
            0,
            canvas.width,
            canvas.height
        );

        // ================= POSISI =================

        const nomorY =
            canvas.height * 0.235;

        const namaY =
            canvas.height * 0.3990;

        const peranY =
            canvas.height * 0.5420;

        // ================= STYLE =================

        ctx.textAlign = 'center';

        ctx.textBaseline = 'middle';

        ctx.fillStyle = '#000';

        // nomor
        const nomorSertifikat =
            await generateNomor();

        ctx.font =
            `bold ${canvas.width*0.0125}px Montserrat`;

        ctx.fillText(
            nomorSertifikat,
            canvas.width/2,
            nomorY
        );

        // nama auto shrink
        let fontSize =
            canvas.width * 0.031;

        ctx.font =
            `700 ${fontSize}px Montserrat`;

        let textWidth =
            ctx.measureText(nama).width;

        const maxWidth =
            canvas.width - 180;

        while(
            textWidth &gt; maxWidth &amp;&amp;
            fontSize &gt; 20
        ){

            fontSize -= 2;

            ctx.font =
                `700 ${fontSize}px Montserrat`;

            textWidth =
                ctx.measureText(nama).width;

        }

        ctx.fillText(
            nama,
            canvas.width/2,
            namaY
        );

        // peran
        ctx.font =
            `bold ${canvas.width*0.024}px Montserrat`;

        ctx.fillText(
            peran,
            canvas.width/2,
            peranY
        );

        // ================= PDF =================

        const imageData =
            canvas.toDataURL(
                'image/jpeg',
                1.0
            );

        const { jsPDF } =
            window.jspdf;

        const pdf =
            new jsPDF({

                orientation:
                    canvas.width &gt; canvas.height
                    ? 'landscape'
                    : 'portrait',

                unit:'px',

                format:[
                    canvas.width,
                    canvas.height
                ]

            });

        pdf.addImage(
            imageData,
            'JPEG',
            0,
            0,
            canvas.width,
            canvas.height
        );

        const safeName =
            nama.replace(/[^a-z0-9]/gi,'_');

        pdf.save(
            `Sertifikat_${safeName}.pdf`
        );

    }catch(error){

        console.error(error);

        alert(
            '❌ Gagal membuat PDF\n\n' +
            error.message +
            '\n\nPastikan file Google Drive bersifat PUBLIC.'
        );

    }finally{

        showLoading(false);

    }

}

&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/sertifikat-kombel_01887761678.html</link><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://lh3.googleusercontent.com/d/1L7TuHgvpP30JNm5lwNSrqSLsTPDxdDbH=s72-c" width="72"/><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-1627330734637838139</guid><pubDate>Thu, 17 Sep 2026 03:15:22 +0000</pubDate><atom:updated>2026-09-17T10:25:44.751+07:00</atom:updated><title>Integrasi Dokumen dan Otomasi Pengolah Kata</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Integrasi Dokumen dan Otomasi Pengolah Kata - Informatika Kelas X&lt;/title&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&amp;family=IBM+Plex+Sans:wght@400;500;600;700&amp;family=Tinos:ital,wght@0,400;0,700;1,400&amp;display=swap" rel="stylesheet"&gt;
&lt;style&gt;
  :root{
    --word: #185ABD;
    --word-dark: #103F86;
    --ribbon: #F1F5FA;
    --paper: #FFFFFF;
    --ink: #1F2430;
    --ink-soft: #57606F;
    --line: #DDE1E6;
    --amber: #C08A1E;
    --green: #1E7A46;
    --red: #C43E2E;
    --radius: 6px;
  }
  *{box-sizing:border-box; margin:0; padding:0;}
  html{scroll-behavior:smooth;}
  body{ font-family:'IBM Plex Sans', sans-serif; background:#FFFFFF; color: var(--ink); line-height:1.6; }
  .mono{font-family:'IBM Plex Mono', monospace;}

  .page-shell{
    max-width: 1140px; margin: 16px auto; background:#FFFFFF; border-radius:14px;
    box-shadow: 0 8px 32px rgba(20,30,50,0.12), 0 1px 3px rgba(20,30,50,0.08); border: 1px solid var(--line);
  }

  /* ===== Hero ===== */
  .ribbon-hero{ background: var(--ribbon); border-bottom: 3px solid var(--word); border-radius:14px 14px 0 0; overflow:hidden; }
  .ribbon-strip{ display:flex; gap:6px; padding: 10px 5% 0; flex-wrap:wrap; }
  .ribbon-tab{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; padding:6px 12px; background:#FFFFFF; border:1px solid var(--line); border-bottom:none; border-radius:4px 4px 0 0; color: var(--ink-soft); }
  .ribbon-tab.active{ color: var(--word); font-weight:700; border-color: var(--word); }
  .hero-body{ padding: 28px 5% 40px; background:#FFFFFF; }
  .eyebrow{ font-family:'IBM Plex Mono', monospace; font-size:0.76rem; letter-spacing:0.14em; text-transform:uppercase; color: var(--word); display:flex; align-items:center; gap:10px; margin-bottom:18px; }
  .eyebrow::before{ content:""; width:10px; height:10px; border:1.5px solid var(--word); border-radius:2px; display:inline-block; }
  h1.title{ font-family:'IBM Plex Mono', monospace; font-weight:700; font-size: clamp(1.7rem, 4vw, 2.7rem); line-height:1.18; color: var(--word-dark); max-width:24ch; }
  .hero-sub{ margin-top:16px; max-width: 70ch; font-size:1.02rem; color: var(--ink-soft); }
  .meta-strip{ margin-top:30px; display:grid; grid-template-columns: repeat(auto-fit,minmax(160px,1fr)); gap:1px; background: var(--line); border:1px solid var(--line); }
  .meta-cell{ background: var(--ribbon); padding:12px 14px; }
  .meta-cell .k{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--word); }
  .meta-cell .v{ margin-top:5px; font-size:0.9rem; font-weight:600; color: var(--ink); }

  /* ===== Content ===== */
  main{ background: var(--paper); }
  .section{ padding: 36px 5%; border-bottom:1px solid #ECEFF3; }
  .section:last-of-type{border-bottom:none;}
  .section-tag{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; letter-spacing:0.14em; text-transform:uppercase; color: var(--ink-soft); display:flex; align-items:center; gap:10px; margin-bottom:10px; }
  .section-tag .num{ color:#fff; background: linear-gradient(135deg, var(--word), var(--word-dark)); padding:3px 8px; border-radius:3px; font-weight:600; }
  h2.section-title{ font-family:'IBM Plex Mono', monospace; font-size: clamp(1.25rem, 2.2vw, 1.7rem); font-weight:700; color: var(--word-dark); margin-bottom: 18px; }
  h3.sub-title{ font-family:'IBM Plex Mono', monospace; font-size:1.02rem; font-weight:600; color: var(--word-dark); margin: 22px 0 10px; display:flex; align-items:center; gap:10px; }
  h3.sub-title::before{ content:""; width:8px;height:8px; background: var(--amber); display:inline-block; border-radius:1px; }
  p{ color:#33414F; max-width: 78ch; margin-bottom: 12px;}
  strong{color:var(--word-dark);}
  ul, ol{ margin: 6px 0 16px 1.3em; max-width: 78ch;}
  li{margin-bottom:7px; color:#33414F;}

  .callout{ border-left: 3px solid var(--word); background:#EBF2FB; padding: 14px 18px; border-radius: 0 6px 6px 0; margin: 16px 0; max-width: 78ch; }
  .callout .lbl{ font-family:'IBM Plex Mono', monospace; font-size:0.68rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--word-dark); font-weight:700; margin-bottom:6px; }
  .callout.warn{ border-left-color: var(--red); background:#FBEAE7; }
  .callout.warn .lbl{ color:#9B3324; }
  .callout.ok{ border-left-color: var(--green); background:#EAF6EF; }
  .callout.ok .lbl{ color:#0F5C30; }

  .card-grid{ display:grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 14px; margin: 16px 0 6px; }
  .card{ background: #FFFFFF; border: 1.5px solid var(--line); border-radius: var(--radius); padding: 16px 18px; box-shadow: 0 2px 8px rgba(20,30,50,0.05); }
  .card .tag{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.1em; text-transform:uppercase; color:#fff; background: var(--word); padding:3px 8px; border-radius:3px; display:inline-block; margin-bottom:10px; }
  .card h4{ font-family:'IBM Plex Mono', monospace; font-size:0.94rem; margin-bottom:6px; color:var(--word-dark); }
  .card p{font-size:0.87rem; margin-bottom:0;}

  .table-scroll{ overflow-x:auto; margin:16px 0; }
  table.compare{ width:100%; border-collapse:collapse; font-size:0.88rem; min-width:480px; }
  table.compare th, table.compare td{ border:1px solid var(--line); padding:10px 12px; text-align:left; vertical-align:top; }
  table.compare th{ background: var(--word); color:#fff; font-family:'IBM Plex Mono', monospace; font-size:0.72rem; letter-spacing:0.04em; text-transform:uppercase; }
  table.compare tr:nth-child(even) td{ background: var(--ribbon); }

  .summary-box{ background: var(--word-dark); color: #fff; border-radius: 8px; padding: 22px 24px; margin-top: 8px; }
  .summary-box h4{ font-family:'IBM Plex Mono', monospace; color:#FCE29A; font-size:0.82rem; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:12px; }
  .summary-box ul{ margin:0 0 0 1.1em; max-width:none; }
  .summary-box li{ color:#E6ECF7; margin-bottom:7px; }

  /* ===== Evaluasi ===== */
  #evaluasi{ background: linear-gradient(180deg, #EEF4FF 0%, #FFFFFF 55%); border-top:3px solid var(--word); }

  .submit-panel{ background: var(--ribbon); border:1px solid var(--line); border-radius:10px; padding:22px 20px; margin-bottom:22px; }
  .student-info{ display:grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap:14px; margin-top:12px; }
  .field label{ display:block; font-family:'IBM Plex Mono', monospace; font-size:0.66rem; letter-spacing:0.06em; text-transform:uppercase; color: var(--word-dark); margin-bottom:6px; font-weight:700; }
  .field input, .field select{ width:100%; padding:9px 11px; border-radius:5px; border:1px solid var(--line); background:#FFFFFF; color: var(--ink); font-family:'IBM Plex Sans', sans-serif; font-size:0.9rem; }
  .field select{cursor:pointer;}
  .field input:focus, .field select:focus{ outline:2px solid var(--word); }

  .primary-btn{
    font-family:'IBM Plex Mono', monospace; font-weight:700; font-size:0.88rem; padding:13px 24px; border-radius:8px; border:none; cursor:pointer;
    background: linear-gradient(135deg, var(--word), var(--word-dark)); color:#fff; box-shadow: 0 4px 14px rgba(16,63,134,0.3);
    transition: transform .15s, box-shadow .15s; margin-top:6px;
  }
  .primary-btn:hover{ transform: translateY(-2px); box-shadow: 0 6px 18px rgba(16,63,134,0.4); }
  .primary-btn.ghost{ background:#fff; color:var(--word-dark); border:1.5px solid var(--word); box-shadow:none; }
  .primary-btn.ghost:hover{ background:#EEF4FF; }
  .primary-btn:disabled{ opacity:0.55; cursor:not-allowed; transform:none; }
  #evalMsg, #praktikMsg{ font-family:'IBM Plex Mono', monospace; font-size:0.8rem; color:#8A6A1E; margin-left:10px; }

  .stage-head{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:26px 0 12px; }
  .stage-badge{ font-family:'IBM Plex Mono', monospace; font-size:0.66rem; letter-spacing:0.08em; text-transform:uppercase; color:#fff; background: var(--word-dark); padding:5px 12px; border-radius:20px; font-weight:700; }
  .stage-head h3{ font-family:'IBM Plex Mono', monospace; font-size:1.05rem; color: var(--word-dark); }

  .attempt-strip{ display:flex; gap:10px; flex-wrap:wrap; margin:14px 0; }
  .attempt-pill{
    font-family:'IBM Plex Mono', monospace; font-size:0.74rem; font-weight:600; padding:9px 16px; border-radius:20px;
    border:1.5px solid var(--line); background:#fff; color: var(--ink-soft); box-shadow: 0 1px 4px rgba(20,30,50,0.06);
  }
  .attempt-pill.pass{ border-color: var(--green); background:#DFF6E7; color:#0F5C30; }
  .attempt-pill.fail{ border-color: var(--red); background:#FBDEDA; color:#8A2A1C; }
  .attempt-pill.current{ box-shadow: 0 0 0 3px rgba(24,90,189,0.2); background:#EEF4FF; border-color:var(--word); color:var(--word-dark); }

  .game-panel{ background:#FFFFFF; border:1px solid var(--line); border-radius:10px; padding:22px; box-shadow: 0 2px 10px rgba(20,30,50,0.05); }
  .game-progress{ font-family:'IBM Plex Mono', monospace; font-size:0.72rem; color: var(--ink-soft); margin-bottom:14px; display:flex; justify-content:space-between; flex-wrap:wrap; gap:8px; }
  .q-instruksi{ font-family:'IBM Plex Mono', monospace; font-size:0.68rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--word); font-weight:700; margin-bottom:8px; }
  .q-text{ font-size:1.05rem; font-weight:600; color: var(--ink); margin-bottom:18px; min-height:52px; }
  .drop-zone{
    border:3px dashed #9DBFE8; border-radius:10px; min-height:88px; display:flex; align-items:center; justify-content:center;
    background:#FAFCFF; color:#8A99AC; font-family:'IBM Plex Mono', monospace; font-size:0.82rem; margin-bottom:18px; transition: all .2s; padding:10px; text-align:center;
  }
  .drop-zone.drag-over{ background:#E2EDFB; border-color: var(--word); }
  .drop-zone.correct{ background:#E4F6EB; border-color: var(--green); border-style:solid; color:#0F5C30; font-weight:700; }
  .drop-zone.wrong{ background:#FBE7E4; border-color: var(--red); border-style:solid; color:#8A2A1C; font-weight:700; }
  .opt-pool{ display:flex; flex-wrap:wrap; gap:9px; justify-content:center; background:var(--ribbon); border:1px solid var(--line); border-radius:10px; padding:14px; }
  .opt-chip{
    font-family:'IBM Plex Mono', monospace; font-size:0.8rem; background: linear-gradient(135deg, var(--word), var(--word-dark)); color:#fff;
    padding:9px 15px; border-radius:7px; cursor:grab; box-shadow:0 2px 6px rgba(24,90,189,0.28); border:none;
  }
  .opt-chip:hover{ filter:brightness(1.1); }
  .opt-chip.dragging{ opacity:0.5; }

  /* ===== Editor dokumen F4 ===== */
  .doc-toolbar{
    position: sticky; top:10px; z-index:20; background:#FFFFFF; border:1.5px solid var(--word);
    border-radius:8px; padding:10px 12px; display:flex; flex-wrap:wrap; gap:6px; align-items:center;
    box-shadow: 0 6px 20px rgba(20,90,189,0.18); margin-bottom:16px;
  }
  .doc-toolbar button{ font-family:'IBM Plex Sans', sans-serif; font-size:0.83rem; background:#F3F5F8; border:1px solid var(--line); border-radius:4px; padding:6px 10px; cursor:pointer; color: var(--ink); }
  .doc-toolbar button:hover{ background:#E4EAF2; }
  .doc-toolbar button.b{font-weight:700;} .doc-toolbar button.i{font-style:italic;} .doc-toolbar button.u{text-decoration:underline;}
  .doc-toolbar select{ font-family:'IBM Plex Sans', sans-serif; font-size:0.8rem; padding:6px 8px; border-radius:4px; border:1px solid var(--line); background:#F3F5F8; }
  .doc-toolbar .sep{ width:1px; height:22px; background:var(--line); margin:0 4px; }
  .doc-toolbar .hint{ font-family:'IBM Plex Mono', monospace; font-size:0.68rem; color: var(--ink-soft); margin-left:auto; }

  .doc-stage{ background:#E7EBF0; border:1px solid var(--line); border-radius:10px; padding:18px; overflow-x:auto; }
  .f4-page{
    width: 215mm; min-height: 330mm; padding: 3cm 3cm 3cm 4cm;
    margin: 0 auto 22px; background:#fff; box-shadow: 0 4px 16px rgba(0,0,0,0.14);
    font-family:'Tinos','Times New Roman', serif; font-size: 12pt; line-height: 1.5; outline:none; position:relative;
  }
  .f4-page::after{
    content: attr(data-pagelabel); position:absolute; top:8px; right:14px;
    font-family:'IBM Plex Mono', monospace; font-size:8pt; color:#B6BDC6; letter-spacing:0.08em;
  }
  .f4-page:focus{ box-shadow: 0 0 0 3px rgba(24,90,189,0.25), 0 4px 16px rgba(0,0,0,0.14); }
  .placeholder-note{ color:#9AA3AE; font-style:italic; }
  .doc-meta{ font-family:'IBM Plex Mono', monospace; font-size:0.72rem; color: var(--ink-soft); margin-top:10px; display:flex; justify-content:space-between; flex-wrap:wrap; gap:10px; }
  .doc-meta .flag{ color: var(--red); font-weight:700; display:none; }
  .doc-meta .flag.show{ display:inline; }

  .checklist{ background:#fff; border:1px solid var(--line); border-radius:8px; padding:16px 18px; margin-top:14px; }
  .checklist h5{ font-family:'IBM Plex Mono', monospace; font-size:0.8rem; color:var(--word-dark); margin-bottom:10px; }
  .checklist ul{ margin:0 0 0 1.1em; }
  .checklist li{ font-size:0.86rem; margin-bottom:5px; }

  /* ===== Lampiran contoh laporan (proteksi salin) ===== */
  .lampiran-wrap{ background:#FBF9F2; border:1.5px solid var(--amber); border-radius:10px; padding:18px 20px; margin:18px 0; }
  .lampiran-head{ display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; margin-bottom:8px; }
  .lampiran-eyebrow{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.1em; text-transform:uppercase; color:#8A6A1E; font-weight:700; }
  .lampiran-head h4{ font-family:'IBM Plex Mono', monospace; font-size:1rem; color:#6B5210; margin-top:4px; }
  .lock-badge{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.06em; text-transform:uppercase; background:#8A5A0A; color:#fff; padding:5px 11px; border-radius:20px; font-weight:700; white-space:nowrap; }
  .lampiran-note{ font-size:0.86rem; color:#5E5238; max-width:none; margin-bottom:14px; }
  .lampiran-list{ display:flex; flex-direction:column; gap:8px; }
  .lampiran-item{ background:#fff; border:1px solid #E2D9BE; border-radius:7px; overflow:hidden; }
  .lampiran-toggle{
    width:100%; text-align:left; background:#FFFDF6; border:none; cursor:pointer; padding:11px 15px;
    font-family:'IBM Plex Mono', monospace; font-size:0.85rem; color:#6B5210; font-weight:600;
    display:flex; justify-content:space-between; align-items:center; gap:10px;
  }
  .lampiran-toggle:hover{ background:#FBF3DF; }
  .lampiran-toggle .chev{ font-size:0.75rem; color:#A88A3E; }
  .lampiran-body{
    display:none; padding:14px 16px; border-top:1px dashed #E2D9BE; background:#fff;
    font-family:'Tinos','Times New Roman', serif; font-size:0.95rem; color:#332C1E; white-space:pre-wrap; line-height:1.55;
    user-select:none; -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none;
    max-height:420px; overflow-y:auto;
  }
  .lampiran-body.open{ display:block; }

  .result-panel{ display:none; margin-top:18px; background:#FFFFFF; border:1.5px solid var(--green); border-radius:8px; padding:20px 22px; }
  .result-panel .score{ font-family:'IBM Plex Mono', monospace; font-size:2.2rem; font-weight:700; color: var(--green); }
  .result-panel .score-lbl{ font-family:'IBM Plex Mono', monospace; font-size:0.68rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--ink-soft); }
  .result-panel p{ color:#33414F; font-size:0.87rem; margin-top:8px; }

  .closing-task{ background:#FFF6E6; border:1.5px solid var(--amber); border-radius:8px; padding:18px 20px; margin-bottom:14px; }
  .closing-task .lbl{ font-family:'IBM Plex Mono', monospace; font-size:0.68rem; letter-spacing:0.1em; text-transform:uppercase; color:#8A5A0A; font-weight:700; margin-bottom:8px; }

  footer{ background: var(--ribbon); color: var(--ink-soft); text-align:center; padding: 22px 5% 30px; font-family:'IBM Plex Mono', monospace; font-size:0.72rem; border-top:1px solid var(--line); border-radius:0 0 14px 14px; }

  /* ===== Area Cetak PDF F4 ===== */
  .print-area{ display:none; }
  .pd-kop{ text-align:center; border-bottom:3px double #000; padding-bottom:10px; margin-bottom:16px; }
  .pd-kop h1{ font-size:15pt; margin-bottom:4px; }
  .pd-kop p{ font-size:9.5pt; margin:0; max-width:none; }
  .pd-meta-table{ width:100%; border-collapse:collapse; margin-bottom:14px; font-size:10pt; }
  .pd-meta-table td{ padding:3px 6px; vertical-align:top; }
  .pd-meta-table td.lbl{ width:118px; font-weight:600; }
  .pd-section-title{ font-size:11pt; font-weight:700; margin:16px 0 8px; border-bottom:1.5px solid #000; padding-bottom:3px; }
  .pd-score-table{ width:100%; border-collapse:collapse; font-size:10pt; margin-bottom:10px; }
  .pd-score-table th, .pd-score-table td{ border:1px solid #000; padding:5px 8px; text-align:left; }
  .pd-score-table th{ background:#EEE; }
  .pd-answer-item{ border:1px solid #999; border-radius:3px; padding:8px 10px; margin-bottom:8px; page-break-inside: avoid; }
  .pd-answer-item .pd-anum{ font-size:9pt; font-weight:700; color:#222; }
  .pd-answer-item .pd-atext{ font-size:10pt; white-space:pre-wrap; margin-top:4px; font-family:'Tinos','Times New Roman', serif; }
  .pd-verdict{ display:inline-block; font-size:8pt; font-weight:700; padding:1px 7px; border-radius:3px; margin-left:8px; border:1px solid #000; }
  .pd-verdict.ok{ background:#E4F5E9; }
  .pd-verdict.no{ background:#FBE4E1; }
  .pd-eval-table{ width:100%; border-collapse:collapse; font-size:9pt; margin-bottom:14px; }
  .pd-eval-table th, .pd-eval-table td{ border:1px solid #999; padding:5px 7px; text-align:left; vertical-align:top; }
  .pd-eval-table th{ background:#EEE; font-size:8.5pt; }
  .pd-signature{ display:flex; justify-content:space-between; margin-top:36px; font-size:10pt; page-break-inside:avoid; }
  .pd-signature div{ text-align:center; width:45%; }
  .pd-signature .line{ margin-top:50px; border-top:1px solid #000; padding-top:4px; }
  .pd-footnote{ font-size:8.5pt; color:#444; margin-top:18px; text-align:center; }

  @media print{
    html, body{ height:auto !important; }
    body *{ visibility:hidden; }
    .print-area, .print-area *{ visibility:visible; }
    .print-area{
      display:block !important; visibility:visible; position:absolute; left:0; top:0; width:100%;
      color:#000; font-family:'IBM Plex Sans', sans-serif; background:#fff; z-index:99999;
    }
    @page{ size: 215mm 330mm; margin: 15mm; }
  }

  @media (max-width:700px){ .hero-body{padding:22px 5% 32px;} .section{padding:26px 5%;} .f4-page{ padding:2cm 1.5cm; } }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="page-shell"&gt;
&lt;div class="ribbon-hero"&gt;
  &lt;div class="ribbon-strip"&gt;
    &lt;div class="ribbon-tab active"&gt;Beranda&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Sisipkan&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Tata Letak&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Referensi&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Surat&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Tinjau&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="hero-body"&gt;
    &lt;div class="eyebrow"&gt;Informatika - Fase E - Kelas X SMK&lt;/div&gt;
    &lt;h1 class="title"&gt;Integrasi Dokumen dan Otomasi Pengolah Kata&lt;/h1&gt;
    &lt;p class="hero-sub"&gt;Materi ini membahas cara menggabungkan berbagai objek (tabel, gambar, bagan, grafik) ke dalam satu dokumen, memanfaatkan template dan formulir, serta menerapkan fitur otomatis pengolah kata untuk menyusun dokumen panjang secara efisien. Studi kasusnya: Laporan Praktik Kerja Lapangan (PKL) sesuai pedoman SMK Negeri 1 Tuntang Tahun 2025/2026.&lt;/p&gt;
    &lt;div class="meta-strip"&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Elemen CP&lt;/div&gt;&lt;div class="v"&gt;Integrasi &amp;amp; Otomasi Dokumen&lt;/div&gt;&lt;/div&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK Kelas X&lt;/div&gt;&lt;/div&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;1 Pertemuan - 4 JP (180 menit)&lt;/div&gt;&lt;/div&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Produk&lt;/div&gt;&lt;div class="v"&gt;Draf Laporan PKL (F4)&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;main&gt;

  &lt;!-- ===================== TUJUAN ===================== --&gt;
  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;00&lt;/span&gt; Capaian &amp;amp; Tujuan Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Ke Mana Materi Ini Menuju&lt;/h2&gt;
    &lt;h3 class="sub-title"&gt;Tujuan Pembelajaran&lt;/h3&gt;
    &lt;p&gt;Murid mampu membuat laporan kerja lapangan yang mengintegrasikan berbagai objek, menggunakan template, formulir, serta fitur otomatis untuk meningkatkan efisiensi pembuatan dokumen.&lt;/p&gt;

    &lt;h3 class="sub-title"&gt;Indikator Ketercapaian&lt;/h3&gt;
    &lt;ol&gt;
      &lt;li&gt;Menjelaskan konsep integrasi objek (tabel, gambar, bagan, grafik) dalam satu dokumen.&lt;/li&gt;
      &lt;li&gt;Menerapkan template dan style untuk menjaga konsistensi format dokumen panjang.&lt;/li&gt;
      &lt;li&gt;Membuat formulir/isian terstruktur (biodata, jurnal kegiatan, lembar konsultasi) dengan tabel.&lt;/li&gt;
      &lt;li&gt;Menggunakan fitur otomatis: daftar isi otomatis, penomoran halaman bertingkat, caption, sitasi, dan mail merge.&lt;/li&gt;
      &lt;li&gt;Menerapkan tata tulis dan sistematika Laporan PKL pada dokumen nyata.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h3 class="sub-title"&gt;Rincian Kegiatan (4 JP x 45 menit)&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Tahap&lt;/th&gt;&lt;th&gt;Kegiatan&lt;/th&gt;&lt;th&gt;Waktu&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pendahuluan&lt;/td&gt;&lt;td&gt;Apersepsi ("Bagaimana laporan 20 halaman bisa rapi tanpa mengetik ulang daftar isi berkali-kali?"), penyampaian tujuan&lt;/td&gt;&lt;td&gt;15 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Eksplorasi Konsep&lt;/td&gt;&lt;td&gt;Penjelasan integrasi objek, template, formulir, dan fitur otomatis pengolah kata&lt;/td&gt;&lt;td&gt;55 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Evaluasi Tahap 1&lt;/td&gt;&lt;td&gt;Kuis drag &amp;amp; drop Master Penyusun Laporan PKL (KKM 75, maksimal 3 percobaan)&lt;/td&gt;&lt;td&gt;35 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti - Evaluasi Tahap 2&lt;/td&gt;&lt;td&gt;Latihan praktik mengetik dan memformat dokumen Laporan PKL pada lembar F4 digital&lt;/td&gt;&lt;td&gt;60 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Unduh bukti PDF F4, catat nilai evaluasi dan tulis resume di buku catatan&lt;/td&gt;&lt;td&gt;15 menit&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== A. INTEGRASI OBJEK ===================== --&gt;
  &lt;section class="section" id="integrasi"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;01&lt;/span&gt; Menggabungkan Objek&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Integrasi Objek dalam Dokumen&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Integrasi dokumen&lt;/strong&gt; adalah proses menggabungkan berbagai jenis objek ke dalam satu dokumen utuh, sehingga informasi tersaji lengkap: teks untuk penjelasan, tabel untuk data, gambar untuk bukti, dan bagan untuk struktur. Dalam Laporan PKL, keempatnya dipakai sekaligus.&lt;/p&gt;

    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Data&lt;/span&gt;&lt;h4&gt;Tabel&lt;/h4&gt;&lt;p&gt;Menyusun data baris-kolom. Dipakai untuk analisis SWOT, jurnal kegiatan harian, dan lembar konsultasi.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Bukti&lt;/span&gt;&lt;h4&gt;Gambar / Foto&lt;/h4&gt;&lt;p&gt;Dokumentasi kegiatan dan denah lokasi. Atur &lt;em&gt;text wrapping&lt;/em&gt; agar teks mengalir rapi di sekitarnya.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Struktur&lt;/span&gt;&lt;h4&gt;SmartArt / Shapes&lt;/h4&gt;&lt;p&gt;Menggambarkan struktur organisasi tempat PKL secara visual dan mudah diperbarui.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Angka&lt;/span&gt;&lt;h4&gt;Grafik / Chart&lt;/h4&gt;&lt;p&gt;Menyajikan data kuantitatif (mis. jumlah produksi per minggu) dari tabel menjadi visual.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Keterangan&lt;/span&gt;&lt;h4&gt;Caption&lt;/h4&gt;&lt;p&gt;Penomoran otomatis objek, mis. "Gambar 2.1" dan "Tabel 2.2", yang menyesuaikan urutan bab.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Rujukan Silang&lt;/span&gt;&lt;h4&gt;Cross-Reference&lt;/h4&gt;&lt;p&gt;Merujuk objek dari teks ("lihat Tabel 2.2") yang nomornya ikut berubah bila urutan bergeser.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Praktik Baik&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Setiap gambar dan tabel dalam Laporan PKL wajib diberi &lt;strong&gt;caption bernomor bab&lt;/strong&gt; (Gambar 2.1 berarti gambar pertama pada BAB II) dan dirujuk di dalam teks. Caption yang dibuat lewat fitur otomatis akan memudahkan pembuatan Daftar Gambar dan Daftar Tabel.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== B. TEMPLATE &amp; STYLE ===================== --&gt;
  &lt;section class="section" id="template"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;02&lt;/span&gt; Konsistensi Format&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Template, Style, dan Section&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Template&lt;/strong&gt; adalah dokumen cetakan awal yang sudah memuat pengaturan margin, jenis huruf, style, dan struktur bab. Dengan template, seluruh murid menghasilkan laporan dengan format seragam tanpa mengatur ulang satu per satu.&lt;/p&gt;

    &lt;h3 class="sub-title"&gt;Tata Tulis Laporan PKL (Wajib)&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Unsur&lt;/th&gt;&lt;th&gt;Ketentuan&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ukuran kertas&lt;/td&gt;&lt;td&gt;F4 (215 mm x 330 mm), berat 70 gsm&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Jenis dan ukuran huruf&lt;/td&gt;&lt;td&gt;Times New Roman, 12 pt&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Margin&lt;/td&gt;&lt;td&gt;Kiri 4 cm; atas, kanan, dan bawah masing-masing 3 cm&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Spasi isi laporan&lt;/td&gt;&lt;td&gt;1,5 spasi&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Spasi khusus&lt;/td&gt;&lt;td&gt;Kata pengantar, daftar isi, kutipan langsung, dan daftar pustaka: 1 spasi (single)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Nomor halaman bagian awal&lt;/td&gt;&lt;td&gt;Angka romawi kecil (i, ii, iii), halaman judul tidak ditampilkan&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Nomor halaman bagian utama&lt;/td&gt;&lt;td&gt;Angka arab (1, 2, 3)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Letak nomor halaman&lt;/td&gt;&lt;td&gt;Kanan atas (header); khusus halaman berjudul BAB: tengah bawah (footer)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Jumlah halaman&lt;/td&gt;&lt;td&gt;Tidak kurang dari 20 halaman&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;

    &lt;div class="callout ok"&gt;
      &lt;div class="lbl"&gt;Kunci Otomasi&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Perbedaan format nomor halaman (romawi lalu arab) dan perbedaan posisinya (kanan atas lalu tengah bawah) hanya mungkin dilakukan otomatis dengan &lt;strong&gt;Section Break&lt;/strong&gt;. Tanpa section break, seluruh dokumen terpaksa memakai satu format nomor halaman yang sama.&lt;/p&gt;
    &lt;/div&gt;

    &lt;h3 class="sub-title"&gt;Hierarki Penomoran Sub Bab&lt;/h3&gt;
    &lt;p&gt;Pedoman menetapkan urutan tingkatan penomoran sebagai berikut, dan dilarang menggunakan simbol gambar seperti panah atau bintang:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;BAB&lt;/strong&gt; ditulis dengan angka romawi: BAB I&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Sub BAB&lt;/strong&gt; dengan huruf kapital: A. Latar Belakang&lt;/li&gt;
      &lt;li&gt;Tingkat berikutnya berurutan: 1. lalu 1.1 lalu a. lalu 1) lalu a)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;

  &lt;!-- ===================== C. FORMULIR ===================== --&gt;
  &lt;section class="section" id="formulir"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;03&lt;/span&gt; Isian Terstruktur&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Formulir dan Dokumen Isian&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Formulir&lt;/strong&gt; adalah dokumen dengan bagian isian yang sudah ditentukan letaknya, sehingga pengisi hanya perlu melengkapi data. Dalam Laporan PKL, terdapat tiga formulir penting yang seluruhnya paling efisien dibuat dengan &lt;strong&gt;tabel&lt;/strong&gt; (bukan dengan spasi atau titik-titik manual yang mudah bergeser).&lt;/p&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Identitas&lt;/span&gt;&lt;h4&gt;Biodata Peserta Didik&lt;/h4&gt;&lt;p&gt;Berisi nama, NIS, kelas, program keahlian, dan pas foto 3x4. Dirapikan dengan tabel tanpa garis.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Harian&lt;/span&gt;&lt;h4&gt;Jurnal Kegiatan PKL&lt;/h4&gt;&lt;p&gt;Tabel berisi kolom No., Hari/Tanggal, Unit Kerja/Pekerjaan, dan Catatan pembimbing dunia kerja.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Bimbingan&lt;/span&gt;&lt;h4&gt;Lembar Konsultasi&lt;/h4&gt;&lt;p&gt;Tabel bukti bimbingan dengan guru pembimbing, minimal 8 kali konsultasi.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Tips Teknis&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Agar titik dua pada formulir biodata sejajar sempurna, gunakan tabel 2 kolom lalu sembunyikan garisnya (&lt;em&gt;No Border&lt;/em&gt;) - jauh lebih rapi dan stabil dibanding menekan spasi atau tab berulang kali.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== D. FITUR OTOMATIS ===================== --&gt;
  &lt;section class="section" id="otomasi"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;04&lt;/span&gt; Efisiensi Kerja&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fitur Otomasi Pengolah Kata&lt;/h2&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Fitur&lt;/th&gt;&lt;th&gt;Fungsi dalam Laporan PKL&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Heading Style&lt;/td&gt;&lt;td&gt;Menandai judul BAB dan sub bab agar dikenali fitur otomatis lain; fondasi seluruh otomasi.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Table of Contents&lt;/td&gt;&lt;td&gt;Membuat Daftar Isi otomatis lengkap dengan nomor halaman; cukup klik Update Table bila ada perubahan.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Page Number + Section Break&lt;/td&gt;&lt;td&gt;Penomoran halaman otomatis dengan format dan posisi berbeda antar bagian.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Insert Caption&lt;/td&gt;&lt;td&gt;Memberi nomor otomatis pada gambar dan tabel, sekaligus bahan Daftar Gambar/Tabel.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Citation &amp;amp; Bibliography&lt;/td&gt;&lt;td&gt;Menyimpan sumber kutipan dan menghasilkan Daftar Pustaka otomatis, terurut abjad.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Mail Merge&lt;/td&gt;&lt;td&gt;Membuat surat pengantar PKL massal dari satu template dan satu daftar data murid.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;SmartArt&lt;/td&gt;&lt;td&gt;Membuat bagan struktur organisasi tempat PKL secara cepat dan mudah diedit.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Find &amp;amp; Replace&lt;/td&gt;&lt;td&gt;Mengganti istilah yang salah di seluruh dokumen sekaligus, mis. nama perusahaan.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;

    &lt;h3 class="sub-title"&gt;Aturan Penulisan Sitasi&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Satu penulis&lt;/strong&gt;: Bowlby (1973) di awal kalimat, atau (Guiffrida, 2006) di akhir kalimat.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Dua penulis&lt;/strong&gt;: gunakan kata "dan" di awal kalimat (Nagy dan Herman, 1987), gunakan simbol "&amp;amp;" di dalam kurung (Sills &amp;amp; Song, 2002).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Tiga penulis atau lebih&lt;/strong&gt;: cukup nama penulis pertama diikuti "dkk." (West, dkk., 1993).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Daftar Pustaka&lt;/strong&gt;: diurutkan abjad A-Z, spasi tunggal, nama jurnal dan judul buku dicetak miring.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;

  &lt;!-- ===================== E. SISTEMATIKA ===================== --&gt;
  &lt;section class="section" id="sistematika"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;05&lt;/span&gt; Struktur Laporan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Sistematika Laporan PKL&lt;/h2&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Kelompok&lt;/th&gt;&lt;th&gt;Isi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bagian Awal&lt;/td&gt;&lt;td&gt;Halaman judul, biodata, lembar pengesahan, lembar persetujuan, kata pengantar, daftar isi, daftar gambar, daftar tabel&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;BAB I Pendahuluan&lt;/td&gt;&lt;td&gt;Latar belakang, tujuan, manfaat, tempat dan waktu PKL&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;BAB II Tinjauan Umum&lt;/td&gt;&lt;td&gt;Sejarah umum, visi misi dan slogan, kegiatan umum, tugas dan fungsi, analisis usaha (SWOT, 5W+1H)&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;BAB III Pelaksanaan PKL&lt;/td&gt;&lt;td&gt;Bidang kerja, pelaksanaan kerja, kendala yang dihadapi, cara mengatasi kendala&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;BAB IV Penutup&lt;/td&gt;&lt;td&gt;Kesimpulan dan saran&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Bagian Akhir&lt;/td&gt;&lt;td&gt;Daftar pustaka, lembar konsultasi, jadwal dinas, dokumentasi kegiatan&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== RANGKUMAN ===================== --&gt;
  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;06&lt;/span&gt; Penguatan Konsep&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rangkuman Materi&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;h4&gt;Poin-Poin Kunci&lt;/h4&gt;
      &lt;ul&gt;
        &lt;li&gt;Integrasi dokumen menggabungkan teks, tabel, gambar, bagan, dan grafik menjadi satu dokumen utuh.&lt;/li&gt;
        &lt;li&gt;Template dan style menjaga konsistensi format di seluruh dokumen panjang.&lt;/li&gt;
        &lt;li&gt;Laporan PKL memakai kertas F4, Times New Roman 12 pt, margin kiri 4 cm dan sisi lain 3 cm, spasi 1,5.&lt;/li&gt;
        &lt;li&gt;Section break memungkinkan format nomor halaman romawi lalu arab dalam satu dokumen.&lt;/li&gt;
        &lt;li&gt;Formulir (biodata, jurnal, lembar konsultasi) paling rapi dibuat menggunakan tabel.&lt;/li&gt;
        &lt;li&gt;Daftar isi, caption, sitasi, dan daftar pustaka dapat dibuat otomatis dari Heading dan data sumber.&lt;/li&gt;
        &lt;li&gt;Mail merge membuat banyak surat serupa dari satu template dan satu sumber data.&lt;/li&gt;
        &lt;li&gt;Sistematika laporan: bagian awal, BAB I sampai BAB IV, lalu bagian akhir.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== EVALUASI ===================== --&gt;
  &lt;section class="section" id="evaluasi"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;07&lt;/span&gt; Tugas Pribadi&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Evaluasi: Master Penyusun Laporan PKL&lt;/h2&gt;
    &lt;p&gt;Evaluasi ini terdiri atas &lt;strong&gt;dua tahap&lt;/strong&gt; yang saling melengkapi: &lt;strong&gt;Tahap 1&lt;/strong&gt; menguji pemahaman aturan lewat kuis drag &amp;amp; drop, dan &lt;strong&gt;Tahap 2&lt;/strong&gt; menguji keterampilan lewat latihan praktik mengetik dan memformat dokumen Laporan PKL langsung di lembar F4 digital. Nilai akhir digabung 50:50 dari kedua tahap.&lt;/p&gt;

    &lt;div class="submit-panel"&gt;
      &lt;h3 class="sub-title" style="margin-top:0;"&gt;Identitas Peserta&lt;/h3&gt;
      &lt;p style="font-size:0.86rem; margin-bottom:0;"&gt;Isi identitas terlebih dahulu agar nilai dapat tercatat dengan benar.&lt;/p&gt;
      &lt;div class="student-info"&gt;
        &lt;div class="field"&gt;&lt;label for="noAbsen"&gt;No. Absen&lt;/label&gt;&lt;input type="number" id="noAbsen" min="1" placeholder="mis. 12"&gt;&lt;/div&gt;
        &lt;div class="field"&gt;&lt;label for="nama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="nama" placeholder="Nama sesuai absensi"&gt;&lt;/div&gt;
        &lt;div class="field"&gt;&lt;label for="kelas"&gt;Kelas&lt;/label&gt;
          &lt;select id="kelas"&gt;
            &lt;option value=""&gt;-- Pilih Kelas --&lt;/option&gt;
            &lt;option value="X TB1"&gt;X TB1&lt;/option&gt;
            &lt;option value="X TB2"&gt;X TB2&lt;/option&gt;
            &lt;option value="X TO1"&gt;X TO1&lt;/option&gt;
            &lt;option value="X TO2"&gt;X TO2&lt;/option&gt;
            &lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;
            &lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;
          &lt;/select&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- TAHAP 1 --&gt;
    &lt;div class="stage-head"&gt;
      &lt;span class="stage-badge"&gt;Tahap 1 - Bobot 50%&lt;/span&gt;
      &lt;h3&gt;Kuis Drag &amp;amp; Drop: Aturan Penyusunan Laporan&lt;/h3&gt;
    &lt;/div&gt;
    &lt;p&gt;Tarik jawaban yang tepat ke kotak dokumen sesuai instruksi pembimbing.&lt;/p&gt;

    &lt;div class="callout warn"&gt;
      &lt;div class="lbl"&gt;Aturan Bermain&lt;/div&gt;
      &lt;p style="margin:0;"&gt;&lt;strong&gt;Jika jawaban salah, soal langsung lanjut ke soal berikutnya dan tidak mendapat nilai&lt;/strong&gt; - tidak ada kesempatan mengulang soal yang sama. Nilai dikirim bila mencapai &lt;strong&gt;KKM = 75&lt;/strong&gt;. Bila belum, tersedia &lt;strong&gt;3 kali kesempatan&lt;/strong&gt;; setelah 3 kali, &lt;strong&gt;nilai tertinggi&lt;/strong&gt; dari ketiga percobaan yang dipakai.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="attempt-strip" id="attemptStrip"&gt;&lt;/div&gt;

    &lt;div class="game-panel" id="gamePanel"&gt;
      &lt;button id="btnMulaiEvaluasi" class="primary-btn"&gt;Mulai Kuis Tahap 1&lt;/button&gt;
      &lt;span id="evalMsg"&gt;&lt;/span&gt;
    &lt;/div&gt;

    &lt;!-- TAHAP 2 --&gt;
    &lt;div class="stage-head"&gt;
      &lt;span class="stage-badge"&gt;Tahap 2 - Bobot 50%&lt;/span&gt;
      &lt;h3&gt;Latihan Praktik: Mengetik &amp;amp; Memformat Dokumen F4&lt;/h3&gt;
    &lt;/div&gt;
    &lt;p&gt;Lembar di bawah ini adalah &lt;strong&gt;area ketik langsung&lt;/strong&gt; seukuran kertas F4 dengan margin sesuai pedoman (kiri 4 cm, sisi lain 3 cm). Ganti seluruh teks dalam tanda kurung siku dengan tulisan kalian sendiri, lalu rapikan menggunakan toolbar. Area ini &lt;strong&gt;menolak aksi tempel (paste)&lt;/strong&gt;.&lt;/p&gt;

    &lt;div class="checklist"&gt;
      &lt;h5&gt;Daftar Periksa yang Dinilai&lt;/h5&gt;
      &lt;ul&gt;
        &lt;li&gt;Mengganti seluruh bagian bertanda kurung siku pada Cover, Lembar Pengesahan, dan BAB I.&lt;/li&gt;
        &lt;li&gt;Mengetik Latar Belakang, Tujuan, dan Manfaat PKL dengan kalimat sendiri (minimal 200 karakter tiap bagian).&lt;/li&gt;
        &lt;li&gt;Menerapkan format: judul rata tengah dan tebal, isi paragraf rata kiri-kanan (Justify).&lt;/li&gt;
        &lt;li&gt;Tidak melakukan salin-tempel dari sumber mana pun.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;

    &lt;!-- ===== LAMPIRAN CONTOH LAPORAN PKL (PANDUAN, TIDAK BISA DISALIN) ===== --&gt;
    &lt;div class="lampiran-wrap"&gt;
      &lt;div class="lampiran-head"&gt;
        &lt;div&gt;
          &lt;div class="lampiran-eyebrow"&gt;Panduan Praktik&lt;/div&gt;
          &lt;h4&gt;Lampiran: Contoh Laporan Praktik Kerja Lapangan (PKL) Tahun 2025/2026&lt;/h4&gt;
        &lt;/div&gt;
        &lt;span class="lock-badge"&gt;Tidak dapat disalin - wajib diketik&lt;/span&gt;
      &lt;/div&gt;
      &lt;p class="lampiran-note"&gt;Berikut contoh laporan resmi dari Buku Pedoman PKL SMK Negeri 1 Tuntang sebagai acuan kalian mengetik. Klik judul bagian untuk membuka isinya. Teks di dalamnya &lt;strong&gt;sengaja diproteksi&lt;/strong&gt; sehingga tidak dapat diblok, disalin, atau di-klik kanan - silakan baca lalu ketik ulang di lembar dokumen di bawah.&lt;/p&gt;

      &lt;div class="lampiran-list" id="lampiranList"&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="doc-toolbar" id="docToolbar"&gt;
      &lt;button class="b" data-cmd="bold" title="Tebal"&gt;B&lt;/button&gt;
      &lt;button class="i" data-cmd="italic" title="Miring"&gt;I&lt;/button&gt;
      &lt;button class="u" data-cmd="underline" title="Garis Bawah"&gt;U&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;button data-cmd="justifyLeft" title="Rata Kiri"&gt;Kiri&lt;/button&gt;
      &lt;button data-cmd="justifyCenter" title="Rata Tengah"&gt;Tengah&lt;/button&gt;
      &lt;button data-cmd="justifyRight" title="Rata Kanan"&gt;Kanan&lt;/button&gt;
      &lt;button data-cmd="justifyFull" title="Rata Kiri-Kanan"&gt;Rata&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;button data-cmd="insertUnorderedList" title="Daftar Butir"&gt;List Bullet&lt;/button&gt;
      &lt;button data-cmd="insertOrderedList" title="Daftar Nomor"&gt;List Nomor&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;select id="fontSizeSelect" title="Ukuran Huruf"&gt;
        &lt;option value="3" selected&gt;12 pt (Isi)&lt;/option&gt;
        &lt;option value="4"&gt;14 pt (Sub Judul)&lt;/option&gt;
        &lt;option value="5"&gt;16 pt (Judul BAB)&lt;/option&gt;
      &lt;/select&gt;
      &lt;select id="fontFamilySelect" title="Jenis Huruf"&gt;
        &lt;option value="Times New Roman"&gt;Times New Roman&lt;/option&gt;
        &lt;option value="Arial"&gt;Arial&lt;/option&gt;
      &lt;/select&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;button id="btnTambahHalaman" title="Tambah Halaman Kosong"&gt;+ Halaman&lt;/button&gt;
      &lt;span class="hint"&gt;Klik di lembar dokumen dulu, lalu gunakan tombol ini&lt;/span&gt;
    &lt;/div&gt;

    &lt;div class="doc-stage" id="docStage"&gt;

      &lt;div class="f4-page" contenteditable="true" data-pagelabel="Halaman 1 - Cover" id="page1" style="text-align:center;"&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div style="font-size:16pt; font-weight:bold;"&gt;
          LAPORAN PRAKTIK KERJA LAPANGAN (PKL)&lt;br&gt;
          TAHUN PELAJARAN 2025/2026&lt;br&gt;&lt;br&gt;
          DI [Ketik Nama Perusahaan Tempat PKL]
        &lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div&gt;&lt;i&gt;Diajukan sebagai salah satu syarat untuk mengikuti&lt;br&gt;Ujian Kompetensi Keahlian&lt;/i&gt;&lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div style="border:1px solid black; width:3cm; height:4cm; margin:0 auto; display:flex; align-items:center; justify-content:center; font-style:italic; color:gray;"&gt;Foto 3x4&lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div style="font-weight:bold; font-size:14pt;"&gt;
          Disusun Oleh:&lt;br&gt;&lt;br&gt;
          NAMA: [Ketik Nama Anda]&lt;br&gt;
          NIS: [Ketik NIS Anda]&lt;br&gt;
          KELAS: [Ketik Kelas Anda]
        &lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div style="font-weight:bold; font-size:16pt;"&gt;
          PEMERINTAH PROVINSI JAWA TENGAH&lt;br&gt;
          DINAS PENDIDIKAN DAN KEBUDAYAAN&lt;br&gt;
          SMK NEGERI 1 TUNTANG&lt;br&gt;
          2025
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="f4-page" contenteditable="true" data-pagelabel="Halaman 2 - Lembar Pengesahan" id="page2"&gt;
        &lt;div style="text-align:center; font-weight:bold; font-size:14pt;"&gt;LEMBAR PENGESAHAN&lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div style="text-align:justify;"&gt;
          Laporan Praktik Kerja Lapangan (PKL) yang disusun oleh:&lt;br&gt;&lt;br&gt;
          Nama&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: [Ketik Nama Anda]&lt;br&gt;
          NIS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: [Ketik NIS Anda]&lt;br&gt;
          Program Keahlian&amp;nbsp;&amp;nbsp;: [Ketik Program Keahlian Anda]&lt;br&gt;
          Tempat PKL&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: [Ketik Nama Perusahaan Tempat PKL]&lt;br&gt;&lt;br&gt;
          Telah diperiksa, disetujui, dan disahkan pada tanggal: [Ketik Tanggal Pengesahan]
        &lt;/div&gt;
        &lt;br&gt;&lt;br&gt;&lt;br&gt;
        &lt;div style="display:flex; justify-content:space-between; text-align:center;"&gt;
          &lt;div style="width:45%;"&gt;
            Pembimbing Industri,&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;
            &lt;b&gt;&lt;u&gt;[Ketik Nama Pembimbing Industri]&lt;/u&gt;&lt;/b&gt;
          &lt;/div&gt;
          &lt;div style="width:45%;"&gt;
            Guru Pembimbing Sekolah,&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;
            &lt;b&gt;&lt;u&gt;[Ketik Nama Guru Pembimbing]&lt;/u&gt;&lt;/b&gt;&lt;br&gt;
            NIP. .....................................
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;div style="text-align:center;"&gt;
          Mengetahui,&lt;br&gt;
          Kepala SMK Negeri 1 Tuntang&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;
          &lt;b&gt;&lt;u&gt;Nanang Nurdiyanto, S.Pd, M.Eng&lt;/u&gt;&lt;/b&gt;&lt;br&gt;
          NIP. 19770402 200501 1 010
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="f4-page" contenteditable="true" data-pagelabel="Halaman 3 - BAB I Pendahuluan" id="page3"&gt;
        &lt;div style="text-align:center; font-weight:bold; font-size:14pt;"&gt;BAB I&lt;br&gt;PENDAHULUAN&lt;/div&gt;
        &lt;br&gt;&lt;br&gt;
        &lt;b&gt;A. Latar Belakang&lt;/b&gt;
        &lt;div style="text-align:justify; text-indent:1.25cm;" class="placeholder-note"&gt;
          [Blok dan hapus teks ini, lalu ketik latar belakang pelaksanaan PKL dengan kalimat sendiri, minimal 200 karakter. Gunakan format Rata (Justify) dan awal paragraf menjorok ke dalam.]
        &lt;/div&gt;
        &lt;br&gt;
        &lt;b&gt;B. Tujuan Praktik Kerja Lapangan&lt;/b&gt;
        &lt;div style="text-align:justify; text-indent:1.25cm;" class="placeholder-note"&gt;
          [Blok dan hapus teks ini, lalu ketik tujuan dilaksanakannya PKL bagi peserta didik, minimal 200 karakter.]
        &lt;/div&gt;
        &lt;br&gt;
        &lt;b&gt;C. Manfaat Praktik Kerja Lapangan&lt;/b&gt;
        &lt;div style="text-align:justify; text-indent:1.25cm;" class="placeholder-note"&gt;
          [Blok dan hapus teks ini, lalu ketik manfaat PKL bagi peserta didik, sekolah, dan dunia kerja, minimal 200 karakter.]
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="doc-meta"&gt;
      &lt;span id="docCharCount"&gt;0 karakter diketik&lt;/span&gt;
      &lt;span class="flag" id="docPasteFlag"&gt;&lt;/span&gt;
    &lt;/div&gt;

    &lt;button id="btnHitungPraktik" class="primary-btn"&gt;Periksa &amp;amp; Hitung Nilai Latihan Praktik&lt;/button&gt;
    &lt;span id="praktikMsg"&gt;&lt;/span&gt;

    &lt;div class="result-panel" id="praktikResultPanel"&gt;
      &lt;div class="score-lbl"&gt;Nilai Latihan Praktik (bobot 50%)&lt;/div&gt;
      &lt;div class="score" id="praktikScoreValue"&gt;0&lt;/div&gt;
      &lt;p id="praktikResultDetail"&gt;&lt;/p&gt;
    &lt;/div&gt;

    &lt;!-- HASIL AKHIR --&gt;
    &lt;div class="result-panel" id="finalResultPanel"&gt;
      &lt;div class="score-lbl" id="finalResultLabel"&gt;Nilai Akhir Evaluasi&lt;/div&gt;
      &lt;div class="score" id="finalScoreValue"&gt;0&lt;/div&gt;
      &lt;p id="finalResultDetail"&gt;&lt;/p&gt;
      &lt;button id="btnCobaLagi" class="primary-btn" style="display:none;"&gt;Coba Lagi Kuis Tahap 1&lt;/button&gt;
      &lt;button id="btnUnduhPDF" class="primary-btn ghost" style="display:none;"&gt;Unduh Bukti Mengerjakan (PDF F4)&lt;/button&gt;
      &lt;p id="pdfHint" style="display:none; font-size:0.8rem; color:var(--ink-soft); margin-top:8px;"&gt;Setelah diunduh, cetak pada kertas F4 dan serahkan kepada guru sebagai bukti pengerjaan.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== PENUTUP ===================== --&gt;
  &lt;section class="section" id="penutup"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;08&lt;/span&gt; Penutup Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Tugas Individu di Buku Catatan&lt;/h2&gt;
    &lt;div class="closing-task"&gt;
      &lt;div class="lbl"&gt;1. Catat Nilai Evaluasi&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Salin &lt;strong&gt;Nilai Akhir Evaluasi&lt;/strong&gt; yang muncul di layar ke buku catatan pada kolom &lt;strong&gt;"Nilai Evaluasi"&lt;/strong&gt; (buat tabel: Tanggal | Materi | Nilai Evaluasi).&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="closing-task"&gt;
      &lt;div class="lbl"&gt;2. Tulis Resume Materi&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Buat &lt;strong&gt;resume materi "Integrasi Dokumen dan Otomasi Pengolah Kata"&lt;/strong&gt; dengan bahasa sendiri, mencakup: integrasi objek, template dan style, formulir, fitur otomatis, serta sistematika dan tata tulis Laporan PKL.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  INFORMATIKA - INTEGRASI DOKUMEN DAN OTOMASI PENGOLAH KATA - FASE E KELAS X SMK
&lt;/footer&gt;
&lt;/div&gt;

&lt;div class="print-area" id="printArea"&gt;&lt;/div&gt;

&lt;script&gt;
  var GAS_ENDPOINT = "https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec";
  var JUDUL_MATERI = "Integrasi Dokumen dan Otomasi Pengolah Kata";
  var KKM = 75;
  var MAX_ATTEMPTS = 3;
  var EVAL_SOAL_COUNT = 12;

  // ============================================================
  // TAHAP 1: KUIS DRAG &amp; DROP
  // ============================================================
  var allOptions = [
    { id:"opt_f4", text:"Kertas F4" },
    { id:"opt_a4", text:"Kertas A4" },
    { id:"opt_margin_kiri", text:"4 cm (Margin Kiri)" },
    { id:"opt_margin_lain", text:"3 cm (Atas, Kanan, Bawah)" },
    { id:"opt_tnr", text:"Times New Roman" },
    { id:"opt_arial", text:"Arial" },
    { id:"opt_size_12", text:"Ukuran 12 pt" },
    { id:"opt_size_14", text:"Ukuran 14 pt" },
    { id:"opt_spasi_1_5", text:"1,5 Spasi" },
    { id:"opt_spasi_1", text:"1 Spasi (Single)" },
    { id:"opt_bag_awal", text:"Bagian Awal" },
    { id:"opt_bag_utama", text:"Bagian Utama" },
    { id:"opt_bag_akhir", text:"Bagian Akhir" },
    { id:"opt_biodata", text:"Biodata Peserta Didik" },
    { id:"opt_pengesahan", text:"Lembar Pengesahan" },
    { id:"opt_toc", text:"Table of Contents" },
    { id:"opt_page_num", text:"Page Number" },
    { id:"opt_header_kanan", text:"Kanan Atas (Header)" },
    { id:"opt_footer_tengah", text:"Tengah Bawah (Footer)" },
    { id:"opt_romawi", text:"Angka Romawi Kecil (i, ii)" },
    { id:"opt_arab", text:"Angka Arab (1, 2, 3)" },
    { id:"opt_citation", text:"Citation / Bibliography" },
    { id:"opt_table", text:"Insert Table" },
    { id:"opt_mailmerge", text:"Mail Merge" },
    { id:"opt_smartart", text:"SmartArt / Shapes" },
    { id:"opt_picture", text:"Insert Picture" },
    { id:"opt_caption", text:"Insert Caption" },
    { id:"opt_section", text:"Section Break" },
    { id:"opt_bab_1", text:"BAB I Pendahuluan" },
    { id:"opt_bab_2", text:"BAB II Tinjauan Umum" },
    { id:"opt_bab_3", text:"BAB III Pelaksanaan PKL" },
    { id:"opt_bab_4", text:"BAB IV Penutup" },
    { id:"opt_jurnal", text:"Jurnal Kegiatan PKL" },
    { id:"opt_konsultasi", text:"Lembar Konsultasi" },
    { id:"opt_abjad", text:"Urut Abjad (A-Z)" },
    { id:"opt_kata_dan", text:"Kata 'dan'" },
    { id:"opt_simbol_dan", text:"Simbol '&amp;'" },
    { id:"opt_dkk", text:"Singkatan 'dkk.'" },
    { id:"opt_italic", text:"Cetak Miring (Italic)" },
    { id:"opt_heading", text:"Heading Style" }
  ];

  var questions = [
    { q:"Ukuran standar kertas yang diwajibkan untuk mencetak Laporan PKL adalah...", correctId:"opt_f4" },
    { q:"Agar teks tidak tertutup saat laporan dijilid, batas kertas sebelah kiri (Margin Kiri) diatur sebesar...", correctId:"opt_margin_kiri" },
    { q:"Berapa ukuran Margin Atas, Kanan, dan Bawah yang benar sesuai pedoman?", correctId:"opt_margin_lain" },
    { q:"Jenis huruf (Font) standar yang diwajibkan untuk seluruh teks laporan adalah...", correctId:"opt_tnr" },
    { q:"Untuk teks paragraf biasa, ukuran huruf yang digunakan adalah...", correctId:"opt_size_12" },
    { q:"Berapa jarak antar baris (Line Spacing) untuk bagian isi utama laporan?", correctId:"opt_spasi_1_5" },
    { q:"Khusus pengetikan Daftar Pustaka dan Kata Pengantar, jarak antar barisnya harus...", correctId:"opt_spasi_1" },
    { q:"Halaman Judul, Kata Pengantar, dan Daftar Isi termasuk kelompok...", correctId:"opt_bag_awal" },
    { q:"BAB I hingga BAB IV yang merupakan inti laporan disebut sebagai...", correctId:"opt_bag_utama" },
    { q:"Daftar Pustaka dan Lampiran-lampiran diletakkan pada...", correctId:"opt_bag_akhir" },
    { q:"Halaman setelah cover yang wajib dilengkapi pas foto 3x4 disebut...", correctId:"opt_biodata" },
    { q:"Lembar berisi tanda tangan pembimbing dan Kepala Sekolah sebagai bukti laporan sah adalah...", correctId:"opt_pengesahan" },
    { q:"Fitur otomatis agar judul BAB dan nomor halamannya terupdate tanpa mengetik manual adalah...", correctId:"opt_toc" },
    { q:"Fitur untuk memberikan nomor pada setiap lembar dokumen secara otomatis adalah...", correctId:"opt_page_num" },
    { q:"Untuk halaman isi biasa (bukan awal BAB), nomor halaman diletakkan di...", correctId:"opt_header_kanan" },
    { q:"Khusus halaman yang memuat Judul BAB baru, nomor halamannya diletakkan di...", correctId:"opt_footer_tengah" },
    { q:"Format nomor halaman untuk Bagian Awal (Kata Pengantar, Daftar Isi) menggunakan...", correctId:"opt_romawi" },
    { q:"Format nomor halaman untuk Bagian Utama (BAB I dst) menggunakan...", correctId:"opt_arab" },
    { q:"Fitur yang memungkinkan format nomor halaman berbeda antar bagian dalam satu dokumen adalah...", correctId:"opt_section" },
    { q:"Fitur otomatis untuk mengelola sumber kutipan dan membuat Daftar Pustaka instan adalah...", correctId:"opt_citation" },
    { q:"Untuk membuat layout formulir Biodata yang rapi dan sejajar, fitur paling efisien adalah...", correctId:"opt_table" },
    { q:"Jika panitia ingin membuat Surat Tugas massal dengan data dari Excel, fitur yang tepat adalah...", correctId:"opt_mailmerge" },
    { q:"Fitur yang cocok untuk membuat visualisasi Struktur Organisasi tempat PKL adalah...", correctId:"opt_smartart" },
    { q:"Untuk menambahkan bukti foto kegiatan di bagian lampiran, digunakan fitur...", correctId:"opt_picture" },
    { q:"Fitur untuk memberi keterangan bernomor otomatis pada gambar (Gambar 2.1) dan tabel adalah...", correctId:"opt_caption" },
    { q:"Fondasi seluruh otomasi dokumen, yang menandai judul bab agar dikenali fitur lain, adalah...", correctId:"opt_heading" },
    { q:"Latar Belakang, Tujuan, dan Manfaat PKL diketik pada bagian...", correctId:"opt_bab_1" },
    { q:"Sejarah Perusahaan, Visi Misi, dan Analisis SWOT dijabarkan pada...", correctId:"opt_bab_2" },
    { q:"Bagian berisi rincian pekerjaan dan kendala yang dihadapi ada di...", correctId:"opt_bab_3" },
    { q:"Kesimpulan hasil PKL dan Saran untuk sekolah/industri ditulis pada...", correctId:"opt_bab_4" },
    { q:"Dokumen lampiran berupa tabel yang wajib diisi setiap hari saat praktik di industri adalah...", correctId:"opt_jurnal" },
    { q:"Tabel lampiran yang membuktikan telah rutin bimbingan dengan guru pembimbing adalah...", correctId:"opt_konsultasi" },
    { q:"Urutan penulisan sumber dalam Daftar Pustaka disusun berdasarkan...", correctId:"opt_abjad" },
    { q:"Mengutip 2 penulis di awal kalimat: 'Nagy ... Herman (1987)'. Kata sambungnya adalah...", correctId:"opt_kata_dan" },
    { q:"Mengutip 2 penulis di dalam kurung: '(Sills ... Song, 2002)'. Simbol sambungnya adalah...", correctId:"opt_simbol_dan" },
    { q:"Jika sumber ditulis 3 orang atau lebih, setelah nama penulis pertama diikuti singkatan...", correctId:"opt_dkk" },
    { q:"Dalam Daftar Pustaka, pemformatan huruf untuk Judul Buku atau Nama Jurnal harus...", correctId:"opt_italic" }
  ];

  function shuffleArray(array){
    var a = array.slice();
    for (var i = a.length - 1; i &gt; 0; i--){
      var j = Math.floor(Math.random() * (i + 1));
      var t = a[i]; a[i] = a[j]; a[j] = t;
    }
    return a;
  }

  function getIdentitas(){
    return {
      noAbsen: document.getElementById('noAbsen').value.trim(),
      nama: document.getElementById('nama').value.trim(),
      kelas: document.getElementById('kelas').value.trim()
    };
  }

  var evalAttemptScores = [];
  var evalAttemptLogs = [];
  var currentLog = [];
  var evalFinalAttemptIndex = null;
  var evalFinalScore = null;
  var praktikScore = null;
  var combinedFinalScore = null;
  var currentSet = [];
  var qIndex = 0;
  var correctCount = 0;
  var draggedChip = null;

  function renderAttemptStrip(){
    var strip = document.getElementById('attemptStrip');
    strip.innerHTML = '';
    for (var i = 0; i &lt; MAX_ATTEMPTS; i++){
      var pill = document.createElement('div');
      pill.className = 'attempt-pill';
      if (i &lt; evalAttemptScores.length){
        pill.className += (evalAttemptScores[i] &gt;= KKM ? ' pass' : ' fail');
        pill.textContent = 'Percobaan ' + (i+1) + ': ' + evalAttemptScores[i];
      } else if (i === evalAttemptScores.length){
        pill.className += ' current';
        pill.textContent = 'Percobaan ' + (i+1) + ': belum dimulai';
      } else {
        pill.textContent = 'Percobaan ' + (i+1) + ': -';
      }
      strip.appendChild(pill);
    }
  }
  renderAttemptStrip();

  document.getElementById('btnMulaiEvaluasi').addEventListener('click', function(){
    var id = getIdentitas();
    if (!id.noAbsen || !id.nama || !id.kelas){
      document.getElementById('evalMsg').textContent = 'Lengkapi identitas terlebih dahulu.';
      return;
    }
    document.getElementById('evalMsg').textContent = '';
    startEvalAttempt();
  });
  document.getElementById('btnCobaLagi').addEventListener('click', function(){
    document.getElementById('finalResultPanel').style.display = 'none';
    startEvalAttempt();
  });

  function startEvalAttempt(){
    qIndex = 0; correctCount = 0; currentLog = [];
    currentSet = shuffleArray(questions).slice(0, EVAL_SOAL_COUNT);
    document.getElementById('finalResultPanel').style.display = 'none';
    renderQuestion();
  }

  function renderQuestion(){
    var panel = document.getElementById('gamePanel');
    if (qIndex &gt;= currentSet.length){ finishEvalAttempt(); return; }

    var q = currentSet[qIndex];
    panel.innerHTML =
      '&lt;div class="game-progress"&gt;&lt;span&gt;Soal ' + (qIndex+1) + ' / ' + EVAL_SOAL_COUNT + '&lt;/span&gt;&lt;span&gt;Benar: ' + correctCount + '&lt;/span&gt;&lt;/div&gt;' +
      '&lt;div class="q-instruksi"&gt;Instruksi Pembimbing&lt;/div&gt;' +
      '&lt;div class="q-text"&gt;' + q.q + '&lt;/div&gt;' +
      '&lt;div class="drop-zone" id="dropZone"&gt;Tarik (drag) jawaban ke sini, atau klik salah satu pilihan&lt;/div&gt;' +
      '&lt;div class="opt-pool" id="optPool"&gt;&lt;/div&gt;';

    var dropZone = document.getElementById('dropZone');
    var optPool = document.getElementById('optPool');

    var correctOpt = null;
    for (var i = 0; i &lt; allOptions.length; i++){ if (allOptions[i].id === q.correctId) correctOpt = allOptions[i]; }
    var distractors = shuffleArray(allOptions.filter(function(o){ return o.id !== q.correctId; })).slice(0, 3);
    var choices = shuffleArray([correctOpt].concat(distractors));

    choices.forEach(function(opt){
      var chip = document.createElement('div');
      chip.className = 'opt-chip';
      chip.draggable = true;
      chip.dataset.id = opt.id;
      chip.textContent = opt.text;
      chip.addEventListener('dragstart', function(){ draggedChip = chip; chip.classList.add('dragging'); });
      chip.addEventListener('dragend', function(){ chip.classList.remove('dragging'); });
      chip.addEventListener('click', function(){ handleAnswer(opt, q, dropZone); });
      optPool.appendChild(chip);
    });

    dropZone.addEventListener('dragover', function(e){ e.preventDefault(); dropZone.classList.add('drag-over'); });
    dropZone.addEventListener('dragleave', function(){ dropZone.classList.remove('drag-over'); });
    dropZone.addEventListener('drop', function(e){
      e.preventDefault();
      dropZone.classList.remove('drag-over');
      if (!draggedChip) return;
      var pickedId = draggedChip.dataset.id;
      var picked = null;
      for (var i = 0; i &lt; allOptions.length; i++){ if (allOptions[i].id === pickedId) picked = allOptions[i]; }
      draggedChip = null;
      handleAnswer(picked, q, dropZone);
    });
  }

  function handleAnswer(picked, q, dropZone){
    if (dropZone.dataset.answered === '1') return;
    dropZone.dataset.answered = '1';

    var correctOpt = null;
    for (var i = 0; i &lt; allOptions.length; i++){ if (allOptions[i].id === q.correctId) correctOpt = allOptions[i]; }
    var isCorrect = picked.id === q.correctId;

    currentLog.push({ q: q.q, userAns: picked.text, correctAns: correctOpt.text, isCorrect: isCorrect });

    if (isCorrect){
      correctCount++;
      dropZone.className = 'drop-zone correct';
      dropZone.textContent = '[BENAR] ' + correctOpt.text;
    } else {
      dropZone.className = 'drop-zone wrong';
      dropZone.textContent = '[SALAH] Jawabanmu: ' + picked.text + '. Yang benar: ' + correctOpt.text;
    }

    setTimeout(function(){ qIndex++; renderQuestion(); }, 1400);
  }

  function finishEvalAttempt(){
    var attemptScore = Math.round((correctCount / EVAL_SOAL_COUNT) * 100);
    evalAttemptScores.push(attemptScore);
    evalAttemptLogs.push(currentLog.slice());
    renderAttemptStrip();

    document.getElementById('gamePanel').innerHTML =
      '&lt;p style="text-align:center; margin:0;"&gt;Percobaan ke-' + evalAttemptScores.length + ' selesai. Benar ' + correctCount + ' dari ' + EVAL_SOAL_COUNT + ' soal. Skor Tahap 1: &lt;strong&gt;' + attemptScore + '&lt;/strong&gt; (KKM = ' + KKM + ')&lt;/p&gt;';

    var panel = document.getElementById('finalResultPanel');
    var label = document.getElementById('finalResultLabel');
    var scoreEl = document.getElementById('finalScoreValue');
    var detailEl = document.getElementById('finalResultDetail');
    var btnCoba = document.getElementById('btnCobaLagi');

    if (attemptScore &gt;= KKM){
      evalFinalScore = attemptScore;
      evalFinalAttemptIndex = evalAttemptScores.length - 1;
      label.textContent = 'Nilai Tahap 1 (Tercapai KKM)';
      scoreEl.textContent = evalFinalScore;
      detailEl.textContent = 'KKM tercapai pada percobaan ke-' + evalAttemptScores.length + '. Lanjutkan ke Tahap 2: Latihan Praktik.';
      btnCoba.style.display = 'none';
      panel.style.display = 'block';
      tryFinalize();
    } else if (evalAttemptScores.length &lt; MAX_ATTEMPTS){
      label.textContent = 'Belum Mencapai KKM';
      scoreEl.textContent = attemptScore;
      detailEl.textContent = 'Skor ' + attemptScore + ' belum mencapai KKM (' + KKM + '). Sisa kesempatan: ' + (MAX_ATTEMPTS - evalAttemptScores.length) + ' kali.';
      btnCoba.style.display = 'inline-block';
      panel.style.display = 'block';
    } else {
      evalFinalScore = Math.max.apply(null, evalAttemptScores);
      evalFinalAttemptIndex = evalAttemptScores.indexOf(evalFinalScore);
      label.textContent = 'Nilai Tahap 1 (Tertinggi dari 3 Percobaan)';
      scoreEl.textContent = evalFinalScore;
      detailEl.textContent = 'Sudah 3 kali percobaan. Nilai tertinggi (' + evalFinalScore + ') yang digunakan. Lanjutkan ke Tahap 2: Latihan Praktik.';
      btnCoba.style.display = 'none';
      panel.style.display = 'block';
      tryFinalize();
    }
  }

  // ============================================================
  // LAMPIRAN CONTOH LAPORAN PKL (panduan, diproteksi dari penyalinan)
  // ============================================================
  var LAMPIRAN = [
    { judul: "1. Halaman Judul (Cover)",
      isi: "LAPORAN\nPRAKTIK KERJA LAPANGAN\nDI PT UNGARAN SARI GARMEN KABUPATEN SEMARANG\n26 NOVEMBER 2025 - 13 MARET 2026\n\n(Logo sekolah, lebar 6 cm x tinggi 6 cm)\n\nDISUSUN OLEH : RIZWAN NADIF AL-HAKIM\n(NIS)\n\nPROGRAM KEAHLIAN TATA BUSANA\nSMK NEGERI 1 TUNTANG\n2025\n\nCatatan format: seluruh teks judul berukuran 14 pt, dicetak tebal, dan rata tengah." },

    { judul: "2. Biodata Peserta Didik",
      isi: "BIODATA PESERTA DIDIK\n\nNama                  : ------------\nNIS                   : -----------\nKelas                 : XII\nProgram Keahlian      : TATA BUSANA\n\n(Kotak PHOTO 3x4 diletakkan di sisi kanan)\n\nNomor halaman: ii\n\nCatatan format: agar titik dua sejajar sempurna, bagian ini dibuat dengan tabel 2 kolom tanpa garis (No Border)." },

    { judul: "3. Lembar Pengesahan",
      isi: "LEMBAR PENGESAHAN\n\nRIZWAN NADIF AL-HAKIM\n(NIS: 1874/215.067)\n\nDipertahankan di depan Tim Penguji PKL\nProgram Keahlian Tata Busana\nSMK Negeri 1 Tuntang\n\nTim Penguji\n\n............................ (Tanggal Pengesahan) (Tanda Tangan)\n(Pembimbing Sekolah)\n\n............................ (Tanggal Pengesahan) (Tanda Tangan)\n(Penguji)\n\nMengetahui,\nKepala SMKN 1 TUNTANG\n\n\nNanang Nurdiyanto, S.Pd, M.Eng\nNIP. 19770402 200501 1 010\n\nNomor halaman: iii" },

    { judul: "4. Lembar Persetujuan",
      isi: "LEMBAR PERSETUJUAN\n\nRIZWAN NADIF AL-HAKIM\n(NIS: 1874/215.067)\n\nLaporan ini ditulis untuk memenuhi sebagian persyaratan\ndalam mengikuti sidang PKL\nProgram Keahlian Tata Busana\n\nMenyetujui untuk diajukan pada ujian PKL:\n\nPembimbing DUDI                    Pembimbing Sekolah\n\n\n.............................      .............................\nNIP..                              NIP..\n\nMengetahui\nKetua Program Keahlian\n\n\n------------------------------------\nNIP.\n\nKepala SMKN 1 TUNTANG\n\n\nNanang Nurdiyanto, S.Pd, M.Eng\nNIP. 19770402 200501 1 010\n\nNomor halaman: iv" },

    { judul: "5. Kata Pengantar",
      isi: "KATA PENGANTAR\n\nPuji dan syukur penyusun panjatkan kepada Allah SWT, karena atas segala rahmat, berkah dan karunia-Nya sehingga penyusun dapat menyelesaikan Laporan Praktik Kerja Lapangan yang bertempat di Laboratorium Kesehatan Daerah Kabupaten Semarang Program Keahlian Teknik Laboratorium Medik. Penyusun menyadari sepenuhnya bahwa selesainya laporan ini berkat bantuan dari berbagai pihak. Pada kesempatan kali ini penyusun ingin menyampaikan terimakasih kepada:\n\n1. ............................ selaku Kepala SMK Negeri 1 Tuntang Kabupaten Semarang\n2. ............................ selaku Wakil Kepala Sekolah Bidang Humas SMK Negeri 1 Tuntang\n3. ............................ selaku Kepala Program Keahlian Tata Busana\n4. Bapak/Ibu ......................, selaku pembimbing Praktik Kerja Lapangan dari SMK Negeri 1 Tuntang\n5. ...........\n6. ...........\n7. Dst. (optional)\n\nPenyusun menyadari masih banyak kekurangan dalam penyusunan Laporan Praktik Kerja Lapangan ini, walaupun demikian semoga laporan ini dapat bermanfaat bagi para pembaca.\n\nKabupaten Semarang, 13 Maret 2026\nPenyusun\n\nNomor halaman: v\nCatatan format: kata pengantar diketik dengan spasi tunggal (single), bukan 1,5." },

    { judul: "6. Daftar Isi",
      isi: "DAFTAR ISI\n\nHalaman Judul ........................................................ i\nHalaman Biodata ..................................................... ii\nHalaman Pengesahan .................................................. iii\nHalaman Persetujuan ................................................. iv\nKata Pengantar ...................................................... v\nDaftar Isi .......................................................... vi\nDaftar Gambar ....................................................... vii\nDaftar Tabel ........................................................ viii\n\nBAB I PENDAHULUAN\nA. Latar Belakang PKL\nB. Tujuan PKL\nC. Manfaat PKL\nD. Tempat dan Waktu PKL\n\nBAB II TINJAUAN UMUM TEMPAT PKL\nA. Sejarah Umum\nB. Visi, Misi, dan Slogan\nC. Kegiatan Umum\nD. Tugas dan Fungsi\nE. Analisis Usaha\n\nBAB III PELAKSANAAN PKL\nA. Bidang Kerja\nB. Pelaksanaan Kerja\nC. Kendala yang Dihadapi\nD. Cara Mengatasi Masalah\n\nBAB IV PENUTUP\nA. Kesimpulan\nB. Saran\n\nDAFTAR PUSTAKA\nLAMPIRAN\n1. Lembar Konsultasi\n2. Jadwal Dinas/Jaga/Shift\n3. Dokumentasi Kegiatan\n\nNomor halaman: vi\nCatatan format: dibuat otomatis melalui References &gt; Table of Contents, bukan diketik manual." },

    { judul: "7. BAB I - Pendahuluan",
      isi: "BAB I\nPENDAHULUAN\n\nA. Latar Belakang Praktik Kerja Lapangan (PKL)\n    SMK Negeri 1 Tuntang Kabupaten Semarang merupakan salah satu Sekolah Menengah Kejuruan (SMK) yang berada di Kabupaten Semarang. dst\n\nB. Tujuan Praktik Kerja Lapangan (PKL)\n    Tujuan adanya PKL ini antara lain sebagai berikut;\n\nC. Manfaat Praktik Kerja Lapangan (PKL)\n    1. Manfaat Bagi Peserta Didik\n       a. Meningkatnya kompetensi keahlian yang telah diperoleh di sekolah.\n    2. Manfaat Bagi Sekolah\n       a. Terjalinnya hubungan kerja sama yang saling menguntungkan antara sekolah\n    3. Manfaat Bagi Dunia Kerja\n       a. Adanya masukan yang positif dan konstruktif dari SMK/MAK untuk perkembangan dunia kerja.\n\nD. Tempat dan Waktu Praktik Kerja Lapangan (PKL)\n    1. Tempat Praktik Kerja Lapangan (PKL)\n       Praktik Kerja Lapangan (PKL) ini dilaksanakan di ................ (Alamat lengkap disertai Kode Pos)\n    2. Waktu Praktik Kerja Lapangan (PKL)\n       Waktu Praktik Kerja Lapangan (PKL) dilaksanakan pada tanggal 26 November 2025 sampai dengan tanggal 13 Maret 2026, dengan jadwal kegiatan terlampir." },

    { judul: "8. BAB II - Tinjauan Umum Tempat PKL",
      isi: "BAB II\nTINJAUAN UMUM TEMPAT PKL\n\nA. Sejarah Umum\n    PT. Ungaran Sari Garmen berdiri sejak ..... dengan dst\n\n    Gambar 2.1 letak PT. Ungaran Sari Garmen Kabupaten Semarang\n    (googlemaps)\n\n    PT. Ungaran Sari Garmen Kabupaten Semarang merupakan .....dst.\n\nB. Visi, Misi dan Slogan\n    Adapun Visi dan Misi didirikannya PT. Ungaran Sari Garmen Kabupaten Semarang adalah sebagai berikut:\n    ..................................dst\n\nC. Kegiatan Umum\n    Tata Busana adalah sebuah program keahlian yang mempelajari dasar-dasar kemampuan dst ................ PT. Ungaran Sari Garmen Kabupaten Semarang seperti ............... dst.\n\nD. Tugas dan Fungsi\n    PT. Ungaran Sari Garmen memiliki tugas dan fungsi ............... dst.\n\nE. Analisis Usaha\n    Analisis usaha adalah proses evaluasi yang dilakukan terhadap suatu bisnis\n\n    1. Analisis SWOT\n       Analisis SWOT adalah teknik yang digunakan untuk mengidentifikasi\n\n       Tabel 2.2 Tabel Analisis SWOT PT. Ungaran Sari Garmen Kabupaten Semarang\n\n       Strength (Kekuatan)    | Weakness (Kelemahan)\n       1. PT. Ungaran Sari    | 1. Kurangnya dalam fasilitas\n          Garmen ...          |    persediaan alat ... dst\n\n       Opportunity (Peluang)  | Threat (Ancaman)\n       1. Peningkatan alat    | 1. Akan ada timbulnya persaingan\n          yang sesuai ...dst  |    dengan ... dst\n\n    2. Analisis Solusi\n       Strategi SO | Strategi WO\n       Strategi ST | Strategi WT\n\n    3. Analisis 5W + 1H\n       Analisis 5W+1H adalah sebuah metode yang digunakan untuk merinci dan memahami suatu peristiwa, situasi, atau masalah dengan cara mengajukan serangkaian pertanyaan yang mencakup lima unsur utama (What, Who, When, Where, Why) ditambah satu unsur tambahan (How)." },

    { judul: "9. BAB III - Pelaksanaan PKL",
      isi: "BAB III\nPELAKSANAAN PKL\n\nA. Bidang Kerja\n    Lokasi Praktikan melaksanakan Praktik Kerja Lapangan (PKL) yaitu di ....................... Praktikan ditempatkan di ........................., yang sifat pekerjaannya berfokus untuk ...............................dst.\n\nB. Pelaksanaan Kerja\n    Selama pelaksanaan Praktik Kerja Lapangan (PKL) Praktikan dibimbing adalah sebagai berikut:\n    ........\n\n    Catatan:\n    Uraian praktikum dikerjakan sesuai dengan tempat PKL.\n    Jabarkan kegiatan setiap poin kompetensi di atas bila dilaksanakan.\n\nC. Kendala yang Dihadapi\n    Dalam menyelesaikan tugas yang diberikan selama Praktik Kerja, yang menjadi kendala saat menyelesaikan tugas meliputi:\n    1. dst\n\nD. Cara Mengatasi Masalah\n    Berikut ini cara yang dilakukan Praktikan dalam mengatasi kendala untuk menyelesaikan tugas yang diberikan, yaitu:\n    1. dst" },

    { judul: "10. BAB IV - Penutup",
      isi: "BAB IV\nPENUTUP\n\nA. Kesimpulan\n    Dari penjelasan diatas kesimpulannya adalah :\n    1. dst\n\nB. Saran\n    Berikut ini adalah saran yang bisa Praktikan berikan dalam pelaksanaan Praktik Kerja Lapangan, yaitu:\n    ...... dst" },

    { judul: "11. Daftar Pustaka",
      isi: "DAFTAR PUSTAKA\n\nBillett, S. (2011). Vocational education: Purposes, traditions and prospects. Springer.\n\nCreswell, J. W. &amp; Poth, C. N. (2018). Qualitative inquiry &amp; research design (4th ed.). SAGE Publication.\n\nKemendikbud. (2014). Garis-garis besar program pembinaan SMK. Direktorat PSMK Kementerian Pendidikan dan Kebudayaan.\n\nKusumam, A., Mukhidin, M., &amp; Hasan, B. (2016). Pengembangan bahan ajar mata pelajaran dasar dan pengukuran listrik untuk sekolah menengah kejuruan. Jurnal Pendidikan Teknologi dan Kejuruan, 23(1), 28-39. http://doi.org/10.21831/jptk.v23i1.9352\n\nWahjusaputri, S., Marlina, E., &amp; Latifah, S. (2020). Developing the teaching factory learning media in a public vocational high school. Jurnal Pendidikan Vokasi, 10 (1), 69-79.\n\nCatatan format: ditulis spasi tunggal, jarak dua spasi antar sumber, diurutkan abjad A-Z, nama jurnal dan judul buku dicetak miring." },

    { judul: "12. Lampiran - Jurnal Kegiatan PKL",
      isi: "JURNAL KEGIATAN PKL\n\nNama Peserta Didik            : Fulan\nDunia Kerja Tempat PKL        : Apotek Seger Waras\nNama Instruktur               : Nama Pembimbing DUDI\nNama Guru Pembimbing          : Febrina/Amalia/Amil\n\nNo. | Hari/Tanggal            | Unit Kerja/Pekerjaan                  | Catatan dan Paraf Dunia Kerja\n1.  | Selasa / 2 Januari 2023 | Membersihkan ruangan                  |\n    |                         | Membaca SOP layanan klien             |\n    |                         | Bertemu dengan Kliyen                 |\n    |                         | Merancang pembuatan video             |\n    |                         | Membuat Skrip                         |\n    |                         | Dst..........                         |\n2.  | Rabu / 3 Januari 2023   | Membersihkan ruangan                  |\n    |                         | Membaca SOP layanan klien             |\n    |                         | Mempersiapkan shooting                |\n    |                         | Dst..........                         |\n\nJurnal kegiatan disusun oleh peserta didik sebagai dokumen pekerjaan yang dilaksanakan.\nCatatan diberikan oleh pembimbing dunia kerja pada setiap kegiatan atau waktu tertentu." },

    { judul: "13. Lampiran - Catatan Kegiatan PKL",
      isi: "CATATAN KEGIATAN PKL\n\nNama Peserta Didik            : Fulan\nDunia Kerja Tempat PKL        : PT. Ungaran Sari Garmen\nNama Instruktur               : Nama Pembimbing DUDI\nNama Guru Pembimbing          : Na'ilir/ Win Rizki/Bagus/Nila Rukmi\n\nA. Nama Pekerjaan\n   Pencatatan Skrip\n\nB. Perencanaan Kegiatan\n   (Jadwal Kegiatan/dokumen perencanaan)\n   Tanggal disepakati pada tanggal 25 Desember 2024\n   Persiapan alat dan bahan\n   Alat :\n   1. Kamera\n   2. Dst...\n   Bahan :\n   1. Kertas HVS\n   2. Dst...\n\nC. Pelaksanaan Kegiatan/Hasil\n   (Uraian proses kerja dan foto hasil)\n   1. Prosedur pencatatan skrip\n      a. Mengikuti prosedur Kesehatan, Keselamatan, dan Keamanan di Tempat Kerja\n      b. Menjaga continuity saat syuting\n      c. Dst.....\n   2. Hasil\n      Hasil pembuatan skrip dapat dilihat pada lampiran.\n\n   (Foto hasil dan Dokumentasi)\n\nD. Catatan Instruktur\n   (Catatan Instruktur)\n\n                                            Tanda Tangan Instruktur\n\n                                            (...........................)" },

    { judul: "14. Lampiran - Lembar Konsultasi Laporan",
      isi: "LEMBAR KONSULTASI LAPORAN\n\nNO. | TANGGAL    | MATERI YANG DIKONSULTASIKAN DAN URAIAN PEMBIMBING          | NAMA DAN TTD PEMBIMBING\n1.  | 23-12-2024 | Pengajuan topik/masalah: editor tidak mampu melakukan      |\n    |            | pengeditan menggunakan aplikasi yang belum diajarkan      |\n    |            | di sekolah ........                                       |\n2.  | .......... | ........................................................  |\n\nCatatan : Konsultasi dilakukan minimal sebanyak 8 kali." },

    { judul: "15. Format Margin dan Tata Tulis",
      isi: "FORMAT PENGGUNAAN MARGIN\n\nMenggunakan kertas ukuran F4 dengan batas pengetikan:\n- 4 cm dari sisi kiri kertas\n- 3 cm dari sisi kanan kertas\n- 3 cm dari sisi atas kertas\n- 3 cm dari sisi bawah kertas\n\nTATA TULIS:\n- Kertas F4 berat 70 gsm\n- Jenis huruf Times New Roman ukuran 12 pt\n- Isi laporan diketik dengan spasi 1,5\n- Kata pengantar, daftar isi, kutipan langsung, dan daftar pustaka: spasi single\n- Jumlah halaman tidak kurang dari 20 halaman\n\nPENULISAN BAB DAN SUB BAB:\n1. Penulisan Judul BAB dengan romawi, seperti BAB I\n2. Penulisan Sub BAB dengan huruf Abjad Kapital, seperti A. Latar Belakang\n3. Penulisan keterangan anak Sub BAB dengan tangga urutan sebagai berikut:\n   A. Latar Belakang\n   1. Sejarah\n   1.1 Bila Sejarah ada sub-sub lainnya dapat dilanjutkan dengan 1.1, 1.2, dst\n   a. Bila hanya penyebutan dapat menggunakan abjad kecil, a, b, c, d, dst.\n   1) Bila di dalam abjad kecil diperlukan keterangan lagi maka menggunakan simbol 1), 2), 3), dst.\n   a) Bila masih diperlukan keterangan tambahan lagi maka menggunakan simbol a), b), c), dst.\n\n   Tidak diperkenankan menggunakan simbol gambar seperti panah, bintang, dll.\n\n4. Nomor halaman bagian awal: romawi kecil (i, ii, iii, dst), kecuali halaman judul tidak ditampilkan.\n5. Nomor halaman bagian utama: angka (1, 2, 3, dst). Bagian akhir tidak diberi nomor halaman.\n6. Letak nomor halaman di kanan atas (header kanan), tetapi untuk halaman yang memuat judul BAB nomor halaman terletak di tengah bawah (footer tengah)." }
  ];

  var lampiranList = document.getElementById('lampiranList');
  LAMPIRAN.forEach(function(item, idx){
    var wrap = document.createElement('div');
    wrap.className = 'lampiran-item';

    var btn = document.createElement('button');
    btn.className = 'lampiran-toggle';
    btn.type = 'button';
    btn.innerHTML = '&lt;span&gt;' + item.judul + '&lt;/span&gt;&lt;span class="chev"&gt;[ buka ]&lt;/span&gt;';

    var body = document.createElement('div');
    body.className = 'lampiran-body';
    body.textContent = item.isi;

    // Proteksi penyalinan: blokir copy, cut, klik kanan, dan seleksi via keyboard
    body.addEventListener('copy', function(e){ e.preventDefault(); alert('Contoh laporan ini tidak dapat disalin. Silakan baca lalu ketik ulang di lembar dokumen.'); });
    body.addEventListener('cut', function(e){ e.preventDefault(); });
    body.addEventListener('contextmenu', function(e){ e.preventDefault(); });
    body.addEventListener('dragstart', function(e){ e.preventDefault(); });

    btn.addEventListener('click', function(){
      var isOpen = body.classList.toggle('open');
      btn.querySelector('.chev').textContent = isOpen ? '[ tutup ]' : '[ buka ]';
    });

    wrap.appendChild(btn);
    wrap.appendChild(body);
    lampiranList.appendChild(wrap);
  });

  // ============================================================
  // TAHAP 2: LATIHAN PRAKTIK DOKUMEN F4
  // ============================================================
  var docPasteDetected = false;
  var currentDocPage = null;
  var pageCounter = 3;

  var PRACTICE_TARGETS = [
    { label:"Nama perusahaan pada Cover", marker:"[Ketik Nama Perusahaan Tempat PKL]" },
    { label:"Nama pada Cover", marker:"[Ketik Nama Anda]" },
    { label:"NIS pada Cover", marker:"[Ketik NIS Anda]" },
    { label:"Kelas pada Cover", marker:"[Ketik Kelas Anda]" },
    { label:"Program Keahlian pada Lembar Pengesahan", marker:"[Ketik Program Keahlian Anda]" },
    { label:"Tanggal Pengesahan", marker:"[Ketik Tanggal Pengesahan]" },
    { label:"Nama Pembimbing Industri", marker:"[Ketik Nama Pembimbing Industri]" },
    { label:"Nama Guru Pembimbing", marker:"[Ketik Nama Guru Pembimbing]" }
  ];

  var PRACTICE_ESSAYS = [
    { label:"A. Latar Belakang", marker:"ketik latar belakang pelaksanaan PKL", minChars:200 },
    { label:"B. Tujuan PKL", marker:"ketik tujuan dilaksanakannya PKL", minChars:200 },
    { label:"C. Manfaat PKL", marker:"ketik manfaat PKL bagi peserta didik", minChars:200 }
  ];

  function getDocPages(){ return document.querySelectorAll('.f4-page'); }

  function getDocText(){
    var pages = getDocPages();
    var out = [];
    for (var i = 0; i &lt; pages.length; i++){ out.push(pages[i].innerText); }
    return out.join('\n');
  }

  function bindPage(page){
    page.addEventListener('focus', function(){ currentDocPage = page; });
    page.addEventListener('paste', function(e){
      e.preventDefault();
      docPasteDetected = true;
      var flag = document.getElementById('docPasteFlag');
      flag.classList.add('show');
      flag.textContent = '[PERINGATAN] Percobaan tempel terdeteksi - nilai latihan praktik dipotong 50%';
    });
    page.addEventListener('drop', function(e){ e.preventDefault(); });
    page.addEventListener('input', updateDocCount);
  }

  function updateDocCount(){
    document.getElementById('docCharCount').textContent = getDocText().trim().length + ' karakter diketik';
  }

  var initPages = getDocPages();
  for (var p = 0; p &lt; initPages.length; p++){ bindPage(initPages[p]); }
  updateDocCount();

  document.getElementById('docToolbar').addEventListener('mousedown', function(e){
    if (e.target.tagName === 'BUTTON') e.preventDefault();
  });
  var docBtns = document.querySelectorAll('#docToolbar button[data-cmd]');
  for (var db = 0; db &lt; docBtns.length; db++){
    (function(btn){
      btn.addEventListener('click', function(){
        if (!currentDocPage){ alert('Klik di dalam lembar dokumen terlebih dahulu.'); return; }
        currentDocPage.focus();
        document.execCommand(btn.dataset.cmd, false, null);
      });
    })(docBtns[db]);
  }
  document.getElementById('fontSizeSelect').addEventListener('change', function(e){
    if (!currentDocPage) return;
    currentDocPage.focus();
    document.execCommand('fontSize', false, e.target.value);
  });
  document.getElementById('fontFamilySelect').addEventListener('change', function(e){
    if (!currentDocPage) return;
    currentDocPage.focus();
    document.execCommand('fontName', false, e.target.value);
  });
  document.getElementById('btnTambahHalaman').addEventListener('click', function(){
    pageCounter++;
    var newPage = document.createElement('div');
    newPage.className = 'f4-page';
    newPage.contentEditable = 'true';
    newPage.setAttribute('data-pagelabel', 'Halaman ' + pageCounter + ' - Tambahan');
    newPage.innerHTML = '&lt;div style="text-align:center; font-weight:bold; font-size:14pt;"&gt;BAB ...&lt;br&gt;...&lt;/div&gt;&lt;br&gt;&lt;div style="text-align:justify; text-indent:1.25cm;"&gt;Ketik isi halaman tambahan di sini.&lt;/div&gt;';
    document.getElementById('docStage').appendChild(newPage);
    bindPage(newPage);
    newPage.scrollIntoView({ behavior:'smooth', block:'start' });
  });

  var practiceDetail = [];

  function hitungPraktik(){
    var docText = getDocText();
    practiceDetail = [];
    var poinIsian = 0;

    PRACTICE_TARGETS.forEach(function(t){
      var stillThere = docText.indexOf(t.marker) !== -1;
      practiceDetail.push({ label: t.label, ok: !stillThere, ket: stillThere ? 'Belum diganti' : 'Sudah diisi' });
      if (!stillThere) poinIsian++;
    });

    var poinEsai = 0;
    PRACTICE_ESSAYS.forEach(function(es){
      var markerGone = docText.indexOf(es.marker) === -1;
      var ok = false, ket = '';
      if (!markerGone){
        ket = 'Teks petunjuk belum dihapus/diganti';
      } else {
        ok = true; ket = 'Sudah ditulis dengan kalimat sendiri';
      }
      practiceDetail.push({ label: es.label, ok: ok, ket: ket });
      if (ok) poinEsai++;
    });

    var totalItem = PRACTICE_TARGETS.length + PRACTICE_ESSAYS.length;
    var base = ((poinIsian + poinEsai) / totalItem) * 100;

    // Bonus kelengkapan tulisan: dokumen yang digarap serius jauh lebih panjang
    var len = docText.trim().length;
    var lenFactor = Math.min(1, len / 2600);
    var score = Math.round(base * 0.75 + lenFactor * 25);
    if (docPasteDetected) score = Math.round(score * 0.5);
    if (score &gt; 100) score = 100;
    return score;
  }

  document.getElementById('btnHitungPraktik').addEventListener('click', function(){
    var id = getIdentitas();
    var msg = document.getElementById('praktikMsg');
    if (!id.noAbsen || !id.nama || !id.kelas){
      msg.textContent = 'Lengkapi identitas terlebih dahulu.';
      return;
    }
    msg.textContent = '';
    praktikScore = hitungPraktik();

    var sudah = 0;
    for (var i = 0; i &lt; practiceDetail.length; i++){ if (practiceDetail[i].ok) sudah++; }

    document.getElementById('praktikScoreValue').textContent = praktikScore;
    var detail = 'Terpenuhi ' + sudah + ' dari ' + practiceDetail.length + ' butir daftar periksa, ditambah penilaian kelengkapan isi dokumen.';
    if (docPasteDetected) detail = '[PERINGATAN] Terdeteksi percobaan tempel - nilai dipotong 50%. ' + detail;
    document.getElementById('praktikResultDetail').textContent = detail;
    document.getElementById('praktikResultPanel').style.display = 'block';

    tryFinalize();
  });

  // ============================================================
  // NILAI AKHIR &amp; PENGIRIMAN
  // ============================================================
  function tryFinalize(){
    if (evalFinalScore === null || praktikScore === null) return;

    combinedFinalScore = Math.round((evalFinalScore * 0.5) + (praktikScore * 0.5));
    var panel = document.getElementById('finalResultPanel');
    panel.style.display = 'block';
    document.getElementById('finalResultLabel').textContent = 'Nilai Akhir Evaluasi (Terkirim ke Guru)';
    document.getElementById('finalScoreValue').textContent = combinedFinalScore;
    document.getElementById('finalResultDetail').textContent =
      'Nilai akhir = 50% Kuis Tahap 1 (' + evalFinalScore + ') + 50% Latihan Praktik (' + praktikScore + ') = ' + combinedFinalScore + '. Mengirim ke guru...';

    document.getElementById('btnUnduhPDF').style.display = 'inline-block';
    document.getElementById('pdfHint').style.display = 'block';

    var id = getIdentitas();
    fetch(GAS_ENDPOINT, {
      method: 'POST',
      mode: 'no-cors',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ noAbsen: id.noAbsen, nama: id.nama, kelas: id.kelas, skor: combinedFinalScore, judulMateri: JUDUL_MATERI })
    }).then(function(){
      document.getElementById('finalResultDetail').textContent =
        'Nilai akhir = 50% Kuis Tahap 1 (' + evalFinalScore + ') + 50% Latihan Praktik (' + praktikScore + ') = ' + combinedFinalScore + '. Nilai sudah terkirim ke guru. Unduh bukti PDF di bawah, lalu catat nilainya di buku catatan.';
    }).catch(function(){
      document.getElementById('finalResultDetail').textContent =
        'Nilai akhir = ' + combinedFinalScore + ', tetapi gagal terkirim otomatis. Unduh bukti PDF di bawah sebagai bukti fisik dan tunjukkan kepada guru.';
    });
  }

  // ============================================================
  // BUKTI PDF F4
  // ============================================================
  function escapeHTML(str){
    return String(str).replace(/&amp;/g,'&amp;amp;').replace(/&lt;/g,'&amp;lt;').replace(/&gt;/g,'&amp;gt;');
  }

  function generatePrintArea(){
    var id = getIdentitas();
    var now = new Date();
    var tanggalCetak = now.toLocaleDateString('id-ID', { day:'numeric', month:'long', year:'numeric' }) +
      ', ' + now.toLocaleTimeString('id-ID', { hour:'2-digit', minute:'2-digit' });

    var html = '';
    html += '&lt;div class="pd-kop"&gt;&lt;h1&gt;BUKTI MENGERJAKAN EVALUASI&lt;/h1&gt;&lt;p&gt;' + escapeHTML(JUDUL_MATERI) + '&lt;/p&gt;&lt;p&gt;Master Penyusun Laporan PKL - Informatika Fase E Kelas X SMK&lt;/p&gt;&lt;/div&gt;';

    html += '&lt;table class="pd-meta-table"&gt;';
    html += '&lt;tr&gt;&lt;td class="lbl"&gt;No. Absen&lt;/td&gt;&lt;td&gt;: ' + escapeHTML(id.noAbsen) + '&lt;/td&gt;&lt;td class="lbl"&gt;Dicetak&lt;/td&gt;&lt;td&gt;: ' + tanggalCetak + '&lt;/td&gt;&lt;/tr&gt;';
    html += '&lt;tr&gt;&lt;td class="lbl"&gt;Nama&lt;/td&gt;&lt;td&gt;: ' + escapeHTML(id.nama) + '&lt;/td&gt;&lt;td class="lbl"&gt;Percobaan Kuis&lt;/td&gt;&lt;td&gt;: ' + evalAttemptScores.length + ' dari ' + MAX_ATTEMPTS + ' kali&lt;/td&gt;&lt;/tr&gt;';
    html += '&lt;tr&gt;&lt;td class="lbl"&gt;Kelas&lt;/td&gt;&lt;td&gt;: ' + escapeHTML(id.kelas) + '&lt;/td&gt;&lt;td class="lbl"&gt;KKM&lt;/td&gt;&lt;td&gt;: ' + KKM + '&lt;/td&gt;&lt;/tr&gt;';
    html += '&lt;/table&gt;';

    html += '&lt;div class="pd-section-title"&gt;Ringkasan Nilai&lt;/div&gt;';
    html += '&lt;table class="pd-score-table"&gt;&lt;tr&gt;&lt;th&gt;Komponen&lt;/th&gt;&lt;th&gt;Bobot&lt;/th&gt;&lt;th&gt;Skor&lt;/th&gt;&lt;/tr&gt;';
    html += '&lt;tr&gt;&lt;td&gt;Tahap 1 - Kuis Drag &amp;amp; Drop (' + EVAL_SOAL_COUNT + ' soal)&lt;/td&gt;&lt;td&gt;50%&lt;/td&gt;&lt;td&gt;' + (evalFinalScore !== null ? evalFinalScore : '-') + '&lt;/td&gt;&lt;/tr&gt;';
    html += '&lt;tr&gt;&lt;td&gt;Tahap 2 - Latihan Praktik Dokumen F4&lt;/td&gt;&lt;td&gt;50%&lt;/td&gt;&lt;td&gt;' + (praktikScore !== null ? praktikScore : '-') + '&lt;/td&gt;&lt;/tr&gt;';
    html += '&lt;tr&gt;&lt;th&gt;Nilai Akhir&lt;/th&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;' + (combinedFinalScore !== null ? combinedFinalScore : '-') + '&lt;/th&gt;&lt;/tr&gt;&lt;/table&gt;';

    if (evalAttemptScores.length){
      var rincian = [];
      for (var i = 0; i &lt; evalAttemptScores.length; i++){ rincian.push('Percobaan ' + (i+1) + ' = ' + evalAttemptScores[i]); }
      html += '&lt;p style="font-size:9.5pt; margin-bottom:12px;"&gt;Rincian percobaan kuis: ' + rincian.join('; ') + '.&lt;/p&gt;';
    }

    if (evalFinalAttemptIndex !== null &amp;&amp; evalAttemptLogs[evalFinalAttemptIndex]){
      html += '&lt;div class="pd-section-title"&gt;Rincian Jawaban Kuis (Percobaan ke-' + (evalFinalAttemptIndex+1) + ')&lt;/div&gt;';
      html += '&lt;table class="pd-eval-table"&gt;&lt;tr&gt;&lt;th style="width:40%;"&gt;Pertanyaan&lt;/th&gt;&lt;th style="width:20%;"&gt;Jawaban Murid&lt;/th&gt;&lt;th style="width:20%;"&gt;Jawaban Benar&lt;/th&gt;&lt;th style="width:20%;"&gt;Status&lt;/th&gt;&lt;/tr&gt;';
      evalAttemptLogs[evalFinalAttemptIndex].forEach(function(r){
        html += '&lt;tr&gt;&lt;td&gt;' + escapeHTML(r.q) + '&lt;/td&gt;&lt;td&gt;' + escapeHTML(r.userAns) + '&lt;/td&gt;&lt;td&gt;' + escapeHTML(r.correctAns) + '&lt;/td&gt;&lt;td&gt;' +
          (r.isCorrect ? '&lt;span class="pd-verdict ok"&gt;BENAR&lt;/span&gt;' : '&lt;span class="pd-verdict no"&gt;SALAH&lt;/span&gt;') + '&lt;/td&gt;&lt;/tr&gt;';
      });
      html += '&lt;/table&gt;';
    }

    if (practiceDetail.length){
      html += '&lt;div class="pd-section-title"&gt;Daftar Periksa Latihan Praktik&lt;/div&gt;';
      html += '&lt;table class="pd-eval-table"&gt;&lt;tr&gt;&lt;th style="width:55%;"&gt;Butir yang Dinilai&lt;/th&gt;&lt;th style="width:25%;"&gt;Keterangan&lt;/th&gt;&lt;th style="width:20%;"&gt;Status&lt;/th&gt;&lt;/tr&gt;';
      practiceDetail.forEach(function(d){
        html += '&lt;tr&gt;&lt;td&gt;' + escapeHTML(d.label) + '&lt;/td&gt;&lt;td&gt;' + escapeHTML(d.ket) + '&lt;/td&gt;&lt;td&gt;' +
          (d.ok ? '&lt;span class="pd-verdict ok"&gt;TERPENUHI&lt;/span&gt;' : '&lt;span class="pd-verdict no"&gt;BELUM&lt;/span&gt;') + '&lt;/td&gt;&lt;/tr&gt;';
      });
      html += '&lt;/table&gt;';
    }

    html += '&lt;div class="pd-section-title"&gt;Hasil Ketikan Dokumen Laporan PKL&lt;/div&gt;';
    var pages = getDocPages();
    for (var pg = 0; pg &lt; pages.length; pg++){
      var label = pages[pg].getAttribute('data-pagelabel') || ('Halaman ' + (pg+1));
      var teks = pages[pg].innerText.trim();
      html += '&lt;div class="pd-answer-item"&gt;&lt;div class="pd-anum"&gt;' + escapeHTML(label) + '&lt;/div&gt;' +
        '&lt;div class="pd-atext"&gt;' + (teks ? escapeHTML(teks) : '(kosong)') + '&lt;/div&gt;&lt;/div&gt;';
    }

    html += '&lt;div class="pd-signature"&gt;' +
      '&lt;div&gt;Peserta Didik&lt;div class="line"&gt;' + escapeHTML(id.nama) + '&lt;/div&gt;&lt;/div&gt;' +
      '&lt;div&gt;Guru Mata Pelajaran&lt;div class="line"&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;' +
      '&lt;/div&gt;';

    html += '&lt;p class="pd-footnote"&gt;Dokumen ini dihasilkan otomatis sebagai bukti pengerjaan evaluasi. Cetak pada kertas F4 dan serahkan kepada guru mata pelajaran.&lt;/p&gt;';

    document.getElementById('printArea').innerHTML = html;
  }

  document.getElementById('btnUnduhPDF').addEventListener('click', function(){
    generatePrintArea();
    setTimeout(function(){ window.print(); }, 150);
  });
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/integrasi-dokumen-dan-otomasi-pengolah.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-3348254440559995971</guid><pubDate>Tue, 15 Sep 2026 00:32:28 +0000</pubDate><atom:updated>2026-09-15T07:32:28.318+07:00</atom:updated><title>Evaluasi Informasi dalam Teks Ceramah</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Evaluasi Informasi dalam Teks Ceramah - Kelas XI SMK&lt;/title&gt;
&lt;style&gt;
  :root{
    --ink:#1B2430;
    --ink-soft:#4A5568;
    --paper:#F7F5F0;
    --card:#FFFFFF;
    --line:#D8D2C4;
    --amber:#E8A33D;
    --amber-dark:#8A5A12;
    --green:#3F7D58;
    --green-bg:#E7F2EB;
    --red:#C1443B;
    --red-bg:#FBEAE8;
  }
  *{box-sizing:border-box;}
  body{margin:0;background:var(--paper);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;line-height:1.6;}
  .wrap{max-width:800px;margin:0 auto;padding:28px 20px 90px;}
  header.top{border-bottom:3px solid var(--ink);padding-bottom:16px;margin-bottom:10px;}
  .kode{font-size:12px;letter-spacing:.04em;color:var(--ink-soft);}
  h1{font-size:23px;margin:6px 0 4px;font-weight:800;}
  .sub{font-size:14px;color:var(--ink-soft);margin:0;}
  .cpbox{background:#FFF6E6;border:1px solid var(--amber);border-left:4px solid var(--amber-dark);border-radius:6px;padding:12px 14px;margin-top:14px;font-size:13.5px;}
  nav.tabs{position:sticky;top:0;background:var(--paper);z-index:10;display:flex;gap:8px;padding:12px 0;border-bottom:1px solid var(--line);margin-bottom:20px;flex-wrap:wrap;}
  nav.tabs a{font-size:13px;font-weight:700;text-decoration:none;color:var(--ink);background:var(--card);border:1px solid var(--line);padding:8px 14px;border-radius:20px;}
  nav.tabs a:hover{background:#F1EFE7;}
  section{margin-bottom:10px;}
  .card{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:22px;margin-bottom:20px;}
  h2.secTitle{font-size:19px;margin:0 0 16px;padding-bottom:10px;border-bottom:3px solid var(--ink);}
  h3{font-size:16px;color:var(--amber-dark);margin:22px 0 8px;}
  h3:first-of-type{margin-top:0;}
  p{font-size:14.5px;margin:8px 0;}
  ul,ol{font-size:14.5px;padding-left:22px;}
  li{margin-bottom:6px;}
  table.materi-table{width:100%;border-collapse:collapse;font-size:13.5px;margin:10px 0 16px;}
  table.materi-table th,table.materi-table td{border:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:top;}
  table.materi-table th{background:#F1EFE7;}
  .contoh-box{background:#FBF9F2;border:1px solid var(--line);border-left:4px solid var(--amber);padding:14px 16px;border-radius:4px;margin:12px 0;font-size:14px;white-space:pre-line;}
  .contoh-box .ct-label{font-weight:800;font-size:12.5px;color:var(--amber-dark);margin-bottom:6px;}
  .rangkuman{background:var(--green-bg);border:1px solid var(--green);border-radius:6px;padding:14px 16px;}
  .rangkuman li{margin-bottom:4px;}
  .taskbox{background:#FFF6E6;border:1px solid var(--amber);border-left:4px solid var(--amber-dark);border-radius:6px;padding:12px 14px;margin:12px 0;font-size:13.5px;}
  label{display:block;font-size:13px;color:var(--ink-soft);margin:12px 0 4px;}
  input[type=text],input[type=number]{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:6px;font-size:15px;background:#fff;color:var(--ink);}
  input:focus{outline:2px solid var(--amber);outline-offset:1px;}
  .btn{display:inline-block;border:none;border-radius:6px;padding:12px 20px;font-size:15px;font-weight:700;cursor:pointer;margin-top:16px;margin-right:8px;}
  .btn-primary{background:var(--ink);color:#fff;}
  .btn-primary:hover{background:#000;}
  .btn-secondary{background:var(--amber);color:#3A2400;}
  .btn-secondary:hover{background:#d6912e;}
  .btn:disabled{opacity:.5;cursor:not-allowed;}
  .err{color:var(--red);font-size:13px;margin-top:8px;display:none;}
  .stimulus{background:#FBF9F2;border:1px solid var(--line);border-left:4px solid var(--amber);padding:14px 16px;border-radius:4px;margin-bottom:14px;font-size:14.5px;white-space:pre-line;}
  .stimulus .st-title{font-weight:800;font-size:14px;margin-bottom:6px;color:var(--amber-dark);}
  .qgroup{margin-bottom:26px;}
  .q{margin-bottom:20px;padding-bottom:18px;border-bottom:1px solid var(--line);}
  .q:last-child{border-bottom:none;}
  .q-num{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--ink-soft);}
  .q-text{font-size:15px;font-weight:600;margin:4px 0 10px;}
  .opt{display:flex;gap:10px;align-items:flex-start;padding:8px 10px;border-radius:6px;cursor:pointer;border:1px solid transparent;font-size:14.5px;}
  .opt:hover{background:#F1EFE7;}
  .opt input{margin-top:3px;}
  .opt.selected{background:#FCEFDA;border-color:var(--amber);}
  #progressWrap{position:sticky;top:0;background:var(--paper);padding:10px 0;z-index:5;border-bottom:1px solid var(--line);margin-bottom:16px;}
  #progressBar{height:8px;background:var(--line);border-radius:4px;overflow:hidden;}
  #progressFill{height:100%;background:var(--amber);width:0%;transition:width .2s;}
  #progressText{font-size:12px;color:var(--ink-soft);margin-top:4px;}
  .result-score{font-size:48px;font-weight:800;margin:6px 0;}
  .pass{color:var(--green);}
  .fail{color:var(--red);}
  .result-box{border-radius:8px;padding:20px;text-align:center;}
  .result-box.pass-bg{background:var(--green-bg);border:1px solid var(--green);}
  .result-box.fail-bg{background:var(--red-bg);border:1px solid var(--red);}
  .hidden{display:none;}
  .essaySection{margin-top:10px;}
  .essayQ{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--line);}
  .essayQ:last-child{border-bottom:none;}
  .essayQ-num{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--ink-soft);}
  .essayQ-text{font-size:15px;font-weight:600;margin:4px 0 8px;}
  textarea.essayInput{width:100%;min-height:80px;padding:10px 12px;border:1px solid var(--line);border-radius:6px;font-size:14.5px;font-family:inherit;resize:vertical;background:#fff;color:var(--ink);}
  textarea.essayInput:focus{outline:2px solid var(--amber);outline-offset:1px;}
  .charCount{font-size:12px;color:var(--ink-soft);text-align:right;margin-top:3px;}
  .charCount.over{color:var(--red);font-weight:700;}
  .pasteWarn{font-size:12.5px;color:var(--red);font-weight:700;margin-top:4px;display:none;}
  .pasteWarn.show{display:block;}
  .sectionLabel{font-size:13px;font-weight:800;letter-spacing:.02em;color:var(--amber-dark);margin:26px 0 4px;border-top:3px solid var(--ink);padding-top:14px;}
  #printArea{display:none;}
  @media print{
    body *{visibility:hidden;}
    #printArea, #printArea *{visibility:visible;}
    #printArea{display:block;position:absolute;left:0;top:0;width:100%;}
    @page{size:215mm 330mm;margin:14mm;}
    .pbreak{break-before:page;page-break-before:always;}
    .noBreak{break-inside:avoid;page-break-inside:avoid;}
  }
  .pf-kop{text-align:center;border-bottom:3px solid #000;padding-bottom:10px;margin-bottom:14px;}
  .pf-kop h2{margin:0;font-size:16px;}
  .pf-kop p{margin:2px 0 0;font-size:12px;}
  .pf-identitas{width:100%;border-collapse:collapse;font-size:13px;margin-bottom:14px;}
  .pf-identitas td{padding:3px 4px;vertical-align:top;}
  .pf-identitas td.lbl{width:150px;color:#333;}
  .pf-skorbox{border:2px solid #000;border-radius:4px;padding:10px 14px;margin-bottom:16px;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;}
  .pf-skoritem{font-size:12px;}
  .pf-skoritem b{display:block;font-size:20px;}
  .pf-essay-q{font-size:13px;font-weight:700;margin-bottom:3px;}
  .pf-essay-a{font-size:12.5px;border:1px solid #999;border-radius:4px;padding:6px 8px;min-height:28px;white-space:pre-wrap;background:#FAFAF7;}
  .pf-ttd{display:flex;justify-content:space-between;margin-top:40px;font-size:12.5px;}
  .pf-ttd div{width:45%;text-align:center;}
  .pf-ttd .garis{margin-top:56px;border-top:1px solid #000;padding-top:4px;}
  .pf-note{font-size:11px;color:#555;margin-top:10px;}
  footer{margin-top:30px;font-size:12px;color:var(--ink-soft);text-align:center;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="wrap"&gt;

  &lt;header class="top"&gt;
    &lt;div class="kode"&gt;DOK/B.INDO/XI/CERAMAH-01&lt;/div&gt;
    &lt;h1&gt;Evaluasi Informasi dalam Teks Ceramah&lt;/h1&gt;
    &lt;p class="sub"&gt;Bahasa Indonesia - Kelas XI SMK - Mengidentifikasi Gagasan Pokok dan Gagasan Pendukung serta Mengevaluasi Informasi dalam Teks Ceramah&lt;/p&gt;
    &lt;div class="cpbox"&gt;&lt;b&gt;Tujuan Pembelajaran:&lt;/b&gt; Murid mampu mengevaluasi informasi dalam teks ceramah serta mengidentifikasi gagasan pokok dan gagasan pendukung.&lt;/div&gt;
  &lt;/header&gt;

  &lt;nav class="tabs"&gt;
    &lt;a href="#materi"&gt;1. Materi&lt;/a&gt;
    &lt;a href="#kelompok"&gt;2. Tugas Kelompok&lt;/a&gt;
    &lt;a href="#individu"&gt;3. Tugas Individu&lt;/a&gt;
  &lt;/nav&gt;

  &lt;section id="materi"&gt;
  &lt;div class="card"&gt;
    &lt;h2 class="secTitle"&gt;Materi: Evaluasi Informasi dalam Teks Ceramah&lt;/h2&gt;

    &lt;h3&gt;A. Mengenal Teks Ceramah&lt;/h3&gt;
    &lt;p&gt;&lt;b&gt;Teks ceramah&lt;/b&gt; adalah teks yang berisi pemaparan gagasan, pengetahuan, atau informasi mengenai suatu topik tertentu yang disampaikan seseorang (penceramah) secara lisan di hadapan orang banyak, dengan tujuan memberi pengetahuan, menyadarkan, meyakinkan, atau mengajak pendengar untuk melakukan sesuatu. Ceramah biasa ditemukan dalam kegiatan keagamaan, upacara bendera, seminar, penyuluhan, hingga pengarahan kerja di dunia industri.&lt;/p&gt;
    &lt;p&gt;Ciri-ciri umum teks ceramah antara lain:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Disampaikan oleh satu orang penceramah kepada khalayak (bersifat satu arah, meskipun bisa diakhiri sesi tanya jawab).&lt;/li&gt;
      &lt;li&gt;Membahas satu topik tertentu secara sistematis dari pembukaan hingga penutup.&lt;/li&gt;
      &lt;li&gt;Bersifat persuasif, yaitu berusaha meyakinkan dan mengajak pendengar.&lt;/li&gt;
      &lt;li&gt;Didukung oleh data, fakta, atau contoh untuk memperkuat gagasan yang disampaikan.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h3&gt;B. Struktur Teks Ceramah&lt;/h3&gt;
    &lt;p&gt;Secara umum, teks ceramah disusun dalam tiga bagian struktur berikut.&lt;/p&gt;
    &lt;table class="materi-table"&gt;
      &lt;tr&gt;&lt;th&gt;Bagian&lt;/th&gt;&lt;th&gt;Isi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Pembukaan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Berisi salam, sapaan kepada hadirin, ucapan syukur, dan pengantar topik yang akan dibahas.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Isi&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Berisi pemaparan gagasan pokok dan gagasan pendukung mengenai topik, disertai data, fakta, contoh, atau alasan logis, serta ajakan (persuasi) kepada pendengar.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Penutup&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Berisi simpulan, harapan penceramah, dan salam penutup.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h3&gt;C. Kaidah Kebahasaan Teks Ceramah&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;b&gt;Kata ganti orang pertama jamak&lt;/b&gt; (kita, kami) untuk membangun kedekatan dengan pendengar. Contoh: "Marilah kita jaga kebersihan lingkungan sekolah."&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Kata sapaan&lt;/b&gt; untuk menyapa pendengar. Contoh: "Bapak/Ibu guru dan teman-teman yang saya hormati."&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Kata teknis/istilah&lt;/b&gt; sesuai topik yang dibahas. Contoh: "disiplin kerja", "Otoritas Jasa Keuangan".&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Konjungsi argumentatif&lt;/b&gt; seperti "oleh karena itu", "dengan demikian", "sebab" untuk menghubungkan alasan dan simpulan.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Kata kerja mental&lt;/b&gt; yang menyatakan sikap/pandangan, seperti "menyimpulkan", "berpendapat", "meyakini".&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Kalimat persuasif&lt;/b&gt; berupa ajakan, seperti "marilah", "mari kita".&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h3&gt;D. Gagasan Pokok dan Gagasan Pendukung&lt;/h3&gt;
    &lt;p&gt;&lt;b&gt;Gagasan pokok&lt;/b&gt; (ide pokok/ide utama) adalah inti persoalan yang dibahas dalam sebuah paragraf atau bagian teks. Gagasan pokok biasanya dituangkan dalam satu kalimat yang disebut kalimat utama.&lt;/p&gt;
    &lt;p&gt;&lt;b&gt;Gagasan pendukung&lt;/b&gt; (ide penjelas) adalah uraian, contoh, data, fakta, atau alasan yang berfungsi memperjelas, memperkuat, atau menjabarkan gagasan pokok. Gagasan pendukung dituangkan dalam kalimat-kalimat penjelas.&lt;/p&gt;
    &lt;p&gt;Berdasarkan letak gagasan pokoknya, paragraf dapat dibedakan menjadi tiga pola berikut.&lt;/p&gt;
    &lt;table class="materi-table"&gt;
      &lt;tr&gt;&lt;th&gt;Pola&lt;/th&gt;&lt;th&gt;Letak Gagasan Pokok&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Deduktif&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Gagasan pokok terletak di awal paragraf, diikuti kalimat-kalimat penjelas.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Induktif&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Gagasan pokok terletak di akhir paragraf, didahului kalimat-kalimat penjelas.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;Campuran&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Gagasan pokok muncul di awal, ditegaskan kembali di akhir paragraf.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;p&gt;Langkah menemukan gagasan pokok dan gagasan pendukung:&lt;/p&gt;
    &lt;ol&gt;
      &lt;li&gt;Bacalah paragraf secara utuh untuk memahami keseluruhan isi.&lt;/li&gt;
      &lt;li&gt;Temukan kalimat yang memuat inti pembahasan (biasanya di awal atau akhir paragraf).&lt;/li&gt;
      &lt;li&gt;Tandai kalimat-kalimat lain sebagai gagasan pendukung jika berisi penjelasan, contoh, sebab-akibat, atau data yang mendukung gagasan pokok tersebut.&lt;/li&gt;
      &lt;li&gt;Rumuskan gagasan pokok dalam satu kalimat singkat menggunakan kata-katamu sendiri.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h3&gt;E. Evaluasi Informasi dalam Teks Ceramah&lt;/h3&gt;
    &lt;p&gt;&lt;b&gt;Mengevaluasi informasi&lt;/b&gt; berarti menilai kelayakan, kebenaran, dan kualitas suatu informasi berdasarkan kriteria tertentu, bukan sekadar menerima dan memahami informasi tersebut secara mentah-mentah. Kemampuan ini sangat penting bagi siswa SMK karena di dunia nyata kita sering menemukan ceramah, motivasi kerja, maupun informasi lisan lain yang isinya belum tentu benar, akurat, atau dapat dipertanggungjawabkan - termasuk potensi berita bohong (hoaks) dan opini yang dibungkus seolah-olah fakta.&lt;/p&gt;
    &lt;p&gt;Berikut enam kriteria yang digunakan untuk mengevaluasi informasi dalam teks ceramah.&lt;/p&gt;
    &lt;table class="materi-table"&gt;
      &lt;tr&gt;&lt;th&gt;Kriteria&lt;/th&gt;&lt;th&gt;Indikator&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;1. Kredibilitas&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Apakah penceramah/sumber informasi memiliki keahlian, pengalaman, atau otoritas yang relevan dengan topik yang dibahas?&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;2. Keakuratan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Apakah data, fakta, dan angka yang disampaikan benar, dapat diverifikasi, dan berasal dari sumber yang jelas (bukan sekadar opini pribadi)?&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;3. Kelogisan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Apakah alasan atau argumen yang dikemukakan masuk akal dan hubungan sebab-akibatnya wajar, bukan generalisasi berlebihan?&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;4. Objektivitas&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Apakah informasi disampaikan secara berimbang, tidak memihak secara berlebihan, dan minim bias pribadi?&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;5. Kelengkapan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Apakah informasi yang disampaikan cukup lengkap sehingga tidak menyesatkan pendengar?&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;&lt;b&gt;6. Kesesuaian tujuan&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Apakah isi ceramah relevan dan sesuai dengan topik serta tujuan ceramah yang disampaikan di awal?&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;p&gt;Langkah-langkah mengevaluasi informasi dalam teks ceramah:&lt;/p&gt;
    &lt;ol&gt;
      &lt;li&gt;Membaca/menyimak teks ceramah secara menyeluruh.&lt;/li&gt;
      &lt;li&gt;Mengidentifikasi struktur teks (pembukaan, isi, penutup).&lt;/li&gt;
      &lt;li&gt;Menemukan gagasan pokok dan gagasan pendukung pada bagian isi.&lt;/li&gt;
      &lt;li&gt;Menguji setiap informasi penting menggunakan keenam kriteria evaluasi di atas.&lt;/li&gt;
      &lt;li&gt;Mencatat temuan (informasi yang kuat maupun yang lemah/perlu diverifikasi).&lt;/li&gt;
      &lt;li&gt;Menyimpulkan dan memberi rekomendasi, misalnya perbaikan yang diperlukan agar informasi lebih dapat dipercaya.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h3&gt;F. Contoh Analisis Singkat&lt;/h3&gt;
    &lt;div class="contoh-box"&gt;
      &lt;div class="ct-label"&gt;CONTOH PENGGALAN CERAMAH&lt;/div&gt;"...Sayangnya, masih banyak siswa yang membuang sampah sembarangan. Menurut pengamatan pribadi saya, hampir separuh siswa di sekolah ini membuang sampah tidak pada tempatnya. Oleh karena itu, marilah kita membiasakan diri membuang sampah pada tempatnya..."&lt;/div&gt;
    &lt;table class="materi-table"&gt;
      &lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Analisis&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Gagasan pokok&lt;/td&gt;&lt;td&gt;Ajakan untuk membiasakan diri membuang sampah pada tempatnya.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Gagasan pendukung&lt;/td&gt;&lt;td&gt;Klaim bahwa hampir separuh siswa membuang sampah sembarangan.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Evaluasi (Keakuratan)&lt;/td&gt;&lt;td&gt;Lemah - klaim "hampir separuh siswa" hanya berdasarkan pengamatan pribadi, bukan data resmi (misalnya survei atau catatan petugas kebersihan), sehingga akurasinya perlu dipertanyakan.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Rekomendasi&lt;/td&gt;&lt;td&gt;Penceramah sebaiknya mencantumkan data resmi agar informasi lebih kredibel dan akurat.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;

    &lt;h3&gt;G. Rangkuman&lt;/h3&gt;
    &lt;div class="rangkuman"&gt;
      &lt;ul&gt;
        &lt;li&gt;Teks ceramah terdiri atas struktur pembukaan, isi, dan penutup.&lt;/li&gt;
        &lt;li&gt;Gagasan pokok adalah inti pembahasan; gagasan pendukung memperjelas gagasan pokok.&lt;/li&gt;
        &lt;li&gt;Pola letak gagasan pokok: deduktif (awal), induktif (akhir), campuran (awal dan akhir).&lt;/li&gt;
        &lt;li&gt;Mengevaluasi informasi berarti menilai kredibilitas, keakuratan, kelogisan, objektivitas, kelengkapan, dan kesesuaian tujuan suatu informasi - bukan menerimanya begitu saja.&lt;/li&gt;
        &lt;li&gt;Kemampuan mengevaluasi informasi penting agar kita tidak mudah terpengaruh oleh informasi lisan yang belum tentu benar.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;/section&gt;

  &lt;section id="kelompok"&gt;
  &lt;div class="card"&gt;
    &lt;h2 class="secTitle"&gt;Tugas Kelompok&lt;/h2&gt;
    &lt;p&gt;&lt;b&gt;Judul:&lt;/b&gt; Mengevaluasi Informasi dalam Teks Ceramah Secara Berkelompok&lt;/p&gt;
    &lt;div class="taskbox"&gt;&lt;b&gt;Ketentuan kelompok:&lt;/b&gt; 1 kelompok terdiri atas 4-5 siswa, dibagi oleh guru atau berdasarkan kesepakatan kelas.&lt;/div&gt;

    &lt;h3&gt;Langkah Pengerjaan&lt;/h3&gt;
    &lt;ol&gt;
      &lt;li&gt;Carilah atau gunakan satu teks/rekaman ceramah (transkrip pidato, ceramah keagamaan, motivasi kerja, atau video ceramah singkat dari sumber yang jelas). Teks/video boleh ditentukan guru atau dicari sendiri oleh kelompok.&lt;/li&gt;
      &lt;li&gt;Bacalah/simaklah teks ceramah tersebut secara menyeluruh bersama anggota kelompok.&lt;/li&gt;
      &lt;li&gt;Identifikasi struktur teks ceramah (pembukaan, isi, penutup).&lt;/li&gt;
      &lt;li&gt;Tentukan gagasan pokok pada setiap paragraf bagian isi.&lt;/li&gt;
      &lt;li&gt;Tentukan minimal dua gagasan pendukung untuk setiap gagasan pokok yang ditemukan.&lt;/li&gt;
      &lt;li&gt;Evaluasi informasi dalam ceramah tersebut menggunakan enam kriteria evaluasi (kredibilitas, keakuratan, kelogisan, objektivitas, kelengkapan, kesesuaian tujuan). Untuk tiap kriteria, berikan skor 1-4 beserta alasannya.&lt;/li&gt;
      &lt;li&gt;Simpulkan apakah ceramah tersebut layak dijadikan rujukan/dipercaya, disertai alasan dan rekomendasi perbaikan (jika ada).&lt;/li&gt;
      &lt;li&gt;Susun laporan tertulis, lalu presentasikan hasilnya di depan kelas (sekitar 10 menit per kelompok, termasuk sesi tanya jawab).&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h3&gt;Format Laporan&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;Halaman judul: judul tugas, nama kelompok, anggota, dan kelas.&lt;/li&gt;
      &lt;li&gt;Identitas teks ceramah yang dianalisis: judul/topik, sumber, nama penceramah (jika ada), tanggal/tautan.&lt;/li&gt;
      &lt;li&gt;Tabel gagasan pokok dan gagasan pendukung per paragraf isi.&lt;/li&gt;
      &lt;li&gt;Tabel hasil evaluasi enam kriteria beserta skor dan alasan.&lt;/li&gt;
      &lt;li&gt;Simpulan dan rekomendasi.&lt;/li&gt;
      &lt;li&gt;Lampiran: naskah/tautan teks ceramah yang dianalisis.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h3&gt;Rubrik Penilaian (skor 1-4 per aspek)&lt;/h3&gt;
    &lt;table class="materi-table"&gt;
      &lt;tr&gt;&lt;th&gt;Aspek&lt;/th&gt;&lt;th&gt;Deskripsi&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan identifikasi struktur&lt;/td&gt;&lt;td&gt;Kesesuaian pembagian pembukaan, isi, dan penutup dengan teks yang dianalisis.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Ketepatan gagasan pokok &amp;amp; pendukung&lt;/td&gt;&lt;td&gt;Kesesuaian dan kelengkapan gagasan pokok serta gagasan pendukung yang ditemukan.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kedalaman evaluasi&lt;/td&gt;&lt;td&gt;Ketajaman analisis pada keenam kriteria evaluasi beserta alasannya.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kualitas simpulan &amp;amp; rekomendasi&lt;/td&gt;&lt;td&gt;Kejelasan dan kelogisan simpulan serta rekomendasi yang diberikan.&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Kerja sama &amp;amp; presentasi&lt;/td&gt;&lt;td&gt;Pembagian tugas antaranggota serta kejelasan penyampaian saat presentasi.&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;p style="font-size:13px;color:var(--ink-soft);"&gt;Total skor maksimal 20, dikonversi ke skala 100 oleh guru. Batas waktu pengumpulan dan jadwal presentasi ditentukan oleh guru mata pelajaran.&lt;/p&gt;
  &lt;/div&gt;
  &lt;/section&gt;

  &lt;section id="individu"&gt;
  &lt;div class="card" id="screen-intro"&gt;
    &lt;h2 class="secTitle"&gt;Tugas Individu&lt;/h2&gt;
    &lt;p style="font-size:14px;"&gt;Tugas individu terdiri atas &lt;b&gt;25 soal pilihan ganda&lt;/b&gt; dan &lt;b&gt;5 soal esai&lt;/b&gt; dari materi Evaluasi Informasi dalam Teks Ceramah. Susunan soal dan urutan opsi jawaban diacak berbeda-beda untuk tiap peserta. Skor dihitung otomatis: &lt;b&gt;Skor PG x 70% + Skor Esai x 30%&lt;/b&gt;. Skor esai dihitung dari kesesuaian jawabanmu dengan konsep-konsep kunci pada tiap soal. &lt;b&gt;Jawaban esai wajib diketik sendiri&lt;/b&gt; - fitur tempel/paste (Ctrl+V, klik kanan, maupun tarik-lepas teks) dinonaktifkan pada kolom esai.&lt;/p&gt;
    &lt;div class="taskbox"&gt;
      &lt;b&gt;PENTING - wajib dilakukan setelah selesai mengerjakan:&lt;/b&gt;
      &lt;ol style="margin:6px 0 0;padding-left:18px;"&gt;
        &lt;li&gt;Jawaban dan skor akan &lt;b&gt;dikirim otomatis ke sistem sekolah&lt;/b&gt; begitu kamu menekan tombol Kirim.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Unduh&lt;/b&gt; bukti mengerjakan (PDF, ukuran F4) lewat tombol "Unduh Bukti Mengerjakan (PDF)".&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Cetak&lt;/b&gt; dokumen tersebut (maksimal 3 lembar).&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Serahkan hasil cetak ke guru mata pelajaran&lt;/b&gt; sebagai bukti fisik pengerjaan tugas.&lt;/li&gt;
      &lt;/ol&gt;
    &lt;/div&gt;

    &lt;label for="absen"&gt;No. Absen&lt;/label&gt;
    &lt;input type="number" id="absen" placeholder="Contoh: 14" min="1"&gt;
    &lt;label for="nama"&gt;Nama Lengkap&lt;/label&gt;
    &lt;input type="text" id="nama" placeholder="Tulis nama lengkapmu"&gt;
    &lt;label for="kelas"&gt;Kelas&lt;/label&gt;
    &lt;input type="text" id="kelas" placeholder="Contoh: XI TKR 1"&gt;
    &lt;div class="err" id="introErr"&gt;Isi No. Absen, Nama Lengkap, dan Kelas terlebih dahulu.&lt;/div&gt;
    &lt;button class="btn btn-primary" id="btnCekStatus"&gt;Lanjutkan&lt;/button&gt;
  &lt;/div&gt;

  &lt;div id="screen-status" class="card hidden"&gt;
    &lt;h2 id="statusGreeting"&gt;&lt;/h2&gt;
    &lt;div id="statusBody"&gt;&lt;/div&gt;
    &lt;button class="btn btn-secondary" id="btnMulai"&gt;Mulai Mengerjakan&lt;/button&gt;
    &lt;button class="btn hidden" id="btnUnduhStatus" style="background:var(--green);color:#fff;"&gt;Unduh Bukti Mengerjakan (PDF)&lt;/button&gt;
    &lt;p class="hidden" id="instruksiStatus" style="font-size:12.5px;color:var(--ink-soft);margin-top:6px;"&gt;Cetak dokumen ini (maks. 3 lembar F4) dan &lt;b&gt;serahkan ke guru mata pelajaran&lt;/b&gt; sebagai bukti pengerjaan.&lt;/p&gt;
    &lt;button class="btn" id="btnGanti" style="background:transparent;border:1px solid var(--line);"&gt;Ganti No. Absen&lt;/button&gt;
  &lt;/div&gt;

  &lt;div id="screen-quiz" class="hidden"&gt;
    &lt;div id="progressWrap"&gt;
      &lt;div id="progressBar"&gt;&lt;div id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;div id="progressText"&gt;0 dari 30 soal terjawab&lt;/div&gt;
    &lt;/div&gt;
    &lt;div id="quizBody"&gt;&lt;/div&gt;
    &lt;div class="err" id="quizErr"&gt;Masih ada soal yang belum dijawab. Lengkapi seluruh soal sebelum mengirim.&lt;/div&gt;
    &lt;button class="btn btn-primary" id="btnKirim"&gt;Kirim Jawaban&lt;/button&gt;
  &lt;/div&gt;

  &lt;div id="screen-result" class="card hidden"&gt;
    &lt;div id="resultBoxOuter"&gt;&lt;/div&gt;
    &lt;button class="btn btn-secondary hidden" id="btnUnduhHasil" style="background:var(--green);color:#fff;"&gt;Unduh Bukti Mengerjakan (PDF)&lt;/button&gt;
    &lt;p id="instruksiHasil" style="font-size:12.5px;color:var(--ink-soft);margin-top:6px;"&gt;Cetak dokumen ini (maks. 3 lembar F4) dan &lt;b&gt;serahkan ke guru mata pelajaran&lt;/b&gt; sebagai bukti pengerjaan.&lt;/p&gt;
    &lt;button class="btn" id="btnSelesai" style="background:transparent;border:1px solid var(--line);"&gt;Kembali ke Awal&lt;/button&gt;
  &lt;/div&gt;
  &lt;/section&gt;

  &lt;footer&gt;DOK/B.INDO/XI/CERAMAH-01 - Evaluasi Informasi dalam Teks Ceramah - 25 Butir Pilihan Ganda + 5 Esai&lt;/footer&gt;
&lt;/div&gt;

&lt;div id="printArea"&gt;&lt;/div&gt;

&lt;script&gt;
const GAS_URL = "https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";

// ===== 25 SOAL PILIHAN GANDA =====
// Grup 0: konsep dasar (tanpa teks stimulus) - 10 soal
// Grup 1-3: masing-masing punya 1 teks ceramah stimulus - 5 soal per grup
const GROUPS = [
{
  title: "Konsep Dasar Teks Ceramah",
  stimulus: null,
  questions: [
    {q:"Teks ceramah adalah teks yang berisi...", opts:["Cerita fiksi tentang tokoh rekaan","Pemaparan gagasan atau informasi di hadapan orang banyak mengenai suatu topik tertentu","Kumpulan resep masakan","Petunjuk langkah kerja suatu alat","Data statistik dalam bentuk tabel"], correct:1},
    {q:"Struktur teks ceramah terdiri atas tiga bagian utama, yaitu...", opts:["Orientasi, komplikasi, resolusi","Pembukaan, isi, penutup","Abstrak, orientasi, koda","Pernyataan umum, deretan penjelas, interpretasi","Tesis, argumentasi, penegasan ulang pendapat"], correct:1},
    {q:"Bagian ceramah yang berisi salam, sapaan, dan pengantar topik termasuk struktur bagian...", opts:["Isi","Penutup","Pembukaan","Argumen","Simpulan"], correct:2},
    {q:"Gagasan pokok adalah...", opts:["Kalimat pembuka paragraf tanpa makna khusus","Ide utama yang menjadi inti pembahasan dalam sebuah paragraf atau teks","Kalimat tanya dalam paragraf","Kutipan langsung dari sumber lain","Judul dari keseluruhan teks"], correct:1},
    {q:"Gagasan pendukung berfungsi untuk...", opts:["Menggantikan gagasan pokok","Memperjelas, memperkuat, atau memberi contoh terhadap gagasan pokok","Menghilangkan gagasan pokok","Menjadi judul paragraf","Menutup keseluruhan teks"], correct:1},
    {q:"Paragraf yang gagasan pokoknya terletak di awal paragraf disebut paragraf berpola...", opts:["Induktif","Deduktif","Campuran","Naratif","Deskriptif"], correct:1},
    {q:"Paragraf yang gagasan pokoknya terletak di akhir paragraf, setelah dijabarkan dengan kalimat-kalimat penjelas terlebih dahulu, disebut paragraf berpola...", opts:["Deduktif","Induktif","Deskriptif","Naratif","Argumentatif"], correct:1},
    {q:"Berikut ini yang termasuk kaidah kebahasaan khas teks ceramah adalah...", opts:["Penggunaan kalimat imperatif untuk langkah kerja","Penggunaan kata ganti orang pertama jamak (kita, kami) untuk membangun kedekatan dengan pendengar","Penggunaan konjungsi kronologis semata","Penggunaan rumus matematika","Penggunaan dialog antartokoh"], correct:1},
    {q:"Mengevaluasi informasi dalam teks ceramah berarti...", opts:["Menghafalkan seluruh isi ceramah kata demi kata","Menilai kelayakan, kebenaran, dan kualitas informasi yang disampaikan penceramah berdasarkan kriteria tertentu","Menuliskan ulang ceramah dengan bahasa sendiri tanpa penilaian","Menerima seluruh isi ceramah tanpa mempertanyakan kebenarannya","Mencari kesalahan ejaan dalam naskah ceramah"], correct:1},
    {q:"Berikut ini yang BUKAN merupakan kriteria evaluasi informasi dalam teks ceramah adalah...", opts:["Kredibilitas penceramah/sumber informasi","Keakuratan data dan fakta yang disampaikan","Kelogisan alasan atau argumen yang dikemukakan","Keindahan rima dan irama kalimat seperti dalam puisi","Kelengkapan informasi yang disampaikan"], correct:3}
  ]
},
{
  title: "Ceramah 1 - Pentingnya Menjaga Kebersihan Lingkungan Sekolah",
  stimulus: "Assalamualaikum warahmatullahi wabarakatuh. Bapak/Ibu guru dan teman-teman yang saya hormati, pagi ini saya ingin mengajak kita semua untuk merenungkan pentingnya menjaga kebersihan lingkungan sekolah.\n\nLingkungan sekolah yang bersih memberikan banyak manfaat bagi warga sekolah. Pertama, lingkungan yang bersih membuat suasana belajar menjadi lebih nyaman sehingga konsentrasi belajar meningkat. Kedua, kebersihan lingkungan dapat mencegah munculnya berbagai penyakit yang disebabkan oleh sampah dan genangan air, seperti demam berdarah dan diare. Ketiga, sekolah yang bersih mencerminkan kedisiplinan dan karakter warga sekolahnya, sehingga dapat meningkatkan citra sekolah di mata masyarakat.\n\nSayangnya, masih banyak siswa yang membuang sampah sembarangan. Menurut pengamatan pribadi saya, hampir separuh siswa di sekolah ini membuang sampah tidak pada tempatnya. Oleh karena itu, marilah kita membiasakan diri membuang sampah pada tempatnya, memilah sampah organik dan anorganik, serta aktif dalam kegiatan Jumat bersih.\n\nDemikian yang dapat saya sampaikan. Semoga kita semua dapat menjadi warga sekolah yang peduli terhadap kebersihan lingkungan. Wassalamualaikum warahmatullahi wabarakatuh.",
  questions: [
    {q:"Apa gagasan pokok pada paragraf kedua ceramah tersebut?", opts:["Ajakan membuang sampah pada tempatnya","Manfaat menjaga kebersihan lingkungan sekolah","Data siswa yang membuang sampah sembarangan","Ucapan salam pembuka","Harapan penutup ceramah"], correct:1},
    {q:"Berikut yang termasuk gagasan pendukung dari gagasan pokok \"manfaat kebersihan lingkungan\" adalah...", opts:["Membiasakan memilah sampah","Lingkungan bersih mencegah penyakit seperti demam berdarah","Ajakan mengikuti Jumat bersih","Salam penutup ceramah","Sapaan kepada guru dan teman-teman"], correct:1},
    {q:"Kalimat \"menurut pengamatan pribadi saya, hampir separuh siswa di sekolah ini membuang sampah tidak pada tempatnya\" merupakan informasi yang lemah dari segi kriteria evaluasi berupa...", opts:["Kelogisan argumen","Objektivitas penceramah","Keakuratan data, karena hanya berdasarkan perkiraan pribadi tanpa data resmi","Kesesuaian dengan tujuan ceramah","Kelengkapan struktur teks"], correct:2},
    {q:"Bagian ceramah yang berisi ajakan \"membiasakan diri membuang sampah pada tempatnya...\" termasuk struktur teks ceramah bagian...", opts:["Pembukaan","Isi (rangkaian argumen dan ajakan)","Penutup","Salam pembuka","Simpulan akhir"], correct:1},
    {q:"Jika penceramah ingin memperkuat keakuratan informasi pada bagian data siswa yang membuang sampah sembarangan, langkah yang paling tepat dilakukan adalah...", opts:["Mengganti kalimat ajakan menjadi kalimat perintah","Menyertakan data resmi, misalnya hasil survei atau catatan petugas kebersihan sekolah","Menghilangkan seluruh bagian data tersebut","Mengubah topik ceramah","Menambahkan lebih banyak ucapan salam"], correct:1}
  ]
},
{
  title: "Ceramah 2 - Membangun Sikap Disiplin di Dunia Kerja",
  stimulus: "Selamat pagi, Bapak/Ibu guru serta adik-adik kelas XI yang saya banggakan. Hari ini saya akan berbicara tentang pentingnya membangun sikap disiplin sejak di bangku SMK, sebagai bekal memasuki dunia kerja.\n\nDisiplin adalah kunci utama keberhasilan seseorang di tempat kerja. Seorang pekerja yang disiplin akan selalu datang tepat waktu, menyelesaikan tugas sesuai target, dan mematuhi prosedur keselamatan kerja yang berlaku. Sebaliknya, pekerja yang tidak disiplin berisiko menimbulkan kerugian, baik bagi dirinya sendiri maupun perusahaan tempatnya bekerja.\n\nMenurut sebuah artikel yang pernah saya baca di internet, seratus persen perusahaan besar di dunia hanya mau merekrut karyawan yang disiplin tanpa terkecuali. Oleh karena itu, mari kita mulai membiasakan disiplin dari hal-hal kecil, seperti datang ke sekolah tepat waktu, mengumpulkan tugas sesuai tenggat, dan menaati tata tertib praktik kerja lapangan.\n\nSemoga dengan kebiasaan disiplin ini, kita semua siap menghadapi dunia kerja yang penuh tantangan. Terima kasih.",
  questions: [
    {q:"Topik utama ceramah tersebut adalah...", opts:["Pentingnya kebersihan lingkungan","Pentingnya sikap disiplin dalam dunia kerja","Cara mencari pekerjaan","Sejarah dunia kerja","Tata tertib sekolah"], correct:1},
    {q:"Gagasan pendukung yang menjelaskan ciri pekerja disiplin pada ceramah tersebut adalah...", opts:["Pekerja disiplin selalu datang tepat waktu, menyelesaikan tugas sesuai target, dan mematuhi prosedur keselamatan kerja","Pekerja disiplin selalu mendapat gaji tinggi","Pekerja disiplin tidak perlu mengikuti pelatihan","Pekerja disiplin bebas dari pengawasan atasan","Pekerja disiplin selalu bekerja lembur"], correct:0},
    {q:"Pernyataan \"seratus persen perusahaan besar di dunia hanya mau merekrut karyawan yang disiplin tanpa terkecuali\" lemah dari segi kriteria evaluasi...", opts:["Kelengkapan struktur teks","Kredibilitas sumber dan keakuratan data, karena bersifat generalisasi mutlak tanpa sumber yang jelas","Kesesuaian dengan tujuan ceramah","Penggunaan kalimat persuasif","Struktur bagian penutup"], correct:1},
    {q:"Frasa \"sebuah artikel yang pernah saya baca di internet\" pada ceramah tersebut menunjukkan kelemahan dari sisi...", opts:["Kelogisan argumen","Sumber informasi yang tidak disebutkan secara jelas dan tidak dapat ditelusuri","Struktur kalimat yang tidak baku","Penggunaan konjungsi","Ketepatan ejaan"], correct:1},
    {q:"Simpulan paling tepat mengenai kualitas informasi dalam ceramah tersebut adalah...", opts:["Seluruh informasi dalam ceramah tersebut sudah akurat dan dapat dipercaya sepenuhnya","Gagasan tentang pentingnya disiplin cukup logis, tetapi klaim persentase mutlak tentang perusahaan perlu diverifikasi lebih lanjut karena sumbernya tidak jelas","Ceramah tersebut tidak memiliki gagasan pokok sama sekali","Ceramah tersebut tidak layak didengarkan karena seluruhnya tidak logis","Ceramah tersebut hanya berisi salam tanpa isi"], correct:1}
  ]
},
{
  title: "Ceramah 3 - Waspada Pinjaman Online Ilegal",
  stimulus: "Assalamualaikum warahmatullahi wabarakatuh. Adik-adik yang saya sayangi, sebentar lagi kalian akan lulus dan memasuki dunia kerja dengan penghasilan sendiri. Oleh sebab itu, penting bagi kita untuk waspada terhadap pinjaman online ilegal.\n\nPinjaman online ilegal biasanya menawarkan proses pencairan dana yang sangat cepat tanpa syarat yang jelas, bunga yang sangat tinggi, serta melakukan penagihan dengan cara mengancam dan mengintimidasi peminjam. Berdasarkan data Otoritas Jasa Keuangan tahun 2023, ribuan aplikasi pinjaman online ilegal telah diblokir karena merugikan masyarakat.\n\nCiri-ciri pinjaman online ilegal antara lain tidak terdaftar di Otoritas Jasa Keuangan, meminta akses ke seluruh data pribadi di ponsel, serta tidak memiliki alamat kantor yang jelas. Untuk itu, sebelum meminjam, pastikan aplikasi tersebut sudah terdaftar dan diawasi oleh Otoritas Jasa Keuangan.\n\nMari kita bijak dalam mengelola keuangan dan menghindari jebakan pinjaman online ilegal, demi masa depan finansial yang lebih baik. Wassalamualaikum warahmatullahi wabarakatuh.",
  questions: [
    {q:"Topik utama ceramah tersebut adalah...", opts:["Pentingnya menabung","Bahaya pinjaman online ilegal","Cara melamar pekerjaan","Pentingnya kebersihan","Pentingnya disiplin kerja"], correct:1},
    {q:"Salah satu ciri pinjaman online ilegal yang disebutkan dalam ceramah adalah...", opts:["Terdaftar resmi di Otoritas Jasa Keuangan","Proses pencairan dana sangat cepat tanpa syarat jelas","Memiliki kantor dengan alamat yang jelas","Bunga pinjaman yang rendah dan wajar","Melakukan penagihan sesuai prosedur hukum"], correct:1},
    {q:"Kalimat \"Berdasarkan data Otoritas Jasa Keuangan tahun 2023, ribuan aplikasi pinjaman online ilegal telah diblokir\" menunjukkan bahwa informasi dalam ceramah tersebut memiliki keunggulan dari segi kriteria evaluasi...", opts:["Kelogisan semata","Kredibilitas sumber dan keakuratan data, karena merujuk pada lembaga resmi","Kesesuaian struktur teks","Penggunaan konjungsi temporal","Ketepatan salam pembuka"], correct:1},
    {q:"Dibandingkan dengan ceramah tentang disiplin kerja pada Teks 2, ceramah tentang pinjaman online ilegal ini lebih unggul dalam hal...", opts:["Panjang teks","Jumlah salam pembuka","Kejelasan dan ketertelusuran sumber informasi yang digunakan","Jumlah paragraf penutup","Penggunaan bahasa daerah"], correct:2},
    {q:"Berdasarkan perbandingan ketiga teks ceramah (kebersihan lingkungan, disiplin kerja, dan pinjaman online ilegal), simpulan yang paling tepat mengenai kualitas informasi adalah...", opts:["Ketiga ceramah memiliki kualitas informasi yang sama baiknya","Ceramah yang mencantumkan sumber data resmi dan jelas cenderung memiliki informasi yang lebih dapat dipertanggungjawabkan dibandingkan yang hanya mengandalkan opini atau sumber tidak jelas","Ceramah yang paling panjang selalu memiliki informasi paling akurat","Ceramah tanpa data sama sekali lebih meyakinkan karena lebih ringkas","Kualitas informasi ceramah tidak dapat dievaluasi"], correct:1}
  ]
}
];

// ===== 5 SOAL ESAI (skor otomatis berbasis konsep kunci) =====
const ESSAYS = [
  { q: "Jelaskan pengertian gagasan pokok dan gagasan pendukung, lalu jelaskan hubungan keduanya dalam sebuah paragraf ceramah!",
    concepts: [["gagasan pokok","ide utama","ide pokok"], ["gagasan pendukung","ide penjelas","ide pendukung"], ["memperjelas","memperkuat","contoh"], ["paragraf"]] },
  { q: "Sebutkan struktur teks ceramah (3 bagian) dan jelaskan secara singkat isi masing-masing bagian!",
    concepts: [["pembukaan"], ["isi"], ["penutup"], ["salam","sapaan"]] },
  { q: "Sebutkan minimal 4 dari 6 kriteria yang digunakan untuk mengevaluasi informasi dalam teks ceramah!",
    concepts: [["kredibilitas"], ["keakuratan","akurat"], ["kelogisan","logis"], ["objektivitas","bias"], ["kelengkapan","lengkap"], ["kesesuaian","relevan","tujuan"]] },
  { q: "Perhatikan kembali ceramah tentang \"disiplin di dunia kerja\" pada soal pilihan ganda. Menurut pendapatmu, mengapa klaim \"seratus persen perusahaan besar di dunia hanya mau merekrut karyawan yang disiplin\" perlu dipertanyakan kebenarannya? Jelaskan alasanmu berdasarkan kriteria evaluasi informasi!",
    concepts: [["sumber","tidak jelas"], ["generalisasi","mutlak","100%","seratus persen"], ["data","tidak ada","tanpa bukti"], ["keakuratan","kredibilitas"]] },
  { q: "Tuliskan satu gagasan pokok dan minimal dua gagasan pendukung dari sebuah ceramah singkat bertema bebas (sesuai bidang keahlianmu), lalu jelaskan secara singkat apakah informasi dalam ceramah buatanmu tersebut sudah memenuhi kriteria keakuratan dan kelogisan!",
    concepts: [["gagasan pokok"], ["gagasan pendukung"], ["akurat","keakuratan"], ["logis","kelogisan"]] }
];
const ESSAY_MAXLEN = 500;
const PG_WEIGHT = 0.7;
const ESSAY_WEIGHT = 0.3;

function scoreEssayAnswer(answerText, concepts){
  const norm = (answerText || '').toLowerCase();
  let found = 0;
  concepts.forEach(altGroup =&gt; {
    const hit = altGroup.some(alt =&gt; norm.includes(alt.toLowerCase()));
    if(hit) found++;
  });
  return {found: found, total: concepts.length, pct: Math.round((found/concepts.length)*100)};
}

const LETTERS = ["A","B","C","D","E"];
let currentAbsen = null, currentNama = null, currentKelas = null, currentRecord = null;
let quizGroups = null;
let answers = {};
let essayAnswers = {};

function shuffle(arr){
  const a = arr.slice();
  for(let i=a.length-1;i&gt;0;i--){
    const j = Math.floor(Math.random()*(i+1));
    [a[i],a[j]] = [a[j],a[i]];
  }
  return a;
}

function buildShuffledQuiz(){
  const groupOrder = shuffle(GROUPS);
  return groupOrder.map(g =&gt; ({
    title: g.title,
    stimulus: g.stimulus,
    questions: shuffle(g.questions).map(q =&gt; {
      const optIdx = shuffle(q.opts.map((_,i)=&gt;i));
      return {
        q: q.q,
        opts: optIdx.map(i =&gt; q.opts[i]),
        correctLetter: LETTERS[optIdx.indexOf(q.correct)]
      };
    })
  }));
}

async function loadRecord(absen){
  try{
    const r = await window.storage.get('ceramah-siswa:'+absen, true);
    return r ? JSON.parse(r.value) : null;
  }catch(e){
    return null;
  }
}
async function saveRecord(absen, record){
  try{
    await window.storage.set('ceramah-siswa:'+absen, JSON.stringify(record), true);
  }catch(e){
    console.error('Gagal menyimpan data', e);
  }
}

function submitToGoogleScript(payload){
  try{
    const params = new URLSearchParams();
    Object.keys(payload).forEach(k =&gt; params.append(k, payload[k] == null ? '' : String(payload[k])));
    fetch(GAS_URL, {method:'POST', mode:'no-cors', body: params}).catch(e =&gt; console.error('Gagal mengirim ke sistem sekolah', e));
  }catch(e){
    console.error('Gagal mengirim ke sistem sekolah', e);
  }
}

document.getElementById('btnCekStatus').addEventListener('click', async () =&gt; {
  const absenEl = document.getElementById('absen');
  const namaEl = document.getElementById('nama');
  const kelasEl = document.getElementById('kelas');
  const absen = absenEl.value.trim();
  const nama = namaEl.value.trim();
  const kelas = kelasEl.value.trim();
  const err = document.getElementById('introErr');
  if(!absen || !nama || !kelas){
    err.style.display = 'block';
    return;
  }
  err.style.display = 'none';
  currentAbsen = absen;
  currentNama = nama;
  currentKelas = kelas;
  currentRecord = await loadRecord(absen);
  if(!currentRecord){
    currentRecord = {absen, nama, kelas, done:false, result:null};
  } else {
    currentRecord.nama = nama;
    currentRecord.kelas = kelas;
  }
  showStatusScreen();
});

document.getElementById('btnGanti').addEventListener('click', () =&gt; {
  document.getElementById('screen-status').classList.add('hidden');
  document.getElementById('screen-intro').classList.remove('hidden');
});

function showStatusScreen(){
  document.getElementById('screen-intro').classList.add('hidden');
  const statusScreen = document.getElementById('screen-status');
  statusScreen.classList.remove('hidden');
  document.getElementById('statusGreeting').textContent = 'Halo, ' + currentNama + ' (No. Absen ' + currentAbsen + ')';
  const body = document.getElementById('statusBody');
  const btnMulai = document.getElementById('btnMulai');
  const btnUnduhStatus = document.getElementById('btnUnduhStatus');
  const instruksiStatus = document.getElementById('instruksiStatus');

  if(currentRecord.done &amp;&amp; currentRecord.result){
    const r = currentRecord.result;
    body.innerHTML =
      '&lt;p style="font-size:14px;"&gt;Tugas individu ini sudah kamu kerjakan dan skornya sudah tersimpan/terkirim.&lt;/p&gt;' +
      '&lt;div class="result-box ' + (r.score &gt;= 75 ? 'pass-bg' : 'fail-bg') + '" style="margin-top:10px;"&gt;' +
        '&lt;div style="font-size:13px;color:var(--ink-soft);"&gt;Nilai akhir&lt;/div&gt;' +
        '&lt;div class="result-score ' + (r.score &gt;= 75 ? 'pass' : 'fail') + '"&gt;' + r.score + '&lt;/div&gt;' +
        '&lt;div style="font-size:12px;color:var(--ink-soft);"&gt;Skor PG ' + r.pgScore + ' (bobot 70%) - Skor Esai ' + r.essayScorePct + ' (bobot 30%)&lt;/div&gt;' +
      '&lt;/div&gt;';
    btnMulai.classList.add('hidden');
    btnUnduhStatus.classList.remove('hidden');
    instruksiStatus.classList.remove('hidden');
  } else {
    body.innerHTML = '&lt;p style="font-size:14px;"&gt;Tugas ini hanya bisa dikerjakan &lt;b&gt;satu kali&lt;/b&gt;. Pastikan kamu sudah membaca materi sebelum mulai.&lt;/p&gt;';
    btnMulai.classList.remove('hidden');
    btnUnduhStatus.classList.add('hidden');
    instruksiStatus.classList.add('hidden');
  }
}

document.getElementById('btnMulai').addEventListener('click', startQuiz);

function startQuiz(){
  answers = {};
  essayAnswers = {};
  quizGroups = buildShuffledQuiz();
  document.getElementById('screen-status').classList.add('hidden');
  document.getElementById('screen-result').classList.add('hidden');
  document.getElementById('screen-quiz').classList.remove('hidden');
  renderQuiz();
  window.scrollTo({top:0, behavior:'smooth'});
}

function esc(s){
  const d = document.createElement('div');
  d.textContent = s;
  return d.innerHTML;
}

function renderQuiz(){
  const body = document.getElementById('quizBody');
  let html = '';
  let counter = 0;
  quizGroups.forEach((g, gi) =&gt; {
    html += '&lt;div class="qgroup"&gt;';
    if(g.stimulus){
      html += '&lt;div class="stimulus"&gt;&lt;div class="st-title"&gt;' + esc(g.title) + '&lt;/div&gt;' + esc(g.stimulus) + '&lt;/div&gt;';
    } else {
      html += '&lt;div class="stimulus" style="border-left-color:var(--ink);"&gt;&lt;div class="st-title"&gt;' + esc(g.title) + '&lt;/div&gt;Soal-soal berikut menguji pemahaman konsep dari materi, tanpa teks bacaan tambahan.&lt;/div&gt;';
    }
    g.questions.forEach((q, qi) =&gt; {
      counter++;
      const qKey = gi + '-' + qi;
      html += '&lt;div class="q" data-qkey="' + qKey + '"&gt;';
      html += '&lt;div class="q-num"&gt;Soal ' + counter + '&lt;/div&gt;';
      html += '&lt;div class="q-text"&gt;' + esc(q.q) + '&lt;/div&gt;';
      q.opts.forEach((opt, oi) =&gt; {
        const letter = LETTERS[oi];
        html += '&lt;label class="opt" data-qkey="' + qKey + '" data-letter="' + letter + '"&gt;' +
          '&lt;input type="radio" name="q-' + qKey + '" value="' + letter + '"&gt;' +
          '&lt;span&gt;&lt;b&gt;' + letter + '.&lt;/b&gt; ' + esc(opt) + '&lt;/span&gt;&lt;/label&gt;';
      });
      html += '&lt;/div&gt;';
    });
    html += '&lt;/div&gt;';
  });

  html += '&lt;div class="sectionLabel"&gt;Bagian II - Esai (5 soal, dinilai otomatis berdasarkan kesesuaian konsep kunci jawabanmu - bobot Esai 30% dari nilai akhir - maksimal ' + ESSAY_MAXLEN + ' karakter per soal - jawaban wajib diketik sendiri, tempel/paste dinonaktifkan)&lt;/div&gt;';
  html += '&lt;div class="essaySection"&gt;';
  ESSAYS.forEach((item, ei) =&gt; {
    html += '&lt;div class="essayQ" data-ekey="' + ei + '"&gt;';
    html += '&lt;div class="essayQ-num"&gt;Esai ' + (ei+1) + '&lt;/div&gt;';
    html += '&lt;div class="essayQ-text"&gt;' + esc(item.q) + '&lt;/div&gt;';
    html += '&lt;textarea class="essayInput" data-ekey="' + ei + '" maxlength="' + ESSAY_MAXLEN + '" placeholder="Ketik jawabanmu di sini (tempel/paste tidak diperbolehkan)..."&gt;&lt;/textarea&gt;';
    html += '&lt;div class="charCount" data-ekey="' + ei + '"&gt;0 / ' + ESSAY_MAXLEN + '&lt;/div&gt;';
    html += '&lt;div class="pasteWarn" data-ekey="' + ei + '"&gt;Menempel (paste) tidak diperbolehkan. Silakan ketik sendiri jawabanmu.&lt;/div&gt;';
    html += '&lt;/div&gt;';
  });
  html += '&lt;/div&gt;';

  body.innerHTML = html;

  body.querySelectorAll('.opt').forEach(optEl =&gt; {
    optEl.addEventListener('click', () =&gt; {
      const qKey = optEl.getAttribute('data-qkey');
      const letter = optEl.getAttribute('data-letter');
      answers[qKey] = letter;
      document.querySelectorAll('.opt[data-qkey="' + qKey + '"]').forEach(o =&gt; o.classList.remove('selected'));
      optEl.classList.add('selected');
      optEl.querySelector('input').checked = true;
      updateProgress();
    });
  });

  body.querySelectorAll('textarea.essayInput').forEach(ta =&gt; {
    ta.addEventListener('input', () =&gt; {
      const ekey = ta.getAttribute('data-ekey');
      essayAnswers[ekey] = ta.value;
      const counterEl = body.querySelector('.charCount[data-ekey="' + ekey + '"]');
      const len = ta.value.length;
      counterEl.textContent = len + ' / ' + ESSAY_MAXLEN;
      counterEl.classList.toggle('over', len &gt;= ESSAY_MAXLEN);
      updateProgress();
    });

    const showPasteWarn = () =&gt; {
      const ekey = ta.getAttribute('data-ekey');
      const warnEl = body.querySelector('.pasteWarn[data-ekey="' + ekey + '"]');
      if(!warnEl) return;
      warnEl.classList.add('show');
      clearTimeout(warnEl._hideTimer);
      warnEl._hideTimer = setTimeout(() =&gt; warnEl.classList.remove('show'), 3500);
    };
    ta.addEventListener('paste', (e) =&gt; { e.preventDefault(); showPasteWarn(); });
    ta.addEventListener('drop', (e) =&gt; { e.preventDefault(); showPasteWarn(); });
    ta.addEventListener('dragover', (e) =&gt; { e.preventDefault(); });
    ta.addEventListener('contextmenu', (e) =&gt; { e.preventDefault(); showPasteWarn(); });
    ta.addEventListener('keydown', (e) =&gt; {
      const key = (e.key || '').toLowerCase();
      const pasteCombo = (e.ctrlKey || e.metaKey) &amp;&amp; key === 'v';
      const shiftInsert = e.shiftKey &amp;&amp; key === 'insert';
      if(pasteCombo || shiftInsert){
        e.preventDefault();
        showPasteWarn();
      }
    });
  });

  updateProgress();
}

function totalPGQuestions(){
  return quizGroups.reduce((sum,g) =&gt; sum + g.questions.length, 0);
}
function totalQuestions(){
  return totalPGQuestions() + ESSAYS.length;
}
function essayAnsweredCount(){
  return ESSAYS.reduce((n,_,ei) =&gt; n + (essayAnswers[ei] &amp;&amp; essayAnswers[ei].trim() ? 1 : 0), 0);
}
function updateProgress(){
  const total = totalQuestions();
  const answered = Object.keys(answers).length + essayAnsweredCount();
  document.getElementById('progressFill').style.width = (answered/total*100) + '%';
  document.getElementById('progressText').textContent = answered + ' dari ' + total + ' soal terjawab (25 PG + 5 esai)';
}

document.getElementById('btnKirim').addEventListener('click', async () =&gt; {
  const total = totalQuestions();
  const answered = Object.keys(answers).length + essayAnsweredCount();
  const errEl = document.getElementById('quizErr');
  if(answered &lt; total){
    errEl.style.display = 'block';
    let target = null;
    const qEls = document.querySelectorAll('.q');
    for(let i=0;i&lt;qEls.length;i++){
      const qk = qEls[i].getAttribute('data-qkey');
      if(!answers[qk]){ target = qEls[i]; break; }
    }
    if(!target){
      const eEls = document.querySelectorAll('.essayQ');
      for(let i=0;i&lt;eEls.length;i++){
        const ek = eEls[i].getAttribute('data-ekey');
        if(!essayAnswers[ek] || !essayAnswers[ek].trim()){ target = eEls[i]; break; }
      }
    }
    if(target) target.scrollIntoView({behavior:'smooth', block:'center'});
    return;
  }
  errEl.style.display = 'none';

  let correctCount = 0;
  quizGroups.forEach((g, gi) =&gt; {
    g.questions.forEach((q, qi) =&gt; {
      const qKey = gi + '-' + qi;
      if(answers[qKey] === q.correctLetter) correctCount++;
    });
  });
  const pgScore = Math.round(correctCount * (100 / totalPGQuestions()));

  const essaySnapshot = ESSAYS.map((item, ei) =&gt; {
    const ans = (essayAnswers[ei] || '').trim();
    const result = scoreEssayAnswer(ans, item.concepts);
    return {q: item.q, a: ans, found: result.found, total: result.total, pct: result.pct};
  });
  const essayScorePct = Math.round(essaySnapshot.reduce((s,e) =&gt; s + e.pct, 0) / essaySnapshot.length);
  const score = Math.round(pgScore * PG_WEIGHT + essayScorePct * ESSAY_WEIGHT);

  const timestamp = new Date().toISOString();
  currentRecord.done = true;
  currentRecord.result = {score: score, pgScore: pgScore, essayScorePct: essayScorePct, correctCount: correctCount, essay: essaySnapshot, timestamp: timestamp};

  await saveRecord(currentAbsen, currentRecord);

  const gasPayload = {
    waktu: timestamp,
    nama: currentRecord.nama,
    kelas: currentRecord.kelas,
    absen: currentRecord.absen,
    materi: "Evaluasi Informasi dalam Teks Ceramah",
    skorPG: pgScore,
    skorEsai: essayScorePct,
    nilaiAkhir: score,
    benarPG: correctCount,
    totalPG: totalPGQuestions()
  };
  essaySnapshot.forEach((e, i) =&gt; { gasPayload['esai' + (i+1)] = e.a; });
  submitToGoogleScript(gasPayload);

  showResult(score, pgScore, essayScorePct);
});

function showResult(score, pgScore, essayScorePct){
  document.getElementById('screen-quiz').classList.add('hidden');
  const resultScreen = document.getElementById('screen-result');
  resultScreen.classList.remove('hidden');
  const passedNow = score &gt;= 75;
  const outer = document.getElementById('resultBoxOuter');

  outer.innerHTML =
    '&lt;div class="result-box ' + (passedNow ? 'pass-bg' : 'fail-bg') + '"&gt;' +
      '&lt;div style="font-size:13px;color:var(--ink-soft);"&gt;Nilai akhir tugas individu&lt;/div&gt;' +
      '&lt;div class="result-score ' + (passedNow ? 'pass' : 'fail') + '"&gt;' + score + ' / 100&lt;/div&gt;' +
      '&lt;div style="font-size:12px;color:var(--ink-soft);margin-bottom:6px;"&gt;Skor PG ' + pgScore + ' (bobot 70%) + Skor Esai ' + essayScorePct + ' (bobot 30%)&lt;/div&gt;' +
      '&lt;div style="font-size:14px;"&gt;Jawaban dan skor sudah dikirim ke sistem sekolah. Jangan lupa unduh, cetak, dan serahkan bukti ke guru.&lt;/div&gt;' +
    '&lt;/div&gt;';

  document.getElementById('btnUnduhHasil').classList.remove('hidden');
  window.scrollTo({top:0, behavior:'smooth'});
}

document.getElementById('btnSelesai').addEventListener('click', () =&gt; {
  document.getElementById('screen-result').classList.add('hidden');
  document.getElementById('screen-intro').classList.remove('hidden');
  document.getElementById('absen').value = '';
  document.getElementById('nama').value = '';
  document.getElementById('kelas').value = '';
});

function generatePrintArea(){
  const r = currentRecord.result;
  if(!r) return;
  const tgl = new Date(r.timestamp || Date.now()).toLocaleDateString('id-ID', {day:'numeric', month:'long', year:'numeric'});
  const statusTxt = r.score &gt;= 75 ? 'Mencapai KKM (75)' : 'Di bawah KKM (75)';

  let html = '';
  html += '&lt;div class="pf-kop noBreak"&gt;';
  html += '&lt;h2&gt;Bukti Mengerjakan Tugas Individu - Evaluasi Informasi dalam Teks Ceramah&lt;/h2&gt;';
  html += '&lt;p&gt;Mata Pelajaran Bahasa Indonesia - Kelas XI SMK - 25 Soal Pilihan Ganda + 5 Soal Esai&lt;/p&gt;';
  html += '&lt;/div&gt;';

  html += '&lt;table class="pf-identitas noBreak"&gt;';
  html += '&lt;tr&gt;&lt;td class="lbl"&gt;Nama Lengkap&lt;/td&gt;&lt;td&gt;: ' + esc(currentRecord.nama || '') + '&lt;/td&gt;' +
          '&lt;td class="lbl"&gt;No. Absen&lt;/td&gt;&lt;td&gt;: ' + esc(currentRecord.absen || '') + '&lt;/td&gt;&lt;/tr&gt;';
  html += '&lt;tr&gt;&lt;td class="lbl"&gt;Kelas&lt;/td&gt;&lt;td&gt;: ' + esc(currentRecord.kelas || '') + '&lt;/td&gt;' +
          '&lt;td class="lbl"&gt;Tanggal&lt;/td&gt;&lt;td&gt;: ' + tgl + '&lt;/td&gt;&lt;/tr&gt;';
  html += '&lt;/table&gt;';

  html += '&lt;div class="pf-skorbox noBreak"&gt;';
  html += '&lt;div class="pf-skoritem"&gt;Skor Pilihan Ganda (70%)&lt;b&gt;' + r.pgScore + ' / 100&lt;/b&gt;&lt;/div&gt;';
  html += '&lt;div class="pf-skoritem"&gt;Skor Esai Otomatis (30%)&lt;b&gt;' + r.essayScorePct + ' / 100&lt;/b&gt;&lt;/div&gt;';
  html += '&lt;div class="pf-skoritem"&gt;Nilai Akhir&lt;b&gt;' + r.score + ' / 100&lt;/b&gt;&lt;/div&gt;';
  html += '&lt;div class="pf-skoritem"&gt;Status&lt;b style="font-size:13px;"&gt;' + statusTxt + '&lt;/b&gt;&lt;/div&gt;';
  html += '&lt;/div&gt;';

  html += '&lt;div class="pf-kop noBreak" style="border-top:1px solid #000;border-bottom:none;padding-top:8px;margin-top:6px;"&gt;&lt;h2 style="font-size:14px;"&gt;Jawaban Esai (skor otomatis berdasarkan cakupan konsep kunci)&lt;/h2&gt;&lt;/div&gt;';

  r.essay.forEach((item, i) =&gt; {
    const breakClass = (i === 2) ? ' pbreak' : '';
    html += '&lt;div class="noBreak' + breakClass + '" style="margin-bottom:10px;"&gt;';
    html += '&lt;div class="pf-essay-q"&gt;' + (i+1) + '. ' + esc(item.q) + ' &lt;span style="font-weight:400;color:#555;"&gt;(cakupan konsep: ' + item.found + '/' + item.total + ' - skor ' + item.pct + ')&lt;/span&gt;&lt;/div&gt;';
    html += '&lt;div class="pf-essay-a"&gt;' + esc(item.a || '(tidak dijawab)') + '&lt;/div&gt;';
    html += '&lt;/div&gt;';
  });

  html += '&lt;div class="pf-ttd noBreak"&gt;';
  html += '&lt;div&gt;Peserta Didik&lt;div class="garis"&gt;' + esc(currentRecord.nama || '') + '&lt;/div&gt;&lt;/div&gt;';
  html += '&lt;div&gt;Guru Mata Pelajaran&lt;div class="garis"&gt;.............................&lt;/div&gt;&lt;/div&gt;';
  html += '&lt;/div&gt;';
  html += '&lt;p class="pf-note"&gt;&lt;b&gt;Dokumen ini wajib dicetak dan diserahkan kepada guru mata pelajaran Bahasa Indonesia&lt;/b&gt; sebagai bukti fisik pengerjaan tugas. Nilai akhir = (Skor Pilihan Ganda x 70%) + (Skor Esai Otomatis x 30%). Skor esai dihitung berdasarkan cakupan konsep kunci pada jawaban, sehingga guru tetap disarankan meninjau ulang jawaban esai untuk memastikan kualitas dan kejujuran akademik. Jawaban dan skor juga telah dikirim otomatis ke sistem sekolah.&lt;/p&gt;';

  document.getElementById('printArea').innerHTML = html;
}

function downloadProof(){
  if(!currentRecord.result){
    alert('Bukti PDF hanya tersedia setelah tugas dikerjakan dan dikirim.');
    return;
  }
  generatePrintArea();
  setTimeout(() =&gt; window.print(), 100);
}

document.getElementById('btnUnduhStatus').addEventListener('click', downloadProof);
document.getElementById('btnUnduhHasil').addEventListener('click', downloadProof);
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/evaluasi-informasi-dalam-teks-ceramah.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-8160615648339880190</guid><pubDate>Tue, 08 Sep 2026 00:52:38 +0000</pubDate><atom:updated>2026-09-21T08:13:54.497+07:00</atom:updated><title>Fitur Lanjut Pengolah Kata untuk Dokumen Profesional</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Fitur Lanjut Pengolah Kata untuk Dokumen Profesional - Informatika Kelas X&lt;/title&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&amp;family=IBM+Plex+Sans:wght@400;500;600;700&amp;family=Tinos:ital,wght@0,400;0,700;1,400&amp;display=swap" rel="stylesheet"&gt;
&lt;style&gt;
  :root{
    --word: #1660D6;
    --word-dark: #0E3E8F;
    --ribbon: #EFF3FA;
    --paper: #FFFFFF;
    --ink: #16202E;
    --ink-soft: #4C5A6B;
    --line: #D7DEE7;
    --amber: #D99A1B;
    --green: #17845A;
    --red: #D6412C;
    --radius: 6px;
  }
  *{box-sizing:border-box; margin:0; padding:0;}
  html{scroll-behavior:smooth;}
  body{ font-family:'IBM Plex Sans', sans-serif; background:#FFFFFF; color: var(--ink); line-height:1.6; }
  .mono{font-family:'IBM Plex Mono', monospace;}
  .serif{font-family:'Tinos', 'Times New Roman', serif;}

  .page-shell{
    max-width: 1140px; margin: 16px auto; background:#FFFFFF; border-radius:14px;
    box-shadow: 0 8px 32px rgba(20,30,50,0.12), 0 1px 3px rgba(20,30,50,0.08); border: 1px solid var(--line);
  }

  /* ===== Ribbon hero ===== */
  .ribbon-hero{ background: var(--ribbon); border-bottom: 3px solid var(--word); border-radius:14px 14px 0 0; overflow:hidden; }
  .ribbon-strip{ display:flex; gap:6px; padding: 10px 5% 0; }
  .ribbon-tab{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; padding:6px 12px; background:#FFFFFF; border:1px solid var(--line); border-bottom:none; border-radius:4px 4px 0 0; color: var(--ink-soft); }
  .ribbon-tab.active{ color: var(--word); font-weight:700; border-color: var(--word); }
  .hero-body{ padding: 28px 5% 40px; background:#FFFFFF; }
  .eyebrow{ font-family:'IBM Plex Mono', monospace; font-size:0.76rem; letter-spacing:0.14em; text-transform:uppercase; color: var(--word); display:flex; align-items:center; gap:10px; margin-bottom:18px; }
  .eyebrow::before{ content:""; width:10px; height:10px; border:1.5px solid var(--word); border-radius:2px; display:inline-block; }
  h1.title{ font-family:'IBM Plex Mono', monospace; font-weight:700; font-size: clamp(1.7rem, 4vw, 2.7rem); line-height:1.18; color: var(--word-dark); max-width:24ch; }
  .hero-sub{ margin-top:16px; max-width: 66ch; font-size:1.02rem; color: var(--ink-soft); }
  .meta-strip{ margin-top:30px; display:grid; grid-template-columns: repeat(auto-fit,minmax(160px,1fr)); gap:1px; background: var(--line); border:1px solid var(--line); }
  .meta-cell{ background: var(--ribbon); padding:12px 14px; }
  .meta-cell .k{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--word); }
  .meta-cell .v{ margin-top:5px; font-size:0.9rem; font-weight:600; color: var(--ink); }

  /* ===== Content ===== */
  main{ background: var(--paper); }
  .section{ padding: 36px 5%; border-bottom:1px solid #ECEFF3; }
  .section:last-of-type{border-bottom:none;}
  .section-tag{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; letter-spacing:0.14em; text-transform:uppercase; color: var(--ink-soft); display:flex; align-items:center; gap:10px; margin-bottom:10px; }
  .section-tag .num{ color:#fff; background: var(--word); padding:3px 8px; border-radius:3px; font-weight:600; }
  h2.section-title{ font-family:'IBM Plex Mono', monospace; font-size: clamp(1.25rem, 2.2vw, 1.7rem); font-weight:700; color: var(--word-dark); margin-bottom: 18px; }
  h3.sub-title{ font-family:'IBM Plex Mono', monospace; font-size:1.02rem; font-weight:600; color: var(--word-dark); margin: 22px 0 10px; display:flex; align-items:center; gap:10px; }
  h3.sub-title::before{ content:""; width:8px;height:8px; background: var(--amber); display:inline-block; border-radius:1px; }
  p{ color:#33414F; max-width: 74ch; margin-bottom: 12px;}
  strong{color:var(--word-dark);}
  ul, ol{ margin: 6px 0 16px 1.3em; max-width: 74ch;}
  li{margin-bottom:7px; color:#33414F;}

  .callout{ border-left: 3px solid var(--word); background:#EBF2FB; padding: 14px 18px; border-radius: 0 6px 6px 0; margin: 16px 0; max-width: 74ch; }
  .callout .lbl{ font-family:'IBM Plex Mono', monospace; font-size:0.68rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--word-dark); font-weight:700; margin-bottom:6px; }
  .callout.warn{ border-left-color: var(--red); background:#FBEAE7; }
  .callout.warn .lbl{ color:#9B3324; }

  .card-grid{ display:grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; margin: 16px 0 6px; }
  .card{ background: #FFFFFF; border: 1.5px solid var(--line); border-radius: var(--radius); padding: 16px 18px; box-shadow: 0 2px 8px rgba(20,30,50,0.05); }
  .card .tag{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.1em; text-transform:uppercase; color:#fff; background: var(--word); padding:3px 8px; border-radius:3px; display:inline-block; margin-bottom:10px; }
  .card h4{ font-family:'IBM Plex Mono', monospace; font-size:0.94rem; margin-bottom:6px; color:var(--word-dark); }
  .card p{font-size:0.87rem; margin-bottom:0;}

  .table-scroll{ overflow-x:auto; margin:16px 0; }
  table.compare{ width:100%; border-collapse:collapse; font-size:0.88rem; min-width:460px; }
  table.compare th, table.compare td{ border:1px solid var(--line); padding:10px 12px; text-align:left; }
  table.compare th{ background: var(--word); color:#fff; font-family:'IBM Plex Mono', monospace; font-size:0.72rem; letter-spacing:0.04em; text-transform:uppercase; }
  table.compare tr:nth-child(even) td{ background: var(--ribbon); }

  .summary-box{ background: var(--word-dark); color: #fff; border-radius: 8px; padding: 22px 24px; margin-top: 8px; }
  .summary-box h4{ font-family:'IBM Plex Mono', monospace; color:#FCE29A; font-size:0.82rem; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:12px; }
  .summary-box ul{ margin:0 0 0 1.1em; max-width:none; }
  .summary-box li{ color:#E6ECF7; margin-bottom:7px; }

  /* ===== Praktik: toolbar &amp; editor ===== */
  #praktik, #evaluasi{ background: linear-gradient(180deg, #EEF4FF 0%, #FFFFFF 60%); border-top:3px solid var(--word); }
  #praktik .section-tag .num, #evaluasi .section-tag .num{ background: linear-gradient(135deg, var(--word), var(--word-dark)); }

  .toolbar{
    position: sticky; top:10px; z-index:20; background:#FFFFFF; border:1.5px solid var(--word);
    border-radius:8px; padding:10px 12px; display:flex; flex-wrap:wrap; gap:6px; align-items:center;
    box-shadow: 0 6px 20px rgba(20,90,189,0.18); margin-bottom:20px;
  }
  .toolbar button{
    font-family:'IBM Plex Sans', sans-serif; font-size:0.85rem; background:#F3F5F8; border:1px solid var(--line);
    border-radius:4px; padding:6px 10px; cursor:pointer; color: var(--ink);
  }
  .toolbar button:hover{ background:#E4EAF2; }
  .toolbar button.b{font-weight:700;} .toolbar button.i{font-style:italic;} .toolbar button.u{text-decoration:underline;}
  .toolbar select{ font-family:'IBM Plex Sans', sans-serif; font-size:0.82rem; padding:6px 8px; border-radius:4px; border:1px solid var(--line); background:#F3F5F8; }
  .toolbar .swatch{ width:22px; height:22px; border-radius:4px; border:1.5px solid var(--line); cursor:pointer; padding:0; }
  .toolbar .sep{ width:1px; height:22px; background:var(--line); margin:0 4px; }
  .toolbar .hint{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; color: var(--ink-soft); margin-left:auto; }

  .latihan-card{ background:#FFFFFF; border:1px solid var(--line); border-left:4px solid var(--word); border-radius:8px; margin-bottom:22px; overflow:hidden; box-shadow: 0 2px 10px rgba(20,30,50,0.05); }
  .latihan-head{ background: linear-gradient(90deg, #EEF4FF, var(--ribbon)); padding:14px 18px; border-bottom:1px solid var(--line); }
  .latihan-head .lnum{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; color: var(--word); letter-spacing:0.08em; }
  .latihan-head h4{ font-family:'IBM Plex Mono', monospace; font-size:1rem; color: var(--word-dark); margin-top:4px; }
  .latihan-body{ padding:16px 18px; }
  .latihan-instruksi{ font-size:0.88rem; color: var(--ink-soft); margin-bottom:12px; }
  .reference-box{
    background:#FBF9F2; border:1px dashed var(--amber); border-radius:6px; padding:12px 14px; margin-bottom:14px;
    font-family:'Tinos','Times New Roman', serif; font-size:0.95rem; color:#3A3226; white-space:pre-wrap;
    user-select:none; -webkit-user-select:none;
  }
  .reference-box .rlbl{ font-family:'IBM Plex Mono', monospace; font-size:0.64rem; letter-spacing:0.08em; text-transform:uppercase; color:#8A6A1E; margin-bottom:8px; display:block; }
  .editor-box{
    min-height:110px; background:#FFFFFF; border:1px solid var(--line); border-radius:6px; padding:14px 16px;
    font-family:'Tinos','Times New Roman', serif; font-size:1rem; color: var(--ink);
    box-shadow: inset 0 0 0 1px transparent; outline:none;
  }
  .editor-box:focus{ border-color: var(--word); box-shadow: 0 0 0 3px rgba(24,90,189,0.12); }
  .editor-box:empty::before{ content: attr(data-placeholder); color:#A6ADB8; }
  .editor-meta{ display:flex; justify-content:space-between; align-items:center; margin-top:8px; font-family:'IBM Plex Mono', monospace; font-size:0.72rem; color: var(--ink-soft); }
  .editor-meta .flag{ color: var(--red); font-weight:700; display:none; }
  .editor-meta .flag.show{ display:inline; }

  /* ===== Submit panel ===== */
  .submit-panel{ background: var(--ribbon); border:1px solid var(--line); border-radius:10px; padding:26px 24px; margin-top:8px; }
  .student-info{ display:grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap:14px; margin: 14px 0 22px; }
  .field label{ display:block; font-family:'IBM Plex Mono', monospace; font-size:0.68rem; letter-spacing:0.06em; text-transform:uppercase; color: var(--word-dark); margin-bottom:6px; }
  .field input, .field select{ width:100%; padding:9px 11px; border-radius:5px; border:1px solid var(--line); background:#FFFFFF; color: var(--ink); font-family:'IBM Plex Sans', sans-serif; font-size:0.9rem; }
  .field select{cursor:pointer;}
  .field input:focus, .field select:focus{ outline:2px solid var(--word); }
  #btnHitung, #btnKirim{ font-family:'IBM Plex Mono', monospace; font-weight:600; font-size:0.85rem; padding:12px 22px; border-radius:6px; border:none; cursor:pointer; }
  #btnHitung{ background: var(--word); color:#fff; }
  #btnKirim{ background: var(--green); color:#fff; display:none; }
  #btnKirim:disabled{ opacity:0.6; cursor:not-allowed; }
  #submitMsg{ font-family:'IBM Plex Mono', monospace; font-size:0.83rem; color:#8A6A1E; margin-left:12px; }
  .result-panel{ display:none; margin-top:18px; background:#FFFFFF; border:1.5px solid var(--green); border-radius:8px; padding:20px 22px; }
  .result-panel .score{ font-family:'IBM Plex Mono', monospace; font-size:2.2rem; font-weight:700; color: var(--green); }
  .result-panel .score-lbl{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase; color: var(--ink-soft); }
  .result-panel p{ color:#33414F; font-size:0.88rem; margin-top:8px; }

  footer{ background: var(--ribbon); color: var(--ink-soft); text-align:center; padding: 22px 5% 30px; font-family:'IBM Plex Mono', monospace; font-size:0.72rem; border-top:1px solid var(--line); border-radius:0 0 14px 14px; overflow:hidden; }

  /* ===== Evaluasi: Dokumen Master (game gabungan) ===== */
  .primary-btn{
    font-family:'IBM Plex Mono', monospace; font-weight:700; font-size:0.9rem; padding:13px 26px; border-radius:8px; border:none; cursor:pointer;
    background: linear-gradient(135deg, var(--word), var(--word-dark)); color:#fff; box-shadow: 0 4px 14px rgba(16,63,134,0.3);
    transition: transform .15s, box-shadow .15s;
  }
  .primary-btn:hover{ transform: translateY(-2px); box-shadow: 0 6px 18px rgba(16,63,134,0.4); }
  .primary-btn.ghost{ background:#fff; color:var(--word-dark); border:1.5px solid var(--word); box-shadow:none; }
  .primary-btn.ghost:hover{ background:#EEF4FF; }

  .attempt-strip{ display:flex; gap:10px; flex-wrap:wrap; margin:16px 0; }
  .attempt-pill{
    font-family:'IBM Plex Mono', monospace; font-size:0.76rem; font-weight:600; padding:9px 16px; border-radius:20px;
    border:1.5px solid var(--line); background:#fff; color: var(--ink-soft); box-shadow: 0 1px 4px rgba(20,30,50,0.06);
  }
  .attempt-pill.done{ border-color: var(--word); color: var(--word-dark); }
  .attempt-pill.pass{ border-color: var(--green); background:#DFF6E7; color:#0F5C30; }
  .attempt-pill.fail{ border-color: var(--red); background:#FBDEDA; color:#8A2A1C; }
  .attempt-pill.current{ box-shadow: 0 0 0 3px rgba(24,90,189,0.2); background:#EEF4FF; }

  .game-panel{ background:#FFFFFF; border:1px solid var(--line); border-radius:10px; padding:22px; box-shadow: 0 2px 10px rgba(20,30,50,0.05); }
  .game-subtitle{ font-family:'IBM Plex Mono', monospace; font-size:0.85rem; color: var(--word-dark); font-weight:700; margin-bottom:14px; }
  .game-progress{ font-family:'IBM Plex Mono', monospace; font-size:0.74rem; color: var(--ink-soft); margin-bottom:16px; }
  .game-instruction{ background:#EBF2FB; border:1px solid #C7DAF2; border-radius:8px; padding:10px 14px; font-size:0.85rem; color: var(--word-dark); margin-bottom:16px; }

  .drop-grid{ display:grid; grid-template-columns: repeat(auto-fit, minmax(260px,1fr)); gap:12px; margin-bottom:18px; }
  .droppable{ background:#FBFBFB; border:2px dashed var(--line); border-radius:8px; padding:12px 14px; transition: all .2s; cursor:pointer; }
  .droppable:hover:not(.filled):not(.wrong){ border-color: var(--word); background:#F5F9FF; }
  .droppable.filled{ border-style:solid; border-color: var(--green); background:#F0FBF4; cursor:default; }
  .droppable.wrong{ border-style:solid; border-color: var(--red); background:#FDF1EF; animation: shakeX .4s; cursor:default; }
  @keyframes shakeX{ 0%,100%{transform:translateX(0);} 25%{transform:translateX(-4px);} 75%{transform:translateX(4px);} }
  .droppable .func-text{ font-size:0.86rem; color:#33414F; margin-bottom:10px; }
  .drop-target{ min-height:44px; background:#F3F5F8; border:1.5px dashed var(--line); border-radius:6px; display:flex; align-items:center; justify-content:center; padding:6px 10px; text-align:center; }
  .drop-target .placeholder-text{ font-family:'IBM Plex Mono', monospace; font-size:0.72rem; color:#A6ADB8; }
  .drop-target .correct-reveal{ font-family:'IBM Plex Mono', monospace; font-size:0.76rem; color:#9B3324; }

  .drag-pool{ background: var(--ribbon); border:1.5px solid var(--line); border-radius:10px; padding:16px; display:flex; flex-wrap:wrap; gap:10px; justify-content:center; }
  .draggable-chip{
    font-family:'IBM Plex Mono', monospace; font-size:0.85rem; background: var(--word); color:#fff;
    padding:10px 16px; border-radius:8px; cursor:pointer; box-shadow:0 2px 6px rgba(24,90,189,0.3);
    border:2px solid transparent; transition: transform .12s, box-shadow .12s; user-select:none; -webkit-tap-highlight-color: transparent;
  }
  .draggable-chip:active{ transform: scale(0.96); }
  .draggable-chip.selected{ background: var(--amber); border-color:#8A6A1E; box-shadow:0 0 0 4px rgba(192,154,62,0.28); transform: scale(1.05); }
  .draggable-chip.used{ display:none; }

  .type-box{ max-width:520px; margin:0 auto; text-align:center; }
  .type-question-text{ font-size:1.02rem; color: var(--ink); min-height:56px; display:flex; align-items:center; justify-content:center; margin-bottom:18px; }
  .type-input{ width:100%; text-align:center; font-size:1.1rem; padding:12px 14px; border:2px solid var(--line); border-radius:8px; font-family:'IBM Plex Mono', monospace; }
  .type-input:focus{ outline:none; border-color: var(--word); }
  .type-input.correct{ border-color: var(--green); background:#F0FBF4; }
  .type-input.wrong{ border-color: var(--red); background:#FDF1EF; }
  .type-feedback{ margin-top:10px; font-family:'IBM Plex Mono', monospace; font-size:0.84rem; min-height:20px; }
  .type-feedback.ok{ color: var(--green); }
  .type-feedback.no{ color:#9B3324; }

  .closing-task{ background:#FFF6E6; border:1.5px solid var(--amber); border-radius:8px; padding:18px 20px; }
  .closing-task .lbl{ font-family:'IBM Plex Mono', monospace; font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase; color:#8A5A0A; font-weight:700; margin-bottom:8px; }

  @media (max-width:700px){ .hero-body{padding:22px 5% 32px;} .section{padding:26px 5%;} }
&lt;/style&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="page-shell"&gt;
&lt;div class="ribbon-hero"&gt;
  &lt;div class="ribbon-strip"&gt;
    &lt;div class="ribbon-tab active"&gt;Beranda&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Sisipkan&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Tata Letak&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Referensi&lt;/div&gt;
    &lt;div class="ribbon-tab"&gt;Surat&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="hero-body"&gt;
    &lt;div class="eyebrow"&gt;Informatika - Fase E - Kelas X SMK&lt;/div&gt;
    &lt;h1 class="title"&gt;Fitur Lanjut Pengolah Kata untuk Dokumen Profesional&lt;/h1&gt;
    &lt;p class="hero-sub"&gt;Materi ini membahas fitur-fitur lanjutan pengolah kata  -  style &amp;amp; heading, daftar isi otomatis, tabel, gambar, mail merge, referensi/sitasi, serta berbagai fitur otomatis lain  -  agar kalian mampu menghasilkan dokumen yang rapi dan profesional.&lt;/p&gt;
    &lt;div class="meta-strip"&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Elemen CP&lt;/div&gt;&lt;div class="v"&gt;Pengolah Kata Lanjut&lt;/div&gt;&lt;/div&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK Kelas X&lt;/div&gt;&lt;/div&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Alokasi Waktu&lt;/div&gt;&lt;div class="v"&gt;1 Pertemuan - 4 JP (180 menit)&lt;/div&gt;&lt;/div&gt;
      &lt;div class="meta-cell"&gt;&lt;div class="k"&gt;Model&lt;/div&gt;&lt;div class="v"&gt;Penjelasan &amp; Praktik Langsung&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;main&gt;

  &lt;!-- ===================== TUJUAN ===================== --&gt;
  &lt;section class="section" id="tujuan"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;00&lt;/span&gt; Capaian &amp; Tujuan Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Ke Mana Materi Ini Menuju&lt;/h2&gt;
    &lt;h3 class="sub-title"&gt;Tujuan Pembelajaran&lt;/h3&gt;
    &lt;p&gt;Murid mampu menggunakan style, heading, daftar isi otomatis, tabel, gambar, mail merge, referensi, dan fitur otomatis lainnya untuk menghasilkan dokumen profesional.&lt;/p&gt;

    &lt;h3 class="sub-title"&gt;Rincian Kegiatan (4 JP x 45 menit)&lt;/h3&gt;
    &lt;div class="table-scroll"&gt;
    &lt;table class="compare"&gt;
      &lt;tr&gt;&lt;th&gt;Tahap&lt;/th&gt;&lt;th&gt;Kegiatan&lt;/th&gt;&lt;th&gt;Waktu&lt;/th&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Pendahuluan&lt;/td&gt;&lt;td&gt;Apersepsi ("Bagaimana laporan/skripsi bisa punya daftar isi yang otomatis mengikuti nomor halaman?"), penyampaian tujuan&lt;/td&gt;&lt;td&gt;15 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti  -  Eksplorasi Konsep&lt;/td&gt;&lt;td&gt;Penjelasan style/heading, daftar isi otomatis, tabel, gambar, mail merge, referensi, fitur otomatis lain&lt;/td&gt;&lt;td&gt;50 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Inti  -  Praktik Langsung&lt;/td&gt;&lt;td&gt;Mengerjakan 19 latihan mengetik &amp;amp; memformat dokumen langsung di halaman ini (wajib diketik sendiri, tidak boleh tempel/paste)&lt;/td&gt;&lt;td&gt;95 menit&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penutup&lt;/td&gt;&lt;td&gt;Refleksi, penguatan konsep, menulis kesimpulan ringkas di buku catatan&lt;/td&gt;&lt;td&gt;20 menit&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== A. STYLE &amp; HEADING ===================== --&gt;
  &lt;section class="section" id="style-heading"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;01&lt;/span&gt; Struktur Dokumen&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Style &amp; Heading&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Style&lt;/strong&gt; adalah kumpulan pengaturan format (jenis huruf, ukuran, warna, spasi) yang disimpan dengan satu nama, sehingga dapat diterapkan berulang kali secara konsisten. &lt;strong&gt;Heading&lt;/strong&gt; adalah style khusus untuk judul bab/subbab (Heading 1, Heading 2, Heading 3, dst.) yang menandai struktur/hierarki dokumen.&lt;/p&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Mengapa Penting&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Dokumen yang judul-judulnya diberi Heading (bukan sekadar huruf besar/tebal manual) dapat langsung dikenali oleh fitur otomatis lain, seperti Daftar Isi Otomatis dan Navigation Pane  -  inilah alasan utama style/heading menjadi fondasi dokumen profesional.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== B. DAFTAR ISI OTOMATIS ===================== --&gt;
  &lt;section class="section" id="toc"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;02&lt;/span&gt; Navigasi Dokumen&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Daftar Isi Otomatis&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Daftar isi otomatis (Table of Contents)&lt;/strong&gt; dibuat dari menu References &amp;gt; Table of Contents, dan secara otomatis mengambil seluruh teks yang diberi style Heading beserta nomor halamannya. Jika ada perubahan judul atau halaman, cukup klik &lt;strong&gt;Update Table&lt;/strong&gt;  -  tidak perlu mengetik ulang satu per satu seperti daftar isi manual.&lt;/p&gt;
  &lt;/section&gt;

  &lt;!-- ===================== C. TABEL &amp; GAMBAR ===================== --&gt;
  &lt;section class="section" id="tabel-gambar"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;03&lt;/span&gt; Elemen Visual&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Tabel &amp; Gambar&lt;/h2&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Data Terstruktur&lt;/span&gt;&lt;h4&gt;Tabel&lt;/h4&gt;&lt;p&gt;Menyusun data dalam baris &amp;amp; kolom; dapat digabung (merge cell), diberi Table Style, dan dihitung otomatis dengan rumus sederhana.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Ilustrasi&lt;/span&gt;&lt;h4&gt;Gambar &amp; Text Wrapping&lt;/h4&gt;&lt;p&gt;Gambar dapat diatur agar teks mengalir di sekitarnya (Square, Tight) atau di belakang/depan teks (Behind/In Front of Text).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Penjelasan Gambar&lt;/span&gt;&lt;h4&gt;Caption&lt;/h4&gt;&lt;p&gt;Keterangan otomatis di bawah gambar/tabel (mis. "Gambar 1.1") yang bisa dirujuk ulang di teks (cross-reference).&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== D. MAIL MERGE ===================== --&gt;
  &lt;section class="section" id="mail-merge"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;04&lt;/span&gt; Surat Massal&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Mail Merge&lt;/h2&gt;
    &lt;p&gt;&lt;strong&gt;Mail merge&lt;/strong&gt; adalah fitur untuk membuat banyak dokumen serupa (mis. surat undangan, sertifikat) secara otomatis, dengan menggabungkan satu &lt;strong&gt;dokumen utama&lt;/strong&gt; (format surat) dan satu &lt;strong&gt;sumber data&lt;/strong&gt; (mis. tabel Excel berisi daftar nama &amp;amp; alamat). Bagian yang berubah-ubah ditandai sebagai &lt;em&gt;merge field&lt;/em&gt;, misalnya &lt;span class="mono"&gt;&amp;lt;&amp;lt;Nama&amp;gt;&amp;gt;&lt;/span&gt; dan &lt;span class="mono"&gt;&amp;lt;&amp;lt;Alamat&amp;gt;&amp;gt;&lt;/span&gt;.&lt;/p&gt;
    &lt;div class="callout"&gt;
      &lt;div class="lbl"&gt;Contoh Penggunaan&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Membuat 30 surat undangan wali murid dengan nama &amp;amp; kelas berbeda-beda, tanpa perlu mengetik ulang 30 kali  -  cukup satu template surat + satu daftar 30 nama.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== E. REFERENSI ===================== --&gt;
  &lt;section class="section" id="referensi"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;05&lt;/span&gt; Kutipan &amp; Rujukan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Referensi &amp; Sitasi&lt;/h2&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Citation/Sitasi&lt;/strong&gt;  -  menandai sumber kutipan dalam teks, mis. (Sugono, 2003).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Daftar Pustaka (Bibliography)&lt;/strong&gt;  -  dibuat otomatis dari seluruh sitasi yang sudah dimasukkan, tersusun sesuai gaya penulisan (APA, MLA, dsb.).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Footnote/Endnote&lt;/strong&gt;  -  catatan kaki/akhir untuk penjelasan tambahan tanpa mengganggu alur teks utama.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;

  &lt;!-- ===================== F. FITUR OTOMATIS LAIN ===================== --&gt;
  &lt;section class="section" id="fitur-lain"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;06&lt;/span&gt; Efisiensi Kerja&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Fitur Otomatis Lainnya&lt;/h2&gt;
    &lt;div class="card-grid"&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Penomoran&lt;/span&gt;&lt;h4&gt;Page Number Otomatis&lt;/h4&gt;&lt;p&gt;Nomor halaman yang otomatis bertambah dan dapat diformat berbeda per bagian (section break).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Koreksi&lt;/span&gt;&lt;h4&gt;Spelling &amp; AutoCorrect&lt;/h4&gt;&lt;p&gt;Mendeteksi salah ketik dan otomatis memperbaiki kesalahan umum saat mengetik.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;span class="tag"&gt;Keamanan Isi&lt;/span&gt;&lt;h4&gt;Watermark &amp; Proteksi&lt;/h4&gt;&lt;p&gt;Menandai dokumen (mis. "DRAFT") atau membatasi siapa yang dapat mengedit bagian tertentu.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== RANGKUMAN ===================== --&gt;
  &lt;section class="section" id="rangkuman"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;07&lt;/span&gt; Penguatan Konsep&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Rangkuman Materi&lt;/h2&gt;
    &lt;div class="summary-box"&gt;
      &lt;h4&gt;Poin-Poin Kunci&lt;/h4&gt;
      &lt;ul&gt;
        &lt;li&gt;Style &amp;amp; Heading membangun struktur dokumen yang konsisten dan dapat dikenali fitur otomatis lain.&lt;/li&gt;
        &lt;li&gt;Daftar isi otomatis dibangun dari Heading, dapat diperbarui otomatis tanpa mengetik ulang.&lt;/li&gt;
        &lt;li&gt;Tabel menyusun data terstruktur; gambar dilengkapi text wrapping dan caption.&lt;/li&gt;
        &lt;li&gt;Mail merge menggabungkan dokumen utama + sumber data untuk membuat banyak surat sekaligus.&lt;/li&gt;
        &lt;li&gt;Referensi/sitasi dan daftar pustaka membantu menyusun karya tulis yang kredibel.&lt;/li&gt;
        &lt;li&gt;Fitur otomatis lain (nomor halaman, autocorrect, watermark) meningkatkan efisiensi dan profesionalitas dokumen.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== TUGAS PRAKTIK ===================== --&gt;
  &lt;section class="section" id="praktik"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;08&lt;/span&gt; Praktik Langsung&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Tugas Praktik: Latihan Mengetik 1-19&lt;/h2&gt;
    &lt;p&gt;Kerjakan seluruh 19 latihan berikut &lt;strong&gt;langsung di halaman ini&lt;/strong&gt;, dengan cara &lt;strong&gt;mengetik ulang sendiri&lt;/strong&gt;  -  bukan menyalin/tempel (paste). Setiap kotak jawaban akan &lt;strong&gt;menolak aksi tempel&lt;/strong&gt;; jika terdeteksi ada percobaan tempel, latihan tersebut otomatis bernilai 0. Gunakan toolbar pemformatan di atas untuk menerapkan Bold, Italic, Underline, warna huruf, perataan, dan daftar bertingkat/bernomor sesuai instruksi masing-masing latihan.&lt;/p&gt;

    &lt;div class="callout warn"&gt;
      &lt;div class="lbl"&gt;Aturan Pengerjaan&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Dilarang menyalin-tempel teks dari mana pun (termasuk dari kotak naskah acuan di atas kolom jawaban). Ketik sendiri persis sesuai naskah, lalu format menggunakan toolbar. Kejujuran mengetik adalah bagian dari penilaian.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="toolbar" id="toolbar"&gt;
      &lt;button class="b" data-cmd="bold" title="Bold"&gt;B&lt;/button&gt;
      &lt;button class="i" data-cmd="italic" title="Italic"&gt;I&lt;/button&gt;
      &lt;button class="u" data-cmd="underline" title="Underline"&gt;U&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;button class="swatch" style="background:#000000" data-cmd="foreColor" data-val="#000000" title="Hitam"&gt;&lt;/button&gt;
      &lt;button class="swatch" style="background:#C43E2E" data-cmd="foreColor" data-val="#C43E2E" title="Merah"&gt;&lt;/button&gt;
      &lt;button class="swatch" style="background:#185ABD" data-cmd="foreColor" data-val="#185ABD" title="Biru"&gt;&lt;/button&gt;
      &lt;button class="swatch" style="background:#1E7A46" data-cmd="foreColor" data-val="#1E7A46" title="Hijau"&gt;&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;button data-cmd="justifyLeft" title="Rata Kiri"&gt;Kiri&lt;/button&gt;
      &lt;button data-cmd="justifyCenter" title="Rata Tengah"&gt;Tengah&lt;/button&gt;
      &lt;button data-cmd="justifyRight" title="Rata Kanan"&gt;Kanan&lt;/button&gt;
      &lt;button data-cmd="justifyFull" title="Rata Kiri-Kanan"&gt;Rata&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;button data-cmd="insertUnorderedList" title="Bullet List"&gt;List Bullet&lt;/button&gt;
      &lt;button data-cmd="insertOrderedList" title="Numbered List"&gt;1. List&lt;/button&gt;
      &lt;span class="sep"&gt;&lt;/span&gt;
      &lt;select id="fontSizeSelect" title="Ukuran Huruf"&gt;
        &lt;option value="2"&gt;Kecil&lt;/option&gt;
        &lt;option value="3" selected&gt;Normal&lt;/option&gt;
        &lt;option value="5"&gt;Besar&lt;/option&gt;
        &lt;option value="7"&gt;Sangat Besar&lt;/option&gt;
      &lt;/select&gt;
      &lt;select id="fontFamilySelect" title="Jenis Huruf"&gt;
        &lt;option value="Times New Roman"&gt;Times New Roman&lt;/option&gt;
        &lt;option value="Arial"&gt;Arial&lt;/option&gt;
        &lt;option value="Courier New"&gt;Courier New&lt;/option&gt;
        &lt;option value="Comic Sans MS"&gt;Comic Sans MS&lt;/option&gt;
      &lt;/select&gt;
      &lt;span class="hint"&gt;Klik di kotak jawaban dulu, lalu gunakan tombol ini&lt;/span&gt;
    &lt;/div&gt;

    &lt;div id="latihanContainer"&gt;&lt;/div&gt;

    &lt;div class="submit-panel"&gt;
      &lt;h3 class="sub-title"&gt;Identitas Peserta&lt;/h3&gt;
      &lt;div class="student-info"&gt;
        &lt;div class="field"&gt;&lt;label for="noAbsen"&gt;No. Absen&lt;/label&gt;&lt;input type="number" id="noAbsen" min="1" placeholder="mis. 12"&gt;&lt;/div&gt;
        &lt;div class="field"&gt;&lt;label for="nama"&gt;Nama Lengkap&lt;/label&gt;&lt;input type="text" id="nama" placeholder="Nama sesuai absensi"&gt;&lt;/div&gt;
        &lt;div class="field"&gt;&lt;label for="kelas"&gt;Kelas&lt;/label&gt;
          &lt;select id="kelas"&gt;
            &lt;option value=""&gt;-- Pilih Kelas --&lt;/option&gt;
            &lt;option value="X TB1"&gt;X TB1&lt;/option&gt;
            &lt;option value="X TB2"&gt;X TB2&lt;/option&gt;
            &lt;option value="X TO1"&gt;X TO1&lt;/option&gt;
            &lt;option value="X TO2"&gt;X TO2&lt;/option&gt;
            &lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;
            &lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;
          &lt;/select&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;button id="btnHitungLatihan"&gt;Hitung Skor Latihan Mengetik&lt;/button&gt;
      &lt;span id="latihanMsg" class="mono" style="font-size:0.83rem; color:#8A6A1E; margin-left:12px;"&gt;&lt;/span&gt;

      &lt;div class="result-panel" id="latihanResultPanel"&gt;
        &lt;div class="score-lbl"&gt;Skor Latihan Mengetik (1-19)&lt;/div&gt;
        &lt;div class="score" id="latihanScoreValue"&gt;0&lt;/div&gt;
        &lt;p id="latihanResultDetail"&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== EVALUASI DOKUMEN MASTER (GAME) ===================== --&gt;
  &lt;section class="section" id="evaluasi"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;09&lt;/span&gt; Evaluasi Pemahaman&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Evaluasi: Dokumen Master&lt;/h2&gt;
    &lt;p&gt;Bagian kedua dari Tugas Praktik ini adalah evaluasi pemahaman fitur pengolah kata dalam bentuk permainan singkat, terdiri dari &lt;strong&gt;Bagian A: Cocokkan Fitur (Drag &amp;amp; Drop)&lt;/strong&gt; dan &lt;strong&gt;Bagian B: Ketik Cepat&lt;/strong&gt;. Skor bagian ini akan digabung 50:50 dengan skor Latihan Mengetik di atas menjadi &lt;strong&gt;nilai akhir Tugas Praktik&lt;/strong&gt;.&lt;/p&gt;

    &lt;div class="callout warn"&gt;
      &lt;div class="lbl"&gt;Aturan Bermain&lt;/div&gt;
      &lt;p style="margin:0;"&gt;&lt;strong&gt;Jika jawaban salah, soal langsung lanjut ke soal berikutnya dan tidak mendapat nilai untuk soal itu&lt;/strong&gt;  -  tidak ada kesempatan mengulang jawaban pada soal yang sama. Nilai akhir dikirim ke guru otomatis jika mencapai &lt;strong&gt;KKM = 75&lt;/strong&gt;. Jika belum mencapai KKM, kalian punya &lt;strong&gt;3 kali kesempatan mengulang evaluasi ini&lt;/strong&gt; (skor Latihan Mengetik tidak perlu diulang); jika setelah 3 kali masih belum mencapai KKM, &lt;strong&gt;nilai tertinggi dari 3 percobaan itulah yang dikirim ke guru&lt;/strong&gt;.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="attempt-strip" id="attemptStrip"&gt;&lt;/div&gt;

    &lt;div class="game-panel" id="gamePanel"&gt;
      &lt;button id="btnMulaiEvaluasi" class="primary-btn"&gt;Mulai Evaluasi Dokumen Master&lt;/button&gt;
    &lt;/div&gt;

    &lt;div class="result-panel" id="finalResultPanel"&gt;
      &lt;div class="score-lbl" id="finalResultLabel"&gt;Nilai Akhir Tugas Praktik&lt;/div&gt;
      &lt;div class="score" id="finalScoreValue"&gt;0&lt;/div&gt;
      &lt;p id="finalResultDetail"&gt;&lt;/p&gt;
      &lt;button id="btnCobaLagi" class="primary-btn" style="display:none; margin-top:14px;"&gt;Coba Lagi Evaluasi&lt;/button&gt;
      &lt;button id="btnUnduhPDF" class="primary-btn ghost" style="display:none; margin-top:14px;"&gt;Unduh Bukti Mengerjakan (PDF)&lt;/button&gt;
      &lt;p id="pdfHint" style="display:none; font-size:0.8rem; color:var(--ink-soft); margin-top:8px;"&gt;Setelah diunduh, cetak dokumen ini (ukuran F4) dan serahkan ke guru mata pelajaran sebagai bukti pengerjaan.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ===================== PENUTUP: BUKU CATATAN ===================== --&gt;
  &lt;section class="section" id="penutup"&gt;
    &lt;div class="section-tag"&gt;&lt;span class="num"&gt;10&lt;/span&gt; Penutup Pembelajaran&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Tugas Individu di Buku Catatan&lt;/h2&gt;
    &lt;div class="closing-task"&gt;
      &lt;div class="lbl"&gt;1. Catat Nilai Evaluasi&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Salin &lt;strong&gt;Nilai Akhir Tugas Praktik&lt;/strong&gt; yang muncul di layar ke buku catatan kalian, pada kolom &lt;strong&gt;"Nilai Evaluasi"&lt;/strong&gt; (buat tabel kecil: Tanggal | Materi | Nilai Evaluasi), sebagai bukti dan rekam jejak pencapaian belajar kalian.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="closing-task" style="margin-top:14px;"&gt;
      &lt;div class="lbl"&gt;2. Tulis Resume Materi&lt;/div&gt;
      &lt;p style="margin:0;"&gt;Buatlah &lt;strong&gt;resume (rangkuman) materi "Fitur Lanjut Pengolah Kata untuk Dokumen Profesional"&lt;/strong&gt; di buku catatan, mencakup: style &amp;amp; heading, daftar isi otomatis, tabel &amp;amp; gambar, mail merge, referensi/sitasi, serta fitur otomatis lainnya. Tulis dengan bahasa dan pemahaman kalian sendiri.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  INFORMATIKA - FITUR LANJUT PENGOLAH KATA UNTUK DOKUMEN PROFESIONAL - FASE E KELAS X SMK
&lt;/footer&gt;
&lt;/div&gt;

&lt;script&gt;
  const GAS_ENDPOINT = "https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec";
  const JUDUL_MATERI = "Fitur Lanjut Pengolah Kata untuk Dokumen Profesional";

  // type: "match" = dinilai berdasarkan kemiripan dengan reference; "open" = dinilai berdasarkan panjang minimal (tugas kreatif tanpa naskah acuan tetap)
  const LATIHAN = [
    { title: "Latihan Mengetik dengan Keyboard", type:"match",
      instruksi: "Ketik ulang alfabet besar-kecil, angka, contoh operasi hitung, dan paragraf berikut PERSIS seperti aslinya (perhatikan huruf besar/kecil dan tanda baca).",
      reference: "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n1234567890\n2 + 2 = 4\nDi dalam sebuah hadits qudsy dijelaskan bahwa Allah SWT sangat menyukai dan mencintai hamba-hamba-Nya, yang banyak mengingat-Nya dan datang kepada-Nya dengan ibadah, membawa kebaikan dan amal-amal saleh serta memohon ampun dan bertobat atas dosa dan kesalahan yang dilakukannya." },
    { title: "Latihan Mengetik Teks/Paragraf", type:"match",
      instruksi: "Ketik ulang paragraf berikut dengan memperhatikan penggunaan tombol Shift untuk simbol di atas angka.",
      reference: "Ingatlah selalu bahwa untuk mengetik tanda yang berada di atas angka, harus menekan shift bersamaan dengan tanda tersebut. Seperti tanda % yang berada di atas angka 5, jika ingin mengetik angka lima saja, kita tidak perlu menekan shift. Contohnya $500 yang digunakan di luar negeri, contoh kedua yaitu seperti judul Tom &amp; Jerry." },
    { title: "Latihan Jenis, Ukuran, dan Gaya Huruf", type:"match",
      instruksi: "Ketik keempat baris berikut, lalu format sesuai keterangannya menggunakan tombol Bold, Italic, dan Underline pada toolbar di atas.",
      reference: "Times New Roman 12 Bold\nTimes New Roman 12 Italic\nTimes New Roman 12 Underline\nTimes New Roman 12 Bold Italic Underline" },
    { title: "Latihan Warna Huruf dan Perataan Paragraf", type:"match",
      instruksi: "Ketik ketiga baris berikut lalu beri warna huruf sesuai keterangan menggunakan tombol warna pada toolbar, kemudian ketik paragraf di bawahnya dan ratakan kiri-kanan (justify).",
      reference: "Times New Roman 12 Font Color: Red\nTimes New Roman 14 Font Color: Blue\nTimes New Roman 16 Font Color: Green\nPerkembangan ilmu pengetahuan dan teknologi semakin lama menjadi semakin penting artinya, baik untuk kepentingan sipil maupun militer, kepentingan damai maupun perang." },
    { title: "Latihan Penomoran dan Pembutiran", type:"match",
      instruksi: "Ketik daftar alat menggunakan Bullet List, lalu ketik daftar bahan menggunakan Numbered List (gunakan tombol list pada toolbar).",
      reference: "Alat-alat yang dibutuhkan: Nampan, Ompreng, Ember, Gayung, Kompor.\nBahan yang diperlukan: Air bersih satu ember, Minyak goreng satu liter, Telur ayam 5 butir, Cabe bawang dan garam secukupnya." },
    { title: "Latihan Tabulasi", type:"match",
      instruksi: "Ketik ulang tiga baris tanggal berikut menggunakan tombol Tab agar sejajar rapi.",
      reference: "Jakarta, 27 Maret 1987\nBandung, 28 Agustus 1945\nSurabaya, 2 Januari 2009" },
    { title: "Latihan Membuat Daftar Isi (Manual)", type:"match",
      instruksi: "Ketik ulang daftar isi berikut lengkap dengan titik-titik penghubung (dot leader) sebelum nomor halaman.",
      reference: "Daftar Isi\nKata Pengantar ..... i\nDaftar Isi ..... ii\nBAB I ..... 1\nBAB II ..... 4\nBAB III ..... 7\nDaftar Pustaka ..... 10" },
    { title: "Latihan Word Art (Simulasi)", type:"open", minChars: 10,
      instruksi: "Karena WordArt tidak tersedia di kotak ini, ketik sebuah judul singkat (nama sekolah/kelas kalian), lalu format dengan ukuran Sangat Besar dan warna mencolok pada toolbar untuk mensimulasikan efek WordArt." },
    { title: "Latihan Shapes dan Teks dalam Shapes (Simulasi)", type:"open", minChars: 10,
      instruksi: "Ketik sebuah kalimat singkat yang biasanya diletakkan di dalam sebuah shape (kotak/oval), lalu format rata tengah (Center) menggunakan toolbar." },
    { title: "Latihan Clip Art &amp; AutoShapes (Deskripsi)", type:"open", minChars: 20,
      instruksi: "Karena bagian ini aslinya berupa gambar clip art, tuliskan deskripsi singkat (2-3 kalimat) tentang ilustrasi yang akan kalian sisipkan di dokumen asli kalian di MS Word." },
    { title: "Latihan Membuat Tabel Data Diri", type:"match",
      instruksi: "Karena kotak jawaban belum berupa tabel sungguhan, ketik data berikut per baris dengan format: Nama | Kelas | Hobi | Cita-cita | Musik.",
      reference: "Nama | Kelas | Hobi | Cita-cita | Musik\nEntong | 2 SD | Baca | Presiden | Pop\nSiti | 3 SD | Ngerumpi | Sekretaris | Rock\nUcup | 4 SD | Menulis | Direktur | Dangdut" },
    { title: "Latihan Membuat Tabel Jadwal Pelajaran", type:"match",
      instruksi: "Ketik jadwal pelajaran berikut per baris dengan format: Hari | Pelajaran 1 | Pelajaran 2 | Pelajaran 3.",
      reference: "Senin | Upacara | Matematika | Bahasa\nSelasa | Bahasa Inggris | IPS | Matematika\nRabu | Agama | Penjas | Matematika" },
    { title: "Latihan Tabel &amp; Menghitung Jumlah Uang", type:"match",
      instruksi: "Ketik data berikut, lalu hitung sendiri jumlahnya (latihan menggabungkan tabel dengan operasi hitung sederhana).",
      reference: "Di Rumah | Di Kantong | Jumlahnya\n5000 | 3000 | 8000\n2000 | 5000 | 7000\n10000 | 500 | 10500" },
    { title: "Latihan Membuat Surat Resmi", type:"match",
      instruksi: "Ketik ulang bagian inti surat resmi berikut, lengkap dengan nomor surat, hal, dan salam pembuka/penutup.",
      reference: "No : 076/FQ/III/2009  Jakarta, 22 Maret 2009\nHal : Pemberitahuan dan Undangan\n\nKepada Yth,\nBapak/Ibu Orang tua murid\nDi tempat\n\nAssalamu'alaikum Wr.Wb.\nSehubungan dengan datangnya hari besar, kami akan mengadakan acara yang Insya Allah akan dilaksanakan pada Hari Minggu, Pukul 07.00 WIB.\n\nWassalamu'alaikum Wr.Wb." },
    { title: "Latihan Membuat Cover Makalah", type:"match",
      instruksi: "Ketik ulang susunan cover makalah berikut, gunakan perataan tengah (Center) dan Bold untuk judul.",
      reference: "MAKALAH\nHUKUM NUN MATI DAN TANWIN\nDisusun sebagai tugas mata pelajaran Agama Islam\n\nDisusun Oleh\nRAHMAT FAUZI\nKelas 8 A\n\nSEKOLAH MENENGAH PERTAMA NEGERI 176\nJAKARTA BARAT\nTAHUN 2010" },
    { title: "Latihan Membuat Pengumuman", type:"match",
      instruksi: "Ketik ulang teks pengumuman berikut sesuai aslinya.",
      reference: "Telah hilang dompet tanpa isinya dengan ciri-ciri warna coklat, berbahan kulit, robek dikit di bagian tengahnya, yang hilang pada Hari Senin, 12 Maret 2010 sekitar pukul 09.00 WIB di sekitar Monas. Bagi yang menemukan, diharapkan menghubungi nomor di bawah ini, terima kasih." },
    { title: "Latihan Membuat Bagan/Struktur Organisasi (Teks Bertingkat)", type:"open", minChars: 30,
      instruksi: "Karena bagan berupa kotak-kotak yang dihubungkan garis, tuliskan struktur organisasi dalam bentuk daftar bertingkat (indentasi) sebagai gambaran teksnya. Contoh: Ketua, lalu di bawahnya Wakil Ketua, lalu Sekretaris &amp; Bendahara, lalu Anggota." },
    { title: "Latihan Drop Cap dan Kolom Koran", type:"match",
      instruksi: "Ketik ulang paragraf berikut, lalu perbesar ukuran huruf pertama saja (blok satu huruf pertama, pilih ukuran Sangat Besar) untuk mensimulasikan efek Drop Cap.",
      reference: "GEGER Jakarta! Selama dua hari diguyur hujan kawasan Jakarta langsung berubah menjadi kota air kotor dengan sampah yang begitu melimpah dan komunitas warga yang terserang berbagai penyakit." },
    { title: "Latihan Simbol dan Karakter Khusus", type:"match",
      instruksi: "Ketik ulang simbol-simbol khusus berikut menggunakan keyboard atau fitur Insert Symbol yang tersedia di perangkat kalian.",
      reference: "± Rp 10.000.000,- untuk satu unit motor\nΠ = 3,14\nx ≥ 7\nSonic®\nSony Entertainment™" }
  ];

  const container = document.getElementById('latihanContainer');
  const pasteFlags = new Array(LATIHAN.length).fill(false);
  let currentEditor = null;

  LATIHAN.forEach((item, idx) =&gt; {
    const card = document.createElement('div');
    card.className = 'latihan-card';

    const head = document.createElement('div');
    head.className = 'latihan-head';
    head.innerHTML = '&lt;div class="lnum"&gt;LATIHAN ' + (idx+1) + ' / ' + LATIHAN.length + '&lt;/div&gt;&lt;h4&gt;' + item.title + '&lt;/h4&gt;';
    card.appendChild(head);

    const body = document.createElement('div');
    body.className = 'latihan-body';

    const instr = document.createElement('div');
    instr.className = 'latihan-instruksi';
    instr.textContent = item.instruksi;
    body.appendChild(instr);

    if (item.reference) {
      const refBox = document.createElement('div');
      refBox.className = 'reference-box';
      const rlbl = document.createElement('span');
      rlbl.className = 'rlbl';
      rlbl.textContent = 'Naskah Acuan (ketik ulang, jangan disalin)';
      refBox.appendChild(rlbl);
      const refText = document.createElement('div');
      refText.textContent = item.reference;
      refBox.appendChild(refText);
      body.appendChild(refBox);
    }

    const editor = document.createElement('div');
    editor.className = 'editor-box';
    editor.contentEditable = 'true';
    editor.setAttribute('data-placeholder', 'Ketik jawaban latihan di sini...');
    editor.dataset.idx = idx;

    editor.addEventListener('focus', () =&gt; { currentEditor = editor; });
    editor.addEventListener('paste', (e) =&gt; {
      e.preventDefault();
      pasteFlags[idx] = true;
      const flagEl = card.querySelector('.flag');
      flagEl.classList.add('show');
      flagEl.textContent = '[PERINGATAN] Percobaan tempel terdeteksi  -  latihan ini akan bernilai 0';
    });
    editor.addEventListener('drop', (e) =&gt; e.preventDefault());

    body.appendChild(editor);

    const meta = document.createElement('div');
    meta.className = 'editor-meta';
    meta.innerHTML = '&lt;span class="charcount"&gt;0 karakter&lt;/span&gt;&lt;span class="flag"&gt;&lt;/span&gt;';
    body.appendChild(meta);

    editor.addEventListener('input', () =&gt; {
      const len = editor.innerText.trim().length;
      meta.querySelector('.charcount').textContent = len + ' karakter';
    });

    card.appendChild(body);
    container.appendChild(card);
  });

  // Toolbar
  document.getElementById('toolbar').addEventListener('mousedown', (e) =&gt; {
    if (e.target.tagName === 'BUTTON') e.preventDefault(); // keep selection in editor
  });
  document.querySelectorAll('#toolbar button[data-cmd]').forEach(btn =&gt; {
    btn.addEventListener('click', () =&gt; {
      if (!currentEditor) { alert('Klik di dalam kotak jawaban terlebih dahulu.'); return; }
      currentEditor.focus();
      const cmd = btn.dataset.cmd;
      const val = btn.dataset.val || null;
      document.execCommand(cmd, false, val);
    });
  });
  document.getElementById('fontSizeSelect').addEventListener('change', (e) =&gt; {
    if (!currentEditor) return;
    currentEditor.focus();
    document.execCommand('fontSize', false, e.target.value);
  });
  document.getElementById('fontFamilySelect').addEventListener('change', (e) =&gt; {
    if (!currentEditor) return;
    currentEditor.focus();
    document.execCommand('fontName', false, e.target.value);
  });

  function normWords(text){
    return Array.from(new Set(
      text.toLowerCase().replace(/[^a-z0-9\s]/gi, ' ').split(/\s+/).filter(w =&gt; w.length &gt;= 3)
    ));
  }

  function scoreLatihan(idx){
    const item = LATIHAN[idx];
    const editor = document.querySelectorAll('.editor-box')[idx];
    const typed = editor.innerText.trim();

    if (pasteFlags[idx]) return 0;
    if (typed.length === 0) return 0;

    if (item.type === 'open') {
      const min = item.minChars || 15;
      return Math.round(Math.min(100, (typed.length / min) * 100));
    }

    const ref = item.reference;
    const lengthRatio = Math.min(1, typed.length / (0.5 * ref.length));
    const refWords = normWords(ref);
    const typedLower = typed.toLowerCase();
    let matched = 0;
    refWords.forEach(w =&gt; { if (typedLower.indexOf(w) !== -1) matched++; });
    const overlapRatio = refWords.length ? (matched / refWords.length) : 1;
    return Math.round(100 * (0.4 * lengthRatio + 0.6 * overlapRatio));
  }

  // ============================================================
  // BAGIAN 1: HITUNG SKOR LATIHAN MENGETIK (1-19)
  // ============================================================
  let latihanScore = null;

  document.getElementById('btnHitungLatihan').addEventListener('click', () =&gt; {
    const noAbsen = document.getElementById('noAbsen').value.trim();
    const nama = document.getElementById('nama').value.trim();
    const kelas = document.getElementById('kelas').value.trim();
    const msg = document.getElementById('latihanMsg');

    if (!noAbsen || !nama || !kelas) {
      msg.textContent = 'Lengkapi No. Absen, Nama, dan Kelas terlebih dahulu.';
      return;
    }

    let total = 0;
    let pasteCount = 0;
    for (let i = 0; i &lt; LATIHAN.length; i++) {
      total += scoreLatihan(i);
      if (pasteFlags[i]) pasteCount++;
    }
    latihanScore = Math.round(total / LATIHAN.length);

    msg.textContent = '';
    document.getElementById('latihanScoreValue').textContent = latihanScore;
    let detail = 'Skor dihitung dari kemiripan &amp; kelengkapan jawaban pada ' + LATIHAN.length + ' latihan (kontribusi 50% dari nilai akhir).';
    if (pasteCount &gt; 0) {
      detail = '[PERINGATAN] Terdeteksi ' + pasteCount + ' latihan dengan percobaan tempel (dinilai 0). ' + detail;
    }
    document.getElementById('latihanResultDetail').textContent = detail;
    document.getElementById('latihanResultPanel').style.display = 'block';

    // Buka bagian evaluasi setelah skor latihan dihitung
    const evalSection = document.getElementById('evaluasi');
    evalSection.style.display = 'block';
    evalSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
  });

  // ============================================================
  // BAGIAN 2: EVALUASI DOKUMEN MASTER (drag&amp;drop + ketik cepat)
  // ============================================================
  const GAS_ENDPOINT2 = GAS_ENDPOINT; // reuse
  const KKM = 75;
  const MAX_ATTEMPTS = 3;
  const EVAL_DRAG_COUNT = 6;
  const EVAL_TYPE_COUNT = 6;

  const dragDropQuestions = [
    { id: "dd1", func: "Fitur untuk membuat daftar pustaka otomatis berdasarkan kutipan.", ans: "References / Bibliography" },
    { id: "dd2", func: "Gaya teks bawaan untuk menentukan judul utama bab dokumen.", ans: "Heading 1" },
    { id: "dd3", func: "Mengirim satu format surat/sertifikat ke banyak penerima otomatis.", ans: "Mail Merge" },
    { id: "dd4", func: "Membuat struktur urutan bab beserta nomor halamannya secara otomatis.", ans: "Table of Contents" },
    { id: "dd5", func: "Memaksa teks berpindah ke halaman baru tanpa menekan enter berkali-kali.", ans: "Page Break" },
    { id: "dd6", func: "Menambahkan keterangan teks kecil tepat di bawah gambar atau tabel.", ans: "Caption" },
    { id: "dd7", func: "Memberikan penjelasan rujukan yang diletakkan di bagian paling bawah halaman.", ans: "Footnote" },
    { id: "dd8", func: "Area berulang di bagian atas setiap lembar halaman dokumen.", ans: "Header" },
    { id: "dd9", func: "Teks atau gambar samar (transparan) di latar belakang halaman.", ans: "Watermark" },
    { id: "dd10", func: "Mengatur orientasi kertas agar melebar (mendatar).", ans: "Landscape" },
    { id: "dd11", func: "Mengatur batas jarak kosong di tepi halaman untuk dicetak.", ans: "Margins" },
    { id: "dd12", func: "Membagi paragraf menjadi beberapa lajur seperti koran.", ans: "Columns" },
    { id: "dd13", func: "Menggabungkan dua sel atau lebih di dalam tabel menjadi satu sel utuh.", ans: "Merge Cells" },
    { id: "dd14", func: "Fitur melacak setiap perubahan, penambahan, penghapusan teks oleh editor.", ans: "Track Changes" },
    { id: "dd15", func: "Menyalin format gaya huruf dari satu teks dan menerapkannya ke teks lain.", ans: "Format Painter" }
  ];

  const typingQuestions = [
    { q: "Ketik nama tab menu untuk mengatur 'Margins' dan ukuran kertas.", a: ["Page Layout", "Layout"] },
    { q: "Fitur 'Heading' biasanya berada di tab menu utama, ketik nama tab-nya.", a: ["Home"] },
    { q: "Ketik nama fitur untuk menyisipkan penomoran halaman otomatis.", a: ["Page Number", "Page Numbers"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk menebalkan teks.", a: ["Ctrl+B", "Ctrl + B", "Control+B"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk memiringkan teks.", a: ["Ctrl+I", "Ctrl + I", "Control+I"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk memberi garis bawah teks.", a: ["Ctrl+U", "Ctrl + U", "Control+U"] },
    { q: "Tombol shortcut untuk membuat paragraf menjadi rata tengah.", a: ["Ctrl+E", "Ctrl + E", "Control+E"] },
    { q: "Tombol shortcut untuk membuat paragraf menjadi rata kiri dan kanan (justify).", a: ["Ctrl+J", "Ctrl + J", "Control+J"] },
    { q: "Ketik nama fitur untuk membatalkan perintah terakhir yang kamu lakukan.", a: ["Undo"] },
    { q: "Ketik nama menu untuk mengubah warna latar belakang keseluruhan halaman.", a: ["Page Color"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk menyimpan dokumen secara cepat.", a: ["Ctrl+S", "Ctrl + S", "Control+S"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk mencetak (print) dokumen.", a: ["Ctrl+P", "Ctrl + P", "Control+P"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk menyalin (copy) teks.", a: ["Ctrl+C", "Ctrl + C", "Control+C"] },
    { q: "Tombol pintas keyboard (Shortcut) untuk menempelkan (paste) teks.", a: ["Ctrl+V", "Ctrl + V", "Control+V"] },
    { q: "Format file ekstensi standar penyimpanan Microsoft Word versi modern (setelah 2007).", a: [".docx", "docx"] },
    { q: "Format dokumen populer yang bentuknya tidak mudah berubah saat dibuka di beda perangkat.", a: [".pdf", "pdf"] },
    { q: "Ketik nama fitur untuk membuat tulisan/teks berukuran besar dan artistik.", a: ["WordArt", "Word Art"] },
    { q: "Fitur pembuat surat massal bernama...", a: ["Mail Merge"] },
    { q: "Membuat huruf pertama paragraf menjadi besar dan memakan beberapa baris disebut...", a: ["Drop Cap"] },
    { q: "Fitur yang mengoreksi ejaan/tata bahasa yang salah dengan garis bawah merah/biru.", a: ["Spelling &amp; Grammar", "Spelling", "Grammar"] }
  ];

  function shuffleArray(array){
    const a = array.slice();
    for (let i = a.length - 1; i &gt; 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
  }

  let evalAttemptScores = [];
  let evalAttemptDetails = [];
  let evalAttemptQuestionLogs = [];
  let currentAttemptDragLog = [];
  let currentAttemptTypeLog = [];
  let evalFinalAttemptIndex = null;
  let evalFinalScore = null;
  let combinedFinalScore = null;
  let selectedChip = null;
  let dragCorrect = 0, dragAnswered = 0;
  let typeCorrect = 0, typeAnswered = 0, typeIndex = 0, currentTypeSet = [];

  function renderAttemptStrip(){
    const strip = document.getElementById('attemptStrip');
    strip.innerHTML = '';
    for (let i = 0; i &lt; MAX_ATTEMPTS; i++) {
      const pill = document.createElement('div');
      pill.className = 'attempt-pill';
      if (i &lt; evalAttemptScores.length) {
        const s = evalAttemptScores[i];
        pill.classList.add('done', s &gt;= KKM ? 'pass' : 'fail');
        pill.textContent = 'Percobaan ' + (i+1) + ': ' + s;
      } else if (i === evalAttemptScores.length) {
        pill.classList.add('current');
        pill.textContent = 'Percobaan ' + (i+1) + ': belum dimulai';
      } else {
        pill.textContent = 'Percobaan ' + (i+1) + ': -';
      }
      strip.appendChild(pill);
    }
  }
  renderAttemptStrip();

  document.getElementById('btnMulaiEvaluasi').addEventListener('click', startEvalAttempt);
  document.getElementById('btnCobaLagi').addEventListener('click', () =&gt; {
    document.getElementById('finalResultPanel').style.display = 'none';
    startEvalAttempt();
  });

  function startEvalAttempt(){
    dragCorrect = 0; dragAnswered = 0;
    typeCorrect = 0; typeAnswered = 0; typeIndex = 0;
    currentAttemptDragLog = [];
    currentAttemptTypeLog = [];
    document.getElementById('finalResultPanel').style.display = 'none';
    renderDragBatch();
  }

  function renderDragBatch(){
    const panel = document.getElementById('gamePanel');
    const pool = shuffleArray(dragDropQuestions).slice(0, EVAL_DRAG_COUNT);
    selectedChip = null;
    panel.innerHTML =
      '&lt;div class="game-subtitle"&gt;Bagian A - Cocokkan Fitur&lt;/div&gt;' +
      '&lt;div class="game-instruction"&gt;Cara main: &lt;strong&gt;1)&lt;/strong&gt; Ketuk salah satu jawaban di kotak biru di bawah untuk memilihnya, &lt;strong&gt;2)&lt;/strong&gt; lalu ketuk kotak soal yang sesuai untuk menempatkannya. Bisa dikerjakan dengan mouse maupun layar sentuh (HP/tablet).&lt;/div&gt;' +
      '&lt;div class="game-progress" id="dragProgress"&gt;0 / ' + EVAL_DRAG_COUNT + ' soal dijawab&lt;/div&gt;' +
      '&lt;div class="drop-grid" id="dropGrid"&gt;&lt;/div&gt;' +
      '&lt;div class="drag-pool" id="dragPool"&gt;&lt;/div&gt;';

    const dropGrid = document.getElementById('dropGrid');
    const dragPool = document.getElementById('dragPool');

    pool.forEach(q =&gt; {
      const zone = document.createElement('div');
      zone.className = 'droppable';
      zone.dataset.expected = q.id;
      zone.innerHTML =
        '&lt;div class="func-text"&gt;' + q.func + '&lt;/div&gt;' +
        '&lt;div class="drop-target"&gt;&lt;span class="placeholder-text"&gt;Ketuk di sini setelah memilih jawaban&lt;/span&gt;&lt;/div&gt;';
      dropGrid.appendChild(zone);

      zone.addEventListener('click', () =&gt; {
        if (zone.classList.contains('filled') || zone.classList.contains('wrong')) return; // sudah dikunci
        if (!selectedChip) return; // belum pilih jawaban
        handleDragDrop(zone, selectedChip, q);
      });
    });

    const shuffledAnswers = shuffleArray(pool);
    shuffledAnswers.forEach(q =&gt; {
      const chip = document.createElement('div');
      chip.className = 'draggable-chip';
      chip.dataset.id = q.id;
      chip.textContent = q.ans;
      chip.addEventListener('click', () =&gt; {
        if (chip.classList.contains('used')) return;
        document.querySelectorAll('.draggable-chip').forEach(c =&gt; c.classList.remove('selected'));
        if (selectedChip === chip) { selectedChip = null; return; } // ketuk lagi = batal pilih
        selectedChip = chip;
        chip.classList.add('selected');
      });
      dragPool.appendChild(chip);
    });
  }

  function handleDragDrop(zone, chip, question){
    const target = zone.querySelector('.drop-target');
    const isCorrect = chip.dataset.id === question.id;
    dragAnswered++;

    currentAttemptDragLog.push({ func: question.func, correctAns: question.ans, userAns: chip.textContent.trim(), isCorrect: isCorrect });

    if (isCorrect) {
      dragCorrect++;
      zone.classList.add('filled');
      target.innerHTML = '&lt;span style="font-family:\'IBM Plex Mono\',monospace; font-size:0.8rem; color:#177A46; font-weight:700;"&gt;[BENAR] ' + question.ans + '&lt;/span&gt;';
      chip.classList.add('used');
    } else {
      // Salah: kunci soal ini (tidak dapat nilai), tetap lanjut - tidak ada pengulangan pada soal yang sama.
      // Chip yang salah TIDAK dihapus dari pool karena mungkin cocok untuk zona lain yang masih terbuka.
      zone.classList.add('wrong');
      target.innerHTML = '&lt;span class="correct-reveal"&gt;[SALAH] Jawaban benar: ' + question.ans + '&lt;/span&gt;';
    }

    chip.classList.remove('selected');
    selectedChip = null;

    document.getElementById('dragProgress').textContent = dragAnswered + ' / ' + EVAL_DRAG_COUNT + ' soal dijawab';

    if (dragAnswered &gt;= EVAL_DRAG_COUNT) {
      setTimeout(renderTypeBatch, 500);
    }
  }

  function renderTypeBatch(){
    currentTypeSet = shuffleArray(typingQuestions).slice(0, EVAL_TYPE_COUNT);
    typeIndex = 0;
    renderTypeQuestion();
  }

  function renderTypeQuestion(){
    const panel = document.getElementById('gamePanel');
    if (typeIndex &gt;= currentTypeSet.length) {
      finishEvalAttempt();
      return;
    }
    const q = currentTypeSet[typeIndex];
    panel.innerHTML =
      '&lt;div class="game-subtitle"&gt;Bagian B - Ketik Cepat&lt;/div&gt;' +
      '&lt;div class="game-progress"&gt;Soal ' + (typeIndex+1) + ' / ' + EVAL_TYPE_COUNT + '&lt;/div&gt;' +
      '&lt;div class="type-box"&gt;' +
        '&lt;div class="type-question-text"&gt;' + q.q + '&lt;/div&gt;' +
        '&lt;form id="typeForm"&gt;&lt;input type="text" id="typeInput" class="type-input" autocomplete="off" placeholder="Ketik jawabanmu..." required&gt;' +
        '&lt;div class="type-feedback" id="typeFeedback"&gt;&lt;/div&gt;&lt;/form&gt;' +
      '&lt;/div&gt;';

    const input = document.getElementById('typeInput');
    setTimeout(() =&gt; input.focus(), 50);

    document.getElementById('typeForm').addEventListener('submit', (e) =&gt; {
      e.preventDefault();
      const userAns = input.value.trim().toLowerCase().replace(/\s+/g, ' ');
      const isCorrect = q.a.some(ans =&gt; ans.toLowerCase().replace(/\s+/g, ' ') === userAns);
      typeAnswered++;
      const feedback = document.getElementById('typeFeedback');

      currentAttemptTypeLog.push({ q: q.q, correctAns: q.a[0], userAns: input.value.trim(), isCorrect: isCorrect });

      if (isCorrect) {
        typeCorrect++;
        input.classList.add('correct');
        feedback.className = 'type-feedback ok';
        feedback.textContent = 'Benar! [BENAR]';
      } else {
        input.classList.add('wrong');
        feedback.className = 'type-feedback no';
        feedback.textContent = 'Kurang tepat. Jawaban: ' + q.a[0];
      }
      input.disabled = true;

      // Salah atau benar, tetap lanjut ke soal berikutnya  -  tidak ada pengulangan.
      setTimeout(() =&gt; { typeIndex++; renderTypeQuestion(); }, 1100);
    });
  }

  function finishEvalAttempt(){
    const attemptScore = Math.round(((dragCorrect + typeCorrect) / (EVAL_DRAG_COUNT + EVAL_TYPE_COUNT)) * 100);
    evalAttemptScores.push(attemptScore);
    evalAttemptDetails.push({ drag: dragCorrect, type: typeCorrect });
    evalAttemptQuestionLogs.push({ drag: currentAttemptDragLog.slice(), type: currentAttemptTypeLog.slice() });
    renderAttemptStrip();

    document.getElementById('gamePanel').innerHTML =
      '&lt;p style="text-align:center; color:#33414F; font-size:0.95rem;"&gt;Percobaan ke-' + evalAttemptScores.length + ' selesai. Skor: &lt;strong&gt;' + attemptScore + '&lt;/strong&gt; (KKM = ' + KKM + ')&lt;/p&gt;';

    const finalPanel = document.getElementById('finalResultPanel');
    const label = document.getElementById('finalResultLabel');
    const scoreEl = document.getElementById('finalScoreValue');
    const detailEl = document.getElementById('finalResultDetail');
    const btnCoba = document.getElementById('btnCobaLagi');

    if (attemptScore &gt;= KKM) {
      evalFinalScore = attemptScore;
      evalFinalAttemptIndex = evalAttemptScores.length - 1;
      label.textContent = 'Nilai Evaluasi (Tercapai KKM)';
      scoreEl.textContent = evalFinalScore;
      detailEl.textContent = 'Selamat, kamu mencapai KKM pada percobaan ke-' + evalAttemptScores.length + '. Nilai evaluasi ini digabung 50:50 dengan skor Latihan Mengetik.';
      btnCoba.style.display = 'none';
      finalPanel.style.display = 'block';
      finalizeAndSubmit();
    } else if (evalAttemptScores.length &lt; MAX_ATTEMPTS) {
      label.textContent = 'Belum Mencapai KKM';
      scoreEl.textContent = attemptScore;
      detailEl.textContent = 'Skor percobaan ke-' + evalAttemptScores.length + ' adalah ' + attemptScore + ', belum mencapai KKM (' + KKM + '). Sisa kesempatan: ' + (MAX_ATTEMPTS - evalAttemptScores.length) + ' kali.';
      btnCoba.style.display = 'inline-block';
      finalPanel.style.display = 'block';
    } else {
      evalFinalScore = Math.max(...evalAttemptScores);
      evalFinalAttemptIndex = evalAttemptScores.indexOf(evalFinalScore);
      label.textContent = 'Nilai Evaluasi (Nilai Tertinggi dari 3 Percobaan)';
      scoreEl.textContent = evalFinalScore;
      detailEl.textContent = 'Percobaan sudah 3 kali dan belum mencapai KKM. Nilai tertinggi (' + evalFinalScore + ') dari 3 percobaan akan digunakan dan digabung 50:50 dengan skor Latihan Mengetik.';
      btnCoba.style.display = 'none';
      finalPanel.style.display = 'block';
      finalizeAndSubmit();
    }
  }

  function finalizeAndSubmit(){
    if (latihanScore === null) {
      document.getElementById('finalResultDetail').textContent += ' (Hitung dulu Skor Latihan Mengetik di Bagian 1 sebelum nilai akhir bisa dikirim.)';
      return;
    }
    combinedFinalScore = Math.round((latihanScore * 0.5) + (evalFinalScore * 0.5));
    document.getElementById('finalResultLabel').textContent = 'Nilai Akhir Tugas Praktik (Terkirim ke Guru)';
    document.getElementById('finalScoreValue').textContent = combinedFinalScore;
    document.getElementById('finalResultDetail').textContent =
      'Nilai akhir = 50% Skor Latihan Mengetik (' + latihanScore + ') + 50% Nilai Evaluasi (' + evalFinalScore + ') = ' + combinedFinalScore + '. Salin nilai ini ke buku catatan kalian pada kolom "Nilai Evaluasi". Mengirim ke guru...';

    const noAbsen = document.getElementById('noAbsen').value.trim();
    const nama = document.getElementById('nama').value.trim();
    const kelas = document.getElementById('kelas').value.trim();

    document.getElementById('btnUnduhPDF').style.display = 'inline-block';
    document.getElementById('pdfHint').style.display = 'block';

    fetch(GAS_ENDPOINT2, {
      method: 'POST',
      mode: 'no-cors',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ noAbsen: noAbsen, nama: nama, kelas: kelas, skor: combinedFinalScore, judulMateri: JUDUL_MATERI })
    }).then(() =&gt; {
      document.getElementById('finalResultDetail').textContent =
        'Nilai akhir = 50% Skor Latihan Mengetik (' + latihanScore + ') + 50% Nilai Evaluasi (' + evalFinalScore + ') = ' + combinedFinalScore + '. Nilai sudah terkirim ke guru. Unduh &amp; cetak bukti mengerjakan di bawah ini, lalu jangan lupa salin ke buku catatan kalian.';
    }).catch(() =&gt; {
      document.getElementById('finalResultDetail').textContent =
        'Nilai akhir = ' + combinedFinalScore + ', tetapi gagal terkirim otomatis. Unduh &amp; cetak bukti mengerjakan di bawah ini sebagai bukti fisik, tunjukkan ke guru, dan tetap catat di buku catatan kalian.';
    });
  }

  // ============================================================
  // BUKTI MENGERJAKAN (PDF, ukuran F4) - dibuat LANGSUNG dengan jsPDF
  // (teks asli/vektor, bukan screenshot atau dialog print browser,
  // sehingga hasilnya konsisten di HP maupun laptop dan tidak kosong)
  // ============================================================
  function downloadBuktiPDF(){
    if (combinedFinalScore === null) {
      alert('Selesaikan dulu Bagian 1 (Latihan Mengetik) dan Bagian 2 (Evaluasi Dokumen Master) sampai Nilai Akhir Tugas Praktik muncul, baru bukti PDF bisa diunduh.');
      return;
    }
    if (!window.jspdf) {
      alert('Pustaka pembuat PDF gagal dimuat (kemungkinan koneksi internet bermasalah). Coba muat ulang halaman lalu unduh lagi.');
      return;
    }

    const noAbsen = document.getElementById('noAbsen').value.trim();
    const nama = document.getElementById('nama').value.trim();
    const kelas = document.getElementById('kelas').value.trim();
    const now = new Date();
    const tanggalCetak = now.toLocaleDateString('id-ID', { day:'numeric', month:'long', year:'numeric' }) +
      ', ' + now.toLocaleTimeString('id-ID', { hour:'2-digit', minute:'2-digit' });

    const { jsPDF } = window.jspdf;
    const doc = new jsPDF({ unit: 'mm', format: [215, 330] }); // ukuran kertas F4
    const pageW = 215, pageH = 330;
    const marginX = 16, marginTop = 16, marginBottom = 16;
    const contentW = pageW - marginX * 2;
    let y = marginTop;

    function ensureSpace(h){
      if (y + h &gt; pageH - marginBottom) { doc.addPage([pageW, pageH]); y = marginTop; }
    }
    function text(str, opts){
      opts = opts || {};
      const size = opts.size || 10;
      const style = opts.style || 'normal';
      const align = opts.align || 'left';
      const gapAfter = opts.gap !== undefined ? opts.gap : 2;
      doc.setFont('helvetica', style);
      doc.setFontSize(size);
      const x = align === 'center' ? pageW / 2 : marginX;
      const lines = doc.splitTextToSize(String(str), contentW);
      lines.forEach(ln =&gt; {
        ensureSpace(size * 0.5);
        doc.text(ln, x, y, { align: align });
        y += size * 0.45;
      });
      y += gapAfter;
    }
    function divider(){
      ensureSpace(5);
      doc.setDrawColor(60);
      doc.setLineWidth(0.3);
      doc.line(marginX, y, pageW - marginX, y);
      y += 5;
    }

    // ---- KOP ----
    text('BUKTI MENGERJAKAN TUGAS PRAKTIK', { size:16, style:'bold', align:'center', gap:2 });
    text(JUDUL_MATERI, { size:11, align:'center', gap:1 });
    text('Informatika - Fase E - Kelas X SMK', { size:9, align:'center', gap:4 });
    divider();

    // ---- IDENTITAS ----
    text('No. Absen : ' + (noAbsen || '-'), { size:10, gap:1 });
    text('Nama      : ' + (nama || '-'), { size:10, gap:1 });
    text('Kelas     : ' + (kelas || '-'), { size:10, gap:1 });
    text('Dicetak   : ' + tanggalCetak, { size:10, gap:4 });
    divider();

    // ---- RINGKASAN NILAI ----
    text('RINGKASAN NILAI', { size:12, style:'bold', gap:3 });
    text('1. Latihan Mengetik (1-' + LATIHAN.length + ')  -  Bobot 50%  -  Skor: ' + (latihanScore !== null ? latihanScore : '-'), { size:10, gap:1 });
    text('2. Evaluasi Dokumen Master (percobaan ke-' + (evalFinalAttemptIndex !== null ? (evalFinalAttemptIndex + 1) : '-') + ' dari ' + evalAttemptScores.length + ', KKM=' + KKM + ')  -  Bobot 50%  -  Skor: ' + (evalFinalScore !== null ? evalFinalScore : '-'), { size:10, gap:1 });
    if (evalAttemptScores.length) {
      text('   Rincian tiap percobaan: ' + evalAttemptScores.map((s,i) =&gt; 'Percobaan ' + (i+1) + ' = ' + s).join('   |   '), { size:9, gap:2 });
    }
    text('NILAI AKHIR TUGAS PRAKTIK : ' + combinedFinalScore, { size:14, style:'bold', gap:4 });
    divider();

    // ---- RINCIAN JAWABAN EVALUASI (BENAR &amp; SALAH) ----
    if (evalFinalAttemptIndex !== null &amp;&amp; evalAttemptQuestionLogs[evalFinalAttemptIndex]) {
      const logDetail = evalAttemptQuestionLogs[evalFinalAttemptIndex];
      text('RINCIAN JAWABAN EVALUASI (Percobaan ke-' + (evalFinalAttemptIndex + 1) + ')', { size:12, style:'bold', gap:3 });

      text('Bagian A - Cocokkan Fitur', { size:10, style:'bold', gap:1 });
      logDetail.drag.forEach((r, i) =&gt; {
        ensureSpace(10);
        text((i+1) + '. ' + r.func, { size:9, gap:0.5 });
        text('    Jawaban kamu: ' + r.userAns + '   |   Jawaban benar: ' + r.correctAns + '   |   ' + (r.isCorrect ? 'BENAR' : 'SALAH'), { size:9, style: r.isCorrect ? 'normal' : 'bold', gap:2 });
      });

      text('Bagian B - Ketik Cepat', { size:10, style:'bold', gap:1 });
      logDetail.type.forEach((r, i) =&gt; {
        ensureSpace(10);
        text((i+1) + '. ' + r.q, { size:9, gap:0.5 });
        text('    Jawaban kamu: ' + (r.userAns || '(kosong)') + '   |   Jawaban benar: ' + r.correctAns + '   |   ' + (r.isCorrect ? 'BENAR' : 'SALAH'), { size:9, style: r.isCorrect ? 'normal' : 'bold', gap:2 });
      });
      divider();
    }

    // ---- JAWABAN LATIHAN MENGETIK 1-19 ----
    text('JAWABAN LATIHAN MENGETIK (1-' + LATIHAN.length + ')', { size:12, style:'bold', gap:3 });
    LATIHAN.forEach((item, idx) =&gt; {
      const editor = document.querySelectorAll('.editor-box')[idx];
      const typed = editor ? editor.innerText.trim() : '';
      const s = scoreLatihan(idx);
      ensureSpace(12);
      text('Latihan ' + (idx+1) + ' - ' + item.title + '  (skor: ' + s + ')', { size:10, style:'bold', gap:1 });
      text(typed ? typed : '(belum diisi)', { size:9, gap:3 });
    });

    divider();
    ensureSpace(22);
    text('Tanda tangan siswa,', { size:10, gap:16 });
    text(nama || '(.....................................)', { size:10, gap:6 });
    ensureSpace(22);
    text('Tanda tangan guru mata pelajaran,', { size:10, gap:16 });
    text('(.....................................)', { size:10, gap:2 });

    const namaFile = 'Bukti-Tugas-Praktik-' + (nama ? nama.replace(/[^a-zA-Z0-9]+/g,'_') : 'siswa') + '.pdf';
    doc.save(namaFile);
  }

  document.getElementById('btnUnduhPDF').addEventListener('click', downloadBuktiPDF);
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/fitur-lanjut-pengolah-kata-untuk.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-5689077165551123896</guid><pubDate>Mon, 07 Sep 2026 05:59:32 +0000</pubDate><atom:updated>2026-09-14T08:22:47.401+07:00</atom:updated><title>Pengenalan Aplikasi Pengolah Kata untuk Administrasi Perkantoran</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Informatika X - Pengenalan Aplikasi Pengolah Kata untuk Administrasi Perkantoran&lt;/title&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,600;8..60,700&amp;family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;style&gt;
  :root{
    --ink:#182634; --paper:#f6f3ea; --card:#ffffff; --line:#dcd3bd;
    --teal:#0e6f63; --teal-dark:#0a5249;
    --gold:#b9791d; --gold-dark:#8f5c12;
    --danger:#c1392b; --ok:#1f7a5c;
    --tint:#e2f2ee;
  }
  *{box-sizing:border-box;}
  body{margin:0; background:var(--paper); color:var(--ink); font-family:'Inter', sans-serif; line-height:1.55; padding-bottom:64px;}
  .letterhead{background:linear-gradient(135deg, var(--teal-dark), var(--teal)); padding:30px 24px 22px; text-align:center; color:#fff;}
  .letterhead .eyebrow{font-size:12.5px; letter-spacing:.06em; color:#cdeee7; margin-bottom:8px; font-weight:600;}
  .letterhead h1{font-family:'Source Serif 4', serif; font-weight:700; font-size:clamp(23px,4vw,31px); margin:0 0 8px; max-width:800px; margin-left:auto; margin-right:auto;}
  .letterhead p.sub{margin:0; font-size:14.5px; color:#e2f3ef; max-width:700px; margin-left:auto; margin-right:auto;}
  main{max-width:820px; margin:0 auto; padding:0 24px;}
  section{margin-top:38px;}
  h2.section-title{
    font-family:'Source Serif 4', serif; font-size:21px; font-weight:700; color:var(--ink);
    background:var(--tint); border-left:6px solid var(--gold); padding:10px 14px; border-radius:3px; margin:0 0 16px;
  }
  h3.step-title{font-family:'Source Serif 4', serif; font-size:17px; font-weight:700; color:var(--teal-dark); margin:0 0 10px; display:flex; align-items:center; gap:8px;}
  h3.step-title .num{background:var(--gold); color:#fff; width:25px; height:25px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:13px; font-family:'Inter',sans-serif; flex:none;}
  .card{background:var(--card); border:1px solid var(--line); border-radius:6px; padding:22px 24px; margin-bottom:16px; box-shadow:0 1px 2px rgba(24,38,52,.04);}
  .card h3{font-family:'Source Serif 4', serif; font-size:17.5px; margin:0 0 8px; color:var(--teal-dark);}
  .card p, .card li{font-size:15px; color:#2b3746;}
  .card ul, .card ol{padding-left:20px; margin:8px 0;}
  table.std{width:100%; border-collapse:collapse; margin-top:8px; font-size:14.5px;}
  table.std th, table.std td{border:1px solid var(--line); padding:8px 10px; text-align:left;}
  table.std th{background:var(--tint); font-family:'Source Serif 4', serif; color:var(--teal-dark);}
  code.kbd{background:#eef3f1; border:1px solid var(--line); border-radius:3px; padding:1px 6px; font-family:monospace; font-size:13px; color:var(--teal-dark);}
  details{margin-bottom:10px;}
  details summary{cursor:pointer; font-weight:600; padding:6px 0; list-style:none; color:var(--ink);}
  details summary::-webkit-details-marker{display:none;}
  details summary::before{content:"+ "; color:var(--gold); font-weight:700;}
  details[open] summary::before{content:"- ";}

  .resume-card{background:#fbf4e6; border:1px solid #e6d3a0; border-left:6px solid var(--gold); border-radius:6px; padding:20px 24px;}
  .resume-card ul{margin:0; padding-left:20px;}
  .resume-card li{font-size:14.5px; margin-bottom:7px; color:#2b3746;}
  .resume-card b{color:var(--gold-dark);}

  fieldset{border:1px solid var(--line); border-radius:8px; padding:20px 22px; background:var(--card); margin-bottom:18px;}
  legend{font-family:'Source Serif 4', serif; font-weight:700; padding:0 8px; color:var(--teal-dark); font-size:16px;}
  label.field-label{display:block; font-size:13.5px; font-weight:600; margin:12px 0 5px;}
  input[type=text], input[type=number], select, textarea{
    width:100%; padding:9px 10px; font-size:15px; border:1px solid #b7ad93; border-radius:4px;
    font-family:inherit; background:#fdfcf9;
  }
  textarea{min-height:110px; resize:vertical; line-height:1.6;}
  input:focus, select:focus, textarea:focus{outline:2px solid var(--gold); outline-offset:1px;}

  .quiz-item{border-top:1px dashed var(--line); padding-top:16px; margin-top:16px;}
  .quiz-item:first-child{border-top:none; padding-top:0; margin-top:0;}
  .quiz-item p.q{font-weight:600; margin:0 0 10px;}
  .quiz-item .opt{display:flex; align-items:flex-start; gap:9px; padding:7px 9px; border-radius:4px; margin-bottom:5px; cursor:pointer; font-size:14.5px;}
  .quiz-item .opt:hover{background:var(--tint);}
  .quiz-item input[type=radio]{margin-top:3px;}

  .essay-item{border-top:1px dashed var(--line); padding-top:16px; margin-top:16px;}
  .essay-item:first-child{border-top:none; padding-top:0; margin-top:0;}
  .essay-item p.q{font-weight:600; margin:0 0 10px;}
  .essay-note{font-size:12.5px; color:var(--danger); margin-top:6px; font-weight:600;}
  .essay-count{font-size:12.5px; color:#7a8390; margin-top:4px;}

  .game-toolbar{display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; flex-wrap:wrap; gap:8px;}
  .game-toolbar a{font-size:13px; color:var(--gold-dark); text-decoration:none; font-weight:700;}
  .game-toolbar a:hover{text-decoration:underline;}
  .game-wrap{border:2px solid var(--teal); border-radius:8px; overflow:hidden; background:#f0fdf4;}
  .game-wrap iframe{display:block; width:100%; height:720px; border:0;}

  .status-chip{display:inline-flex; align-items:center; gap:6px; font-size:13.5px; font-weight:700; padding:7px 14px; border-radius:20px; background:#f5e8d2; color:var(--gold-dark); margin-top:12px;}
  .status-chip.ok-chip{background:#d9f0e5; color:var(--ok);}
  .status-chip .dot{width:8px; height:8px; border-radius:50%; background:currentColor;}

  button.submit-btn{background:linear-gradient(135deg, var(--teal-dark), var(--teal)); color:#fff; border:none; border-radius:6px; padding:14px 22px; font-size:15.5px; font-weight:700; cursor:pointer; font-family:'Inter', sans-serif; width:100%; margin-top:6px;}
  button.submit-btn:hover{filter:brightness(1.08);}
  button.submit-btn:disabled{background:#b9c0bd; cursor:not-allowed;}

  button.pdf-btn{background:linear-gradient(135deg, var(--gold-dark), var(--gold)); color:#fff; border:none; border-radius:6px; padding:14px 22px; font-size:15.5px; font-weight:700; cursor:pointer; font-family:'Inter', sans-serif; width:100%; margin-top:10px;}
  button.pdf-btn:hover{filter:brightness(1.08);}
  button.pdf-btn:disabled{background:#d8cbb0; cursor:not-allowed;}

  .msg{padding:12px 14px; border-radius:6px; font-size:14.5px; margin-top:14px; display:none;}
  .msg.show{display:block;}
  .msg.ok{background:#d9f0e5; color:var(--ok); border:1px solid #a9dcc3;}
  .msg.err{background:#fbe2db; color:var(--danger); border:1px solid #eeb7a7;}
  .msg.wait{background:#eef0eb; color:#555; border:1px solid var(--line);}

  .stamp{display:inline-block; border:2px solid var(--gold); color:var(--gold-dark); border-radius:6px; padding:6px 12px; font-family:'Source Serif 4', serif; font-weight:700; font-size:13px; transform:rotate(-3deg);}
  .checklist{font-size:13.5px; color:#3d4a58; margin-top:14px; padding:14px 16px; background:var(--tint); border-radius:6px;}
  .checklist div{display:flex; align-items:center; gap:8px; margin-bottom:6px;}
  .checklist .mark{width:16px; height:16px; border-radius:50%; border:2px solid #9aa5a1; display:inline-block; flex:none; position:relative;}
  .checklist .mark.done{background:var(--ok); border-color:var(--ok);}
  .checklist .mark.done::after{content:"v"; color:#fff; font-size:10px; font-weight:700; position:absolute; inset:0; display:flex; align-items:center; justify-content:center;}

  footer{max-width:820px; margin:48px auto 0; padding:0 24px; font-size:12.5px; color:#7a8390; text-align:center;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="letterhead"&gt;
  &lt;div class="eyebrow"&gt;MATA PELAJARAN INFORMATIKA - KELAS X SMK&lt;/div&gt;
  &lt;h1&gt;Pengenalan Aplikasi Pengolah Kata untuk Administrasi Perkantoran&lt;/h1&gt;
  &lt;p class="sub"&gt;Membuat, mengedit, memformat, dan menyimpan dokumen administrasi sesuai standar perkantoran&lt;/p&gt;
&lt;/div&gt;

&lt;main&gt;

  &lt;!-- ================= MATERI ================= --&gt;
  &lt;section id="materi"&gt;
    &lt;h2 class="section-title"&gt;A. Materi Pembelajaran&lt;/h2&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;1. Apa itu Aplikasi Pengolah Kata?&lt;/h3&gt;
      &lt;p&gt;&lt;b&gt;Aplikasi pengolah kata (word processor)&lt;/b&gt; adalah perangkat lunak yang digunakan untuk membuat, menyunting (mengedit), memformat, menyimpan, dan mencetak dokumen berbasis teks. Dalam dunia administrasi perkantoran, aplikasi ini menjadi alat utama untuk menghasilkan surat, memo, nota dinas, laporan, proposal, undangan, hingga dokumen kontrak.&lt;/p&gt;
      &lt;p&gt;Berbeda dengan mesin tik manual, aplikasi pengolah kata modern memungkinkan pengguna mengedit teks berulang kali tanpa merusak dokumen, menyisipkan gambar dan tabel, memeriksa ejaan otomatis, serta menyimpan dokumen dalam berbagai format digital yang mudah dibagikan.&lt;/p&gt;
      &lt;table class="std"&gt;
        &lt;tr&gt;&lt;th&gt;Aplikasi&lt;/th&gt;&lt;th&gt;Sifat&lt;/th&gt;&lt;th&gt;Karakteristik&lt;/th&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Microsoft Word&lt;/td&gt;&lt;td&gt;Berbayar (desktop)&lt;/td&gt;&lt;td&gt;Standar industri, fitur paling lengkap, format asli .docx&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Google Docs&lt;/td&gt;&lt;td&gt;Gratis (berbasis web/cloud)&lt;/td&gt;&lt;td&gt;Kolaborasi real-time, otomatis tersimpan di Google Drive&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;LibreOffice Writer&lt;/td&gt;&lt;td&gt;Gratis &amp; open source&lt;/td&gt;&lt;td&gt;Alternatif Word untuk desktop, kompatibel dengan .docx&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;WPS Writer&lt;/td&gt;&lt;td&gt;Gratis (versi dasar)&lt;/td&gt;&lt;td&gt;Ringan, tampilan mirip Microsoft Word&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;2. Mengenal Antarmuka (Ribbon/Menu)&lt;/h3&gt;
      &lt;p&gt;Sebelum bekerja, kenali dulu letak perintah pada aplikasi pengolah kata. Microsoft Word menggunakan sistem &lt;b&gt;Ribbon&lt;/b&gt; berisi tab menu, sedangkan Google Docs menggunakan &lt;b&gt;menu bar&lt;/b&gt; klasik, namun fungsinya setara:&lt;/p&gt;
      &lt;table class="std"&gt;
        &lt;tr&gt;&lt;th&gt;Microsoft Word (Ribbon)&lt;/th&gt;&lt;th&gt;Google Docs (Menu)&lt;/th&gt;&lt;th&gt;Fungsi utama&lt;/th&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Home&lt;/td&gt;&lt;td&gt;Format&lt;/td&gt;&lt;td&gt;Font, ukuran huruf, warna, perataan teks, bullet/numbering&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Insert&lt;/td&gt;&lt;td&gt;Insert&lt;/td&gt;&lt;td&gt;Menyisipkan gambar, tabel, header/footer, nomor halaman, shape&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Layout&lt;/td&gt;&lt;td&gt;File &amp;gt; Page setup&lt;/td&gt;&lt;td&gt;Ukuran kertas, orientasi, margin, kolom&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;References&lt;/td&gt;&lt;td&gt;Insert &amp;gt; Table of contents&lt;/td&gt;&lt;td&gt;Daftar isi, catatan kaki, kutipan&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Review&lt;/td&gt;&lt;td&gt;Tools&lt;/td&gt;&lt;td&gt;Spell check, komentar, lacak perubahan (track changes)&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;View&lt;/td&gt;&lt;td&gt;View&lt;/td&gt;&lt;td&gt;Mode tampilan, zoom, tampilkan penggaris&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;3. Membuat Dokumen Baru&lt;/h3&gt;
      &lt;ol&gt;
        &lt;li&gt;Buka aplikasi pengolah kata, lalu pilih &lt;b&gt;Blank document / Dokumen kosong&lt;/b&gt; atau gunakan template yang tersedia (misalnya template surat resmi).&lt;/li&gt;
        &lt;li&gt;Atur terlebih dahulu ukuran kertas (umumnya &lt;b&gt;A4&lt;/b&gt; untuk dokumen perkantoran di Indonesia) melalui menu &lt;i&gt;Layout &amp;gt; Size&lt;/i&gt;.&lt;/li&gt;
        &lt;li&gt;Atur margin halaman sebelum mulai mengetik agar tata letak dokumen konsisten.&lt;/li&gt;
        &lt;li&gt;Mulai mengetik isi dokumen sesuai kebutuhan (surat, laporan, memo, dsb).&lt;/li&gt;
      &lt;/ol&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;4. Mengedit Dokumen&lt;/h3&gt;
      &lt;p&gt;Mengedit adalah proses menyunting teks yang sudah ditulis. Beberapa teknik dasar mengedit dokumen:&lt;/p&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;b&gt;Menyisipkan (Insert)&lt;/b&gt; - menambahkan kata, kalimat, gambar, atau tabel di posisi kursor.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Menghapus (Delete/Backspace)&lt;/b&gt; - membuang karakter, kata, atau paragraf yang tidak diperlukan.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Menyalin &amp; menempel (Copy-Paste)&lt;/b&gt; - menggandakan teks/objek ke bagian lain dokumen.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Memindahkan (Cut-Paste)&lt;/b&gt; - memindahkan teks/objek dari satu tempat ke tempat lain.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Mencari &amp; mengganti (Find &amp; Replace)&lt;/b&gt; - menemukan kata tertentu dan menggantinya secara otomatis di seluruh dokumen, sangat berguna saat merevisi nomor surat atau nama instansi yang berulang.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Undo &amp; Redo&lt;/b&gt; - membatalkan atau mengulang perubahan terakhir.&lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;Shortcut keyboard yang wajib dikuasai staf administrasi:&lt;/p&gt;
      &lt;p&gt;
        &lt;code class="kbd"&gt;Ctrl+C&lt;/code&gt; salin ,
        &lt;code class="kbd"&gt;Ctrl+X&lt;/code&gt; potong ,
        &lt;code class="kbd"&gt;Ctrl+V&lt;/code&gt; tempel ,
        &lt;code class="kbd"&gt;Ctrl+Z&lt;/code&gt; batalkan ,
        &lt;code class="kbd"&gt;Ctrl+Y&lt;/code&gt; ulangi ,
        &lt;code class="kbd"&gt;Ctrl+S&lt;/code&gt; simpan ,
        &lt;code class="kbd"&gt;Ctrl+P&lt;/code&gt; cetak ,
        &lt;code class="kbd"&gt;Ctrl+F&lt;/code&gt; cari
      &lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;5. Memformat Dokumen&lt;/h3&gt;
      &lt;p&gt;Format dokumen menentukan tampilan visual sekaligus keterbacaan dan profesionalitas dokumen resmi.&lt;/p&gt;
      &lt;details&gt;
        &lt;summary&gt;Jenis dan ukuran huruf (font)&lt;/summary&gt;
        &lt;p&gt;Dokumen resmi perkantoran di Indonesia umumnya menggunakan huruf &lt;b&gt;Times New Roman ukuran 12pt&lt;/b&gt; atau &lt;b&gt;Arial ukuran 11pt&lt;/b&gt;, karena mudah dibaca dan bersifat formal. Hindari font dekoratif (misalnya Comic Sans) untuk dokumen resmi.&lt;/p&gt;
      &lt;/details&gt;
      &lt;details&gt;
        &lt;summary&gt;Perataan teks (alignment)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;b&gt;Rata kiri (Align Left)&lt;/b&gt; - umum untuk draf/kerja.&lt;/li&gt;
          &lt;li&gt;&lt;b&gt;Rata tengah (Center)&lt;/b&gt; - untuk judul dokumen.&lt;/li&gt;
          &lt;li&gt;&lt;b&gt;Rata kanan (Align Right)&lt;/b&gt; - untuk tempat &amp; tanggal surat.&lt;/li&gt;
          &lt;li&gt;&lt;b&gt;Rata kiri-kanan (Justify)&lt;/b&gt; - standar untuk isi/paragraf surat resmi agar tepi kiri dan kanan sama rata.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/details&gt;
      &lt;details&gt;
        &lt;summary&gt;Spasi baris &amp; margin&lt;/summary&gt;
        &lt;p&gt;Spasi baris dokumen resmi umumnya &lt;b&gt;1,15 sampai 1,5&lt;/b&gt;. Margin standar (Normal) yang lazim digunakan adalah &lt;b&gt;2,54 cm&lt;/b&gt; di seluruh sisi, meskipun instansi tertentu memakai margin khusus (misal 4 cm kiri &amp; atas, 3 cm kanan &amp; bawah) sesuai pedoman tata naskah dinas.&lt;/p&gt;
      &lt;/details&gt;
      &lt;details&gt;
        &lt;summary&gt;Elemen pendukung lain&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;b&gt;Bullet &amp; Numbering&lt;/b&gt; - merapikan daftar poin atau langkah bernomor.&lt;/li&gt;
          &lt;li&gt;&lt;b&gt;Tabel&lt;/b&gt; - menyajikan data terstruktur (misalnya daftar hadir, rincian anggaran).&lt;/li&gt;
          &lt;li&gt;&lt;b&gt;Header/Footer &amp; Page Number&lt;/b&gt; - nomor halaman dan identitas dokumen berulang di tiap halaman.&lt;/li&gt;
          &lt;li&gt;&lt;b&gt;Watermark, Page Break, Columns&lt;/b&gt; - pengaturan tata letak lanjutan untuk dokumen kompleks (kontrak, buletin, laporan panjang).&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/details&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;6. Standar Dokumen Administrasi Perkantoran&lt;/h3&gt;
      &lt;p&gt;Dokumen resmi (misalnya surat dinas) memiliki struktur baku yang harus dikenali sebelum diketik:&lt;/p&gt;
      &lt;table class="std"&gt;
        &lt;tr&gt;&lt;th&gt;Bagian&lt;/th&gt;&lt;th&gt;Keterangan&lt;/th&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Kop surat (letterhead)&lt;/td&gt;&lt;td&gt;Logo, nama instansi, dan alamat di bagian atas dokumen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Nomor, Hal, Lampiran&lt;/td&gt;&lt;td&gt;Kode administrasi dan ringkasan isi surat&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Tanggal surat&lt;/td&gt;&lt;td&gt;Ditulis rata kanan, mengikuti tempat penerbitan&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Alamat tujuan&lt;/td&gt;&lt;td&gt;Nama dan alamat penerima surat&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Salam pembuka &amp; isi&lt;/td&gt;&lt;td&gt;Paragraf pembuka, isi pokok, dan penutup&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Tanda tangan &amp; nama jelas&lt;/td&gt;&lt;td&gt;Pengesahan dokumen oleh pejabat berwenang&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;7. Menyimpan Dokumen&lt;/h3&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;b&gt;.docx&lt;/b&gt; - format standar Microsoft Word, mudah diedit kembali.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;.pdf&lt;/b&gt; - untuk dokumen final yang tidak boleh diubah, tampilan konsisten di semua perangkat.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;.odt&lt;/b&gt; - format terbuka (open document), digunakan LibreOffice.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;.rtf&lt;/b&gt; - format teks kaya yang kompatibel lintas aplikasi lama.&lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;Gunakan shortcut &lt;code class="kbd"&gt;Ctrl+S&lt;/code&gt; secara berkala saat mengetik agar dokumen tidak hilang. Beri &lt;b&gt;nama file yang jelas&lt;/b&gt;, memuat jenis dokumen, nomor/nama, dan tanggal, misalnya &lt;code class="kbd"&gt;Surat_Undangan_Rapat_04Sep2026.docx&lt;/code&gt;, bukan nama acak seperti &lt;code class="kbd"&gt;dokumen1.docx&lt;/code&gt;. Untuk dokumen berbasis Google Docs, penyimpanan berjalan otomatis ke Google Drive setiap ada perubahan.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;8. Kolaborasi &amp; Berbagi Dokumen&lt;/h3&gt;
      &lt;p&gt;Aplikasi berbasis cloud seperti Google Docs memungkinkan beberapa staf mengerjakan satu dokumen secara bersamaan (real-time). Pengaturan akses berbagi umumnya terdiri dari:&lt;/p&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;b&gt;Viewer (Pelihat)&lt;/b&gt; - hanya bisa membaca dokumen.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Commenter (Pemberi komentar)&lt;/b&gt; - bisa menambahkan catatan tanpa mengubah isi.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Editor&lt;/b&gt; - bisa mengubah langsung isi dokumen.&lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;Fitur &lt;b&gt;Version History / Riwayat Versi&lt;/b&gt; mencatat setiap perubahan sehingga dokumen bisa dikembalikan ke versi sebelumnya jika terjadi kesalahan, sangat penting untuk dokumen administrasi yang dikerjakan bersama tim.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
      &lt;h3&gt;9. Etika dan Keamanan Dokumen Digital&lt;/h3&gt;
      &lt;ul&gt;
        &lt;li&gt;Jangan mengubah atau menghapus dokumen milik orang lain tanpa izin, terutama pada dokumen bersama (shared document).&lt;/li&gt;
        &lt;li&gt;Lindungi dokumen penting/rahasia dengan kata sandi (password protect) sebelum dibagikan.&lt;/li&gt;
        &lt;li&gt;Cantumkan sumber jika mengutip isi dari dokumen atau publikasi lain (hindari plagiarisme).&lt;/li&gt;
        &lt;li&gt;Selalu buat cadangan (backup) dokumen penting di lebih dari satu tempat penyimpanan.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ================= RESUME ================= --&gt;
  &lt;section id="resume"&gt;
    &lt;h2 class="section-title"&gt;B. Resume Materi&lt;/h2&gt;
    &lt;div class="resume-card"&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;b&gt;Definisi:&lt;/b&gt; aplikasi pengolah kata (word processor) untuk membuat, mengedit, memformat, menyimpan, dan mencetak dokumen teks. Contoh: Microsoft Word, Google Docs, LibreOffice Writer, WPS Writer.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Antarmuka:&lt;/b&gt; perintah terkumpul dalam Ribbon (Word: Home, Insert, Layout, References, Review, View) atau menu bar (Google Docs: Format, Insert, File, Tools, View).&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Membuat dokumen:&lt;/b&gt; mulai dari Blank document, atur ukuran kertas A4, lalu atur margin sebelum mengetik.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Mengedit:&lt;/b&gt; insert, delete, copy-paste, cut-paste, find &amp; replace, undo-redo, dipercepat dengan shortcut &lt;code class="kbd"&gt;Ctrl+C/X/V/Z/Y/S/P/F&lt;/code&gt;.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Memformat:&lt;/b&gt; font resmi Times New Roman 12pt / Arial 11pt; perataan left/center/right/justify; spasi baris 1,15 sampai 1,5; margin standar 2,54 cm; dilengkapi bullet, numbering, tabel, header/footer.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Struktur surat resmi:&lt;/b&gt; kop surat, nomor/hal/lampiran, tanggal, alamat tujuan, salam &amp; isi, tanda tangan.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Menyimpan:&lt;/b&gt; .docx (bisa diedit), .pdf (final, tidak diedit), .odt, .rtf, dengan nama file jelas (jenis dokumen + nama/nomor + tanggal).&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Kolaborasi:&lt;/b&gt; hak akses viewer/commenter/editor, dan version history untuk melacak &amp; mengembalikan perubahan.&lt;/li&gt;
        &lt;li&gt;&lt;b&gt;Etika &amp; keamanan:&lt;/b&gt; hormati dokumen bersama, lindungi dokumen rahasia dengan password, hindari plagiarisme, selalu backup.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;!-- ================= TUGAS PRIBADI (GABUNGAN) ================= --&gt;
  &lt;section id="tugas"&gt;
    &lt;h2 class="section-title"&gt;C. Tugas Pribadi (Dikerjakan Langsung di Web Ini)&lt;/h2&gt;
    &lt;div class="card"&gt;
      &lt;p&gt;Tugas ini terdiri atas empat langkah: identitas, uji keterampilan (game), soal pilihan ganda, dan soal esai. &lt;b&gt;Nilai Evaluasi Akhir bersumber dari 15 soal (10 PG + 5 esai) dengan skor total 100&lt;/b&gt;, sedangkan skor game ditampilkan terpisah sebagai informasi keterampilan. Setelah berhasil dikirim, unduh bukti pengerjaan dalam format PDF ukuran F4, cetak, lalu serahkan ke guru. &lt;span class="stamp"&gt;1x kirim per siswa&lt;/span&gt;&lt;/p&gt;
    &lt;/div&gt;

    &lt;form id="taskForm" novalidate&gt;

      &lt;fieldset&gt;
        &lt;legend&gt;Identitas Siswa&lt;/legend&gt;
        &lt;label class="field-label" for="noAbsen"&gt;No. Absen&lt;/label&gt;
        &lt;input type="number" id="noAbsen" min="1" max="60" placeholder="Contoh: 12" required&gt;
        &lt;label class="field-label" for="nama"&gt;Nama Lengkap&lt;/label&gt;
        &lt;input type="text" id="nama" placeholder="Tulis nama lengkap" required&gt;
        &lt;label class="field-label" for="kelas"&gt;Kelas&lt;/label&gt;
        &lt;select id="kelas" required&gt;
          &lt;option value="" disabled selected&gt;- Pilih kelas -&lt;/option&gt;
          &lt;option value="X TB1"&gt;X TB1&lt;/option&gt;
          &lt;option value="X TB2"&gt;X TB2&lt;/option&gt;
          &lt;option value="X TO1"&gt;X TO1&lt;/option&gt;
          &lt;option value="X TO2"&gt;X TO2&lt;/option&gt;
          &lt;option value="X TJKT1"&gt;X TJKT1&lt;/option&gt;
          &lt;option value="X TJKT2"&gt;X TJKT2&lt;/option&gt;
        &lt;/select&gt;
      &lt;/fieldset&gt;

      &lt;fieldset&gt;
        &lt;h3 class="step-title"&gt;&lt;span class="num"&gt;1&lt;/span&gt; Uji Keterampilan - Game Master Pengolah Kata&lt;/h3&gt;
        &lt;p style="font-size:14.5px; color:#4a5568; margin-top:0;"&gt;Seret (drag) kartu tugas ke ikon fitur pengolah kata yang tepat. Mainkan sampai selesai (50 tantangan). Skor ini bersifat informasi keterampilan dan tidak dihitung ke Nilai Evaluasi Akhir.&lt;/p&gt;
        &lt;div class="game-toolbar"&gt;
          &lt;span&gt;&lt;/span&gt;
          &lt;a href="data:text/html;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImlkIj4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCwgbWF4aW11bS1zY2FsZT0xLjAsIHVzZXItc2NhbGFibGU9bm8iPgogICAgPHRpdGxlPkdhbWUgQWRtaW5pc3RyYXNpOiBNYXN0ZXIgUGVuZ29sYWggS2F0YTwvdGl0bGU+CiAgICA8IS0tIFRhaWx3aW5kIENTUyB1bnR1ayBTdHlsaW5nIENlcGF0IGRhbiBSZXNwb25zaWYgLS0+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4udGFpbHdpbmRjc3MuY29tIj48L3NjcmlwdD4KICAgIDwhLS0gRm9udEF3ZXNvbWUgdW50dWsgSWtvbiBNZW51IFBlbmdvbGFoIEthdGEgLS0+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2ZvbnQtYXdlc29tZS82LjQuMC9jc3MvYWxsLm1pbi5jc3MiPgogICAgPCEtLSBHb29nbGUgRm9udHMgKFBvcHBpbnMpIC0tPgogICAgPGxpbmsgaHJlZj0iaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1Qb3BwaW5zOndnaHRANDAwOzYwMDs3MDAmZGlzcGxheT1zd2FwIiByZWw9InN0eWxlc2hlZXQiPgogICAgCiAgICA8c3R5bGU+CiAgICAgICAgYm9keSB7CiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiAnUG9wcGlucycsIHNhbnMtc2VyaWY7CiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmMGZkZjQ7IC8qIEhpamF1IHNhbmdhdCBtdWRhL3NlZ2FyICovCiAgICAgICAgICAgIHRvdWNoLWFjdGlvbjogcGFuLXk7IC8qIE1lbmNlZ2FoIHB1bGwtdG8tcmVmcmVzaCBkaSBtb2JpbGUgKi8KICAgICAgICAgICAgb3ZlcmZsb3cteDogaGlkZGVuOwogICAgICAgIH0KCiAgICAgICAgLyogQW5pbWFzaSAqLwogICAgICAgIC5ib3VuY2UtaW4gewogICAgICAgICAgICBhbmltYXRpb246IGJvdW5jZUluIDAuNXMgY3ViaWMtYmV6aWVyKDAuMTc1LCAwLjg4NSwgMC4zMiwgMS4yNzUpOwogICAgICAgIH0KICAgICAgICBAa2V5ZnJhbWVzIGJvdW5jZUluIHsKICAgICAgICAgICAgMCUgeyB0cmFuc2Zvcm06IHNjYWxlKDAuNSk7IG9wYWNpdHk6IDA7IH0KICAgICAgICAgICAgMTAwJSB7IHRyYW5zZm9ybTogc2NhbGUoMSk7IG9wYWNpdHk6IDE7IH0KICAgICAgICB9CgogICAgICAgIC5zaGFrZSB7CiAgICAgICAgICAgIGFuaW1hdGlvbjogc2hha2UgMC40czsKICAgICAgICB9CiAgICAgICAgQGtleWZyYW1lcyBzaGFrZSB7CiAgICAgICAgICAgIDAlLCAxMDAlIHsgdHJhbnNmb3JtOiB0cmFuc2xhdGVYKDApOyB9CiAgICAgICAgICAgIDI1JSB7IHRyYW5zZm9ybTogdHJhbnNsYXRlWCgtMTBweCk7IH0KICAgICAgICAgICAgNTAlIHsgdHJhbnNmb3JtOiB0cmFuc2xhdGVYKDEwcHgpOyB9CiAgICAgICAgICAgIDc1JSB7IHRyYW5zZm9ybTogdHJhbnNsYXRlWCgtMTBweCk7IH0KICAgICAgICB9CgogICAgICAgIC8qIFN0eWxpbmcgS2FydHUgRHJhZ2dhYmxlICovCiAgICAgICAgLmRyYWdnYWJsZS1jYXJkIHsKICAgICAgICAgICAgY3Vyc29yOiBncmFiOwogICAgICAgICAgICB0b3VjaC1hY3Rpb246IG5vbmU7IC8qIFBlbnRpbmcgdW50dWsgY3VzdG9tIHRvdWNoIGRyYWcgKi8KICAgICAgICAgICAgdXNlci1zZWxlY3Q6IG5vbmU7CiAgICAgICAgICAgIHRyYW5zaXRpb246IHRyYW5zZm9ybSAwLjFzOwogICAgICAgIH0KICAgICAgICAuZHJhZ2dhYmxlLWNhcmQ6YWN0aXZlIHsKICAgICAgICAgICAgY3Vyc29yOiBncmFiYmluZzsKICAgICAgICB9CiAgICAgICAgLmRyYWdnaW5nIHsKICAgICAgICAgICAgb3BhY2l0eTogMC44OwogICAgICAgICAgICB0cmFuc2Zvcm06IHNjYWxlKDEuMDUpOwogICAgICAgICAgICB6LWluZGV4OiA1MDsKICAgICAgICB9CgogICAgICAgIC8qIFN0eWxpbmcgRHJvcCBab25lICovCiAgICAgICAgLmRyb3Atem9uZSB7CiAgICAgICAgICAgIHRyYW5zaXRpb246IGFsbCAwLjNzIGVhc2U7CiAgICAgICAgfQogICAgICAgIC5kcm9wLXpvbmUuZHJhZy1vdmVyIHsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2RiZWFmZTsgLyogQmlydSBtdWRhIHNhYXQgZGktaG92ZXIgKi8KICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjM2I4MmY2OwogICAgICAgICAgICB0cmFuc2Zvcm06IHNjYWxlKDEuMDUpOwogICAgICAgIH0KICAgICAgICAuZHJvcC16b25lLmNvcnJlY3QgewogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZGNmY2U3OwogICAgICAgICAgICBib3JkZXItY29sb3I6ICMyMmM1NWU7CiAgICAgICAgICAgIGNvbG9yOiAjMTY2NTM0OwogICAgICAgIH0KICAgICAgICAuZHJvcC16b25lLndyb25nIHsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZlZTJlMjsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZWY0NDQ0OwogICAgICAgICAgICBjb2xvcjogIzk5MWIxYjsKICAgICAgICB9CgogICAgICAgIC8qIFV0aWxpdHkgdW50dWsgY3VzdG9tIGRyYWcgbW9iaWxlICovCiAgICAgICAgLmFic29sdXRlLWRyYWcgewogICAgICAgICAgICBwb3NpdGlvbjogYWJzb2x1dGUgIWltcG9ydGFudDsKICAgICAgICAgICAgcG9pbnRlci1ldmVudHM6IG5vbmU7IC8qIFN1cGF5YSBlbGVtZW4gZGkgYmF3YWhueWEgKGRyb3Agem9uZSkgYmlzYSB0ZXJkZXRla3NpICovCiAgICAgICAgfQogICAgPC9zdHlsZT4KPC9oZWFkPgo8Ym9keSBjbGFzcz0ibWluLWgtc2NyZWVuIGZsZXggZmxleC1jb2wgaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIHAtNCI+CgogICAgPCEtLSBMQVlBUiBNVUxBSSAtLT4KICAgIDxkaXYgaWQ9InN0YXJ0LXNjcmVlbiIgY2xhc3M9ImJnLXdoaXRlIHJvdW5kZWQtM3hsIHNoYWRvdy0yeGwgcC04IG1heC13LW1kIHctZnVsbCB0ZXh0LWNlbnRlciBib3VuY2UtaW4gYm9yZGVyLXQtOCBib3JkZXItYmx1ZS01MDAiPgogICAgICAgIDxpIGNsYXNzPSJmYS1zb2xpZCBmYS1sYXB0b3AtZmlsZSB0ZXh0LTZ4bCB0ZXh0LWJsdWUtNTAwIG1iLTQiPjwvaT4KICAgICAgICA8aDEgY2xhc3M9InRleHQtM3hsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTIiPk1hc3RlciBQZW5nb2xhaCBLYXRhPC9oMT4KICAgICAgICA8cCBjbGFzcz0idGV4dC1ncmF5LTYwMCBtYi02IHRleHQtc20iPlVqaSBrZWFobGlhbiBhZG1pbmlzdHJhc2kgcGVya2FudG9yYW5tdSEgQmFjYSB0dWdhcyBkYXJpIGF0YXNhbiBkYW4gdGFyaWsgKGRyYWcpIGthcnR1IHR1Z2FzIGtlIGFsYXQvZml0dXIgeWFuZyB0ZXBhdC48L3A+CiAgICAgICAgPGJ1dHRvbiBvbmNsaWNrPSJzdGFydEdhbWUoKSIgY2xhc3M9InctZnVsbCBiZy1ibHVlLTYwMCBob3ZlcjpiZy1ibHVlLTcwMCB0ZXh0LXdoaXRlIGZvbnQtYm9sZCBweS0zIHB4LTYgcm91bmRlZC14bCB0cmFuc2l0aW9uIGR1cmF0aW9uLTIwMCB0cmFuc2Zvcm0gaG92ZXI6c2NhbGUtMTA1IHNoYWRvdy1sZyI+CiAgICAgICAgICAgIE11bGFpIEJla2VyamEgPGkgY2xhc3M9ImZhLXNvbGlkIGZhLXBsYXkgbWwtMiI+PC9pPgogICAgICAgIDwvYnV0dG9uPgogICAgPC9kaXY+CgogICAgPCEtLSBBUkVBIFBFUk1BSU5BTiAtLT4KICAgIDxkaXYgaWQ9ImdhbWUtc2NyZWVuIiBjbGFzcz0iaGlkZGVuIHctZnVsbCBtYXgtdy0zeGwgZmxleCBmbGV4LWNvbCBoLVs5MHZoXSBqdXN0aWZ5LWJldHdlZW4iPgogICAgICAgIDwhLS0gSGVhZGVyOiBTa29yICYgUHJvZ3JlcyAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJiZy13aGl0ZSByb3VuZGVkLTJ4bCBzaGFkb3ctbWQgcC00IGZsZXgganVzdGlmeS1iZXR3ZWVuIGl0ZW1zLWNlbnRlciBtYi00Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC1ibHVlLTYwMCBmb250LWJvbGQiPgogICAgICAgICAgICAgICAgPGkgY2xhc3M9ImZhLXNvbGlkIGZhLWxpc3QtY2hlY2siPjwvaT4gVHVnYXM6IDxzcGFuIGlkPSJwcm9ncmVzcy10ZXh0Ij4xLzEwPC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC1ncmVlbi02MDAgZm9udC1ib2xkIHRleHQteGwiPgogICAgICAgICAgICAgICAgPGkgY2xhc3M9ImZhLXNvbGlkIGZhLXN0YXIgdGV4dC15ZWxsb3ctNDAwIj48L2k+IFNrb3I6IDxzcGFuIGlkPSJzY29yZS10ZXh0Ij4wPC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPCEtLSBBcmVhIFBlcnRhbnlhYW4gKEthcnR1IERyYWdnYWJsZSkgLS0+CiAgICAgICAgPGRpdiBjbGFzcz0iZmxleC1ncm93IGZsZXggZmxleC1jb2wgaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIG1iLTYgcmVsYXRpdmUiIGlkPSJkcmFnLWNvbnRhaW5lciI+CiAgICAgICAgICAgIDxoMiBjbGFzcz0idGV4dC1ncmF5LTYwMCB0ZXh0LWNlbnRlciB0ZXh0LXNtIGZvbnQtc2VtaWJvbGQgbWItMyB1cHBlcmNhc2UgdHJhY2tpbmctd2lkZSI+VHVnYXMgQW5kYTo8L2gyPgogICAgICAgICAgICA8ZGl2IGlkPSJ0YXNrLWNhcmQiIGNsYXNzPSJkcmFnZ2FibGUtY2FyZCBiZy1ibHVlLTUwMCB0ZXh0LXdoaXRlIHJvdW5kZWQtMnhsIHNoYWRvdy14bCBwLTYgdy1mdWxsIG1heC13LXNtIHRleHQtY2VudGVyIGJvcmRlci1iLTQgYm9yZGVyLWJsdWUtNzAwIG14LWF1dG8iIGRyYWdnYWJsZT0idHJ1ZSI+CiAgICAgICAgICAgICAgICA8aSBjbGFzcz0iZmEtc29saWQgZmEtZW52ZWxvcGUtb3Blbi10ZXh0IHRleHQtM3hsIG1iLTMgdGV4dC1ibHVlLTIwMCI+PC9pPgogICAgICAgICAgICAgICAgPHAgaWQ9InRhc2stZGVzYyIgY2xhc3M9InRleHQtbGcgZm9udC1zZW1pYm9sZCBsZWFkaW5nLXNudWciPgogICAgICAgICAgICAgICAgICAgIDwhLS0gRGVza3JpcHNpIGFrYW4gZGktbG9hZCBkaSBzaW5pIC0tPgogICAgICAgICAgICAgICAgPC9wPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0ibXQtNCB0ZXh0LWJsdWUtMjAwIHRleHQteHMgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTIiPgogICAgICAgICAgICAgICAgICAgIDxpIGNsYXNzPSJmYS1zb2xpZCBmYS1oYW5kLXBvaW50ZXIiPjwvaT4gVGFyaWsga2FydHUgaW5pIGtlIGFsYXQgZGkgYmF3YWgKICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPCEtLSBBcmVhIERyb3AgWm9uZSAoUGlsaWhhbiBBbGF0KSAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJncmlkIGdyaWQtY29scy0yIG1kOmdyaWQtY29scy0zIGdhcC0zIG1kOmdhcC00IHBiLTQiIGlkPSJkcm9wLXpvbmVzLWNvbnRhaW5lciI+CiAgICAgICAgICAgIDwhLS0gS290YWsgcGlsaWhhbiBha2FuIGRpLWdlbmVyYXRlIG9sZWggSmF2YVNjcmlwdCAtLT4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgoKICAgIDwhLS0gTEFZQVIgQUtISVIgLS0+CiAgICA8ZGl2IGlkPSJlbmQtc2NyZWVuIiBjbGFzcz0iaGlkZGVuIGJnLXdoaXRlIHJvdW5kZWQtM3hsIHNoYWRvdy0yeGwgcC04IG1heC13LW1kIHctZnVsbCB0ZXh0LWNlbnRlciBib3VuY2UtaW4gYm9yZGVyLXQtOCBib3JkZXItZ3JlZW4tNTAwIj4KICAgICAgICA8aSBjbGFzcz0iZmEtc29saWQgZmEtYXdhcmQgdGV4dC03eGwgdGV4dC15ZWxsb3ctNDAwIG1iLTQiPjwvaT4KICAgICAgICA8aDEgY2xhc3M9InRleHQtM3hsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTIiPktlcmphIEJhZ3VzITwvaDE+CiAgICAgICAgPHAgY2xhc3M9InRleHQtZ3JheS02MDAgbWItMiI+QW5kYSB0ZWxhaCBtZW55ZWxlc2Fpa2FuIGphbSBrZXJqYSBkZW5nYW4gYmFpay48L3A+CiAgICAgICAgPGRpdiBjbGFzcz0idGV4dC01eGwgZm9udC1ib2xkIHRleHQtYmx1ZS02MDAgbXktNiIgaWQ9ImZpbmFsLXNjb3JlIj4xMDA8L2Rpdj4KICAgICAgICA8cCBpZD0iZmVlZGJhY2stbXNnIiBjbGFzcz0idGV4dC1ncmF5LTYwMCBmb250LXNlbWlib2xkIG1iLTYiPjwvcD4KICAgICAgICA8YnV0dG9uIG9uY2xpY2s9InJlc2V0R2FtZSgpIiBjbGFzcz0idy1mdWxsIGJnLWdyZWVuLTUwMCBob3ZlcjpiZy1ncmVlbi02MDAgdGV4dC13aGl0ZSBmb250LWJvbGQgcHktMyBweC02IHJvdW5kZWQteGwgdHJhbnNpdGlvbiBkdXJhdGlvbi0yMDAgc2hhZG93LWxnIj4KICAgICAgICAgICAgTWFpbiBMYWdpIDxpIGNsYXNzPSJmYS1zb2xpZCBmYS1yb3RhdGUtcmlnaHQgbWwtMiI+PC9pPgogICAgICAgIDwvYnV0dG9uPgogICAgPC9kaXY+CgogICAgPHNjcmlwdD4KICAgICAgICAvLyAtLS0gREFUQSBNQVNURVIgLS0tCiAgICAgICAgLy8gS3VtcHVsYW4gc2VtdWEgYWxhdC9maXR1ciB1bnR1ayBtZW1idWF0IHBlbmdlY29oIChkaXN0cmFjdG9ycykKICAgICAgICBjb25zdCBhbGxUb29scyA9IFsKICAgICAgICAgICAgeyBpZDogJ25ldycsIG5hbWU6ICdOZXcgRG9jdW1lbnQnLCBpY29uOiAnZmEtZmlsZS1jaXJjbGUtcGx1cycgfSwKICAgICAgICAgICAgeyBpZDogJ3NhdmUnLCBuYW1lOiAnU2F2ZSAvIFNhdmUgQXMnLCBpY29uOiAnZmEtZmxvcHB5LWRpc2snIH0sCiAgICAgICAgICAgIHsgaWQ6ICdib2xkJywgbmFtZTogJ0JvbGQnLCBpY29uOiAnZmEtYm9sZCcgfSwKICAgICAgICAgICAgeyBpZDogJ2p1c3RpZnknLCBuYW1lOiAnSnVzdGlmeScsIGljb246ICdmYS1hbGlnbi1qdXN0aWZ5JyB9LAogICAgICAgICAgICB7IGlkOiAnY29weScsIG5hbWU6ICdDb3B5JywgaWNvbjogJ2ZhLWNvcHknIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwYWdlc2l6ZScsIG5hbWU6ICdQYWdlIFNpemUnLCBpY29uOiAnZmEtZmlsZS1saW5lcycgfSwKICAgICAgICAgICAgeyBpZDogJ3VuZG8nLCBuYW1lOiAnVW5kbycsIGljb246ICdmYS1yb3RhdGUtbGVmdCcgfSwKICAgICAgICAgICAgeyBpZDogJ251bWJlcmluZycsIG5hbWU6ICdOdW1iZXJpbmcnLCBpY29uOiAnZmEtbGlzdC1vbCcgfSwKICAgICAgICAgICAgeyBpZDogJ2ZpbmQnLCBuYW1lOiAnRmluZCAmIFJlcGxhY2UnLCBpY29uOiAnZmEtbWFnbmlmeWluZy1nbGFzcy1hcnJvdy1yaWdodCcgfSwKICAgICAgICAgICAgeyBpZDogJ3BkZicsIG5hbWU6ICdFeHBvcnQgUERGJywgaWNvbjogJ2ZhLWZpbGUtcGRmJyB9LAogICAgICAgICAgICB7IGlkOiAnaXRhbGljJywgbmFtZTogJ0l0YWxpYycsIGljb246ICdmYS1pdGFsaWMnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwcmludCcsIG5hbWU6ICdQcmludCcsIGljb246ICdmYS1wcmludCcgfSwKICAgICAgICAgICAgeyBpZDogJ2NlbnRlcicsIG5hbWU6ICdDZW50ZXIgQWxpZ24nLCBpY29uOiAnZmEtYWxpZ24tY2VudGVyJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9udGNvbG9yJywgbmFtZTogJ0ZvbnQgQ29sb3InLCBpY29uOiAnZmEtcGFsZXR0ZScgfSwKICAgICAgICAgICAgeyBpZDogJ3VuZGVybGluZScsIG5hbWU6ICdVbmRlcmxpbmUnLCBpY29uOiAnZmEtdW5kZXJsaW5lJyB9LAogICAgICAgICAgICB7IGlkOiAnYWxpZ25sZWZ0JywgbmFtZTogJ0FsaWduIExlZnQnLCBpY29uOiAnZmEtYWxpZ24tbGVmdCcgfSwKICAgICAgICAgICAgeyBpZDogJ2FsaWducmlnaHQnLCBuYW1lOiAnQWxpZ24gUmlnaHQnLCBpY29uOiAnZmEtYWxpZ24tcmlnaHQnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdjdXQnLCBuYW1lOiAnQ3V0JywgaWNvbjogJ2ZhLXNjaXNzb3JzJyB9LAogICAgICAgICAgICB7IGlkOiAncGFzdGUnLCBuYW1lOiAnUGFzdGUnLCBpY29uOiAnZmEtcGFzdGUnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdyZWRvJywgbmFtZTogJ1JlZG8nLCBpY29uOiAnZmEtcm90YXRlLXJpZ2h0JyB9LAogICAgICAgICAgICB7IGlkOiAnYnVsbGV0cycsIG5hbWU6ICdCdWxsZXRzJywgaWNvbjogJ2ZhLWxpc3QtdWwnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdsaW5lc3BhY2luZycsIG5hbWU6ICdMaW5lIFNwYWNpbmcnLCBpY29uOiAnZmEtYXJyb3dzLXVwLWRvd24nIH0sCiAgICAgICAgICAgIHsgaWQ6ICdtYXJnaW5zJywgbmFtZTogJ01hcmdpbnMnLCBpY29uOiAnZmEtbWF4aW1pemUnIH0sCiAgICAgICAgICAgIHsgaWQ6ICd0YWJsZScsIG5hbWU6ICdJbnNlcnQgVGFibGUnLCBpY29uOiAnZmEtdGFibGUnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwaWN0dXJlJywgbmFtZTogJ0luc2VydCBQaWN0dXJlJywgaWNvbjogJ2ZhLWltYWdlJyB9LAogICAgICAgICAgICB7IGlkOiAnaGVhZGVyJywgbmFtZTogJ0hlYWRlcicsIGljb246ICdmYS1oZWFkaW5nJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9vdGVyJywgbmFtZTogJ0Zvb3RlcicsIGljb246ICdmYS1zaG9lLXByaW50cycgfSwKICAgICAgICAgICAgeyBpZDogJ3BhZ2VudW1iZXInLCBuYW1lOiAnUGFnZSBOdW1iZXInLCBpY29uOiAnZmEtYXJyb3ctZG93bi0xLTknIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwYWdlYnJlYWsnLCBuYW1lOiAnUGFnZSBCcmVhaycsIGljb246ICdmYS1maWxlLWRhc2hlZC1saW5lJyB9LAogICAgICAgICAgICB7IGlkOiAnY29sdW1ucycsIG5hbWU6ICdDb2x1bW5zJywgaWNvbjogJ2ZhLXRhYmxlLWNvbHVtbnMnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdvcmllbnRhdGlvbicsIG5hbWU6ICdPcmllbnRhdGlvbicsIGljb246ICdmYS1tb2JpbGUtc2NyZWVuJyB9LAogICAgICAgICAgICB7IGlkOiAnd2F0ZXJtYXJrJywgbmFtZTogJ1dhdGVybWFyaycsIGljb246ICdmYS1zdGFtcCcgfSwKICAgICAgICAgICAgeyBpZDogJ3NwZWxsY2hlY2snLCBuYW1lOiAnU3BlbGwgQ2hlY2snLCBpY29uOiAnZmEtc3BlbGwtY2hlY2snIH0sCiAgICAgICAgICAgIHsgaWQ6ICdtYWlsbWVyZ2UnLCBuYW1lOiAnTWFpbCBNZXJnZScsIGljb246ICdmYS1lbnZlbG9wZXMtYnVsaycgfSwKICAgICAgICAgICAgeyBpZDogJ3N1cGVyc2NyaXB0JywgbmFtZTogJ1N1cGVyc2NyaXB0JywgaWNvbjogJ2ZhLXN1cGVyc2NyaXB0JyB9LAogICAgICAgICAgICB7IGlkOiAnc3Vic2NyaXB0JywgbmFtZTogJ1N1YnNjcmlwdCcsIGljb246ICdmYS1zdWJzY3JpcHQnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdoaWdobGlnaHQnLCBuYW1lOiAnSGlnaGxpZ2h0JywgaWNvbjogJ2ZhLWhpZ2hsaWdodGVyJyB9LAogICAgICAgICAgICB7IGlkOiAnaHlwZXJsaW5rJywgbmFtZTogJ0h5cGVybGluaycsIGljb246ICdmYS1saW5rJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9ybWF0cGFpbnRlcicsIG5hbWU6ICdGb3JtYXQgUGFpbnRlcicsIGljb246ICdmYS1wYWludC1yb2xsZXInIH0sCiAgICAgICAgICAgIHsgaWQ6ICd0ZXh0Ym94JywgbmFtZTogJ1RleHQgQm94JywgaWNvbjogJ2ZhLXNxdWFyZS1wZW4nIH0sCiAgICAgICAgICAgIHsgaWQ6ICd3b3JkY291bnQnLCBuYW1lOiAnV29yZCBDb3VudCcsIGljb246ICdmYS1jYWxjdWxhdG9yJyB9LAogICAgICAgICAgICB7IGlkOiAnem9vbScsIG5hbWU6ICdab29tJywgaWNvbjogJ2ZhLW1hZ25pZnlpbmctZ2xhc3MtcGx1cycgfSwKICAgICAgICAgICAgeyBpZDogJ3N0cmlrZXRocm91Z2gnLCBuYW1lOiAnU3RyaWtldGhyb3VnaCcsIGljb246ICdmYS1zdHJpa2V0aHJvdWdoJyB9LAogICAgICAgICAgICB7IGlkOiAnaW5kZW50aW5jcmVhc2UnLCBuYW1lOiAnSW5jcmVhc2UgSW5kZW50JywgaWNvbjogJ2ZhLWluZGVudCcgfSwKICAgICAgICAgICAgeyBpZDogJ2luZGVudGRlY3JlYXNlJywgbmFtZTogJ0RlY3JlYXNlIEluZGVudCcsIGljb246ICdmYS1vdXRkZW50JyB9LAogICAgICAgICAgICB7IGlkOiAnY2hhcnQnLCBuYW1lOiAnSW5zZXJ0IENoYXJ0JywgaWNvbjogJ2ZhLWNoYXJ0LXBpZScgfSwKICAgICAgICAgICAgeyBpZDogJ3NoYXBlcycsIG5hbWU6ICdJbnNlcnQgU2hhcGVzJywgaWNvbjogJ2ZhLXNoYXBlcycgfSwKICAgICAgICAgICAgeyBpZDogJ3N5bWJvbCcsIG5hbWU6ICdJbnNlcnQgU3ltYm9sJywgaWNvbjogJ2ZhLW9tZWdhJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9udGZhbWlseScsIG5hbWU6ICdGb250IFN0eWxlJywgaWNvbjogJ2ZhLWZvbnQnIH0KICAgICAgICBdOwoKICAgICAgICAvLyBEYWZ0YXIgNTAgUGVydGFueWFhbgogICAgICAgIGNvbnN0IHF1ZXN0aW9ucyA9IFsKICAgICAgICAgICAgLy8gU2VzaSAxOiBEYXNhciAmIFBlbnlpbXBhbmFuCiAgICAgICAgICAgIHsgdGFzazogIkJvcyBtZW1pbnRhIEFuZGEgbWVtYnVhdCBzdXJhdCBwZW5hd2FyYW4gYmFydSBkYXJpIGtlcnRhcyBrb3NvbmcuIiwgY29ycmVjdElkOiAibmV3IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIHN1ZGFoIG1lbmdldGlrIGxhcG9yYW4uIFNla2FyYW5nIEFuZGEgaGFydXMgbWVueWltcGFubnlhIGRlbmdhbiBuYW1hIGZpbGUgYmFydSAnTGFwb3Jhbl9CdWxhbl9JbmknLiIsIGNvcnJlY3RJZDogInNhdmUiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkRva3VtZW4ga29udHJhayBzdWRhaCBmaW5hbC4gQW5kYSBoYXJ1cyBtZW55aW1wYW5ueWEga2UgZm9ybWF0IHlhbmcgdGlkYWsgYmlzYSBkaWVkaXQgc2VtYmFyYW5nYW4uIiwgY29ycmVjdElkOiAicGRmIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJTaW1wYW4gcGVtYmFydWFuIHBla2VyamFhbiB5YW5nIGJhcnUgc2FqYSBBbmRhIGtldGlrIGFnYXIgdGlkYWsgaGlsYW5nIGppa2EgdGliYS10aWJhIG1hdGkgbGFtcHUuIiwgY29ycmVjdElkOiAic2F2ZSIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQ2V0YWsgMyByYW5na2FwIGRva3VtZW4gcGVyamFuamlhbiBrZXJqYXNhbWEgaW5pIG1lbmdndW5ha2FuIHByaW50ZXIgZGkgcnVhbmcgbWVldGluZy4iLCBjb3JyZWN0SWQ6ICJwcmludCIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgMjogRm9ybWF0IEZvbnQgJiBUZWtzCiAgICAgICAgICAgIHsgdGFzazogIkp1ZHVsIGtvcCBzdXJhdCBoYXJ1cyB0ZXJsaWhhdCBsZWJpaCB0ZWJhbCBkYW4gbWVuY29sb2sgZGFyaSB0ZWtzIGxhaW5ueWEuIiwgY29ycmVjdElkOiAiYm9sZCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSBnYXJpcyBiYXdhaCBwYWRhIGthdGEgJ1BFTlRJTkcnIGRpIHBlbmd1bXVtYW4gaW50ZXJuYWwga2FudG9yLiIsIGNvcnJlY3RJZDogInVuZGVybGluZSIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiSmFkaWthbiB0ZWtzIGt1dGlwYW4gcGVudGluZyBpbmkgbWVuamFkaSBtaXJpbmcgYWdhciBiZXJiZWRhIGRhcmkgdGVrcyBwYXJhZ3JhZiBsYWlubnlhLiIsIGNvcnJlY3RJZDogIml0YWxpYyIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVWJhaCBqZW5pcyBnYXlhIHR1bGlzYW4gZG9rdW1lbiBzdXJhdCByZXNtaSBpbmkgZGFyaSBmb250IGRlZmF1bHQgbWVuamFkaSBBcmlhbCBhdGF1IFRpbWVzIE5ldyBSb21hbi4iLCBjb3JyZWN0SWQ6ICJmb250ZmFtaWx5IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJVYmFoIHdhcm5hIHRla3MgcGVyaW5nYXRhbiBkaSBwZW5ndW11bWFuIGluaSBkYXJpIGhpdGFtIG1lbmphZGkgbWVyYWggdGVyYW5nLiIsIGNvcnJlY3RJZDogImZvbnRjb2xvciIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSB3YXJuYSBsYXRhciBrdW5pbmcgKHNlcGVydGkgc3RhYmlsbykgcGFkYSBrYWxpbWF0IHlhbmcgaGFydXMgc2VnZXJhIGRpcmV2aXNpIG9sZWggdGltLiIsIGNvcnJlY3RJZDogImhpZ2hsaWdodCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSBnYXJpcyBjb3JldGFuIGRpIHRlbmdhaCB0ZWtzIHVudHVrIG1lbmFuZGFpIGhhcmdhIGxhbWEgcHJvZHVrIHlhbmcgc3VkYWggZGlza29uLiIsIGNvcnJlY3RJZDogInN0cmlrZXRocm91Z2giIH0sCiAgICAgICAgICAgIHsgdGFzazogIlNhbGluIGdheWEgaHVydWYgZGFuIHdhcm5hIGRhcmkgSnVkdWwgMSwgbGFsdSB0ZXJhcGthbiBzZWNhcmEgaW5zdGFuIGtlIEp1ZHVsIDIgdGFucGEgbWVuZ2F0dXIgdWxhbmcuIiwgY29ycmVjdElkOiAiZm9ybWF0cGFpbnRlciIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgMzogUGVyYXRhYW4gJiBQYXJhZ3JhZgogICAgICAgICAgICB7IHRhc2s6ICJBZ2FyIHN1cmF0IHJlc21pIHRlcmxpaGF0IHJhcGksIHNpc2kga2FuYW4gZGFuIGtpcmkgcGFyYWdyYWYgaGFydXMgc2VqYWphciByYXRhLiIsIGNvcnJlY3RJZDogImp1c3RpZnkiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkZvcm1hdCB0YW5nZ2FsIHN1cmF0IGRpIGJhZ2lhbiBhdGFzIGhhcnVzIGRpcmF0YWthbiBraHVzdXMga2Ugc2lzaSBraXJpIGRva3VtZW4uIiwgY29ycmVjdElkOiAiYWxpZ25sZWZ0IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQb3Npc2lrYW4gYmxvayB0YW5kYSB0YW5nYW4gZGlyZWt0dXIgYWdhciBiZXJhZGEgc2VqYWphciBkaSBtYXJnaW4ga2FuYW4gYmF3YWguIiwgY29ycmVjdElkOiAiYWxpZ25yaWdodCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiUHVzYXRrYW4ganVkdWwgbGFwb3JhbiBhZ2FyIGJlcmFkYSB0ZXBhdCBkaSB0ZW5nYWgtdGVuZ2FoIGhhbGFtYW4gc2VjYXJhIGhvcml6b250YWwuIiwgY29ycmVjdElkOiAiY2VudGVyIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUZWtzIGxhcG9yYW4gdGVybGFsdSBwYWRhdCBkaWJhY2EuIFViYWggamFyYWsgYW50YXIgYmFyaXMgdGVrcyAoc3Bhc2kgYmFyaXMpIG1lbmphZGkgMSw1LiIsIGNvcnJlY3RJZDogImxpbmVzcGFjaW5nIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJEb3JvbmcgcGFyYWdyYWYgaW5pIGxlYmloIG1lbmpvcm9rIGtlIGRhbGFtIChrZSBrYW5hbikgYWdhciBtYXN1ayBzZWJhZ2FpIGJhZ2lhbiBkYXJpIHN1Yi1iYWIuIiwgY29ycmVjdElkOiAiaW5kZW50aW5jcmVhc2UiIH0sCiAgICAgICAgICAgIHsgdGFzazogIlRhcmlrIGtlbWJhbGkgdGVrcyB5YW5nIHBvc2lzaW55YSB0ZXJsYWx1IG1lbmpvcm9rIGtlIGRhbGFtIGFnYXIgc2VqYWphciBsYWdpIGRlbmdhbiBiYXRhcyBraXJpIG5vcm1hbC4iLCBjb3JyZWN0SWQ6ICJpbmRlbnRkZWNyZWFzZSIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgNDogTmF2aWdhc2ksIENvcHksIFBhc3RlICYgVW5kbwogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIG1lbWJhdGFsa2FuIHNlYnVhaCBrZXRpa2FuIChVbmRvKSwgdGFwaSBiZXJ1YmFoIHBpa2lyYW4gZGFuIGluZ2luIG1lbmdlbWJhbGlrYW4ga2V0aWthbiB0ZXJzZWJ1dC4iLCBjb3JyZWN0SWQ6ICJyZWRvIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIGluZ2luIG1lbWluZGFoa2FuIHRla3MgYWxhbWF0IGRhcmkgZG9rdW1lbiBsYW1hIHRhbnBhIG1lbmdldGlrIHVsYW5nIChMYW5na2FoIHBlcnRhbWE6IFNhbGluKS4iLCBjb3JyZWN0SWQ6ICJjb3B5IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJTZXRlbGFoIG1lbnlhbGluIChDb3B5KSB0ZWtzIGRhcmkgZW1haWwsIHRlbXBlbGthbiB0ZWtzIHRlcnNlYnV0IGtlIGRhbGFtIGRva3VtZW4gV29yZCBBbmRhLiIsIGNvcnJlY3RJZDogInBhc3RlIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQaW5kYWhrYW4gcGFyYWdyYWYgaW5pIGtlIGJhYiBsYWluLiAoTGFuZ2thaCBwZXJ0YW1hOiBQb3RvbmcvaGlsYW5na2FuIGRhcmkgdGVtcGF0IGFzbGlueWEpLiIsIGNvcnJlY3RJZDogImN1dCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVXBzISBBbmRhIHRpZGFrIHNlbmdhamEgbWVuZ2hhcHVzIHBhcmFncmFmIHBlbnRpbmcuIEJhdGFsa2FuIGtlc2FsYWhhbiB0ZXJzZWJ1dCBhZ2FyIGtlbWJhbGkgc2VwZXJ0aSBzZW11bGEuIiwgY29ycmVjdElkOiAidW5kbyIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQWRhIGtlc2FsYWhhbiBrZXRpayBuYW1hIHBlcnVzYWhhYW4gZGkgNTAgaGFsYW1hbi4gVGVtdWthbiBrYXRhIHRlcnNlYnV0IGRhbiBnYW50aSBzZW11YW55YSBzZWthbGlndXMuIiwgY29ycmVjdElkOiAiZmluZCIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgNTogVGF0YSBMZXRhayBIYWxhbWFuIChQYWdlIExheW91dCkKICAgICAgICAgICAgeyB0YXNrOiAiU3RhbmRhciBwZXJ1c2FoYWFuIG1lbmdoYXJ1c2thbiBkb2t1bWVuIHJlc21pIGRpY2V0YWsga2h1c3VzIGRpIGtlcnRhcyB1a3VyYW4gQTQuIiwgY29ycmVjdElkOiAicGFnZXNpemUiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkF0dXIgYmF0YXMga29zb25nIGRpIHBpbmdnaXIga2VydGFzIChtYXJnaW4gYXRhcywgYmF3YWgsIGtpcmksIGthbmFuKSBtZW5qYWRpIHRlcGF0IDIgY20uIiwgY29ycmVjdElkOiAibWFyZ2lucyIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVWJhaCBwb3Npc2kga2VydGFzIHVudHVrIG1lbmNldGFrIHRhYmVsIHBhbmphbmcgZGFyaSBiZXJkaXJpIChQb3J0cmFpdCkgbWVuamFkaSBtZW5kYXRhciAoTGFuZHNjYXBlKS4iLCBjb3JyZWN0SWQ6ICJvcmllbnRhdGlvbiIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVWJhaCBmb3JtYXQgYXJ0aWtlbCBidWxldGluIHBlcnVzYWhhYW4gaW5pIG1lbmphZGkgZHVhIGJhZ2lhbiB0ZWtzIHlhbmcgYmVyZGFtcGluZ2FuIChzZXBlcnRpIGtvcmFuKS4iLCBjb3JyZWN0SWQ6ICJjb2x1bW5zIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQYWtzYSBqdWR1bCBCYWIgMiBhZ2FyIGxhbmdzdW5nIGRpbXVsYWkgZGkgaGFsYW1hbiBiYXJ1LCB0ZXJwaXNhaCBkYXJpIGhhbGFtYW4gQmFiIDEuIiwgY29ycmVjdElkOiAicGFnZWJyZWFrIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUYW1iYWhrYW4gdHVsaXNhbiB0cmFuc3BhcmFuIGJlcnR1bGlza2FuICdSQUhBU0lBJyBkaSBsYXRhciBiZWxha2FuZyBoYWxhbWFuIGtvbnRyYWsga2VyamEuIiwgY29ycmVjdElkOiAid2F0ZXJtYXJrIiB9LAogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2VzaSA2OiBFbGVtZW4gU2lzaXBhbiAoSW5zZXJ0IEVsZW1lbnRzKQogICAgICAgICAgICB7IHRhc2s6ICJTaXNpcGthbiBnYW1iYXIgbG9nbyBwZXJ1c2FoYWFuIEFuZGEgZGkgYmFnaWFuIHN1ZHV0IGF0YXMgZG9rdW1lbiBwcm9wb3NhbC4iLCBjb3JyZWN0SWQ6ICJwaWN0dXJlIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJNYXN1a2thbiBkYWZ0YXIgbmFtYSBkYW4gbm9tb3IgdGVsZXBvbiBrbGllbiBkYWxhbSBmb3JtYXQgYmFyaXMgZGFuIGtvbG9tIGtvdGFrLWtvdGFrIHlhbmcgcmFwaS4iLCBjb3JyZWN0SWQ6ICJ0YWJsZSIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVGFtYmFoa2FuIG5hbWEgaW5zdGFuc2kgYWdhciBzZWxhbHUgbXVuY3VsIGJlcnVsYW5nIGRpIG1hcmdpbiBhdGFzIHNldGlhcCBoYWxhbWFuIGRva3VtZW4uIiwgY29ycmVjdElkOiAiaGVhZGVyIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUYW1iYWhrYW4gYWxhbWF0IGRhbiBrb250YWsga2FudG9yIGFnYXIgb3RvbWF0aXMgdGVyY2V0YWsgZGkgbWFyZ2luIGJhd2FoIHNldGlhcCBoYWxhbWFuLiIsIGNvcnJlY3RJZDogImZvb3RlciIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSBub21vciB1cnV0IGhhbGFtYW4gZGkgcG9qb2sgYWdhciBkb2t1bWVuIDEwMCBsZW1iYXIgaW5pIHRpZGFrIHRlcnN1c3VuIGFjYWsgamlrYSB0ZXJjZWNlci4iLCBjb3JyZWN0SWQ6ICJwYWdlbnVtYmVyIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJCdWF0IHNlYnVhaCBrb3RhayB0ZWtzIG1lbGF5YW5nIHlhbmcgYmlzYSBkaWdlc2VyIGJlYmFzIHVudHVrIG1lbWJlcmkgY2F0YXRhbiB0YW1iYWhhbiBkaSBwaW5nZ2lyIHN1cmF0LiIsIGNvcnJlY3RJZDogInRleHRib3giIH0sCiAgICAgICAgICAgIHsgdGFzazogIlNpc2lwa2FuIGJlbnR1ayBiYW5ndW4gZGF0YXIgKHBhbmFoL2tvdGFrKSB1bnR1ayBtZW5nZ2FtYmFyIGRpYWdyYW0gYWx1ciBvcGVyYXNpb25hbCAoU09QKS4iLCBjb3JyZWN0SWQ6ICJzaGFwZXMiIH0sCiAgICAgICAgICAgIHsgdGFzazogIlNpc2lwa2FuIGdyYWZpayBkaWFncmFtIGJhdGFuZyB1bnR1ayBtZW12aXN1YWxpc2FzaWthbiBwZXJzZW50YXNlIGRhdGEgcGVuanVhbGFuIGJ1bGFuIGluaS4iLCBjb3JyZWN0SWQ6ICJjaGFydCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiU2lzaXBrYW4gdGF1dGFuIGludGVybmV0IChsaW5rKSBhZ2FyIHRla3MgJ0tsaWsgZGkgc2luaScgYmlzYSBkaWtsaWsgZGFuIG1lbWJ1a2Egd2Vic2l0ZSBwcm9maWwgcGVydXNhaGFhbi4iLCBjb3JyZWN0SWQ6ICJoeXBlcmxpbmsiIH0sCiAgICAgICAgICAgIHsgdGFzazogIk1hc3Vra2FuIGthcmFrdGVyIGtodXN1cyBzZXBlcnRpIGxhbWJhbmcgSGFrIENpcHRhICjCqSkgYXRhdSBUcmFkZW1hcmsgKOKEoikgeWFuZyB0aWRhayBhZGEgZGkga2V5Ym9hcmQuIiwgY29ycmVjdElkOiAic3ltYm9sIiB9LAogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2VzaSA3OiBGaXR1ciBMYW5qdXRhbiAmIFBlbm9tb3JhbgogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIHBlcmx1IG1lbWJ1YXQgZGFmdGFyIGxhbmdrYWgtbGFuZ2thaCBrZXJqYSB5YW5nIGJlcnVydXRhbiBzZWNhcmEgb3RvbWF0aXMgKDEsIDIsIDMsIGRzdCkuIiwgY29ycmVjdElkOiAibnVtYmVyaW5nIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJCdWF0IGRhZnRhciBwZXJhbGF0YW4gQVRLIGthbnRvciBtZW5nZ3VuYWthbiBzaW1ib2wgdGl0aWstdGl0aWsgYnVsYXQgZGkgZGVwYW5ueWEgKGJ1a2FuIGFuZ2thKS4iLCBjb3JyZWN0SWQ6ICJidWxsZXRzIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJLZXRpa2thbiBhbmdrYSAyIGtlY2lsIG1lbGF5YW5nIGRpIGF0YXMgcGFkYSB0ZWtzIG1ldGVyIHBlcnNlZ2kgKG3CsikgdW50dWsgbGFwb3JhbiBzZXJ0aWZpa2F0IHRhbmFoLiIsIGNvcnJlY3RJZDogInN1cGVyc2NyaXB0IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUdWxpc2thbiBhbmdrYSAyIGtlY2lsIGRpIGJhd2FoIHBhZGEgcnVtdXMga2ltaWEgYWlyIChI4oKCTykgZGkganVybmFsIHBlbmVsaXRpYW4gbGluZ2t1bmdhbi4iLCBjb3JyZWN0SWQ6ICJzdWJzY3JpcHQiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkJ1YXQgMTAwIHN1cmF0IHVuZGFuZ2FuIHJhcGF0IGRhcmkgc2F0dSBmb3JtYXQsIGRpIG1hbmEgbmFtYSBwZW5lcmltYW55YSBkaXRhcmlrIG90b21hdGlzIGRhcmkgZGF0YWJhc2UgRXhjZWwuIiwgY29ycmVjdElkOiAibWFpbG1lcmdlIiB9LAogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2VzaSA4OiBSZXZpZXcgJiBUYW1waWxhbgogICAgICAgICAgICB7IHRhc2s6ICJKYWxhbmthbiBwZW5nZWNla2FuIG90b21hdGlzIHVudHVrIG1lbGloYXQgYXBha2FoIGFkYSBrYXRhLWthdGEgYmFoYXNhIEluZ2dyaXMgeWFuZyBzYWxhaCBrZXRpayAodHlwbykuIiwgY29ycmVjdElkOiAic3BlbGxjaGVjayIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQXRhc2FuIEFuZGEgYmVydGFueWEgdW50dWsgYmFoYW4gcHJlc2VudGFzaSwgc3VkYWggYmVyYXBhIGJhbnlhayBqdW1sYWgga2F0YSB5YW5nIEFuZGEga2V0aWsgZGFsYW0gYXJ0aWtlbCBpbmk/IiwgY29ycmVjdElkOiAid29yZGNvdW50IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQZXJiZXNhciAoWm9vbSkgdGFtcGlsYW4gbGVtYmFyIGtlcmphIEFuZGEgZGkgbGF5YXIga29tcHV0ZXIgbWVuamFkaSAxNTAlIGFnYXIgaHVydWZueWEgdGVybGloYXQgbGViaWggamVsYXMuIiwgY29ycmVjdElkOiAiem9vbSIgfQogICAgICAgIF07CgogICAgICAgIC8vIC0tLSBWQVJJQUJFTCBTVEFURSAtLS0KICAgICAgICBsZXQgY3VycmVudFF1ZXN0aW9uSW5kZXggPSAwOwogICAgICAgIGxldCBzY29yZSA9IDA7CiAgICAgICAgbGV0IGN1cnJlbnRPcHRpb25zID0gW107CiAgICAgICAgCiAgICAgICAgLy8gLS0tIEVMRU1FTiBET00gLS0tCiAgICAgICAgY29uc3Qgc3RhcnRTY3JlZW4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc3RhcnQtc2NyZWVuJyk7CiAgICAgICAgY29uc3QgZ2FtZVNjcmVlbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdnYW1lLXNjcmVlbicpOwogICAgICAgIGNvbnN0IGVuZFNjcmVlbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlbmQtc2NyZWVuJyk7CiAgICAgICAgY29uc3QgdGFza0NhcmQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGFzay1jYXJkJyk7CiAgICAgICAgY29uc3QgdGFza0Rlc2MgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGFzay1kZXNjJyk7CiAgICAgICAgY29uc3QgZHJvcFpvbmVzQ29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Ryb3Atem9uZXMtY29udGFpbmVyJyk7CiAgICAgICAgY29uc3QgcHJvZ3Jlc3NUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Byb2dyZXNzLXRleHQnKTsKICAgICAgICBjb25zdCBzY29yZVRleHQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2NvcmUtdGV4dCcpOwoKICAgICAgICAvLyBWYXJpYWJlbCB1bnR1ayBEcmFnIFRvdWNoIE1vYmlsZQogICAgICAgIGxldCBzdGFydFggPSAwLCBzdGFydFkgPSAwOwogICAgICAgIGxldCBpbml0aWFsWCA9IDAsIGluaXRpYWxZID0gMDsKICAgICAgICBsZXQgY2xvbmVDYXJkID0gbnVsbDsKCiAgICAgICAgLy8gLS0tIEZVTkdTSSBMT0dJS0EgR0FNRSAtLS0KCiAgICAgICAgZnVuY3Rpb24gc3RhcnRHYW1lKCkgewogICAgICAgICAgICBzdGFydFNjcmVlbi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKICAgICAgICAgICAgZ2FtZVNjcmVlbi5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgICAgICAgICAgZ2FtZVNjcmVlbi5jbGFzc0xpc3QuYWRkKCdib3VuY2UtaW4nKTsKICAgICAgICAgICAgY3VycmVudFF1ZXN0aW9uSW5kZXggPSAwOwogICAgICAgICAgICBzY29yZSA9IDA7CiAgICAgICAgICAgIHVwZGF0ZVNjb3JlKCk7CiAgICAgICAgICAgIGxvYWRRdWVzdGlvbigpOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gbG9hZFF1ZXN0aW9uKCkgewogICAgICAgICAgICAvLyBDZWsgamlrYSBnYW1lIHNlbGVzYWkKICAgICAgICAgICAgaWYgKGN1cnJlbnRRdWVzdGlvbkluZGV4ID49IHF1ZXN0aW9ucy5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGVuZEdhbWUoKTsKICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgY29uc3QgcSA9IHF1ZXN0aW9uc1tjdXJyZW50UXVlc3Rpb25JbmRleF07CiAgICAgICAgICAgIHRhc2tEZXNjLnRleHRDb250ZW50ID0gcS50YXNrOwogICAgICAgICAgICBwcm9ncmVzc1RleHQudGV4dENvbnRlbnQgPSBgJHtjdXJyZW50UXVlc3Rpb25JbmRleCArIDF9LyR7cXVlc3Rpb25zLmxlbmd0aH1gOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gUmVzZXQga2FydHUgdHVnYXMKICAgICAgICAgICAgdGFza0NhcmQuc3R5bGUudmlzaWJpbGl0eSA9ICd2aXNpYmxlJzsKICAgICAgICAgICAgdGFza0NhcmQuc3R5bGUub3BhY2l0eSA9ICcxJzsKICAgICAgICAgICAgdGFza0NhcmQuY2xhc3NMaXN0LnJlbW92ZSgnc2hha2UnKTsKICAgICAgICAgICAgdGFza0NhcmQuY2xhc3NMaXN0LmFkZCgnYm91bmNlLWluJyk7CiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGFza0NhcmQuY2xhc3NMaXN0LnJlbW92ZSgnYm91bmNlLWluJyksIDUwMCk7CgogICAgICAgICAgICBnZW5lcmF0ZURyb3Bab25lcyhxLmNvcnJlY3RJZCk7CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBnZW5lcmF0ZURyb3Bab25lcyhjb3JyZWN0SWQpIHsKICAgICAgICAgICAgZHJvcFpvbmVzQ29udGFpbmVyLmlubmVySFRNTCA9ICcnOwogICAgICAgICAgICBjdXJyZW50T3B0aW9ucyA9IFtdOwoKICAgICAgICAgICAgLy8gQ2FyaSBvYmplayBhbGF0IHlhbmcgYmVuYXIKICAgICAgICAgICAgY29uc3QgY29ycmVjdFRvb2wgPSBhbGxUb29scy5maW5kKHQgPT4gdC5pZCA9PT0gY29ycmVjdElkKTsKICAgICAgICAgICAgY3VycmVudE9wdGlvbnMucHVzaChjb3JyZWN0VG9vbCk7CgogICAgICAgICAgICAvLyBQaWxpaCA0IHBlbmdlY29oIChkaXN0cmFjdG9ycykgc2VjYXJhIGFjYWsKICAgICAgICAgICAgLy8gVG90YWwgNSBwaWxpaGFuICgxIGJlbmFyLCA0IHNhbGFoKSBkaSBzZXRpYXAgc29hbAogICAgICAgICAgICBsZXQgZGlzdHJhY3RvcnMgPSBhbGxUb29scy5maWx0ZXIodCA9PiB0LmlkICE9PSBjb3JyZWN0SWQpOwogICAgICAgICAgICBkaXN0cmFjdG9ycyA9IGRpc3RyYWN0b3JzLnNvcnQoKCkgPT4gMC41IC0gTWF0aC5yYW5kb20oKSkuc2xpY2UoMCwgNCk7CiAgICAgICAgICAgIGN1cnJlbnRPcHRpb25zID0gY3VycmVudE9wdGlvbnMuY29uY2F0KGRpc3RyYWN0b3JzKTsKCiAgICAgICAgICAgIC8vIEFjYWsgdXJ1dGFuIHRhbXBpbAogICAgICAgICAgICBjdXJyZW50T3B0aW9ucy5zb3J0KCgpID0+IDAuNSAtIE1hdGgucmFuZG9tKCkpOwoKICAgICAgICAgICAgLy8gUmVuZGVyIGtvdGFrIGFsYXQKICAgICAgICAgICAgY3VycmVudE9wdGlvbnMuZm9yRWFjaCh0b29sID0+IHsKICAgICAgICAgICAgICAgIGNvbnN0IHpvbmUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgICAgICAgICAgIHpvbmUuY2xhc3NOYW1lID0gJ2Ryb3Atem9uZSBiZy13aGl0ZSBib3JkZXItNCBib3JkZXItZGFzaGVkIGJvcmRlci1ncmF5LTMwMCByb3VuZGVkLTJ4bCBwLTQgZmxleCBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgdGV4dC1jZW50ZXIgY3Vyc29yLXBvaW50ZXIgbWluLWgtWzEwMHB4XSBzaGFkb3ctc20nOwogICAgICAgICAgICAgICAgem9uZS5kYXRhc2V0LmlkID0gdG9vbC5pZDsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgem9uZS5pbm5lckhUTUwgPSBgCiAgICAgICAgICAgICAgICAgICAgPGkgY2xhc3M9ImZhLXNvbGlkICR7dG9vbC5pY29ufSB0ZXh0LTN4bCB0ZXh0LWdyYXktNTAwIG1iLTIgem9uZS1pY29uIHRyYW5zaXRpb24tY29sb3JzIj48L2k+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCB0ZXh0LXNtIHpvbmUtdGV4dCB0cmFuc2l0aW9uLWNvbG9ycyI+JHt0b29sLm5hbWV9PC9zcGFuPgogICAgICAgICAgICAgICAgYDsKCiAgICAgICAgICAgICAgICAvLyBFdmVudCBMaXN0ZW5lcnMgdW50dWsgTW91c2UgKERlc2t0b3ApCiAgICAgICAgICAgICAgICB6b25lLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdvdmVyJywgaGFuZGxlRHJhZ092ZXIpOwogICAgICAgICAgICAgICAgem9uZS5hZGRFdmVudExpc3RlbmVyKCdkcmFnZW50ZXInLCBoYW5kbGVEcmFnRW50ZXIpOwogICAgICAgICAgICAgICAgem9uZS5hZGRFdmVudExpc3RlbmVyKCdkcmFnbGVhdmUnLCBoYW5kbGVEcmFnTGVhdmUpOwogICAgICAgICAgICAgICAgem9uZS5hZGRFdmVudExpc3RlbmVyKCdkcm9wJywgaGFuZGxlRHJvcCk7CgogICAgICAgICAgICAgICAgZHJvcFpvbmVzQ29udGFpbmVyLmFwcGVuZENoaWxkKHpvbmUpOwogICAgICAgICAgICB9KTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIHVwZGF0ZVNjb3JlKCkgewogICAgICAgICAgICBzY29yZVRleHQudGV4dENvbnRlbnQgPSBzY29yZTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGNoZWNrQW5zd2VyKHNlbGVjdGVkSWQsIGRyb3Bab25lRWxlbWVudCkgewogICAgICAgICAgICBjb25zdCBjb3JyZWN0SWQgPSBxdWVzdGlvbnNbY3VycmVudFF1ZXN0aW9uSW5kZXhdLmNvcnJlY3RJZDsKICAgICAgICAgICAgY29uc3QgaWNvbkVsID0gZHJvcFpvbmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy56b25lLWljb24nKTsKICAgICAgICAgICAgY29uc3QgdGV4dEVsID0gZHJvcFpvbmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy56b25lLXRleHQnKTsKCiAgICAgICAgICAgIGlmIChzZWxlY3RlZElkID09PSBjb3JyZWN0SWQpIHsKICAgICAgICAgICAgICAgIC8vIEJlbmFyCiAgICAgICAgICAgICAgICBzY29yZSArPSAxMDsKICAgICAgICAgICAgICAgIHVwZGF0ZVNjb3JlKCk7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIGRyb3Bab25lRWxlbWVudC5jbGFzc0xpc3QuYWRkKCdjb3JyZWN0Jyk7CiAgICAgICAgICAgICAgICBpY29uRWwuY2xhc3NMaXN0LnJlbW92ZSgndGV4dC1ncmF5LTUwMCcpOwogICAgICAgICAgICAgICAgaWNvbkVsLmNsYXNzTGlzdC5hZGQoJ3RleHQtZ3JlZW4tNjAwJyk7CiAgICAgICAgICAgICAgICB0ZXh0RWwuY2xhc3NMaXN0LmFkZCgndGV4dC1ncmVlbi04MDAnKTsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgLy8gU2VtYnVueWlrYW4ga2FydHUgdHVnYXMgZGVuZ2FuIGVmZWsKICAgICAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLm9wYWNpdHkgPSAnMCc7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIC8vIExhbmp1dCBwZXJ0YW55YWFuIGJlcmlrdXRueWEKICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIGN1cnJlbnRRdWVzdGlvbkluZGV4Kys7CiAgICAgICAgICAgICAgICAgICAgbG9hZFF1ZXN0aW9uKCk7CiAgICAgICAgICAgICAgICB9LCAxMDAwKTsKCiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAvLyBTYWxhaCDigJQgdGlkYWsgZGFwYXQgbmlsYWksIGxhbmdzdW5nIGxhbmp1dCBrZSBzb2FsIGJlcmlrdXRueWEKICAgICAgICAgICAgICAgIGRyb3Bab25lRWxlbWVudC5jbGFzc0xpc3QuYWRkKCd3cm9uZycpOwogICAgICAgICAgICAgICAgaWNvbkVsLmNsYXNzTGlzdC5yZW1vdmUoJ3RleHQtZ3JheS01MDAnKTsKICAgICAgICAgICAgICAgIGljb25FbC5jbGFzc0xpc3QuYWRkKCd0ZXh0LXJlZC02MDAnKTsKICAgICAgICAgICAgICAgIHRleHRFbC5jbGFzc0xpc3QuYWRkKCd0ZXh0LXJlZC04MDAnKTsKCiAgICAgICAgICAgICAgICB0YXNrQ2FyZC5jbGFzc0xpc3QuYWRkKCdzaGFrZScpOwogICAgICAgICAgICAgICAgdGFza0NhcmQuc3R5bGUudmlzaWJpbGl0eSA9ICd2aXNpYmxlJzsgLy8gUGFzdGlrYW4gdGVybGloYXQgbGFnaQoKICAgICAgICAgICAgICAgIC8vIEt1bmNpIHNlbXVhIGtvdGFrIGphd2FiYW4gYWdhciB0aWRhayBiaXNhIGRpY29iYSB1bGFuZyBwYWRhIHNvYWwgaW5pCiAgICAgICAgICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuZHJvcC16b25lJykuZm9yRWFjaCh6ID0+IHsKICAgICAgICAgICAgICAgICAgICB6LnN0eWxlLnBvaW50ZXJFdmVudHMgPSAnbm9uZSc7CiAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAvLyBTZW1idW55aWthbiBrYXJ0dSB0dWdhcyBsYWx1IGxhbmp1dCBrZSBwZXJ0YW55YWFuIGJlcmlrdXRueWEKICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLm9wYWNpdHkgPSAnMCc7CiAgICAgICAgICAgICAgICB9LCA1MDApOwoKICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIGN1cnJlbnRRdWVzdGlvbkluZGV4Kys7CiAgICAgICAgICAgICAgICAgICAgbG9hZFF1ZXN0aW9uKCk7CiAgICAgICAgICAgICAgICB9LCAxMTAwKTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gZW5kR2FtZSgpIHsKICAgICAgICAgICAgZ2FtZVNjcmVlbi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKICAgICAgICAgICAgZW5kU2NyZWVuLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgICAgICAgICBlbmRTY3JlZW4uY2xhc3NMaXN0LmFkZCgnYm91bmNlLWluJyk7CiAgICAgICAgICAgIAogICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmluYWwtc2NvcmUnKS50ZXh0Q29udGVudCA9IHNjb3JlOwoKICAgICAgICAgICAgLy8gS2lyaW0gc2tvciBha2hpciBrZSBoYWxhbWFuIGluZHVrIChkaXBha2FpIHVudHVrIG1lbnlhdHVrYW4KICAgICAgICAgICAgLy8gaGFzaWwgZ2FtZSBkZW5nYW4gVHVnYXMgUHJpYmFkaSBkaSBoYWxhbWFuIG1hdGVyaSkKICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgIHdpbmRvdy5wYXJlbnQucG9zdE1lc3NhZ2UoewogICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJ21hc3Rlci1wZW5nb2xhaC1rYXRhJywKICAgICAgICAgICAgICAgICAgICBzY29yZTogc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgbWF4U2NvcmU6IHF1ZXN0aW9ucy5sZW5ndGggKiAxMAogICAgICAgICAgICAgICAgfSwgJyonKTsKICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7IC8qIGFiYWlrYW4gamlrYSB0aWRhayBiZXJqYWxhbiBkaSBkYWxhbSBpZnJhbWUgKi8gfQogICAgICAgICAgICAKICAgICAgICAgICAgbGV0IGZlZWRiYWNrID0gIiI7CiAgICAgICAgICAgIC8vIE1lbnllc3VhaWthbiBsb2dpa2EgcG9pbiB1bnR1ayBtYWtzaW1hbCBza29yIDUwMCAoNTAgc29hbCkKICAgICAgICAgICAgaWYgKHNjb3JlID09PSA1MDApIGZlZWRiYWNrID0gIkx1YXIgQmlhc2EgU2VtcHVybmEhIEFuZGEgYWRhbGFoIFNhbmcgTWFzdGVyIFBlbmdvbGFoIEthdGEuIPCfjJ8iOwogICAgICAgICAgICBlbHNlIGlmIChzY29yZSA+PSA0MDApIGZlZWRiYWNrID0gIkhlYmF0IHNla2FsaSEgS2V0ZXJhbXBpbGFuIGFwbGlrYXNpIHBlcmthbnRvcmFuIEFuZGEgc2V0YXJhIHNla3JldGFyaXMgaGFuZGFsLiDwn5GNIjsKICAgICAgICAgICAgZWxzZSBpZiAoc2NvcmUgPj0gMjUwKSBmZWVkYmFjayA9ICJDdWt1cCBiYWlrLCB0YXBpIG1hc2loIGJhbnlhayBmaXR1ciBsYW5qdXRhbiB5YW5nIHBlcmx1IEFuZGEgcGVsYWphcmkuIFRldGFwIHNlbWFuZ2F0ISDwn5OaIjsKICAgICAgICAgICAgZWxzZSBmZWVkYmFjayA9ICJKYW5nYW4gbWVueWVyYWghIFRlcnVzIGJlcmxhdGloIGFnYXIgQW5kYSBsZWJpaCB0ZXJiaWFzYSBkZW5nYW4gZml0dXItZml0dXIgYWRtaW5pc3RyYXNpLiDwn5KqIjsKICAgICAgICAgICAgCiAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmZWVkYmFjay1tc2cnKS50ZXh0Q29udGVudCA9IGZlZWRiYWNrOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVzZXRHYW1lKCkgewogICAgICAgICAgICBlbmRTY3JlZW4uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICAgICAgICAgIHN0YXJ0R2FtZSgpOwogICAgICAgIH0KCiAgICAgICAgLy8gLS0tIEVWRU5UIEhBTkRMRVJTIERSQUcgJiBEUk9QIChERVNLVE9QIC8gTU9VU0UpIC0tLQogICAgICAgIAogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdzdGFydCcsIChlKSA9PiB7CiAgICAgICAgICAgIGUuZGF0YVRyYW5zZmVyLnNldERhdGEoJ3RleHQvcGxhaW4nLCAndGFzaycpOwogICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRhc2tDYXJkLmNsYXNzTGlzdC5hZGQoJ29wYWNpdHktNTAnKSwgMCk7CiAgICAgICAgfSk7CgogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdlbmQnLCAoKSA9PiB7CiAgICAgICAgICAgIHRhc2tDYXJkLmNsYXNzTGlzdC5yZW1vdmUoJ29wYWNpdHktNTAnKTsKICAgICAgICB9KTsKCiAgICAgICAgZnVuY3Rpb24gaGFuZGxlRHJhZ092ZXIoZSkgewogICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7IC8vIFdhamliIGFnYXIgYmlzYSBkaS1kcm9wCiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYW5kbGVEcmFnRW50ZXIoZSkgewogICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7CiAgICAgICAgICAgIGNvbnN0IHpvbmUgPSBlLnRhcmdldC5jbG9zZXN0KCcuZHJvcC16b25lJyk7CiAgICAgICAgICAgIGlmICh6b25lKSB6b25lLmNsYXNzTGlzdC5hZGQoJ2RyYWctb3ZlcicpOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gaGFuZGxlRHJhZ0xlYXZlKGUpIHsKICAgICAgICAgICAgY29uc3Qgem9uZSA9IGUudGFyZ2V0LmNsb3Nlc3QoJy5kcm9wLXpvbmUnKTsKICAgICAgICAgICAgaWYgKHpvbmUpIHpvbmUuY2xhc3NMaXN0LnJlbW92ZSgnZHJhZy1vdmVyJyk7CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYW5kbGVEcm9wKGUpIHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICBjb25zdCB6b25lID0gZS50YXJnZXQuY2xvc2VzdCgnLmRyb3Atem9uZScpOwogICAgICAgICAgICBpZiAoem9uZSkgewogICAgICAgICAgICAgICAgem9uZS5jbGFzc0xpc3QucmVtb3ZlKCdkcmFnLW92ZXInKTsKICAgICAgICAgICAgICAgIGNvbnN0IHNlbGVjdGVkSWQgPSB6b25lLmRhdGFzZXQuaWQ7CiAgICAgICAgICAgICAgICBjaGVja0Fuc3dlcihzZWxlY3RlZElkLCB6b25lKTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8gLS0tIEVWRU5UIEhBTkRMRVJTIERSQUcgJiBEUk9QIChNT0JJTEUgLyBUT1VDSCBTQ1JFRU4pIC0tLQogICAgICAgIC8vIEthcmVuYSBIVE1MNSBEcmFnICYgRHJvcCBrdXJhbmcgcmVzcG9uc2lmIGRpIGxheWFyIHNlbnR1aCwga2l0YSBidWF0IGt1c3RvbQogICAgICAgIAogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBoYW5kbGVUb3VjaFN0YXJ0LCB7cGFzc2l2ZTogZmFsc2V9KTsKICAgICAgICB0YXNrQ2FyZC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaG1vdmUnLCBoYW5kbGVUb3VjaE1vdmUsIHtwYXNzaXZlOiBmYWxzZX0pOwogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoZW5kJywgaGFuZGxlVG91Y2hFbmQpOwoKICAgICAgICBmdW5jdGlvbiBoYW5kbGVUb3VjaFN0YXJ0KGUpIHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOyAvLyBNZW5jZWdhaCBzY3JvbGxpbmcgc2FhdCBtZW55ZW50dWgga2FydHUKICAgICAgICAgICAgY29uc3QgdG91Y2ggPSBlLnRvdWNoZXNbMF07CiAgICAgICAgICAgIAogICAgICAgICAgICAvLyBCdWF0IGtsb25pbmdhbiBrYXJ0dSB1bnR1ayB2aXN1YWwgZHJhZwogICAgICAgICAgICBjb25zdCByZWN0ID0gdGFza0NhcmQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7CiAgICAgICAgICAgIGNsb25lQ2FyZCA9IHRhc2tDYXJkLmNsb25lTm9kZSh0cnVlKTsKICAgICAgICAgICAgY2xvbmVDYXJkLmNsYXNzTGlzdC5hZGQoJ2Fic29sdXRlLWRyYWcnLCAnc2hhZG93LTJ4bCcsICdvcGFjaXR5LTkwJywgJ3NjYWxlLTEwNScpOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2V0IHBvc2lzaSBhd2FsCiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS53aWR0aCA9IGAke3JlY3Qud2lkdGh9cHhgOwogICAgICAgICAgICBjbG9uZUNhcmQuc3R5bGUuaGVpZ2h0ID0gYCR7cmVjdC5oZWlnaHR9cHhgOwogICAgICAgICAgICBjbG9uZUNhcmQuc3R5bGUubGVmdCA9IGAke3JlY3QubGVmdH1weGA7CiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS50b3AgPSBgJHtyZWN0LnRvcH1weGA7CiAgICAgICAgICAgIAogICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNsb25lQ2FyZCk7CiAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJzsgLy8gU2VtYnVueWlrYW4gYXNsaW55YQoKICAgICAgICAgICAgLy8gSGl0dW5nIG9mZnNldCBzZW50dWhhbgogICAgICAgICAgICBzdGFydFggPSB0b3VjaC5jbGllbnRYOwogICAgICAgICAgICBzdGFydFkgPSB0b3VjaC5jbGllbnRZOwogICAgICAgICAgICBpbml0aWFsWCA9IHJlY3QubGVmdDsKICAgICAgICAgICAgaW5pdGlhbFkgPSByZWN0LnRvcDsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGhhbmRsZVRvdWNoTW92ZShlKSB7CiAgICAgICAgICAgIGlmICghY2xvbmVDYXJkKSByZXR1cm47CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgICAgICAgICAgY29uc3QgdG91Y2ggPSBlLnRvdWNoZXNbMF07CiAgICAgICAgICAgIAogICAgICAgICAgICBjb25zdCBkeCA9IHRvdWNoLmNsaWVudFggLSBzdGFydFg7CiAgICAgICAgICAgIGNvbnN0IGR5ID0gdG91Y2guY2xpZW50WSAtIHN0YXJ0WTsKICAgICAgICAgICAgCiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS5sZWZ0ID0gYCR7aW5pdGlhbFggKyBkeH1weGA7CiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS50b3AgPSBgJHtpbml0aWFsWSArIGR5fXB4YDsKCiAgICAgICAgICAgIC8vIEVmZWsgaG92ZXIgcGFkYSBkcm9wem9uZSBzYWF0IHRvdWNoIG1vdmUKICAgICAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmRyb3Atem9uZScpLmZvckVhY2goeiA9PiB6LmNsYXNzTGlzdC5yZW1vdmUoJ2RyYWctb3ZlcicpKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlbWJ1bnlpa2FuIGtsb25pbmdhbiBzZW1lbnRhcmEgdW50dWsgbWVuZGV0ZWtzaSBlbGVtZW4gZGkgYmF3YWhueWEKICAgICAgICAgICAgY2xvbmVDYXJkLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgICAgICAgIGNvbnN0IGVsZW1CZWxvdyA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQodG91Y2guY2xpZW50WCwgdG91Y2guY2xpZW50WSk7CiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKCiAgICAgICAgICAgIGlmIChlbGVtQmVsb3cpIHsKICAgICAgICAgICAgICAgIGNvbnN0IHpvbmUgPSBlbGVtQmVsb3cuY2xvc2VzdCgnLmRyb3Atem9uZScpOwogICAgICAgICAgICAgICAgaWYgKHpvbmUpIHpvbmUuY2xhc3NMaXN0LmFkZCgnZHJhZy1vdmVyJyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGhhbmRsZVRvdWNoRW5kKGUpIHsKICAgICAgICAgICAgaWYgKCFjbG9uZUNhcmQpIHJldHVybjsKICAgICAgICAgICAgCiAgICAgICAgICAgIGNvbnN0IHRvdWNoID0gZS5jaGFuZ2VkVG91Y2hlc1swXTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIERldGVrc2kgem9uYSBkcm9wCiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgICAgICAgICBjb25zdCBlbGVtQmVsb3cgPSBkb2N1bWVudC5lbGVtZW50RnJvbVBvaW50KHRvdWNoLmNsaWVudFgsIHRvdWNoLmNsaWVudFkpOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gSGFwdXMga2xvbmluZ2FuIHZpc3VhbAogICAgICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGNsb25lQ2FyZCk7CiAgICAgICAgICAgIGNsb25lQ2FyZCA9IG51bGw7CiAgICAgICAgICAgIAogICAgICAgICAgICBsZXQgZHJvcHBlZE9uWm9uZSA9IGZhbHNlOwoKICAgICAgICAgICAgaWYgKGVsZW1CZWxvdykgewogICAgICAgICAgICAgICAgY29uc3Qgem9uZSA9IGVsZW1CZWxvdy5jbG9zZXN0KCcuZHJvcC16b25lJyk7CiAgICAgICAgICAgICAgICBpZiAoem9uZSkgewogICAgICAgICAgICAgICAgICAgIGRyb3BwZWRPblpvbmUgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHpvbmUuY2xhc3NMaXN0LnJlbW92ZSgnZHJhZy1vdmVyJyk7CiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWRJZCA9IHpvbmUuZGF0YXNldC5pZDsKICAgICAgICAgICAgICAgICAgICBjaGVja0Fuc3dlcihzZWxlY3RlZElkLCB6b25lKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy8gSmlrYSB0aWRhayBkaWxlcGFzIGRpIGRyb3Agem9uZSwga2VtYmFsaWthbiBrYXJ0dSBrZSBwb3Npc2kgYXdhbAogICAgICAgICAgICBpZiAoIWRyb3BwZWRPblpvbmUpIHsKICAgICAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLnZpc2liaWxpdHkgPSAndmlzaWJsZSc7CiAgICAgICAgICAgICAgICB0YXNrQ2FyZC5jbGFzc0xpc3QuYWRkKCdzaGFrZScpOwogICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0YXNrQ2FyZC5jbGFzc0xpc3QucmVtb3ZlKCdzaGFrZScpLCA0MDApOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgIDwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4=" target="_blank" rel="noopener"&gt;Buka game di layar penuh (tab baru)&lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="game-wrap"&gt;
          &lt;iframe id="gameFrame" src="data:text/html;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImlkIj4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCwgbWF4aW11bS1zY2FsZT0xLjAsIHVzZXItc2NhbGFibGU9bm8iPgogICAgPHRpdGxlPkdhbWUgQWRtaW5pc3RyYXNpOiBNYXN0ZXIgUGVuZ29sYWggS2F0YTwvdGl0bGU+CiAgICA8IS0tIFRhaWx3aW5kIENTUyB1bnR1ayBTdHlsaW5nIENlcGF0IGRhbiBSZXNwb25zaWYgLS0+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4udGFpbHdpbmRjc3MuY29tIj48L3NjcmlwdD4KICAgIDwhLS0gRm9udEF3ZXNvbWUgdW50dWsgSWtvbiBNZW51IFBlbmdvbGFoIEthdGEgLS0+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2ZvbnQtYXdlc29tZS82LjQuMC9jc3MvYWxsLm1pbi5jc3MiPgogICAgPCEtLSBHb29nbGUgRm9udHMgKFBvcHBpbnMpIC0tPgogICAgPGxpbmsgaHJlZj0iaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1Qb3BwaW5zOndnaHRANDAwOzYwMDs3MDAmZGlzcGxheT1zd2FwIiByZWw9InN0eWxlc2hlZXQiPgogICAgCiAgICA8c3R5bGU+CiAgICAgICAgYm9keSB7CiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiAnUG9wcGlucycsIHNhbnMtc2VyaWY7CiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmMGZkZjQ7IC8qIEhpamF1IHNhbmdhdCBtdWRhL3NlZ2FyICovCiAgICAgICAgICAgIHRvdWNoLWFjdGlvbjogcGFuLXk7IC8qIE1lbmNlZ2FoIHB1bGwtdG8tcmVmcmVzaCBkaSBtb2JpbGUgKi8KICAgICAgICAgICAgb3ZlcmZsb3cteDogaGlkZGVuOwogICAgICAgIH0KCiAgICAgICAgLyogQW5pbWFzaSAqLwogICAgICAgIC5ib3VuY2UtaW4gewogICAgICAgICAgICBhbmltYXRpb246IGJvdW5jZUluIDAuNXMgY3ViaWMtYmV6aWVyKDAuMTc1LCAwLjg4NSwgMC4zMiwgMS4yNzUpOwogICAgICAgIH0KICAgICAgICBAa2V5ZnJhbWVzIGJvdW5jZUluIHsKICAgICAgICAgICAgMCUgeyB0cmFuc2Zvcm06IHNjYWxlKDAuNSk7IG9wYWNpdHk6IDA7IH0KICAgICAgICAgICAgMTAwJSB7IHRyYW5zZm9ybTogc2NhbGUoMSk7IG9wYWNpdHk6IDE7IH0KICAgICAgICB9CgogICAgICAgIC5zaGFrZSB7CiAgICAgICAgICAgIGFuaW1hdGlvbjogc2hha2UgMC40czsKICAgICAgICB9CiAgICAgICAgQGtleWZyYW1lcyBzaGFrZSB7CiAgICAgICAgICAgIDAlLCAxMDAlIHsgdHJhbnNmb3JtOiB0cmFuc2xhdGVYKDApOyB9CiAgICAgICAgICAgIDI1JSB7IHRyYW5zZm9ybTogdHJhbnNsYXRlWCgtMTBweCk7IH0KICAgICAgICAgICAgNTAlIHsgdHJhbnNmb3JtOiB0cmFuc2xhdGVYKDEwcHgpOyB9CiAgICAgICAgICAgIDc1JSB7IHRyYW5zZm9ybTogdHJhbnNsYXRlWCgtMTBweCk7IH0KICAgICAgICB9CgogICAgICAgIC8qIFN0eWxpbmcgS2FydHUgRHJhZ2dhYmxlICovCiAgICAgICAgLmRyYWdnYWJsZS1jYXJkIHsKICAgICAgICAgICAgY3Vyc29yOiBncmFiOwogICAgICAgICAgICB0b3VjaC1hY3Rpb246IG5vbmU7IC8qIFBlbnRpbmcgdW50dWsgY3VzdG9tIHRvdWNoIGRyYWcgKi8KICAgICAgICAgICAgdXNlci1zZWxlY3Q6IG5vbmU7CiAgICAgICAgICAgIHRyYW5zaXRpb246IHRyYW5zZm9ybSAwLjFzOwogICAgICAgIH0KICAgICAgICAuZHJhZ2dhYmxlLWNhcmQ6YWN0aXZlIHsKICAgICAgICAgICAgY3Vyc29yOiBncmFiYmluZzsKICAgICAgICB9CiAgICAgICAgLmRyYWdnaW5nIHsKICAgICAgICAgICAgb3BhY2l0eTogMC44OwogICAgICAgICAgICB0cmFuc2Zvcm06IHNjYWxlKDEuMDUpOwogICAgICAgICAgICB6LWluZGV4OiA1MDsKICAgICAgICB9CgogICAgICAgIC8qIFN0eWxpbmcgRHJvcCBab25lICovCiAgICAgICAgLmRyb3Atem9uZSB7CiAgICAgICAgICAgIHRyYW5zaXRpb246IGFsbCAwLjNzIGVhc2U7CiAgICAgICAgfQogICAgICAgIC5kcm9wLXpvbmUuZHJhZy1vdmVyIHsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2RiZWFmZTsgLyogQmlydSBtdWRhIHNhYXQgZGktaG92ZXIgKi8KICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjM2I4MmY2OwogICAgICAgICAgICB0cmFuc2Zvcm06IHNjYWxlKDEuMDUpOwogICAgICAgIH0KICAgICAgICAuZHJvcC16b25lLmNvcnJlY3QgewogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZGNmY2U3OwogICAgICAgICAgICBib3JkZXItY29sb3I6ICMyMmM1NWU7CiAgICAgICAgICAgIGNvbG9yOiAjMTY2NTM0OwogICAgICAgIH0KICAgICAgICAuZHJvcC16b25lLndyb25nIHsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZlZTJlMjsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZWY0NDQ0OwogICAgICAgICAgICBjb2xvcjogIzk5MWIxYjsKICAgICAgICB9CgogICAgICAgIC8qIFV0aWxpdHkgdW50dWsgY3VzdG9tIGRyYWcgbW9iaWxlICovCiAgICAgICAgLmFic29sdXRlLWRyYWcgewogICAgICAgICAgICBwb3NpdGlvbjogYWJzb2x1dGUgIWltcG9ydGFudDsKICAgICAgICAgICAgcG9pbnRlci1ldmVudHM6IG5vbmU7IC8qIFN1cGF5YSBlbGVtZW4gZGkgYmF3YWhueWEgKGRyb3Agem9uZSkgYmlzYSB0ZXJkZXRla3NpICovCiAgICAgICAgfQogICAgPC9zdHlsZT4KPC9oZWFkPgo8Ym9keSBjbGFzcz0ibWluLWgtc2NyZWVuIGZsZXggZmxleC1jb2wgaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIHAtNCI+CgogICAgPCEtLSBMQVlBUiBNVUxBSSAtLT4KICAgIDxkaXYgaWQ9InN0YXJ0LXNjcmVlbiIgY2xhc3M9ImJnLXdoaXRlIHJvdW5kZWQtM3hsIHNoYWRvdy0yeGwgcC04IG1heC13LW1kIHctZnVsbCB0ZXh0LWNlbnRlciBib3VuY2UtaW4gYm9yZGVyLXQtOCBib3JkZXItYmx1ZS01MDAiPgogICAgICAgIDxpIGNsYXNzPSJmYS1zb2xpZCBmYS1sYXB0b3AtZmlsZSB0ZXh0LTZ4bCB0ZXh0LWJsdWUtNTAwIG1iLTQiPjwvaT4KICAgICAgICA8aDEgY2xhc3M9InRleHQtM3hsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTIiPk1hc3RlciBQZW5nb2xhaCBLYXRhPC9oMT4KICAgICAgICA8cCBjbGFzcz0idGV4dC1ncmF5LTYwMCBtYi02IHRleHQtc20iPlVqaSBrZWFobGlhbiBhZG1pbmlzdHJhc2kgcGVya2FudG9yYW5tdSEgQmFjYSB0dWdhcyBkYXJpIGF0YXNhbiBkYW4gdGFyaWsgKGRyYWcpIGthcnR1IHR1Z2FzIGtlIGFsYXQvZml0dXIgeWFuZyB0ZXBhdC48L3A+CiAgICAgICAgPGJ1dHRvbiBvbmNsaWNrPSJzdGFydEdhbWUoKSIgY2xhc3M9InctZnVsbCBiZy1ibHVlLTYwMCBob3ZlcjpiZy1ibHVlLTcwMCB0ZXh0LXdoaXRlIGZvbnQtYm9sZCBweS0zIHB4LTYgcm91bmRlZC14bCB0cmFuc2l0aW9uIGR1cmF0aW9uLTIwMCB0cmFuc2Zvcm0gaG92ZXI6c2NhbGUtMTA1IHNoYWRvdy1sZyI+CiAgICAgICAgICAgIE11bGFpIEJla2VyamEgPGkgY2xhc3M9ImZhLXNvbGlkIGZhLXBsYXkgbWwtMiI+PC9pPgogICAgICAgIDwvYnV0dG9uPgogICAgPC9kaXY+CgogICAgPCEtLSBBUkVBIFBFUk1BSU5BTiAtLT4KICAgIDxkaXYgaWQ9ImdhbWUtc2NyZWVuIiBjbGFzcz0iaGlkZGVuIHctZnVsbCBtYXgtdy0zeGwgZmxleCBmbGV4LWNvbCBoLVs5MHZoXSBqdXN0aWZ5LWJldHdlZW4iPgogICAgICAgIDwhLS0gSGVhZGVyOiBTa29yICYgUHJvZ3JlcyAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJiZy13aGl0ZSByb3VuZGVkLTJ4bCBzaGFkb3ctbWQgcC00IGZsZXgganVzdGlmeS1iZXR3ZWVuIGl0ZW1zLWNlbnRlciBtYi00Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC1ibHVlLTYwMCBmb250LWJvbGQiPgogICAgICAgICAgICAgICAgPGkgY2xhc3M9ImZhLXNvbGlkIGZhLWxpc3QtY2hlY2siPjwvaT4gVHVnYXM6IDxzcGFuIGlkPSJwcm9ncmVzcy10ZXh0Ij4xLzEwPC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0idGV4dC1ncmVlbi02MDAgZm9udC1ib2xkIHRleHQteGwiPgogICAgICAgICAgICAgICAgPGkgY2xhc3M9ImZhLXNvbGlkIGZhLXN0YXIgdGV4dC15ZWxsb3ctNDAwIj48L2k+IFNrb3I6IDxzcGFuIGlkPSJzY29yZS10ZXh0Ij4wPC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPCEtLSBBcmVhIFBlcnRhbnlhYW4gKEthcnR1IERyYWdnYWJsZSkgLS0+CiAgICAgICAgPGRpdiBjbGFzcz0iZmxleC1ncm93IGZsZXggZmxleC1jb2wgaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIG1iLTYgcmVsYXRpdmUiIGlkPSJkcmFnLWNvbnRhaW5lciI+CiAgICAgICAgICAgIDxoMiBjbGFzcz0idGV4dC1ncmF5LTYwMCB0ZXh0LWNlbnRlciB0ZXh0LXNtIGZvbnQtc2VtaWJvbGQgbWItMyB1cHBlcmNhc2UgdHJhY2tpbmctd2lkZSI+VHVnYXMgQW5kYTo8L2gyPgogICAgICAgICAgICA8ZGl2IGlkPSJ0YXNrLWNhcmQiIGNsYXNzPSJkcmFnZ2FibGUtY2FyZCBiZy1ibHVlLTUwMCB0ZXh0LXdoaXRlIHJvdW5kZWQtMnhsIHNoYWRvdy14bCBwLTYgdy1mdWxsIG1heC13LXNtIHRleHQtY2VudGVyIGJvcmRlci1iLTQgYm9yZGVyLWJsdWUtNzAwIG14LWF1dG8iIGRyYWdnYWJsZT0idHJ1ZSI+CiAgICAgICAgICAgICAgICA8aSBjbGFzcz0iZmEtc29saWQgZmEtZW52ZWxvcGUtb3Blbi10ZXh0IHRleHQtM3hsIG1iLTMgdGV4dC1ibHVlLTIwMCI+PC9pPgogICAgICAgICAgICAgICAgPHAgaWQ9InRhc2stZGVzYyIgY2xhc3M9InRleHQtbGcgZm9udC1zZW1pYm9sZCBsZWFkaW5nLXNudWciPgogICAgICAgICAgICAgICAgICAgIDwhLS0gRGVza3JpcHNpIGFrYW4gZGktbG9hZCBkaSBzaW5pIC0tPgogICAgICAgICAgICAgICAgPC9wPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0ibXQtNCB0ZXh0LWJsdWUtMjAwIHRleHQteHMgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgZ2FwLTIiPgogICAgICAgICAgICAgICAgICAgIDxpIGNsYXNzPSJmYS1zb2xpZCBmYS1oYW5kLXBvaW50ZXIiPjwvaT4gVGFyaWsga2FydHUgaW5pIGtlIGFsYXQgZGkgYmF3YWgKICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPCEtLSBBcmVhIERyb3AgWm9uZSAoUGlsaWhhbiBBbGF0KSAtLT4KICAgICAgICA8ZGl2IGNsYXNzPSJncmlkIGdyaWQtY29scy0yIG1kOmdyaWQtY29scy0zIGdhcC0zIG1kOmdhcC00IHBiLTQiIGlkPSJkcm9wLXpvbmVzLWNvbnRhaW5lciI+CiAgICAgICAgICAgIDwhLS0gS290YWsgcGlsaWhhbiBha2FuIGRpLWdlbmVyYXRlIG9sZWggSmF2YVNjcmlwdCAtLT4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgoKICAgIDwhLS0gTEFZQVIgQUtISVIgLS0+CiAgICA8ZGl2IGlkPSJlbmQtc2NyZWVuIiBjbGFzcz0iaGlkZGVuIGJnLXdoaXRlIHJvdW5kZWQtM3hsIHNoYWRvdy0yeGwgcC04IG1heC13LW1kIHctZnVsbCB0ZXh0LWNlbnRlciBib3VuY2UtaW4gYm9yZGVyLXQtOCBib3JkZXItZ3JlZW4tNTAwIj4KICAgICAgICA8aSBjbGFzcz0iZmEtc29saWQgZmEtYXdhcmQgdGV4dC03eGwgdGV4dC15ZWxsb3ctNDAwIG1iLTQiPjwvaT4KICAgICAgICA8aDEgY2xhc3M9InRleHQtM3hsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTIiPktlcmphIEJhZ3VzITwvaDE+CiAgICAgICAgPHAgY2xhc3M9InRleHQtZ3JheS02MDAgbWItMiI+QW5kYSB0ZWxhaCBtZW55ZWxlc2Fpa2FuIGphbSBrZXJqYSBkZW5nYW4gYmFpay48L3A+CiAgICAgICAgPGRpdiBjbGFzcz0idGV4dC01eGwgZm9udC1ib2xkIHRleHQtYmx1ZS02MDAgbXktNiIgaWQ9ImZpbmFsLXNjb3JlIj4xMDA8L2Rpdj4KICAgICAgICA8cCBpZD0iZmVlZGJhY2stbXNnIiBjbGFzcz0idGV4dC1ncmF5LTYwMCBmb250LXNlbWlib2xkIG1iLTYiPjwvcD4KICAgICAgICA8YnV0dG9uIG9uY2xpY2s9InJlc2V0R2FtZSgpIiBjbGFzcz0idy1mdWxsIGJnLWdyZWVuLTUwMCBob3ZlcjpiZy1ncmVlbi02MDAgdGV4dC13aGl0ZSBmb250LWJvbGQgcHktMyBweC02IHJvdW5kZWQteGwgdHJhbnNpdGlvbiBkdXJhdGlvbi0yMDAgc2hhZG93LWxnIj4KICAgICAgICAgICAgTWFpbiBMYWdpIDxpIGNsYXNzPSJmYS1zb2xpZCBmYS1yb3RhdGUtcmlnaHQgbWwtMiI+PC9pPgogICAgICAgIDwvYnV0dG9uPgogICAgPC9kaXY+CgogICAgPHNjcmlwdD4KICAgICAgICAvLyAtLS0gREFUQSBNQVNURVIgLS0tCiAgICAgICAgLy8gS3VtcHVsYW4gc2VtdWEgYWxhdC9maXR1ciB1bnR1ayBtZW1idWF0IHBlbmdlY29oIChkaXN0cmFjdG9ycykKICAgICAgICBjb25zdCBhbGxUb29scyA9IFsKICAgICAgICAgICAgeyBpZDogJ25ldycsIG5hbWU6ICdOZXcgRG9jdW1lbnQnLCBpY29uOiAnZmEtZmlsZS1jaXJjbGUtcGx1cycgfSwKICAgICAgICAgICAgeyBpZDogJ3NhdmUnLCBuYW1lOiAnU2F2ZSAvIFNhdmUgQXMnLCBpY29uOiAnZmEtZmxvcHB5LWRpc2snIH0sCiAgICAgICAgICAgIHsgaWQ6ICdib2xkJywgbmFtZTogJ0JvbGQnLCBpY29uOiAnZmEtYm9sZCcgfSwKICAgICAgICAgICAgeyBpZDogJ2p1c3RpZnknLCBuYW1lOiAnSnVzdGlmeScsIGljb246ICdmYS1hbGlnbi1qdXN0aWZ5JyB9LAogICAgICAgICAgICB7IGlkOiAnY29weScsIG5hbWU6ICdDb3B5JywgaWNvbjogJ2ZhLWNvcHknIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwYWdlc2l6ZScsIG5hbWU6ICdQYWdlIFNpemUnLCBpY29uOiAnZmEtZmlsZS1saW5lcycgfSwKICAgICAgICAgICAgeyBpZDogJ3VuZG8nLCBuYW1lOiAnVW5kbycsIGljb246ICdmYS1yb3RhdGUtbGVmdCcgfSwKICAgICAgICAgICAgeyBpZDogJ251bWJlcmluZycsIG5hbWU6ICdOdW1iZXJpbmcnLCBpY29uOiAnZmEtbGlzdC1vbCcgfSwKICAgICAgICAgICAgeyBpZDogJ2ZpbmQnLCBuYW1lOiAnRmluZCAmIFJlcGxhY2UnLCBpY29uOiAnZmEtbWFnbmlmeWluZy1nbGFzcy1hcnJvdy1yaWdodCcgfSwKICAgICAgICAgICAgeyBpZDogJ3BkZicsIG5hbWU6ICdFeHBvcnQgUERGJywgaWNvbjogJ2ZhLWZpbGUtcGRmJyB9LAogICAgICAgICAgICB7IGlkOiAnaXRhbGljJywgbmFtZTogJ0l0YWxpYycsIGljb246ICdmYS1pdGFsaWMnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwcmludCcsIG5hbWU6ICdQcmludCcsIGljb246ICdmYS1wcmludCcgfSwKICAgICAgICAgICAgeyBpZDogJ2NlbnRlcicsIG5hbWU6ICdDZW50ZXIgQWxpZ24nLCBpY29uOiAnZmEtYWxpZ24tY2VudGVyJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9udGNvbG9yJywgbmFtZTogJ0ZvbnQgQ29sb3InLCBpY29uOiAnZmEtcGFsZXR0ZScgfSwKICAgICAgICAgICAgeyBpZDogJ3VuZGVybGluZScsIG5hbWU6ICdVbmRlcmxpbmUnLCBpY29uOiAnZmEtdW5kZXJsaW5lJyB9LAogICAgICAgICAgICB7IGlkOiAnYWxpZ25sZWZ0JywgbmFtZTogJ0FsaWduIExlZnQnLCBpY29uOiAnZmEtYWxpZ24tbGVmdCcgfSwKICAgICAgICAgICAgeyBpZDogJ2FsaWducmlnaHQnLCBuYW1lOiAnQWxpZ24gUmlnaHQnLCBpY29uOiAnZmEtYWxpZ24tcmlnaHQnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdjdXQnLCBuYW1lOiAnQ3V0JywgaWNvbjogJ2ZhLXNjaXNzb3JzJyB9LAogICAgICAgICAgICB7IGlkOiAncGFzdGUnLCBuYW1lOiAnUGFzdGUnLCBpY29uOiAnZmEtcGFzdGUnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdyZWRvJywgbmFtZTogJ1JlZG8nLCBpY29uOiAnZmEtcm90YXRlLXJpZ2h0JyB9LAogICAgICAgICAgICB7IGlkOiAnYnVsbGV0cycsIG5hbWU6ICdCdWxsZXRzJywgaWNvbjogJ2ZhLWxpc3QtdWwnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdsaW5lc3BhY2luZycsIG5hbWU6ICdMaW5lIFNwYWNpbmcnLCBpY29uOiAnZmEtYXJyb3dzLXVwLWRvd24nIH0sCiAgICAgICAgICAgIHsgaWQ6ICdtYXJnaW5zJywgbmFtZTogJ01hcmdpbnMnLCBpY29uOiAnZmEtbWF4aW1pemUnIH0sCiAgICAgICAgICAgIHsgaWQ6ICd0YWJsZScsIG5hbWU6ICdJbnNlcnQgVGFibGUnLCBpY29uOiAnZmEtdGFibGUnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwaWN0dXJlJywgbmFtZTogJ0luc2VydCBQaWN0dXJlJywgaWNvbjogJ2ZhLWltYWdlJyB9LAogICAgICAgICAgICB7IGlkOiAnaGVhZGVyJywgbmFtZTogJ0hlYWRlcicsIGljb246ICdmYS1oZWFkaW5nJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9vdGVyJywgbmFtZTogJ0Zvb3RlcicsIGljb246ICdmYS1zaG9lLXByaW50cycgfSwKICAgICAgICAgICAgeyBpZDogJ3BhZ2VudW1iZXInLCBuYW1lOiAnUGFnZSBOdW1iZXInLCBpY29uOiAnZmEtYXJyb3ctZG93bi0xLTknIH0sCiAgICAgICAgICAgIHsgaWQ6ICdwYWdlYnJlYWsnLCBuYW1lOiAnUGFnZSBCcmVhaycsIGljb246ICdmYS1maWxlLWRhc2hlZC1saW5lJyB9LAogICAgICAgICAgICB7IGlkOiAnY29sdW1ucycsIG5hbWU6ICdDb2x1bW5zJywgaWNvbjogJ2ZhLXRhYmxlLWNvbHVtbnMnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdvcmllbnRhdGlvbicsIG5hbWU6ICdPcmllbnRhdGlvbicsIGljb246ICdmYS1tb2JpbGUtc2NyZWVuJyB9LAogICAgICAgICAgICB7IGlkOiAnd2F0ZXJtYXJrJywgbmFtZTogJ1dhdGVybWFyaycsIGljb246ICdmYS1zdGFtcCcgfSwKICAgICAgICAgICAgeyBpZDogJ3NwZWxsY2hlY2snLCBuYW1lOiAnU3BlbGwgQ2hlY2snLCBpY29uOiAnZmEtc3BlbGwtY2hlY2snIH0sCiAgICAgICAgICAgIHsgaWQ6ICdtYWlsbWVyZ2UnLCBuYW1lOiAnTWFpbCBNZXJnZScsIGljb246ICdmYS1lbnZlbG9wZXMtYnVsaycgfSwKICAgICAgICAgICAgeyBpZDogJ3N1cGVyc2NyaXB0JywgbmFtZTogJ1N1cGVyc2NyaXB0JywgaWNvbjogJ2ZhLXN1cGVyc2NyaXB0JyB9LAogICAgICAgICAgICB7IGlkOiAnc3Vic2NyaXB0JywgbmFtZTogJ1N1YnNjcmlwdCcsIGljb246ICdmYS1zdWJzY3JpcHQnIH0sCiAgICAgICAgICAgIHsgaWQ6ICdoaWdobGlnaHQnLCBuYW1lOiAnSGlnaGxpZ2h0JywgaWNvbjogJ2ZhLWhpZ2hsaWdodGVyJyB9LAogICAgICAgICAgICB7IGlkOiAnaHlwZXJsaW5rJywgbmFtZTogJ0h5cGVybGluaycsIGljb246ICdmYS1saW5rJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9ybWF0cGFpbnRlcicsIG5hbWU6ICdGb3JtYXQgUGFpbnRlcicsIGljb246ICdmYS1wYWludC1yb2xsZXInIH0sCiAgICAgICAgICAgIHsgaWQ6ICd0ZXh0Ym94JywgbmFtZTogJ1RleHQgQm94JywgaWNvbjogJ2ZhLXNxdWFyZS1wZW4nIH0sCiAgICAgICAgICAgIHsgaWQ6ICd3b3JkY291bnQnLCBuYW1lOiAnV29yZCBDb3VudCcsIGljb246ICdmYS1jYWxjdWxhdG9yJyB9LAogICAgICAgICAgICB7IGlkOiAnem9vbScsIG5hbWU6ICdab29tJywgaWNvbjogJ2ZhLW1hZ25pZnlpbmctZ2xhc3MtcGx1cycgfSwKICAgICAgICAgICAgeyBpZDogJ3N0cmlrZXRocm91Z2gnLCBuYW1lOiAnU3RyaWtldGhyb3VnaCcsIGljb246ICdmYS1zdHJpa2V0aHJvdWdoJyB9LAogICAgICAgICAgICB7IGlkOiAnaW5kZW50aW5jcmVhc2UnLCBuYW1lOiAnSW5jcmVhc2UgSW5kZW50JywgaWNvbjogJ2ZhLWluZGVudCcgfSwKICAgICAgICAgICAgeyBpZDogJ2luZGVudGRlY3JlYXNlJywgbmFtZTogJ0RlY3JlYXNlIEluZGVudCcsIGljb246ICdmYS1vdXRkZW50JyB9LAogICAgICAgICAgICB7IGlkOiAnY2hhcnQnLCBuYW1lOiAnSW5zZXJ0IENoYXJ0JywgaWNvbjogJ2ZhLWNoYXJ0LXBpZScgfSwKICAgICAgICAgICAgeyBpZDogJ3NoYXBlcycsIG5hbWU6ICdJbnNlcnQgU2hhcGVzJywgaWNvbjogJ2ZhLXNoYXBlcycgfSwKICAgICAgICAgICAgeyBpZDogJ3N5bWJvbCcsIG5hbWU6ICdJbnNlcnQgU3ltYm9sJywgaWNvbjogJ2ZhLW9tZWdhJyB9LAogICAgICAgICAgICB7IGlkOiAnZm9udGZhbWlseScsIG5hbWU6ICdGb250IFN0eWxlJywgaWNvbjogJ2ZhLWZvbnQnIH0KICAgICAgICBdOwoKICAgICAgICAvLyBEYWZ0YXIgNTAgUGVydGFueWFhbgogICAgICAgIGNvbnN0IHF1ZXN0aW9ucyA9IFsKICAgICAgICAgICAgLy8gU2VzaSAxOiBEYXNhciAmIFBlbnlpbXBhbmFuCiAgICAgICAgICAgIHsgdGFzazogIkJvcyBtZW1pbnRhIEFuZGEgbWVtYnVhdCBzdXJhdCBwZW5hd2FyYW4gYmFydSBkYXJpIGtlcnRhcyBrb3NvbmcuIiwgY29ycmVjdElkOiAibmV3IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIHN1ZGFoIG1lbmdldGlrIGxhcG9yYW4uIFNla2FyYW5nIEFuZGEgaGFydXMgbWVueWltcGFubnlhIGRlbmdhbiBuYW1hIGZpbGUgYmFydSAnTGFwb3Jhbl9CdWxhbl9JbmknLiIsIGNvcnJlY3RJZDogInNhdmUiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkRva3VtZW4ga29udHJhayBzdWRhaCBmaW5hbC4gQW5kYSBoYXJ1cyBtZW55aW1wYW5ueWEga2UgZm9ybWF0IHlhbmcgdGlkYWsgYmlzYSBkaWVkaXQgc2VtYmFyYW5nYW4uIiwgY29ycmVjdElkOiAicGRmIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJTaW1wYW4gcGVtYmFydWFuIHBla2VyamFhbiB5YW5nIGJhcnUgc2FqYSBBbmRhIGtldGlrIGFnYXIgdGlkYWsgaGlsYW5nIGppa2EgdGliYS10aWJhIG1hdGkgbGFtcHUuIiwgY29ycmVjdElkOiAic2F2ZSIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQ2V0YWsgMyByYW5na2FwIGRva3VtZW4gcGVyamFuamlhbiBrZXJqYXNhbWEgaW5pIG1lbmdndW5ha2FuIHByaW50ZXIgZGkgcnVhbmcgbWVldGluZy4iLCBjb3JyZWN0SWQ6ICJwcmludCIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgMjogRm9ybWF0IEZvbnQgJiBUZWtzCiAgICAgICAgICAgIHsgdGFzazogIkp1ZHVsIGtvcCBzdXJhdCBoYXJ1cyB0ZXJsaWhhdCBsZWJpaCB0ZWJhbCBkYW4gbWVuY29sb2sgZGFyaSB0ZWtzIGxhaW5ueWEuIiwgY29ycmVjdElkOiAiYm9sZCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSBnYXJpcyBiYXdhaCBwYWRhIGthdGEgJ1BFTlRJTkcnIGRpIHBlbmd1bXVtYW4gaW50ZXJuYWwga2FudG9yLiIsIGNvcnJlY3RJZDogInVuZGVybGluZSIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiSmFkaWthbiB0ZWtzIGt1dGlwYW4gcGVudGluZyBpbmkgbWVuamFkaSBtaXJpbmcgYWdhciBiZXJiZWRhIGRhcmkgdGVrcyBwYXJhZ3JhZiBsYWlubnlhLiIsIGNvcnJlY3RJZDogIml0YWxpYyIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVWJhaCBqZW5pcyBnYXlhIHR1bGlzYW4gZG9rdW1lbiBzdXJhdCByZXNtaSBpbmkgZGFyaSBmb250IGRlZmF1bHQgbWVuamFkaSBBcmlhbCBhdGF1IFRpbWVzIE5ldyBSb21hbi4iLCBjb3JyZWN0SWQ6ICJmb250ZmFtaWx5IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJVYmFoIHdhcm5hIHRla3MgcGVyaW5nYXRhbiBkaSBwZW5ndW11bWFuIGluaSBkYXJpIGhpdGFtIG1lbmphZGkgbWVyYWggdGVyYW5nLiIsIGNvcnJlY3RJZDogImZvbnRjb2xvciIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSB3YXJuYSBsYXRhciBrdW5pbmcgKHNlcGVydGkgc3RhYmlsbykgcGFkYSBrYWxpbWF0IHlhbmcgaGFydXMgc2VnZXJhIGRpcmV2aXNpIG9sZWggdGltLiIsIGNvcnJlY3RJZDogImhpZ2hsaWdodCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSBnYXJpcyBjb3JldGFuIGRpIHRlbmdhaCB0ZWtzIHVudHVrIG1lbmFuZGFpIGhhcmdhIGxhbWEgcHJvZHVrIHlhbmcgc3VkYWggZGlza29uLiIsIGNvcnJlY3RJZDogInN0cmlrZXRocm91Z2giIH0sCiAgICAgICAgICAgIHsgdGFzazogIlNhbGluIGdheWEgaHVydWYgZGFuIHdhcm5hIGRhcmkgSnVkdWwgMSwgbGFsdSB0ZXJhcGthbiBzZWNhcmEgaW5zdGFuIGtlIEp1ZHVsIDIgdGFucGEgbWVuZ2F0dXIgdWxhbmcuIiwgY29ycmVjdElkOiAiZm9ybWF0cGFpbnRlciIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgMzogUGVyYXRhYW4gJiBQYXJhZ3JhZgogICAgICAgICAgICB7IHRhc2s6ICJBZ2FyIHN1cmF0IHJlc21pIHRlcmxpaGF0IHJhcGksIHNpc2kga2FuYW4gZGFuIGtpcmkgcGFyYWdyYWYgaGFydXMgc2VqYWphciByYXRhLiIsIGNvcnJlY3RJZDogImp1c3RpZnkiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkZvcm1hdCB0YW5nZ2FsIHN1cmF0IGRpIGJhZ2lhbiBhdGFzIGhhcnVzIGRpcmF0YWthbiBraHVzdXMga2Ugc2lzaSBraXJpIGRva3VtZW4uIiwgY29ycmVjdElkOiAiYWxpZ25sZWZ0IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQb3Npc2lrYW4gYmxvayB0YW5kYSB0YW5nYW4gZGlyZWt0dXIgYWdhciBiZXJhZGEgc2VqYWphciBkaSBtYXJnaW4ga2FuYW4gYmF3YWguIiwgY29ycmVjdElkOiAiYWxpZ25yaWdodCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiUHVzYXRrYW4ganVkdWwgbGFwb3JhbiBhZ2FyIGJlcmFkYSB0ZXBhdCBkaSB0ZW5nYWgtdGVuZ2FoIGhhbGFtYW4gc2VjYXJhIGhvcml6b250YWwuIiwgY29ycmVjdElkOiAiY2VudGVyIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUZWtzIGxhcG9yYW4gdGVybGFsdSBwYWRhdCBkaWJhY2EuIFViYWggamFyYWsgYW50YXIgYmFyaXMgdGVrcyAoc3Bhc2kgYmFyaXMpIG1lbmphZGkgMSw1LiIsIGNvcnJlY3RJZDogImxpbmVzcGFjaW5nIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJEb3JvbmcgcGFyYWdyYWYgaW5pIGxlYmloIG1lbmpvcm9rIGtlIGRhbGFtIChrZSBrYW5hbikgYWdhciBtYXN1ayBzZWJhZ2FpIGJhZ2lhbiBkYXJpIHN1Yi1iYWIuIiwgY29ycmVjdElkOiAiaW5kZW50aW5jcmVhc2UiIH0sCiAgICAgICAgICAgIHsgdGFzazogIlRhcmlrIGtlbWJhbGkgdGVrcyB5YW5nIHBvc2lzaW55YSB0ZXJsYWx1IG1lbmpvcm9rIGtlIGRhbGFtIGFnYXIgc2VqYWphciBsYWdpIGRlbmdhbiBiYXRhcyBraXJpIG5vcm1hbC4iLCBjb3JyZWN0SWQ6ICJpbmRlbnRkZWNyZWFzZSIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgNDogTmF2aWdhc2ksIENvcHksIFBhc3RlICYgVW5kbwogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIG1lbWJhdGFsa2FuIHNlYnVhaCBrZXRpa2FuIChVbmRvKSwgdGFwaSBiZXJ1YmFoIHBpa2lyYW4gZGFuIGluZ2luIG1lbmdlbWJhbGlrYW4ga2V0aWthbiB0ZXJzZWJ1dC4iLCBjb3JyZWN0SWQ6ICJyZWRvIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIGluZ2luIG1lbWluZGFoa2FuIHRla3MgYWxhbWF0IGRhcmkgZG9rdW1lbiBsYW1hIHRhbnBhIG1lbmdldGlrIHVsYW5nIChMYW5na2FoIHBlcnRhbWE6IFNhbGluKS4iLCBjb3JyZWN0SWQ6ICJjb3B5IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJTZXRlbGFoIG1lbnlhbGluIChDb3B5KSB0ZWtzIGRhcmkgZW1haWwsIHRlbXBlbGthbiB0ZWtzIHRlcnNlYnV0IGtlIGRhbGFtIGRva3VtZW4gV29yZCBBbmRhLiIsIGNvcnJlY3RJZDogInBhc3RlIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQaW5kYWhrYW4gcGFyYWdyYWYgaW5pIGtlIGJhYiBsYWluLiAoTGFuZ2thaCBwZXJ0YW1hOiBQb3RvbmcvaGlsYW5na2FuIGRhcmkgdGVtcGF0IGFzbGlueWEpLiIsIGNvcnJlY3RJZDogImN1dCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVXBzISBBbmRhIHRpZGFrIHNlbmdhamEgbWVuZ2hhcHVzIHBhcmFncmFmIHBlbnRpbmcuIEJhdGFsa2FuIGtlc2FsYWhhbiB0ZXJzZWJ1dCBhZ2FyIGtlbWJhbGkgc2VwZXJ0aSBzZW11bGEuIiwgY29ycmVjdElkOiAidW5kbyIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQWRhIGtlc2FsYWhhbiBrZXRpayBuYW1hIHBlcnVzYWhhYW4gZGkgNTAgaGFsYW1hbi4gVGVtdWthbiBrYXRhIHRlcnNlYnV0IGRhbiBnYW50aSBzZW11YW55YSBzZWthbGlndXMuIiwgY29ycmVjdElkOiAiZmluZCIgfSwKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlc2kgNTogVGF0YSBMZXRhayBIYWxhbWFuIChQYWdlIExheW91dCkKICAgICAgICAgICAgeyB0YXNrOiAiU3RhbmRhciBwZXJ1c2FoYWFuIG1lbmdoYXJ1c2thbiBkb2t1bWVuIHJlc21pIGRpY2V0YWsga2h1c3VzIGRpIGtlcnRhcyB1a3VyYW4gQTQuIiwgY29ycmVjdElkOiAicGFnZXNpemUiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkF0dXIgYmF0YXMga29zb25nIGRpIHBpbmdnaXIga2VydGFzIChtYXJnaW4gYXRhcywgYmF3YWgsIGtpcmksIGthbmFuKSBtZW5qYWRpIHRlcGF0IDIgY20uIiwgY29ycmVjdElkOiAibWFyZ2lucyIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVWJhaCBwb3Npc2kga2VydGFzIHVudHVrIG1lbmNldGFrIHRhYmVsIHBhbmphbmcgZGFyaSBiZXJkaXJpIChQb3J0cmFpdCkgbWVuamFkaSBtZW5kYXRhciAoTGFuZHNjYXBlKS4iLCBjb3JyZWN0SWQ6ICJvcmllbnRhdGlvbiIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVWJhaCBmb3JtYXQgYXJ0aWtlbCBidWxldGluIHBlcnVzYWhhYW4gaW5pIG1lbmphZGkgZHVhIGJhZ2lhbiB0ZWtzIHlhbmcgYmVyZGFtcGluZ2FuIChzZXBlcnRpIGtvcmFuKS4iLCBjb3JyZWN0SWQ6ICJjb2x1bW5zIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQYWtzYSBqdWR1bCBCYWIgMiBhZ2FyIGxhbmdzdW5nIGRpbXVsYWkgZGkgaGFsYW1hbiBiYXJ1LCB0ZXJwaXNhaCBkYXJpIGhhbGFtYW4gQmFiIDEuIiwgY29ycmVjdElkOiAicGFnZWJyZWFrIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUYW1iYWhrYW4gdHVsaXNhbiB0cmFuc3BhcmFuIGJlcnR1bGlza2FuICdSQUhBU0lBJyBkaSBsYXRhciBiZWxha2FuZyBoYWxhbWFuIGtvbnRyYWsga2VyamEuIiwgY29ycmVjdElkOiAid2F0ZXJtYXJrIiB9LAogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2VzaSA2OiBFbGVtZW4gU2lzaXBhbiAoSW5zZXJ0IEVsZW1lbnRzKQogICAgICAgICAgICB7IHRhc2s6ICJTaXNpcGthbiBnYW1iYXIgbG9nbyBwZXJ1c2FoYWFuIEFuZGEgZGkgYmFnaWFuIHN1ZHV0IGF0YXMgZG9rdW1lbiBwcm9wb3NhbC4iLCBjb3JyZWN0SWQ6ICJwaWN0dXJlIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJNYXN1a2thbiBkYWZ0YXIgbmFtYSBkYW4gbm9tb3IgdGVsZXBvbiBrbGllbiBkYWxhbSBmb3JtYXQgYmFyaXMgZGFuIGtvbG9tIGtvdGFrLWtvdGFrIHlhbmcgcmFwaS4iLCBjb3JyZWN0SWQ6ICJ0YWJsZSIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiVGFtYmFoa2FuIG5hbWEgaW5zdGFuc2kgYWdhciBzZWxhbHUgbXVuY3VsIGJlcnVsYW5nIGRpIG1hcmdpbiBhdGFzIHNldGlhcCBoYWxhbWFuIGRva3VtZW4uIiwgY29ycmVjdElkOiAiaGVhZGVyIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUYW1iYWhrYW4gYWxhbWF0IGRhbiBrb250YWsga2FudG9yIGFnYXIgb3RvbWF0aXMgdGVyY2V0YWsgZGkgbWFyZ2luIGJhd2FoIHNldGlhcCBoYWxhbWFuLiIsIGNvcnJlY3RJZDogImZvb3RlciIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQmVyaSBub21vciB1cnV0IGhhbGFtYW4gZGkgcG9qb2sgYWdhciBkb2t1bWVuIDEwMCBsZW1iYXIgaW5pIHRpZGFrIHRlcnN1c3VuIGFjYWsgamlrYSB0ZXJjZWNlci4iLCBjb3JyZWN0SWQ6ICJwYWdlbnVtYmVyIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJCdWF0IHNlYnVhaCBrb3RhayB0ZWtzIG1lbGF5YW5nIHlhbmcgYmlzYSBkaWdlc2VyIGJlYmFzIHVudHVrIG1lbWJlcmkgY2F0YXRhbiB0YW1iYWhhbiBkaSBwaW5nZ2lyIHN1cmF0LiIsIGNvcnJlY3RJZDogInRleHRib3giIH0sCiAgICAgICAgICAgIHsgdGFzazogIlNpc2lwa2FuIGJlbnR1ayBiYW5ndW4gZGF0YXIgKHBhbmFoL2tvdGFrKSB1bnR1ayBtZW5nZ2FtYmFyIGRpYWdyYW0gYWx1ciBvcGVyYXNpb25hbCAoU09QKS4iLCBjb3JyZWN0SWQ6ICJzaGFwZXMiIH0sCiAgICAgICAgICAgIHsgdGFzazogIlNpc2lwa2FuIGdyYWZpayBkaWFncmFtIGJhdGFuZyB1bnR1ayBtZW12aXN1YWxpc2FzaWthbiBwZXJzZW50YXNlIGRhdGEgcGVuanVhbGFuIGJ1bGFuIGluaS4iLCBjb3JyZWN0SWQ6ICJjaGFydCIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiU2lzaXBrYW4gdGF1dGFuIGludGVybmV0IChsaW5rKSBhZ2FyIHRla3MgJ0tsaWsgZGkgc2luaScgYmlzYSBkaWtsaWsgZGFuIG1lbWJ1a2Egd2Vic2l0ZSBwcm9maWwgcGVydXNhaGFhbi4iLCBjb3JyZWN0SWQ6ICJoeXBlcmxpbmsiIH0sCiAgICAgICAgICAgIHsgdGFzazogIk1hc3Vra2FuIGthcmFrdGVyIGtodXN1cyBzZXBlcnRpIGxhbWJhbmcgSGFrIENpcHRhICjCqSkgYXRhdSBUcmFkZW1hcmsgKOKEoikgeWFuZyB0aWRhayBhZGEgZGkga2V5Ym9hcmQuIiwgY29ycmVjdElkOiAic3ltYm9sIiB9LAogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2VzaSA3OiBGaXR1ciBMYW5qdXRhbiAmIFBlbm9tb3JhbgogICAgICAgICAgICB7IHRhc2s6ICJBbmRhIHBlcmx1IG1lbWJ1YXQgZGFmdGFyIGxhbmdrYWgtbGFuZ2thaCBrZXJqYSB5YW5nIGJlcnVydXRhbiBzZWNhcmEgb3RvbWF0aXMgKDEsIDIsIDMsIGRzdCkuIiwgY29ycmVjdElkOiAibnVtYmVyaW5nIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJCdWF0IGRhZnRhciBwZXJhbGF0YW4gQVRLIGthbnRvciBtZW5nZ3VuYWthbiBzaW1ib2wgdGl0aWstdGl0aWsgYnVsYXQgZGkgZGVwYW5ueWEgKGJ1a2FuIGFuZ2thKS4iLCBjb3JyZWN0SWQ6ICJidWxsZXRzIiB9LAogICAgICAgICAgICB7IHRhc2s6ICJLZXRpa2thbiBhbmdrYSAyIGtlY2lsIG1lbGF5YW5nIGRpIGF0YXMgcGFkYSB0ZWtzIG1ldGVyIHBlcnNlZ2kgKG3CsikgdW50dWsgbGFwb3JhbiBzZXJ0aWZpa2F0IHRhbmFoLiIsIGNvcnJlY3RJZDogInN1cGVyc2NyaXB0IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJUdWxpc2thbiBhbmdrYSAyIGtlY2lsIGRpIGJhd2FoIHBhZGEgcnVtdXMga2ltaWEgYWlyIChI4oKCTykgZGkganVybmFsIHBlbmVsaXRpYW4gbGluZ2t1bmdhbi4iLCBjb3JyZWN0SWQ6ICJzdWJzY3JpcHQiIH0sCiAgICAgICAgICAgIHsgdGFzazogIkJ1YXQgMTAwIHN1cmF0IHVuZGFuZ2FuIHJhcGF0IGRhcmkgc2F0dSBmb3JtYXQsIGRpIG1hbmEgbmFtYSBwZW5lcmltYW55YSBkaXRhcmlrIG90b21hdGlzIGRhcmkgZGF0YWJhc2UgRXhjZWwuIiwgY29ycmVjdElkOiAibWFpbG1lcmdlIiB9LAogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2VzaSA4OiBSZXZpZXcgJiBUYW1waWxhbgogICAgICAgICAgICB7IHRhc2s6ICJKYWxhbmthbiBwZW5nZWNla2FuIG90b21hdGlzIHVudHVrIG1lbGloYXQgYXBha2FoIGFkYSBrYXRhLWthdGEgYmFoYXNhIEluZ2dyaXMgeWFuZyBzYWxhaCBrZXRpayAodHlwbykuIiwgY29ycmVjdElkOiAic3BlbGxjaGVjayIgfSwKICAgICAgICAgICAgeyB0YXNrOiAiQXRhc2FuIEFuZGEgYmVydGFueWEgdW50dWsgYmFoYW4gcHJlc2VudGFzaSwgc3VkYWggYmVyYXBhIGJhbnlhayBqdW1sYWgga2F0YSB5YW5nIEFuZGEga2V0aWsgZGFsYW0gYXJ0aWtlbCBpbmk/IiwgY29ycmVjdElkOiAid29yZGNvdW50IiB9LAogICAgICAgICAgICB7IHRhc2s6ICJQZXJiZXNhciAoWm9vbSkgdGFtcGlsYW4gbGVtYmFyIGtlcmphIEFuZGEgZGkgbGF5YXIga29tcHV0ZXIgbWVuamFkaSAxNTAlIGFnYXIgaHVydWZueWEgdGVybGloYXQgbGViaWggamVsYXMuIiwgY29ycmVjdElkOiAiem9vbSIgfQogICAgICAgIF07CgogICAgICAgIC8vIC0tLSBWQVJJQUJFTCBTVEFURSAtLS0KICAgICAgICBsZXQgY3VycmVudFF1ZXN0aW9uSW5kZXggPSAwOwogICAgICAgIGxldCBzY29yZSA9IDA7CiAgICAgICAgbGV0IGN1cnJlbnRPcHRpb25zID0gW107CiAgICAgICAgCiAgICAgICAgLy8gLS0tIEVMRU1FTiBET00gLS0tCiAgICAgICAgY29uc3Qgc3RhcnRTY3JlZW4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc3RhcnQtc2NyZWVuJyk7CiAgICAgICAgY29uc3QgZ2FtZVNjcmVlbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdnYW1lLXNjcmVlbicpOwogICAgICAgIGNvbnN0IGVuZFNjcmVlbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlbmQtc2NyZWVuJyk7CiAgICAgICAgY29uc3QgdGFza0NhcmQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGFzay1jYXJkJyk7CiAgICAgICAgY29uc3QgdGFza0Rlc2MgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGFzay1kZXNjJyk7CiAgICAgICAgY29uc3QgZHJvcFpvbmVzQ29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Ryb3Atem9uZXMtY29udGFpbmVyJyk7CiAgICAgICAgY29uc3QgcHJvZ3Jlc3NUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Byb2dyZXNzLXRleHQnKTsKICAgICAgICBjb25zdCBzY29yZVRleHQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2NvcmUtdGV4dCcpOwoKICAgICAgICAvLyBWYXJpYWJlbCB1bnR1ayBEcmFnIFRvdWNoIE1vYmlsZQogICAgICAgIGxldCBzdGFydFggPSAwLCBzdGFydFkgPSAwOwogICAgICAgIGxldCBpbml0aWFsWCA9IDAsIGluaXRpYWxZID0gMDsKICAgICAgICBsZXQgY2xvbmVDYXJkID0gbnVsbDsKCiAgICAgICAgLy8gLS0tIEZVTkdTSSBMT0dJS0EgR0FNRSAtLS0KCiAgICAgICAgZnVuY3Rpb24gc3RhcnRHYW1lKCkgewogICAgICAgICAgICBzdGFydFNjcmVlbi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKICAgICAgICAgICAgZ2FtZVNjcmVlbi5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgICAgICAgICAgZ2FtZVNjcmVlbi5jbGFzc0xpc3QuYWRkKCdib3VuY2UtaW4nKTsKICAgICAgICAgICAgY3VycmVudFF1ZXN0aW9uSW5kZXggPSAwOwogICAgICAgICAgICBzY29yZSA9IDA7CiAgICAgICAgICAgIHVwZGF0ZVNjb3JlKCk7CiAgICAgICAgICAgIGxvYWRRdWVzdGlvbigpOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gbG9hZFF1ZXN0aW9uKCkgewogICAgICAgICAgICAvLyBDZWsgamlrYSBnYW1lIHNlbGVzYWkKICAgICAgICAgICAgaWYgKGN1cnJlbnRRdWVzdGlvbkluZGV4ID49IHF1ZXN0aW9ucy5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGVuZEdhbWUoKTsKICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgY29uc3QgcSA9IHF1ZXN0aW9uc1tjdXJyZW50UXVlc3Rpb25JbmRleF07CiAgICAgICAgICAgIHRhc2tEZXNjLnRleHRDb250ZW50ID0gcS50YXNrOwogICAgICAgICAgICBwcm9ncmVzc1RleHQudGV4dENvbnRlbnQgPSBgJHtjdXJyZW50UXVlc3Rpb25JbmRleCArIDF9LyR7cXVlc3Rpb25zLmxlbmd0aH1gOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gUmVzZXQga2FydHUgdHVnYXMKICAgICAgICAgICAgdGFza0NhcmQuc3R5bGUudmlzaWJpbGl0eSA9ICd2aXNpYmxlJzsKICAgICAgICAgICAgdGFza0NhcmQuc3R5bGUub3BhY2l0eSA9ICcxJzsKICAgICAgICAgICAgdGFza0NhcmQuY2xhc3NMaXN0LnJlbW92ZSgnc2hha2UnKTsKICAgICAgICAgICAgdGFza0NhcmQuY2xhc3NMaXN0LmFkZCgnYm91bmNlLWluJyk7CiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGFza0NhcmQuY2xhc3NMaXN0LnJlbW92ZSgnYm91bmNlLWluJyksIDUwMCk7CgogICAgICAgICAgICBnZW5lcmF0ZURyb3Bab25lcyhxLmNvcnJlY3RJZCk7CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBnZW5lcmF0ZURyb3Bab25lcyhjb3JyZWN0SWQpIHsKICAgICAgICAgICAgZHJvcFpvbmVzQ29udGFpbmVyLmlubmVySFRNTCA9ICcnOwogICAgICAgICAgICBjdXJyZW50T3B0aW9ucyA9IFtdOwoKICAgICAgICAgICAgLy8gQ2FyaSBvYmplayBhbGF0IHlhbmcgYmVuYXIKICAgICAgICAgICAgY29uc3QgY29ycmVjdFRvb2wgPSBhbGxUb29scy5maW5kKHQgPT4gdC5pZCA9PT0gY29ycmVjdElkKTsKICAgICAgICAgICAgY3VycmVudE9wdGlvbnMucHVzaChjb3JyZWN0VG9vbCk7CgogICAgICAgICAgICAvLyBQaWxpaCA0IHBlbmdlY29oIChkaXN0cmFjdG9ycykgc2VjYXJhIGFjYWsKICAgICAgICAgICAgLy8gVG90YWwgNSBwaWxpaGFuICgxIGJlbmFyLCA0IHNhbGFoKSBkaSBzZXRpYXAgc29hbAogICAgICAgICAgICBsZXQgZGlzdHJhY3RvcnMgPSBhbGxUb29scy5maWx0ZXIodCA9PiB0LmlkICE9PSBjb3JyZWN0SWQpOwogICAgICAgICAgICBkaXN0cmFjdG9ycyA9IGRpc3RyYWN0b3JzLnNvcnQoKCkgPT4gMC41IC0gTWF0aC5yYW5kb20oKSkuc2xpY2UoMCwgNCk7CiAgICAgICAgICAgIGN1cnJlbnRPcHRpb25zID0gY3VycmVudE9wdGlvbnMuY29uY2F0KGRpc3RyYWN0b3JzKTsKCiAgICAgICAgICAgIC8vIEFjYWsgdXJ1dGFuIHRhbXBpbAogICAgICAgICAgICBjdXJyZW50T3B0aW9ucy5zb3J0KCgpID0+IDAuNSAtIE1hdGgucmFuZG9tKCkpOwoKICAgICAgICAgICAgLy8gUmVuZGVyIGtvdGFrIGFsYXQKICAgICAgICAgICAgY3VycmVudE9wdGlvbnMuZm9yRWFjaCh0b29sID0+IHsKICAgICAgICAgICAgICAgIGNvbnN0IHpvbmUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgICAgICAgICAgICAgIHpvbmUuY2xhc3NOYW1lID0gJ2Ryb3Atem9uZSBiZy13aGl0ZSBib3JkZXItNCBib3JkZXItZGFzaGVkIGJvcmRlci1ncmF5LTMwMCByb3VuZGVkLTJ4bCBwLTQgZmxleCBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgdGV4dC1jZW50ZXIgY3Vyc29yLXBvaW50ZXIgbWluLWgtWzEwMHB4XSBzaGFkb3ctc20nOwogICAgICAgICAgICAgICAgem9uZS5kYXRhc2V0LmlkID0gdG9vbC5pZDsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgem9uZS5pbm5lckhUTUwgPSBgCiAgICAgICAgICAgICAgICAgICAgPGkgY2xhc3M9ImZhLXNvbGlkICR7dG9vbC5pY29ufSB0ZXh0LTN4bCB0ZXh0LWdyYXktNTAwIG1iLTIgem9uZS1pY29uIHRyYW5zaXRpb24tY29sb3JzIj48L2k+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCB0ZXh0LXNtIHpvbmUtdGV4dCB0cmFuc2l0aW9uLWNvbG9ycyI+JHt0b29sLm5hbWV9PC9zcGFuPgogICAgICAgICAgICAgICAgYDsKCiAgICAgICAgICAgICAgICAvLyBFdmVudCBMaXN0ZW5lcnMgdW50dWsgTW91c2UgKERlc2t0b3ApCiAgICAgICAgICAgICAgICB6b25lLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdvdmVyJywgaGFuZGxlRHJhZ092ZXIpOwogICAgICAgICAgICAgICAgem9uZS5hZGRFdmVudExpc3RlbmVyKCdkcmFnZW50ZXInLCBoYW5kbGVEcmFnRW50ZXIpOwogICAgICAgICAgICAgICAgem9uZS5hZGRFdmVudExpc3RlbmVyKCdkcmFnbGVhdmUnLCBoYW5kbGVEcmFnTGVhdmUpOwogICAgICAgICAgICAgICAgem9uZS5hZGRFdmVudExpc3RlbmVyKCdkcm9wJywgaGFuZGxlRHJvcCk7CgogICAgICAgICAgICAgICAgZHJvcFpvbmVzQ29udGFpbmVyLmFwcGVuZENoaWxkKHpvbmUpOwogICAgICAgICAgICB9KTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIHVwZGF0ZVNjb3JlKCkgewogICAgICAgICAgICBzY29yZVRleHQudGV4dENvbnRlbnQgPSBzY29yZTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGNoZWNrQW5zd2VyKHNlbGVjdGVkSWQsIGRyb3Bab25lRWxlbWVudCkgewogICAgICAgICAgICBjb25zdCBjb3JyZWN0SWQgPSBxdWVzdGlvbnNbY3VycmVudFF1ZXN0aW9uSW5kZXhdLmNvcnJlY3RJZDsKICAgICAgICAgICAgY29uc3QgaWNvbkVsID0gZHJvcFpvbmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy56b25lLWljb24nKTsKICAgICAgICAgICAgY29uc3QgdGV4dEVsID0gZHJvcFpvbmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy56b25lLXRleHQnKTsKCiAgICAgICAgICAgIGlmIChzZWxlY3RlZElkID09PSBjb3JyZWN0SWQpIHsKICAgICAgICAgICAgICAgIC8vIEJlbmFyCiAgICAgICAgICAgICAgICBzY29yZSArPSAxMDsKICAgICAgICAgICAgICAgIHVwZGF0ZVNjb3JlKCk7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIGRyb3Bab25lRWxlbWVudC5jbGFzc0xpc3QuYWRkKCdjb3JyZWN0Jyk7CiAgICAgICAgICAgICAgICBpY29uRWwuY2xhc3NMaXN0LnJlbW92ZSgndGV4dC1ncmF5LTUwMCcpOwogICAgICAgICAgICAgICAgaWNvbkVsLmNsYXNzTGlzdC5hZGQoJ3RleHQtZ3JlZW4tNjAwJyk7CiAgICAgICAgICAgICAgICB0ZXh0RWwuY2xhc3NMaXN0LmFkZCgndGV4dC1ncmVlbi04MDAnKTsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgLy8gU2VtYnVueWlrYW4ga2FydHUgdHVnYXMgZGVuZ2FuIGVmZWsKICAgICAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLm9wYWNpdHkgPSAnMCc7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIC8vIExhbmp1dCBwZXJ0YW55YWFuIGJlcmlrdXRueWEKICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIGN1cnJlbnRRdWVzdGlvbkluZGV4Kys7CiAgICAgICAgICAgICAgICAgICAgbG9hZFF1ZXN0aW9uKCk7CiAgICAgICAgICAgICAgICB9LCAxMDAwKTsKCiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAvLyBTYWxhaCDigJQgdGlkYWsgZGFwYXQgbmlsYWksIGxhbmdzdW5nIGxhbmp1dCBrZSBzb2FsIGJlcmlrdXRueWEKICAgICAgICAgICAgICAgIGRyb3Bab25lRWxlbWVudC5jbGFzc0xpc3QuYWRkKCd3cm9uZycpOwogICAgICAgICAgICAgICAgaWNvbkVsLmNsYXNzTGlzdC5yZW1vdmUoJ3RleHQtZ3JheS01MDAnKTsKICAgICAgICAgICAgICAgIGljb25FbC5jbGFzc0xpc3QuYWRkKCd0ZXh0LXJlZC02MDAnKTsKICAgICAgICAgICAgICAgIHRleHRFbC5jbGFzc0xpc3QuYWRkKCd0ZXh0LXJlZC04MDAnKTsKCiAgICAgICAgICAgICAgICB0YXNrQ2FyZC5jbGFzc0xpc3QuYWRkKCdzaGFrZScpOwogICAgICAgICAgICAgICAgdGFza0NhcmQuc3R5bGUudmlzaWJpbGl0eSA9ICd2aXNpYmxlJzsgLy8gUGFzdGlrYW4gdGVybGloYXQgbGFnaQoKICAgICAgICAgICAgICAgIC8vIEt1bmNpIHNlbXVhIGtvdGFrIGphd2FiYW4gYWdhciB0aWRhayBiaXNhIGRpY29iYSB1bGFuZyBwYWRhIHNvYWwgaW5pCiAgICAgICAgICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuZHJvcC16b25lJykuZm9yRWFjaCh6ID0+IHsKICAgICAgICAgICAgICAgICAgICB6LnN0eWxlLnBvaW50ZXJFdmVudHMgPSAnbm9uZSc7CiAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAvLyBTZW1idW55aWthbiBrYXJ0dSB0dWdhcyBsYWx1IGxhbmp1dCBrZSBwZXJ0YW55YWFuIGJlcmlrdXRueWEKICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLm9wYWNpdHkgPSAnMCc7CiAgICAgICAgICAgICAgICB9LCA1MDApOwoKICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIGN1cnJlbnRRdWVzdGlvbkluZGV4Kys7CiAgICAgICAgICAgICAgICAgICAgbG9hZFF1ZXN0aW9uKCk7CiAgICAgICAgICAgICAgICB9LCAxMTAwKTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gZW5kR2FtZSgpIHsKICAgICAgICAgICAgZ2FtZVNjcmVlbi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKICAgICAgICAgICAgZW5kU2NyZWVuLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogICAgICAgICAgICBlbmRTY3JlZW4uY2xhc3NMaXN0LmFkZCgnYm91bmNlLWluJyk7CiAgICAgICAgICAgIAogICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmluYWwtc2NvcmUnKS50ZXh0Q29udGVudCA9IHNjb3JlOwoKICAgICAgICAgICAgLy8gS2lyaW0gc2tvciBha2hpciBrZSBoYWxhbWFuIGluZHVrIChkaXBha2FpIHVudHVrIG1lbnlhdHVrYW4KICAgICAgICAgICAgLy8gaGFzaWwgZ2FtZSBkZW5nYW4gVHVnYXMgUHJpYmFkaSBkaSBoYWxhbWFuIG1hdGVyaSkKICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgIHdpbmRvdy5wYXJlbnQucG9zdE1lc3NhZ2UoewogICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJ21hc3Rlci1wZW5nb2xhaC1rYXRhJywKICAgICAgICAgICAgICAgICAgICBzY29yZTogc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgbWF4U2NvcmU6IHF1ZXN0aW9ucy5sZW5ndGggKiAxMAogICAgICAgICAgICAgICAgfSwgJyonKTsKICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7IC8qIGFiYWlrYW4gamlrYSB0aWRhayBiZXJqYWxhbiBkaSBkYWxhbSBpZnJhbWUgKi8gfQogICAgICAgICAgICAKICAgICAgICAgICAgbGV0IGZlZWRiYWNrID0gIiI7CiAgICAgICAgICAgIC8vIE1lbnllc3VhaWthbiBsb2dpa2EgcG9pbiB1bnR1ayBtYWtzaW1hbCBza29yIDUwMCAoNTAgc29hbCkKICAgICAgICAgICAgaWYgKHNjb3JlID09PSA1MDApIGZlZWRiYWNrID0gIkx1YXIgQmlhc2EgU2VtcHVybmEhIEFuZGEgYWRhbGFoIFNhbmcgTWFzdGVyIFBlbmdvbGFoIEthdGEuIPCfjJ8iOwogICAgICAgICAgICBlbHNlIGlmIChzY29yZSA+PSA0MDApIGZlZWRiYWNrID0gIkhlYmF0IHNla2FsaSEgS2V0ZXJhbXBpbGFuIGFwbGlrYXNpIHBlcmthbnRvcmFuIEFuZGEgc2V0YXJhIHNla3JldGFyaXMgaGFuZGFsLiDwn5GNIjsKICAgICAgICAgICAgZWxzZSBpZiAoc2NvcmUgPj0gMjUwKSBmZWVkYmFjayA9ICJDdWt1cCBiYWlrLCB0YXBpIG1hc2loIGJhbnlhayBmaXR1ciBsYW5qdXRhbiB5YW5nIHBlcmx1IEFuZGEgcGVsYWphcmkuIFRldGFwIHNlbWFuZ2F0ISDwn5OaIjsKICAgICAgICAgICAgZWxzZSBmZWVkYmFjayA9ICJKYW5nYW4gbWVueWVyYWghIFRlcnVzIGJlcmxhdGloIGFnYXIgQW5kYSBsZWJpaCB0ZXJiaWFzYSBkZW5nYW4gZml0dXItZml0dXIgYWRtaW5pc3RyYXNpLiDwn5KqIjsKICAgICAgICAgICAgCiAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmZWVkYmFjay1tc2cnKS50ZXh0Q29udGVudCA9IGZlZWRiYWNrOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVzZXRHYW1lKCkgewogICAgICAgICAgICBlbmRTY3JlZW4uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7CiAgICAgICAgICAgIHN0YXJ0R2FtZSgpOwogICAgICAgIH0KCiAgICAgICAgLy8gLS0tIEVWRU5UIEhBTkRMRVJTIERSQUcgJiBEUk9QIChERVNLVE9QIC8gTU9VU0UpIC0tLQogICAgICAgIAogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdzdGFydCcsIChlKSA9PiB7CiAgICAgICAgICAgIGUuZGF0YVRyYW5zZmVyLnNldERhdGEoJ3RleHQvcGxhaW4nLCAndGFzaycpOwogICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRhc2tDYXJkLmNsYXNzTGlzdC5hZGQoJ29wYWNpdHktNTAnKSwgMCk7CiAgICAgICAgfSk7CgogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdlbmQnLCAoKSA9PiB7CiAgICAgICAgICAgIHRhc2tDYXJkLmNsYXNzTGlzdC5yZW1vdmUoJ29wYWNpdHktNTAnKTsKICAgICAgICB9KTsKCiAgICAgICAgZnVuY3Rpb24gaGFuZGxlRHJhZ092ZXIoZSkgewogICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7IC8vIFdhamliIGFnYXIgYmlzYSBkaS1kcm9wCiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYW5kbGVEcmFnRW50ZXIoZSkgewogICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7CiAgICAgICAgICAgIGNvbnN0IHpvbmUgPSBlLnRhcmdldC5jbG9zZXN0KCcuZHJvcC16b25lJyk7CiAgICAgICAgICAgIGlmICh6b25lKSB6b25lLmNsYXNzTGlzdC5hZGQoJ2RyYWctb3ZlcicpOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gaGFuZGxlRHJhZ0xlYXZlKGUpIHsKICAgICAgICAgICAgY29uc3Qgem9uZSA9IGUudGFyZ2V0LmNsb3Nlc3QoJy5kcm9wLXpvbmUnKTsKICAgICAgICAgICAgaWYgKHpvbmUpIHpvbmUuY2xhc3NMaXN0LnJlbW92ZSgnZHJhZy1vdmVyJyk7CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYW5kbGVEcm9wKGUpIHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgICAgICBjb25zdCB6b25lID0gZS50YXJnZXQuY2xvc2VzdCgnLmRyb3Atem9uZScpOwogICAgICAgICAgICBpZiAoem9uZSkgewogICAgICAgICAgICAgICAgem9uZS5jbGFzc0xpc3QucmVtb3ZlKCdkcmFnLW92ZXInKTsKICAgICAgICAgICAgICAgIGNvbnN0IHNlbGVjdGVkSWQgPSB6b25lLmRhdGFzZXQuaWQ7CiAgICAgICAgICAgICAgICBjaGVja0Fuc3dlcihzZWxlY3RlZElkLCB6b25lKTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8gLS0tIEVWRU5UIEhBTkRMRVJTIERSQUcgJiBEUk9QIChNT0JJTEUgLyBUT1VDSCBTQ1JFRU4pIC0tLQogICAgICAgIC8vIEthcmVuYSBIVE1MNSBEcmFnICYgRHJvcCBrdXJhbmcgcmVzcG9uc2lmIGRpIGxheWFyIHNlbnR1aCwga2l0YSBidWF0IGt1c3RvbQogICAgICAgIAogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBoYW5kbGVUb3VjaFN0YXJ0LCB7cGFzc2l2ZTogZmFsc2V9KTsKICAgICAgICB0YXNrQ2FyZC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaG1vdmUnLCBoYW5kbGVUb3VjaE1vdmUsIHtwYXNzaXZlOiBmYWxzZX0pOwogICAgICAgIHRhc2tDYXJkLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoZW5kJywgaGFuZGxlVG91Y2hFbmQpOwoKICAgICAgICBmdW5jdGlvbiBoYW5kbGVUb3VjaFN0YXJ0KGUpIHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOyAvLyBNZW5jZWdhaCBzY3JvbGxpbmcgc2FhdCBtZW55ZW50dWgga2FydHUKICAgICAgICAgICAgY29uc3QgdG91Y2ggPSBlLnRvdWNoZXNbMF07CiAgICAgICAgICAgIAogICAgICAgICAgICAvLyBCdWF0IGtsb25pbmdhbiBrYXJ0dSB1bnR1ayB2aXN1YWwgZHJhZwogICAgICAgICAgICBjb25zdCByZWN0ID0gdGFza0NhcmQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7CiAgICAgICAgICAgIGNsb25lQ2FyZCA9IHRhc2tDYXJkLmNsb25lTm9kZSh0cnVlKTsKICAgICAgICAgICAgY2xvbmVDYXJkLmNsYXNzTGlzdC5hZGQoJ2Fic29sdXRlLWRyYWcnLCAnc2hhZG93LTJ4bCcsICdvcGFjaXR5LTkwJywgJ3NjYWxlLTEwNScpOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2V0IHBvc2lzaSBhd2FsCiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS53aWR0aCA9IGAke3JlY3Qud2lkdGh9cHhgOwogICAgICAgICAgICBjbG9uZUNhcmQuc3R5bGUuaGVpZ2h0ID0gYCR7cmVjdC5oZWlnaHR9cHhgOwogICAgICAgICAgICBjbG9uZUNhcmQuc3R5bGUubGVmdCA9IGAke3JlY3QubGVmdH1weGA7CiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS50b3AgPSBgJHtyZWN0LnRvcH1weGA7CiAgICAgICAgICAgIAogICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNsb25lQ2FyZCk7CiAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJzsgLy8gU2VtYnVueWlrYW4gYXNsaW55YQoKICAgICAgICAgICAgLy8gSGl0dW5nIG9mZnNldCBzZW50dWhhbgogICAgICAgICAgICBzdGFydFggPSB0b3VjaC5jbGllbnRYOwogICAgICAgICAgICBzdGFydFkgPSB0b3VjaC5jbGllbnRZOwogICAgICAgICAgICBpbml0aWFsWCA9IHJlY3QubGVmdDsKICAgICAgICAgICAgaW5pdGlhbFkgPSByZWN0LnRvcDsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGhhbmRsZVRvdWNoTW92ZShlKSB7CiAgICAgICAgICAgIGlmICghY2xvbmVDYXJkKSByZXR1cm47CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgICAgICAgICAgY29uc3QgdG91Y2ggPSBlLnRvdWNoZXNbMF07CiAgICAgICAgICAgIAogICAgICAgICAgICBjb25zdCBkeCA9IHRvdWNoLmNsaWVudFggLSBzdGFydFg7CiAgICAgICAgICAgIGNvbnN0IGR5ID0gdG91Y2guY2xpZW50WSAtIHN0YXJ0WTsKICAgICAgICAgICAgCiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS5sZWZ0ID0gYCR7aW5pdGlhbFggKyBkeH1weGA7CiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS50b3AgPSBgJHtpbml0aWFsWSArIGR5fXB4YDsKCiAgICAgICAgICAgIC8vIEVmZWsgaG92ZXIgcGFkYSBkcm9wem9uZSBzYWF0IHRvdWNoIG1vdmUKICAgICAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmRyb3Atem9uZScpLmZvckVhY2goeiA9PiB6LmNsYXNzTGlzdC5yZW1vdmUoJ2RyYWctb3ZlcicpKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFNlbWJ1bnlpa2FuIGtsb25pbmdhbiBzZW1lbnRhcmEgdW50dWsgbWVuZGV0ZWtzaSBlbGVtZW4gZGkgYmF3YWhueWEKICAgICAgICAgICAgY2xvbmVDYXJkLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgICAgICAgIGNvbnN0IGVsZW1CZWxvdyA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQodG91Y2guY2xpZW50WCwgdG91Y2guY2xpZW50WSk7CiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKCiAgICAgICAgICAgIGlmIChlbGVtQmVsb3cpIHsKICAgICAgICAgICAgICAgIGNvbnN0IHpvbmUgPSBlbGVtQmVsb3cuY2xvc2VzdCgnLmRyb3Atem9uZScpOwogICAgICAgICAgICAgICAgaWYgKHpvbmUpIHpvbmUuY2xhc3NMaXN0LmFkZCgnZHJhZy1vdmVyJyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGhhbmRsZVRvdWNoRW5kKGUpIHsKICAgICAgICAgICAgaWYgKCFjbG9uZUNhcmQpIHJldHVybjsKICAgICAgICAgICAgCiAgICAgICAgICAgIGNvbnN0IHRvdWNoID0gZS5jaGFuZ2VkVG91Y2hlc1swXTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIERldGVrc2kgem9uYSBkcm9wCiAgICAgICAgICAgIGNsb25lQ2FyZC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgICAgICAgICBjb25zdCBlbGVtQmVsb3cgPSBkb2N1bWVudC5lbGVtZW50RnJvbVBvaW50KHRvdWNoLmNsaWVudFgsIHRvdWNoLmNsaWVudFkpOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gSGFwdXMga2xvbmluZ2FuIHZpc3VhbAogICAgICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGNsb25lQ2FyZCk7CiAgICAgICAgICAgIGNsb25lQ2FyZCA9IG51bGw7CiAgICAgICAgICAgIAogICAgICAgICAgICBsZXQgZHJvcHBlZE9uWm9uZSA9IGZhbHNlOwoKICAgICAgICAgICAgaWYgKGVsZW1CZWxvdykgewogICAgICAgICAgICAgICAgY29uc3Qgem9uZSA9IGVsZW1CZWxvdy5jbG9zZXN0KCcuZHJvcC16b25lJyk7CiAgICAgICAgICAgICAgICBpZiAoem9uZSkgewogICAgICAgICAgICAgICAgICAgIGRyb3BwZWRPblpvbmUgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHpvbmUuY2xhc3NMaXN0LnJlbW92ZSgnZHJhZy1vdmVyJyk7CiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWRJZCA9IHpvbmUuZGF0YXNldC5pZDsKICAgICAgICAgICAgICAgICAgICBjaGVja0Fuc3dlcihzZWxlY3RlZElkLCB6b25lKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy8gSmlrYSB0aWRhayBkaWxlcGFzIGRpIGRyb3Agem9uZSwga2VtYmFsaWthbiBrYXJ0dSBrZSBwb3Npc2kgYXdhbAogICAgICAgICAgICBpZiAoIWRyb3BwZWRPblpvbmUpIHsKICAgICAgICAgICAgICAgIHRhc2tDYXJkLnN0eWxlLnZpc2liaWxpdHkgPSAndmlzaWJsZSc7CiAgICAgICAgICAgICAgICB0YXNrQ2FyZC5jbGFzc0xpc3QuYWRkKCdzaGFrZScpOwogICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0YXNrQ2FyZC5jbGFzc0xpc3QucmVtb3ZlKCdzaGFrZScpLCA0MDApOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgIDwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4=" title="Game Master Pengolah Kata" loading="lazy"&gt;&lt;/iframe&gt;
        &lt;/div&gt;
        &lt;div class="status-chip" id="gameStatusChip"&gt;&lt;span class="dot"&gt;&lt;/span&gt; Game belum dimainkan/selesai&lt;/div&gt;
      &lt;/fieldset&gt;

      &lt;fieldset&gt;
        &lt;h3 class="step-title"&gt;&lt;span class="num"&gt;2&lt;/span&gt; Soal Pilihan Ganda (60 poin)&lt;/h3&gt;
        &lt;p style="font-size:14.5px; color:#4a5568; margin-top:0;"&gt;Jawab 10 soal pilihan ganda berikut berdasarkan materi di atas. Setiap soal bernilai 6 poin.&lt;/p&gt;
        &lt;div id="quizWrap"&gt;&lt;/div&gt;
      &lt;/fieldset&gt;

      &lt;fieldset&gt;
        &lt;h3 class="step-title"&gt;&lt;span class="num"&gt;3&lt;/span&gt; Soal Esai (40 poin)&lt;/h3&gt;
        &lt;p style="font-size:14.5px; color:#4a5568; margin-top:0;"&gt;Jawab 5 soal esai berikut dengan kalimatmu sendiri berdasarkan materi. Setiap soal bernilai 8 poin.&lt;/p&gt;
        &lt;p class="essay-note"&gt;Jawaban wajib diketik manual. Tempel (paste), seret-lepas teks, dan klik kanan pada kolom jawaban dinonaktifkan.&lt;/p&gt;
        &lt;div id="essayWrap"&gt;&lt;/div&gt;
      &lt;/fieldset&gt;

      &lt;fieldset&gt;
        &lt;h3 class="step-title"&gt;&lt;span class="num"&gt;4&lt;/span&gt; Kirim Hasil &amp; Unduh Bukti&lt;/h3&gt;
        &lt;div class="checklist"&gt;
          &lt;div&gt;&lt;span class="mark" id="chkIdentitas"&gt;&lt;/span&gt; Identitas siswa terisi lengkap&lt;/div&gt;
          &lt;div&gt;&lt;span class="mark" id="chkGame"&gt;&lt;/span&gt; Game uji keterampilan selesai dimainkan&lt;/div&gt;
          &lt;div&gt;&lt;span class="mark" id="chkKuis"&gt;&lt;/span&gt; Semua soal pilihan ganda terjawab&lt;/div&gt;
          &lt;div&gt;&lt;span class="mark" id="chkEsai"&gt;&lt;/span&gt; Semua soal esai terisi (diketik manual)&lt;/div&gt;
        &lt;/div&gt;
        &lt;button type="submit" class="submit-btn" id="submitBtn"&gt;Kirim Hasil Tugas&lt;/button&gt;
        &lt;div class="msg wait" id="msgWait"&gt;Mengirim hasil, mohon tunggu...&lt;/div&gt;
        &lt;div class="msg ok" id="msgOk"&gt;&lt;/div&gt;
        &lt;div class="msg err" id="msgErr"&gt;&lt;/div&gt;
        &lt;button type="button" class="pdf-btn" id="pdfBtn" disabled&gt;Unduh Bukti Mengerjakan (PDF - F4)&lt;/button&gt;
        &lt;p style="font-size:12.5px; color:#7a8390; margin-top:8px; margin-bottom:0;"&gt;Tombol unduh aktif setelah pengiriman berhasil. Cetak dokumen ini (maks. 1 lembar F4) dan serahkan ke guru mata pelajaran sebagai bukti fisik pengerjaan tugas.&lt;/p&gt;
      &lt;/fieldset&gt;

    &lt;/form&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;Informatika Kelas X - Pengenalan Aplikasi Pengolah Kata untuk Administrasi Perkantoran - DOK/INFORMATIKA/X/PENGOLAH-KATA&lt;/footer&gt;

&lt;iframe name="hidden_iframe" id="hidden_iframe" style="display:none;"&gt;&lt;/iframe&gt;
&lt;form id="gform" action="https://script.google.com/macros/s/AKfycbwKp7DrBTALJMlw8crjK7RgXIO8LVcU4yVEd9WJNs_vgSXkVxfw7fwkGbOT2IBNOjg3uQ/exec" method="POST" target="hidden_iframe" style="display:none;"&gt;
  &lt;input type="hidden" name="no absen" id="gNoAbsen"&gt;
  &lt;input type="hidden" name="nama" id="gNama"&gt;
  &lt;input type="hidden" name="kelas" id="gKelas"&gt;
  &lt;input type="hidden" name="nilai evaluasi" id="gNilai"&gt;
  &lt;input type="hidden" name="judul materi" id="gJudul"&gt;
&lt;/form&gt;

&lt;script&gt;
const JUDUL_MATERI = "Pengenalan Aplikasi Pengolah Kata untuk Administrasi Perkantoran";
const DOC_CODE = "DOK/INFORMATIKA/X/PENGOLAH-KATA";

/* ---------- Soal Pilihan Ganda (60 poin, 6 poin/soal) ---------- */
const QUESTIONS = [
  { q: "1. Aplikasi pengolah kata digunakan terutama untuk...", opts: ["Mengolah angka dan rumus matematika","Membuat dan mengedit dokumen berbasis teks","Mengedit video","Membuat presentasi slide"], correct: 1 },
  { q: "2. Berikut yang termasuk aplikasi pengolah kata, KECUALI...", opts: ["Microsoft Word","Google Docs","Microsoft Excel","LibreOffice Writer"], correct: 2 },
  { q: "3. Perintah cepat (shortcut) untuk menyimpan dokumen adalah...", opts: ["Ctrl + P","Ctrl + S","Ctrl + C","Ctrl + Z"], correct: 1 },
  { q: "4. Ukuran margin standar (Normal) yang umum digunakan pada dokumen perkantoran adalah...", opts: ["1 cm di semua sisi","2,54 cm di semua sisi","5 cm di semua sisi","0,5 cm di semua sisi"], correct: 1 },
  { q: "5. Jenis huruf (font) yang umum dipakai untuk surat resmi formal adalah...", opts: ["Comic Sans MS","Times New Roman atau Arial","Wingdings","Papyrus"], correct: 1 },
  { q: "6. Bagian dokumen surat resmi yang berisi nama instansi, alamat, dan logo disebut...", opts: ["Kop surat (letterhead)","Lampiran","Perihal","Tembusan"], correct: 0 },
  { q: "7. Fitur Find &amp; Replace digunakan untuk...", opts: ["Mencetak dokumen","Mencari dan mengganti kata secara otomatis","Menyisipkan gambar","Mengatur margin"], correct: 1 },
  { q: "8. Format file yang cocok untuk dokumen final yang tidak ingin diedit ulang oleh penerima adalah...", opts: [".docx",".txt",".pdf",".rtf"], correct: 2 },
  { q: "9. Perataan teks yang membuat teks rata di sisi kiri dan kanan sekaligus disebut...", opts: ["Align Left","Align Right","Center","Justify"], correct: 3 },
  { q: "10. Sebelum menyimpan dokumen administrasi, penamaan file yang baik sebaiknya memuat...", opts: ["Nama acak dan tanggal sembarang","Jenis dokumen, nama/nomor, dan tanggal secara jelas","Hanya angka","Emoji dan simbol"], correct: 1 }
];

const quizWrap = document.getElementById('quizWrap');
QUESTIONS.forEach((item, qi) =&gt; {
  const div = document.createElement('div');
  div.className = 'quiz-item';
  let optsHtml = '';
  item.opts.forEach((opt, oi) =&gt; {
    optsHtml += `&lt;label class="opt"&gt;&lt;input type="radio" name="q${qi}" value="${oi}"&gt;&lt;span&gt;${String.fromCharCode(97+oi)}. ${opt}&lt;/span&gt;&lt;/label&gt;`;
  });
  div.innerHTML = `&lt;p class="q"&gt;${item.q}&lt;/p&gt;${optsHtml}`;
  quizWrap.appendChild(div);
});

/* ---------- Soal Esai (40 poin, 8 poin/soal, dinilai dari kata kunci materi) ---------- */
const ESSAY = [
  {
    q: "1. Jelaskan perbedaan aplikasi pengolah kata berbasis desktop dan berbasis cloud (online), lengkap dengan satu contoh aplikasi untuk masing-masing jenis.",
    groups: [["desktop"], ["cloud","online","internet"], ["microsoft word"], ["google docs"], ["offline"], ["kolaborasi","real-time","realtime","bersama-sama"]]
  },
  {
    q: "2. Sebutkan minimal tiga teknik dasar mengedit dokumen beserta shortcut keyboard yang digunakan untuk masing-masing teknik tersebut.",
    groups: [["copy","salin"], ["paste","tempel"], ["cut","potong"], ["undo","batalkan"], ["redo","ulangi"], ["find","replace","cari","ganti"], ["ctrl"]]
  },
  {
    q: "3. Jelaskan standar format penulisan dokumen resmi perkantoran meliputi jenis huruf, ukuran huruf, margin, dan spasi baris yang tepat.",
    groups: [["times new roman"], ["arial"], ["12"], ["margin"], ["2,54","2.54"], ["spasi"], ["1,15","1.15","1,5","1.5"], ["justify","rata kiri kanan"]]
  },
  {
    q: "4. Sebutkan bagian-bagian struktur surat resmi secara berurutan, mulai dari kop surat hingga tanda tangan.",
    groups: [["kop surat"], ["nomor"], ["hal","lampiran"], ["tanggal"], ["alamat tujuan","alamat"], ["salam","isi"], ["tanda tangan"]]
  },
  {
    q: "5. Jelaskan mengapa etika dan keamanan dokumen digital penting diterapkan di lingkungan administrasi perkantoran.",
    groups: [["password","kata sandi"], ["backup","cadangan"], ["izin"], ["plagiarisme"], ["rahasia","kerahasiaan"], ["hak akses","akses"]]
  }
];
const POIN_PER_ESAI = 40 / ESSAY.length;

const essayWrap = document.getElementById('essayWrap');
ESSAY.forEach((item, ei) =&gt; {
  const div = document.createElement('div');
  div.className = 'essay-item';
  div.innerHTML = `&lt;p class="q"&gt;${item.q}&lt;/p&gt;
    &lt;textarea id="essay${ei}" data-idx="${ei}" placeholder="Ketik jawabanmu di sini (minimal beberapa kalimat)..."&gt;&lt;/textarea&gt;
    &lt;p class="essay-count" id="essayCount${ei}"&gt;0 karakter&lt;/p&gt;`;
  essayWrap.appendChild(div);
});

// Nonaktifkan paste, drag-drop teks, dan klik kanan pada semua kolom esai
document.querySelectorAll('#essayWrap textarea').forEach((ta, idx) =&gt; {
  ta.addEventListener('paste', e =&gt; {
    e.preventDefault();
    showEssayBlockedNotice(idx);
  });
  ta.addEventListener('drop', e =&gt; e.preventDefault());
  ta.addEventListener('contextmenu', e =&gt; e.preventDefault());
  ta.addEventListener('input', () =&gt; {
    document.getElementById('essayCount' + idx).textContent = ta.value.length + ' karakter';
    updateChecklist();
  });
});
function showEssayBlockedNotice(idx){
  const el = document.getElementById('essayCount' + idx);
  const old = el.textContent;
  el.textContent = 'Tempel (paste) dinonaktifkan - ketik manual jawabanmu.';
  el.style.color = 'var(--danger)';
  setTimeout(() =&gt; { el.textContent = old; el.style.color = ''; }, 2200);
}

function scoreEssay(idx){
  const text = document.getElementById('essay' + idx).value.toLowerCase().trim();
  if(text.length &lt; 15) return 0;
  const groups = ESSAY[idx].groups;
  let matched = 0;
  groups.forEach(g =&gt; { if(g.some(k =&gt; text.includes(k))) matched++; });
  return Math.round((matched / groups.length) * POIN_PER_ESAI);
}

/* ---------- Skor game via postMessage dari iframe (informasi, tidak masuk nilai evaluasi) ---------- */
let gameFinished = false;
let gameScoreRaw = null;
const GAME_MAX = 500;
const gameStatusChip = document.getElementById('gameStatusChip');

window.addEventListener('message', function(e){
  if(e.data &amp;&amp; e.data.source === 'master-pengolah-kata'){
    gameScoreRaw = e.data.score;
    gameFinished = true;
    const pct = Math.round((gameScoreRaw / GAME_MAX) * 100);
    gameStatusChip.classList.add('ok-chip');
    gameStatusChip.innerHTML = `&lt;span class="dot"&gt;&lt;/span&gt; Game selesai - Skor: ${gameScoreRaw}/${GAME_MAX} (${pct}%)`;
    updateChecklist();
  }
});

/* ---------- Validasi &amp; checklist real-time ---------- */
const form = document.getElementById('taskForm');
const chkIdentitas = document.getElementById('chkIdentitas');
const chkGame = document.getElementById('chkGame');
const chkKuis = document.getElementById('chkKuis');
const chkEsai = document.getElementById('chkEsai');

function isIdentitasLengkap(){
  return document.getElementById('noAbsen').value.trim() &amp;&amp;
         document.getElementById('nama').value.trim() &amp;&amp;
         document.getElementById('kelas').value;
}
function isKuisLengkap(){
  return QUESTIONS.every((_, qi) =&gt; form.querySelector(`input[name="q${qi}"]:checked`));
}
function isEsaiLengkap(){
  return ESSAY.every((_, ei) =&gt; document.getElementById('essay' + ei).value.trim().length &gt;= 15);
}
function updateChecklist(){
  chkIdentitas.classList.toggle('done', !!isIdentitasLengkap());
  chkGame.classList.toggle('done', gameFinished);
  chkKuis.classList.toggle('done', isKuisLengkap());
  chkEsai.classList.toggle('done', isEsaiLengkap());
}
form.addEventListener('input', updateChecklist);
form.addEventListener('change', updateChecklist);
updateChecklist();

/* ---------- Submit ---------- */
const submitBtn = document.getElementById('submitBtn');
const pdfBtn = document.getElementById('pdfBtn');
const msgWait = document.getElementById('msgWait');
const msgOk = document.getElementById('msgOk');
const msgErr = document.getElementById('msgErr');

let hasilAkhir = null; // menyimpan rincian nilai untuk PDF

form.addEventListener('submit', function(e){
  e.preventDefault();
  msgOk.classList.remove('show');
  msgErr.classList.remove('show');

  const noAbsen = document.getElementById('noAbsen').value.trim();
  const nama = document.getElementById('nama').value.trim();
  const kelas = document.getElementById('kelas').value;

  if(!isIdentitasLengkap()){
    msgErr.textContent = "Lengkapi dahulu No. Absen, Nama, dan Kelas.";
    msgErr.classList.add('show');
    return;
  }
  if(!gameFinished){
    msgErr.textContent = "Selesaikan dahulu game uji keterampilan sampai layar akhir muncul.";
    msgErr.classList.add('show');
    return;
  }
  let unanswered = [];
  QUESTIONS.forEach((_, qi) =&gt; { if(!form.querySelector(`input[name="q${qi}"]:checked`)) unanswered.push(qi+1); });
  if(unanswered.length){
    msgErr.textContent = "Soal pilihan ganda nomor " + unanswered.join(', ') + " belum dijawab.";
    msgErr.classList.add('show');
    return;
  }
  if(!isEsaiLengkap()){
    msgErr.textContent = "Lengkapi semua jawaban esai (ketik manual, minimal beberapa kalimat).";
    msgErr.classList.add('show');
    return;
  }

  let correctCount = 0;
  QUESTIONS.forEach((item, qi) =&gt; {
    const checked = form.querySelector(`input[name="q${qi}"]:checked`);
    if(parseInt(checked.value, 10) === item.correct) correctCount++;
  });
  const skorPG = Math.round((correctCount / QUESTIONS.length) * 60);

  let skorEsai = 0;
  ESSAY.forEach((_, ei) =&gt; { skorEsai += scoreEssay(ei); });
  skorEsai = Math.min(40, skorEsai);

  const nilaiEvaluasi = Math.min(100, skorPG + skorEsai);
  const skorGamePersen = Math.round((gameScoreRaw / GAME_MAX) * 100);

  hasilAkhir = {
    noAbsen, nama, kelas,
    skorPG, skorEsai, nilaiEvaluasi,
    skorGameRaw: gameScoreRaw, skorGamePersen,
    waktu: new Date()
  };

  document.getElementById('gNoAbsen').value = noAbsen;
  document.getElementById('gNama').value = nama;
  document.getElementById('gKelas').value = kelas;
  document.getElementById('gNilai').value = nilaiEvaluasi;
  document.getElementById('gJudul').value = JUDUL_MATERI;

  submitBtn.disabled = true;
  submitBtn.textContent = "Mengirim...";
  msgWait.classList.add('show');
  document.getElementById('gform').submit();

  setTimeout(() =&gt; {
    msgWait.classList.remove('show');
    msgOk.textContent = `Hasil terkirim. PG: ${skorPG}/60, Esai: ${skorEsai}/40 -&gt; Nilai Evaluasi Akhir: ${nilaiEvaluasi}/100. Skor Uji Keterampilan (informasi): ${skorGamePersen}/100.`;
    msgOk.classList.add('show');
    submitBtn.textContent = "Sudah Terkirim";
    pdfBtn.disabled = false;
  }, 1400);
});

/* ---------- Unduh Bukti Mengerjakan (PDF, ukuran F4 = 215 x 330 mm) ---------- */
pdfBtn.addEventListener('click', function(){
  if(!hasilAkhir) return;
  const { jsPDF } = window.jspdf;
  const doc = new jsPDF({ unit: 'mm', format: [215, 330] });
  const pageW = 215;
  let y = 22;

  doc.setFont('helvetica', 'bold');
  doc.setFontSize(16);
  doc.text('BUKTI MENGERJAKAN TUGAS', pageW/2, y, { align: 'center' }); y += 8;

  doc.setFontSize(12);
  doc.text('Informatika Kelas X SMK', pageW/2, y, { align: 'center' }); y += 6;

  doc.setFont('helvetica', 'normal');
  doc.setFontSize(11);
  doc.text(JUDUL_MATERI, pageW/2, y, { align: 'center', maxWidth: pageW - 40 }); y += 10;

  doc.setFontSize(9);
  doc.text(DOC_CODE, pageW/2, y, { align: 'center' }); y += 6;

  doc.setLineWidth(0.6);
  doc.line(20, y, pageW - 20, y); y += 10;

  doc.setFont('helvetica', 'bold');
  doc.setFontSize(12);
  doc.text('Identitas Siswa', 20, y); y += 8;

  doc.setFont('helvetica', 'normal');
  doc.setFontSize(11);
  const rows1 = [
    ['No. Absen', hasilAkhir.noAbsen],
    ['Nama Lengkap', hasilAkhir.nama],
    ['Kelas', hasilAkhir.kelas],
    ['Tanggal &amp; Waktu', hasilAkhir.waktu.toLocaleString('id-ID')]
  ];
  rows1.forEach(r =&gt; {
    doc.text(r[0], 22, y);
    doc.text(':', 62, y);
    doc.text(String(r[1]), 66, y);
    y += 7;
  });
  y += 6;

  doc.setFont('helvetica', 'bold');
  doc.setFontSize(12);
  doc.text('Rincian Nilai', 20, y); y += 8;

  doc.setFont('helvetica', 'normal');
  doc.setFontSize(11);
  const rows2 = [
    ['Skor Pilihan Ganda (60 poin)', hasilAkhir.skorPG + ' / 60'],
    ['Skor Esai (40 poin)', hasilAkhir.skorEsai + ' / 40'],
    ['Nilai Evaluasi Akhir (100 poin)', hasilAkhir.nilaiEvaluasi + ' / 100'],
  ];
  rows2.forEach(r =&gt; {
    doc.text(r[0], 22, y);
    doc.text(':', 92, y);
    doc.text(String(r[1]), 96, y);
    y += 7;
  });

  doc.setFont('helvetica', 'bold');
  doc.setFontSize(14);
  doc.text('Nilai Evaluasi Akhir: ' + hasilAkhir.nilaiEvaluasi + ' / 100', pageW/2, y + 6, { align: 'center' });
  y += 16;

  doc.setFont('helvetica', 'normal');
  doc.setFontSize(10);
  doc.text('Skor Uji Keterampilan / Game (informasi tambahan, tidak termasuk Nilai Evaluasi):', 22, y); y += 6;
  doc.text(hasilAkhir.skorGameRaw + ' / 500  (' + hasilAkhir.skorGamePersen + ' / 100)', 22, y); y += 12;

  doc.setLineWidth(0.3);
  doc.line(20, y, pageW - 20, y); y += 10;

  doc.setFontSize(10);
  const catatan = 'Dokumen ini adalah bukti otomatis bahwa siswa telah menyelesaikan tugas pada tanggal dan waktu di atas. Cetak dokumen ini (ukuran kertas F4, maksimal 1 lembar) dan serahkan kepada guru mata pelajaran sebagai bukti fisik pengerjaan tugas.';
  const wrapped = doc.splitTextToSize(catatan, pageW - 40);
  doc.text(wrapped, 20, y); y += wrapped.length * 5 + 14;

  doc.text('Tanda tangan Siswa,', 30, y);
  doc.text('Tanda tangan Guru,', pageW - 70, y);
  y += 22;
  doc.text('(' + hasilAkhir.nama + ')', 30, y);
  doc.text('(......................................)', pageW - 70, y);

  const safeName = hasilAkhir.nama.replace(/[^a-zA-Z0-9]+/g, '_');
  doc.save(`Bukti_Tugas_${safeName}_${hasilAkhir.noAbsen}.pdf`);
});
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/pengenalan-aplikasi-pengolah-kata-untuk.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-4680929617969456386</guid><pubDate>Fri, 04 Sep 2026 06:09:31 +0000</pubDate><atom:updated>2026-09-04T13:11:10.859+07:00</atom:updated><title>SERTIFIKAT KOMBEL</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Generator Sertifikat&lt;/title&gt;

&lt;link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@700&amp;display=swap" rel="stylesheet"&gt;

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;

&lt;style&gt;

body{
    font-family:Arial,sans-serif;
    background:#eef2f7;
    padding:20px;
    max-width:700px;
    margin:auto;
}

.card{
    background:white;
    padding:24px;
    border-radius:20px;
    box-shadow:0 10px 20px rgba(0,0,0,.1);
}

h2{
    text-align:center;
    color:#1e4668;
}

.confirm-box{
    background:#f0f6ff;
    border-left:5px solid #2c7da0;
    padding:16px;
    border-radius:16px;
    margin-bottom:20px;
    text-align:center;
}

.confirm-box p{
    margin-top:0;
    font-weight:bold;
    color:#1e4668;
}

.confirm-buttons{
    display:flex;
    gap:10px;
    margin-top:10px;
}

.confirm-buttons button{
    flex:1;
}

.yes-btn{
    background:#1f8a4c;
}

.yes-btn:hover{
    background:#166638;
}

.no-btn{
    background:#c0392b;
}

.no-btn:hover{
    background:#962d22;
}

input,select,button{
    width:100%;
    padding:12px;
    margin-top:10px;
    border-radius:12px;
    border:1px solid #ccc;
    font-size:16px;
}

button{
    background:#2c7da0;
    color:white;
    border:none;
    cursor:pointer;
    font-weight:bold;
}

button:hover{
    background:#1f5e7a;
}

button:disabled{
    background:#94a3b8;
    cursor:not-allowed;
}

input:disabled,
select:disabled{
    background:#f1f5f9;
}

#preview{
    width:100%;
    border-radius:12px;
    margin-top:10px;
}

.loading{
    display:none;
    margin-top:15px;
    color:#1e4668;
    font-weight:bold;
    text-align:center;
}

.status{
    margin-top:12px;
    font-weight:bold;
}

.success{
    color:#1f8a4c;
}

.error{
    color:#c0392b;
}

&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="card"&gt;

&lt;h2&gt;&#128220; Generator Sertifikat&lt;/h2&gt;

&lt;!-- ================= KONFIRMASI ================= --&gt;

&lt;div class="confirm-box"&gt;

&lt;p&gt;&#128269; Apakah Anda mengikuti pelatihan?&lt;/p&gt;

&lt;div class="confirm-buttons"&gt;

&lt;button class="yes-btn" onclick="confirmIkut()"&gt;
✅ YA, IKUT
&lt;/button&gt;

&lt;button class="no-btn" onclick="confirmTidakIkut()"&gt;
❌ TIDAK
&lt;/button&gt;

&lt;/div&gt;

&lt;div id="statusKonfirmasi" class="status"&gt;&lt;/div&gt;

&lt;/div&gt;

&lt;!-- ================= FORM ================= --&gt;

&lt;label&gt;Nama Lengkap&lt;/label&gt;

&lt;input
type="text"
id="nama"
placeholder="Contoh: Ahmad Fauzi"
disabled&gt;

&lt;label&gt;Peran&lt;/label&gt;

&lt;select id="peran" disabled&gt;
    &lt;option&gt;Peserta&lt;/option&gt;
    &lt;option&gt;Narasumber&lt;/option&gt;
&lt;/select&gt;

&lt;img
id="preview"
src="https://lh3.googleusercontent.com/d/1Hu72T5j34moAaJG9Xnr1rGH3POtJ-Pug"
alt="Template"&gt;

&lt;button
id="downloadBtn"
onclick="generatePDF()"
disabled&gt;

⬇️ UNDUH PDF

&lt;/button&gt;

&lt;div class="loading" id="loading"&gt;
Memproses PDF...
&lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;

// ================= TEMPLATE (DIUBAH) =================

const FILE_ID =
'1Hu72T5j34moAaJG9Xnr1rGH3POtJ-Pug';  // &lt;-- ID baru

const TEMPLATE_URL =
`https://lh3.googleusercontent.com/d/${FILE_ID}`;

// ================= UTIL =================

function showLoading(show){

    document.getElementById('loading').style.display =
        show ? 'block' : 'none';

}

function getRomanMonth(monthIndex){

    const romawi = [
        "I","II","III","IV","V","VI",
        "VII","VIII","IX","X","XI","XII"
    ];

    return romawi[monthIndex];

}

function capitalizeEachWord(text){

    return text
        .toLowerCase()
        .replace(/\b\w/g,c=&gt;c.toUpperCase());

}

// ================= KONFIRMASI =================

function confirmIkut(){

    document.getElementById('nama').disabled = false;

    document.getElementById('peran').disabled = false;

    document.getElementById('downloadBtn').disabled = false;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="success"&gt;✅ Konfirmasi diterima. Anda dapat mengunduh sertifikat.&lt;/span&gt;';

}

function confirmTidakIkut(){

    document.getElementById('nama').disabled = true;

    document.getElementById('peran').disabled = true;

    document.getElementById('downloadBtn').disabled = true;

    document.getElementById('statusKonfirmasi').innerHTML =
        '&lt;span class="error"&gt;❌ Anda tidak dapat mengunduh sertifikat.&lt;/span&gt;';

}

// ================= LOAD IMAGE =================

function loadImage(url){

    return new Promise((resolve,reject)=&gt;{

        const img = new Image();

        img.crossOrigin = "anonymous";

        img.onload = ()=&gt;resolve(img);

        img.onerror = ()=&gt;{

            reject(
                new Error(
                    'Template Google Drive gagal dimuat'
                )
            );

        };

        img.src = url + '?t=' + Date.now();

    });

}

// ================= NOMOR =================

async function generateNomor(){

    const now = new Date();

    const bulan =
        getRomanMonth(now.getMonth());

    const tahun =
        now.getFullYear();

    const nomor =
        Math.floor(Math.random()*900)+100;

    return `${nomor}/K/${bulan}/${tahun}`;

}

// ================= PDF =================

async function generatePDF(){

    const namaInput =
        document.getElementById('nama');

    const peranSelect =
        document.getElementById('peran');

    let nama =
        namaInput.value.trim();

    const peran =
        peranSelect.value;

    if(!nama){

        alert('Nama wajib diisi');

        return;

    }

    nama =
        capitalizeEachWord(nama);

    showLoading(true);

    try{

        await document.fonts.ready;

        const img =
            await loadImage(TEMPLATE_URL);

        const canvas =
            document.createElement('canvas');

        canvas.width = img.width;
        canvas.height = img.height;

        const ctx =
            canvas.getContext('2d');

        ctx.drawImage(
            img,
            0,
            0,
            canvas.width,
            canvas.height
        );

        // ================= POSISI =================

        const nomorY =
            canvas.height * 0.235;

        const namaY =
            canvas.height * 0.3990;

        const peranY =
            canvas.height * 0.5420;

        // ================= STYLE =================

        ctx.textAlign = 'center';

        ctx.textBaseline = 'middle';

        ctx.fillStyle = '#000';

        // nomor
        const nomorSertifikat =
            await generateNomor();

        ctx.font =
            `bold ${canvas.width*0.0125}px Montserrat`;

        ctx.fillText(
            nomorSertifikat,
            canvas.width/2,
            nomorY
        );

        // nama auto shrink
        let fontSize =
            canvas.width * 0.031;

        ctx.font =
            `700 ${fontSize}px Montserrat`;

        let textWidth =
            ctx.measureText(nama).width;

        const maxWidth =
            canvas.width - 180;

        while(
            textWidth &gt; maxWidth &amp;&amp;
            fontSize &gt; 20
        ){

            fontSize -= 2;

            ctx.font =
                `700 ${fontSize}px Montserrat`;

            textWidth =
                ctx.measureText(nama).width;

        }

        ctx.fillText(
            nama,
            canvas.width/2,
            namaY
        );

        // peran
        ctx.font =
            `bold ${canvas.width*0.024}px Montserrat`;

        ctx.fillText(
            peran,
            canvas.width/2,
            peranY
        );

        // ================= PDF =================

        const imageData =
            canvas.toDataURL(
                'image/jpeg',
                1.0
            );

        const { jsPDF } =
            window.jspdf;

        const pdf =
            new jsPDF({

                orientation:
                    canvas.width &gt; canvas.height
                    ? 'landscape'
                    : 'portrait',

                unit:'px',

                format:[
                    canvas.width,
                    canvas.height
                ]

            });

        pdf.addImage(
            imageData,
            'JPEG',
            0,
            0,
            canvas.width,
            canvas.height
        );

        const safeName =
            nama.replace(/[^a-z0-9]/gi,'_');

        pdf.save(
            `Sertifikat_${safeName}.pdf`
        );

    }catch(error){

        console.error(error);

        alert(
            '❌ Gagal membuat PDF\n\n' +
            error.message +
            '\n\nPastikan file Google Drive bersifat PUBLIC.'
        );

    }finally{

        showLoading(false);

    }

}

&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/sertifikat-kombel.html</link><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://lh3.googleusercontent.com/d/1Hu72T5j34moAaJG9Xnr1rGH3POtJ-Pug=s72-c" width="72"/><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-7228811827870023901</guid><pubDate>Fri, 04 Sep 2026 03:08:01 +0000</pubDate><atom:updated>2026-09-11T07:40:01.163+07:00</atom:updated><title>Ulangan Harian 1 — Petunjuk Kerja BAHASA INDONESIA KELAS XI SMK</title><description>&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;Ulangan Harian 1 - Petunjuk Kerja - Kelas XI SMK&lt;/title&gt;
&lt;style&gt;
 :root{
 --ink:#1B2430;
 --ink-soft:#4A5568;
 --paper:#F7F5F0;
 --card:#FFFFFF;
 --line:#D8D2C4;
 --amber:#E8A33D;
 --amber-dark:#8A5A12;
 --green:#3F7D58;
 --green-bg:#E7F2EB;
 --red:#C1443B;
 --red-bg:#FBEAE8;
 }
 *{box-sizing:border-box;}
 body{
 margin:0;
 background:var(--paper);
 color:var(--ink);
 font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
 line-height:1.55;
 }
 .wrap{max-width:760px;margin:0 auto;padding:28px 20px 80px;}
 header.top{border-bottom:3px solid var(--ink);padding-bottom:16px;margin-bottom:22px;}
 .kode{font-size:12px;letter-spacing:.04em;color:var(--ink-soft);}
 h1{font-size:22px;margin:6px 0 4px;font-weight:800;}
 .sub{font-size:14px;color:var(--ink-soft);margin:0;}
 .meta-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:14px;}
 .meta-chip{background:var(--card);border:1px solid var(--line);border-radius:6px;padding:8px 12px;font-size:13px;min-width:130px;}
 .meta-chip b{display:block;font-size:16px;}
 .card{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:20px;margin-bottom:18px;}
 .card h2{font-size:16px;margin:0 0 10px;}
 label{display:block;font-size:13px;color:var(--ink-soft);margin:12px 0 4px;}
 input[type=text],input[type=number]{
 width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:6px;
 font-size:15px;background:#fff;color:var(--ink);
 }
 input:focus{outline:2px solid var(--amber);outline-offset:1px;}
 .btn{
 display:inline-block;border:none;border-radius:6px;padding:12px 20px;
 font-size:15px;font-weight:700;cursor:pointer;margin-top:16px;
 }
 .btn-primary{background:var(--ink);color:#fff;}
 .btn-primary:hover{background:#000;}
 .btn-secondary{background:var(--amber);color:#3A2400;}
 .btn-secondary:hover{background:#d6912e;}
 .btn:disabled{opacity:.5;cursor:not-allowed;}
 .err{color:var(--red);font-size:13px;margin-top:8px;display:none;}
 .stimulus{
 background:#FBF9F2;border:1px solid var(--line);border-left:4px solid var(--amber);
 padding:14px 16px;border-radius:4px;margin-bottom:14px;font-size:14.5px;white-space:pre-line;
 }
 .stimulus .st-title{font-weight:800;font-size:14px;margin-bottom:6px;color:var(--amber-dark);}
 .qgroup{margin-bottom:26px;}
 .q{margin-bottom:20px;padding-bottom:18px;border-bottom:1px solid var(--line);}
 .q:last-child{border-bottom:none;}
 .q-num{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--ink-soft);}
 .q-text{font-size:15px;font-weight:600;margin:4px 0 10px;}
 .opt{
 display:flex;gap:10px;align-items:flex-start;padding:8px 10px;border-radius:6px;
 cursor:pointer;border:1px solid transparent;font-size:14.5px;
 }
 .opt:hover{background:#F1EFE7;}
 .opt input{margin-top:3px;}
 .opt.selected{background:#FCEFDA;border-color:var(--amber);}
 #progressWrap{position:sticky;top:0;background:var(--paper);padding:10px 0;z-index:5;border-bottom:1px solid var(--line);margin-bottom:16px;}
 #progressBar{height:8px;background:var(--line);border-radius:4px;overflow:hidden;}
 #progressFill{height:100%;background:var(--amber);width:0%;transition:width .2s;}
 #progressText{font-size:12px;color:var(--ink-soft);margin-top:4px;}
 .result-score{font-size:48px;font-weight:800;margin:6px 0;}
 .pass{color:var(--green);}
 .fail{color:var(--red);}
 .result-box{border-radius:8px;padding:20px;text-align:center;}
 .result-box.pass-bg{background:var(--green-bg);border:1px solid var(--green);}
 .result-box.fail-bg{background:var(--red-bg);border:1px solid var(--red);}
 .attempt-log{margin-top:14px;font-size:13px;}
 .attempt-log div{display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px dashed var(--line);}
 .hidden{display:none;}
 footer{margin-top:30px;font-size:12px;color:var(--ink-soft);text-align:center;}

 .essaySection{margin-top:10px;}
 .essayQ{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--line);}
 .essayQ:last-child{border-bottom:none;}
 .essayQ-num{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--ink-soft);}
 .essayQ-text{font-size:15px;font-weight:600;margin:4px 0 8px;}
 textarea.essayInput{
 width:100%;min-height:80px;padding:10px 12px;border:1px solid var(--line);
 border-radius:6px;font-size:14.5px;font-family:inherit;resize:vertical;background:#fff;color:var(--ink);
 }
 textarea.essayInput:focus{outline:2px solid var(--amber);outline-offset:1px;}
 .charCount{font-size:12px;color:var(--ink-soft);text-align:right;margin-top:3px;}
 .charCount.over{color:var(--red);font-weight:700;}
 .pasteWarn{font-size:12.5px;color:var(--red);font-weight:700;margin-top:4px;display:none;}
 .pasteWarn.show{display:block;}
 .sectionLabel{font-size:13px;font-weight:800;letter-spacing:.02em;color:var(--amber-dark);margin:26px 0 4px;border-top:3px solid var(--ink);padding-top:14px;}

 #printArea{display:none;}
 @media print{
 body *{visibility:hidden;}
 #printArea, #printArea *{visibility:visible;}
 #printArea{display:block;position:absolute;left:0;top:0;width:100%;}
 @page{size:215mm 330mm;margin:14mm;}
 .pbreak{break-before:page;page-break-before:always;}
 .noBreak{break-inside:avoid;page-break-inside:avoid;}
 }
 .pf-kop{text-align:center;border-bottom:3px solid #000;padding-bottom:10px;margin-bottom:14px;}
 .pf-kop h2{margin:0;font-size:16px;}
 .pf-kop p{margin:2px 0 0;font-size:12px;}
 .pf-identitas{width:100%;border-collapse:collapse;font-size:13px;margin-bottom:14px;}
 .pf-identitas td{padding:3px 4px;vertical-align:top;}
 .pf-identitas td.lbl{width:150px;color:#333;}
 .pf-skorbox{border:2px solid #000;border-radius:4px;padding:10px 14px;margin-bottom:16px;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;}
 .pf-skoritem{font-size:12px;}
 .pf-skoritem b{display:block;font-size:20px;}
 .pf-essay-q{font-size:13px;font-weight:700;margin-bottom:3px;}
 .pf-essay-a{font-size:12.5px;border:1px solid #999;border-radius:4px;padding:6px 8px;min-height:28px;white-space:pre-wrap;background:#FAFAF7;}
 .pf-ttd{display:flex;justify-content:space-between;margin-top:40px;font-size:12.5px;}
 .pf-ttd div{width:45%;text-align:center;}
 .pf-ttd .garis{margin-top:56px;border-top:1px solid #000;padding-top:4px;}
 .pf-note{font-size:11px;color:#555;margin-top:10px;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="wrap"&gt;

 &lt;header class="top"&gt;
 &lt;div class="kode"&gt;DOK/B.INDO/XI/PROSEDUR-UH1&lt;/div&gt;
 &lt;h1&gt;Ulangan Harian 1 - Petunjuk Kerja&lt;/h1&gt;
 &lt;p class="sub"&gt;Bahasa Indonesia - Kelas XI SMK - Materi: teks prosedur / petunjuk kerja (evaluasi informasi, perbandingan teks, kaidah kebahasaan, penyusunan &amp;amp; penyajian)&lt;/p&gt;
 &lt;div class="meta-row"&gt;
 &lt;div class="meta-chip"&gt;Pilihan ganda&lt;b&gt;40 butir - bobot 70%&lt;/b&gt;&lt;/div&gt;
 &lt;div class="meta-chip"&gt;Esai&lt;b&gt;10 butir - bobot 30%&lt;/b&gt;&lt;/div&gt;
 &lt;div class="meta-chip"&gt;KKM (nilai akhir)&lt;b&gt;75&lt;/b&gt;&lt;/div&gt;
 &lt;div class="meta-chip"&gt;Kesempatan&lt;b&gt;3 kali&lt;/b&gt;&lt;/div&gt;
 &lt;/div&gt;
 &lt;/header&gt;

 &lt;div id="screen-intro" class="card"&gt;
 &lt;h2&gt;Sebelum mengerjakan&lt;/h2&gt;
 &lt;p style="font-size:14px;"&gt;Setiap peserta mendapat susunan soal dan urutan opsi jawaban yang berbeda-beda, jadi jangan heran jika berbeda dengan teman sebangku. Ulangan terdiri atas &lt;b&gt;40 soal pilihan ganda&lt;/b&gt; dan &lt;b&gt;10 soal esai&lt;/b&gt;. Keduanya dinilai &lt;b&gt;otomatis oleh sistem&lt;/b&gt;: skor esai dihitung dari kesesuaian jawabanmu dengan konsep-konsep kunci pada setiap soal. Nilai akhir = &lt;b&gt;Skor PG x 70% + Skor Esai x 30%&lt;/b&gt;. Kamu punya &lt;b&gt;3 kali kesempatan&lt;/b&gt;. Begitu nilai akhir mencapai atau melebihi KKM (75), nilai itu langsung tersimpan. Jika sampai percobaan ke-3 belum mencapai KKM, sistem otomatis menyimpan &lt;b&gt;nilai tertinggi&lt;/b&gt; beserta rinciannya. &lt;b&gt;Jawaban esai wajib diketik sendiri&lt;/b&gt; - fitur tempel/paste (baik lewat Ctrl+V, klik kanan, maupun tarik-lepas teks) dinonaktifkan pada kolom esai.&lt;/p&gt;
 &lt;p style="font-size:14px;"&gt;Isi No. Absen yang sama setiap kali mencoba, supaya sistem bisa menghitung sisa kesempatanmu dengan benar.&lt;/p&gt;
 &lt;div style="background:#FFF6E6;border:1px solid var(--amber);border-left:4px solid var(--amber-dark);border-radius:6px;padding:12px 14px;margin:10px 0 16px;font-size:13.5px;"&gt;
 &lt;b style="color:var(--amber-dark);"&gt;PENTING - wajib dilakukan setelah nilai akhir tersimpan:&lt;/b&gt;
 &lt;ol style="margin:6px 0 0;padding-left:18px;"&gt;
 &lt;li&gt;&lt;b&gt;Unduh&lt;/b&gt; bukti mengerjakan (PDF, ukuran F4) lewat tombol "Unduh Bukti Mengerjakan (PDF)".&lt;/li&gt;
 &lt;li&gt;&lt;b&gt;Cetak&lt;/b&gt; dokumen tersebut (maksimal 3 lembar).&lt;/li&gt;
 &lt;li&gt;&lt;b&gt;Serahkan hasil cetak ke guru mata pelajaran&lt;/b&gt; sebagai bukti fisik pengerjaan ulangan.&lt;/li&gt;
 &lt;/ol&gt;
 &lt;/div&gt;

 &lt;label for="absen"&gt;No. Absen&lt;/label&gt;
 &lt;input type="number" id="absen" placeholder="Contoh: 14" min="1"&gt;
 &lt;label for="nama"&gt;Nama Lengkap&lt;/label&gt;
 &lt;input type="text" id="nama" placeholder="Tulis nama lengkapmu"&gt;
 &lt;label for="kelas"&gt;Kelas&lt;/label&gt;
 &lt;input type="text" id="kelas" placeholder="Contoh: XI TKR 1"&gt;
 &lt;div class="err" id="introErr"&gt;Isi No. Absen, Nama Lengkap, dan Kelas terlebih dahulu.&lt;/div&gt;
 &lt;button class="btn btn-primary" id="btnCekStatus"&gt;Lanjutkan&lt;/button&gt;
 &lt;/div&gt;

 &lt;div id="screen-status" class="card hidden"&gt;
 &lt;h2 id="statusGreeting"&gt;&lt;/h2&gt;
 &lt;div id="statusBody"&gt;&lt;/div&gt;
 &lt;button class="btn btn-secondary" id="btnMulai"&gt;Mulai Mengerjakan&lt;/button&gt;
 &lt;button class="btn hidden" id="btnUnduhStatus" style="background:var(--green);color:#fff;"&gt;Unduh Bukti Mengerjakan (PDF)&lt;/button&gt;
 &lt;p class="hidden" id="instruksiStatus" style="font-size:12.5px;color:var(--ink-soft);margin-top:6px;"&gt;Setelah diunduh, cetak dokumen ini (maks. 3 lembar F4) dan &lt;b&gt;serahkan ke guru mata pelajaran&lt;/b&gt; sebagai bukti pengerjaan.&lt;/p&gt;
 &lt;button class="btn" id="btnGanti" style="background:transparent;border:1px solid var(--line);"&gt;Ganti No. Absen&lt;/button&gt;
 &lt;/div&gt;

 &lt;div id="screen-quiz" class="hidden"&gt;
 &lt;div id="progressWrap"&gt;
 &lt;div id="progressBar"&gt;&lt;div id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
 &lt;div id="progressText"&gt;0 dari 40 soal terjawab&lt;/div&gt;
 &lt;/div&gt;
 &lt;div id="quizBody"&gt;&lt;/div&gt;
 &lt;div class="err" id="quizErr"&gt;Masih ada soal yang belum dijawab. Lengkapi seluruh soal sebelum mengirim.&lt;/div&gt;
 &lt;button class="btn btn-primary" id="btnKirim"&gt;Kirim Jawaban&lt;/button&gt;
 &lt;/div&gt;

 &lt;div id="screen-result" class="card hidden"&gt;
 &lt;div id="resultBoxOuter"&gt;&lt;/div&gt;
 &lt;div class="attempt-log" id="attemptLog"&gt;&lt;/div&gt;
 &lt;button class="btn btn-secondary hidden" id="btnUnduhHasil" style="background:var(--green);color:#fff;"&gt;Unduh Bukti Mengerjakan (PDF)&lt;/button&gt;
 &lt;p class="hidden" id="instruksiHasil" style="font-size:12.5px;color:var(--ink-soft);margin-top:6px;"&gt;Setelah diunduh, cetak dokumen ini (maks. 3 lembar F4) dan &lt;b&gt;serahkan ke guru mata pelajaran&lt;/b&gt; sebagai bukti pengerjaan.&lt;/p&gt;
 &lt;button class="btn btn-secondary hidden" id="btnCobaLagi"&gt;Coba Lagi&lt;/button&gt;
 &lt;button class="btn" id="btnSelesai" style="background:transparent;border:1px solid var(--line);"&gt;Kembali ke Awal&lt;/button&gt;
 &lt;/div&gt;

 &lt;footer&gt;DOK/B.INDO/XI/PROSEDUR-UH1 - Ulangan Harian 1, 40 Butir Pilihan Ganda + 10 Esai, Pokok Bahasan Petunjuk Kerja&lt;/footer&gt;
&lt;/div&gt;

&lt;div id="printArea"&gt;&lt;/div&gt;

&lt;script&gt;
const GROUPS = [
{
 title: "Teks 1 - Cara Mengganti Oli Sepeda Motor",
 stimulus: "Mengganti oli mesin secara berkala menjaga performa sepeda motor tetap optimal. Berikut langkah mengganti oli mesin sepeda motor jenis matic.\n\nAlat dan bahan: kunci pas 17, wadah penampung oli bekas, oli mesin baru sesuai spesifikasi pabrikan, corong, kain lap.\n\nLangkah-langkah:\n1) Panaskan mesin selama tiga menit agar oli lama lebih mudah mengalir, lalu matikan mesin.\n2) Letakkan wadah penampung tepat di bawah baut pembuangan oli.\n3) Buka baut pembuangan oli menggunakan kunci pas 17 secara perlahan agar oli tidak tumpah ke luar wadah.\n4) Biarkan oli lama mengalir habis selama kurang lebih lima menit.\n5) Pasang kembali baut pembuangan oli dan kencangkan hingga rapat, tetapi jangan berlebihan agar ulir baut tidak rusak.\n6) Buka tutup pengisian oli, lalu tuangkan oli baru menggunakan corong sesuai takaran yang tertera pada buku manual.\n7) Pasang kembali tutup pengisian oli, nyalakan mesin, dan periksa ketinggian oli menggunakan dipstick.",
 questions: [
 {q:"Alat yang digunakan untuk membuka baut pembuangan oli adalah...", opts:["Obeng plus","Kunci pas 17","Tang kombinasi","Kunci inggris","Kunci L"], correct:1},
 {q:"Berdasarkan teks, mesin perlu dipanaskan sebelum oli dikuras dengan tujuan...", opts:["Mempercepat proses pemasangan baut","Membuat oli lama lebih mudah mengalir","Mendinginkan komponen mesin","Mengurangi tekanan pada karburator","Menstabilkan putaran mesin"], correct:1},
 {q:"Jika langkah ke-3 dan ke-5 pada teks tersebut ditukar urutannya, akibat yang paling mungkin terjadi adalah...", opts:["Oli baru akan tercampur dengan oli lama","Baut pembuangan tidak dapat dibuka sama sekali","Oli lama tidak dapat dikeluarkan karena baut belum dibuka saat wadah sudah terpasang","Proses penggantian oli menjadi lebih cepat","Tidak ada perbedaan hasil karena urutan bebas"], correct:2},
 {q:"Kalimat \"kencangkan hingga rapat, tetapi jangan berlebihan agar ulir baut tidak rusak\" berfungsi sebagai...", opts:["Latar belakang masalah","Peringatan sekaligus alasan tindakan","Kesimpulan prosedur","Tujuan utama teks","Definisi istilah teknis"], correct:1},
 {q:"Informasi yang TIDAK termuat secara eksplisit dalam teks tersebut adalah...", opts:["Jenis kunci yang digunakan","Lama waktu memanaskan mesin","Interval jarak tempuh ideal untuk mengganti oli","Alat untuk mengecek ketinggian oli","Tujuan memasang wadah penampung"], correct:2}
 ]
},
{
 title: "Teks 2 - Petunjuk Penggunaan Alat Pemadam Api Ringan (APAR)",
 stimulus: "Alat Pemadam Api Ringan (APAR) jenis serbuk kimia kering merupakan alat pertolongan pertama saat terjadi kebakaran skala kecil. Ikuti langkah berikut saat menggunakan APAR, yang dikenal dengan istilah PASS.\n\n1) Pull (tarik): Tarik pin pengaman yang terdapat pada bagian atas tabung.\n2) Aim (arahkan): Arahkan corong atau selang ke titik api, bukan ke kobaran asap.\n3) Squeeze (tekan): Tekan tuas atau pemicu secara mantap agar media pemadam keluar.\n4) Sweep (ayunkan): Ayunkan corong ke kiri dan ke kanan menyapu dasar api hingga api benar-benar padam.\n\nSebelum menggunakan APAR, pastikan posisi tubuh membelakangi arah angin agar asap dan media pemadam tidak mengenai wajah. Setelah kebakaran padam, jangan langsung meninggalkan lokasi; tunggu beberapa saat untuk memastikan tidak ada bara api yang menyala kembali.",
 questions: [
 {q:"Singkatan PASS pada teks tersebut merujuk pada...", opts:["Empat jenis APAR","Empat tahapan menggunakan APAR","Empat bahan pemadam api","Empat lokasi penyimpanan APAR","Empat ukuran tabung"], correct:1},
 {q:"Bagian tabung yang pertama kali harus ditarik adalah...", opts:["Selang","Corong","Pin pengaman","Tuas pemicu","Label tabung"], correct:2},
 {q:"Tujuan mengarahkan corong ke titik api, bukan ke kobaran asap adalah...", opts:["Agar media pemadam lebih hemat","Karena asap tidak mengandung oksigen","Agar sumber api langsung padam, bukan hanya menghilangkan asapnya","Supaya pengguna tidak terlihat panik","Karena asap dapat merusak tabung APAR"], correct:2},
 {q:"Posisi tubuh membelakangi arah angin saat memadamkan api bertujuan untuk...", opts:["Mempercepat proses penyemprotan","Menghindari asap dan media pemadam mengenai wajah pengguna","Membuat api lebih cepat menyebar","Mengurangi tekanan dalam tabung","Memudahkan pengambilan tabung cadangan"], correct:1},
 {q:"Simpulan yang paling tepat berdasarkan seluruh isi teks tersebut adalah...", opts:["APAR hanya dapat digunakan oleh petugas pemadam kebakaran profesional","Penggunaan APAR memerlukan urutan tindakan dan kewaspadaan hingga api benar-benar padam","APAR jenis serbuk kimia kering tidak boleh digunakan untuk kebakaran skala kecil","Kobaran asap lebih berbahaya daripada api itu sendiri","Tuas pemicu hanya berfungsi jika pin belum dilepas"], correct:1}
 ]
},
{
 title: "Teks 3 - Dua Metode Menyeduh Kopi (Perbandingan)",
 stimulus: "Teks A - Menyeduh Kopi Metode V60\nAlat: dripper V60, kertas filter, kopi bubuk giling sedang-halus 15 gram, air panas suhu 92-96 derajat Celsius, timbangan, kettle leher angsa.\nLangkah: 1) Basahi kertas filter dengan air panas untuk menghilangkan aroma kertas. 2) Masukkan kopi bubuk ke dalam filter, ratakan permukaannya. 3) Tuang air panas sedikit demi sedikit membentuk gerakan memutar selama 30 detik untuk proses blooming. 4) Lanjutkan menuang air secara bertahap hingga total air mencapai 250 ml dalam waktu sekitar tiga menit. 5) Biarkan air menetes habis sebelum dripper diangkat.\n\nTeks B - Menyeduh Kopi Metode Tubruk\nAlat: cangkir atau gelas tahan panas, kopi bubuk kasar 10 gram, air panas suhu 90 derajat Celsius, sendok.\nLangkah: 1) Masukkan kopi bubuk langsung ke dalam cangkir. 2) Tuangkan air panas hingga cangkir hampir penuh. 3) Aduk sebentar agar bubuk kopi tercampur rata dengan air. 4) Diamkan selama tiga hingga empat menit hingga ampas kopi mengendap di dasar cangkir. 5) Kopi siap dinikmati tanpa perlu disaring.",
 questions: [
 {q:"Perbedaan utama alat yang digunakan pada kedua metode tersebut adalah...", opts:["Metode V60 tidak memerlukan air panas","Metode V60 menggunakan dripper dan kertas filter, sedangkan metode tubruk tidak","Metode tubruk memerlukan timbangan, metode V60 tidak","Kedua metode sama-sama tidak menggunakan air panas","Metode tubruk memerlukan kettle leher angsa"], correct:1},
 {q:"Persamaan dari kedua prosedur penyeduhan kopi tersebut adalah...", opts:["Keduanya menggunakan kertas filter","Keduanya memerlukan proses penyaringan","Keduanya menggunakan air panas dan kopi bubuk sebagai bahan utama","Keduanya menghasilkan volume kopi yang sama persis","Keduanya membutuhkan waktu penyeduhan yang identik"], correct:2},
 {q:"Ukuran gilingan kopi pada metode V60 lebih halus dibandingkan metode tubruk. Hal ini kemungkinan besar berkaitan dengan...", opts:["Warna kopi yang dihasilkan","Adanya proses penyaringan menggunakan kertas filter pada metode V60","Suhu air yang digunakan","Jenis cangkir yang dipakai","Lama waktu penyimpanan kopi bubuk"], correct:1},
 {q:"Jika seseorang tidak memiliki kertas filter maupun dripper tetapi ingin menikmati kopi dengan cepat tanpa proses penuangan bertahap, metode yang paling sesuai untuk direkomendasikan adalah...", opts:["Metode V60, karena hasilnya lebih jernih","Metode tubruk, karena alat yang dibutuhkan lebih sederhana dan langkahnya lebih singkat","Kedua metode sama-sama tidak dapat dilakukan","Metode V60, karena prosesnya lebih cepat daripada tubruk","Tidak dapat ditentukan karena teks tidak membahas kecepatan penyeduhan"], correct:1},
 {q:"Lama waktu penuangan air pada metode V60 hingga mencapai 250 ml adalah sekitar...", opts:["30 detik","1 menit","3 menit","5 menit","10 menit"], correct:2}
 ]
},
{
 title: "Teks 4 - Langkah Mendaftar Akun SIAKAD Sekolah",
 stimulus: "Berikut langkah pendaftaran akun SIAKAD bagi peserta didik baru.\n1) Buka peramban (browser) lalu ketik alamat siakad.sekolah.sch.id pada bilah alamat.\n2) Klik tombol \"Daftar Akun Baru\" yang terletak di pojok kanan atas laman.\n3) Isi formulir menggunakan Nomor Induk Siswa (NIS) dan tanggal lahir sesuai data yang tercatat di tata usaha.\n4) Buat kata sandi yang terdiri atas kombinasi huruf, angka, dan simbol, minimal delapan karakter.\n5) Klik tombol \"Kirim\", kemudian periksa surel (email) untuk menemukan tautan aktivasi akun.\n6) Klik tautan aktivasi tersebut dalam waktu 24 jam agar akun tidak perlu didaftarkan ulang.\n7) Masuk (login) ke SIAKAD menggunakan NIS dan kata sandi yang telah dibuat.",
 questions: [
 {q:"Data yang digunakan untuk mengisi formulir pendaftaran adalah...", opts:["Nomor telepon dan alamat rumah","NIS dan tanggal lahir","Nama orang tua dan pekerjaan","Alamat surel dan nomor rekening","Nomor induk kependudukan dan agama"], correct:1},
 {q:"Tautan aktivasi akun dikirimkan melalui...", opts:["Pesan singkat (SMS)","Aplikasi SIAKAD langsung","Surel (email)","Media sosial sekolah","Surat resmi dari tata usaha"], correct:2},
 {q:"Batas waktu mengklik tautan aktivasi sebagaimana disebutkan dalam teks adalah...", opts:["1 jam","12 jam","24 jam","48 jam","7 hari"], correct:2},
 {q:"Apabila siswa tidak mengklik tautan aktivasi dalam batas waktu yang ditentukan, konsekuensi yang paling mungkin terjadi berdasarkan teks adalah...", opts:["Akun otomatis aktif dengan sendirinya","Siswa harus mendaftarkan akun dari awal","Kata sandi akan direset otomatis oleh sistem","Data NIS akan terhapus dari sistem sekolah","Tidak ada konsekuensi apa pun"], correct:1},
 {q:"Kalimat \"Buat kata sandi yang terdiri atas kombinasi huruf, angka, dan simbol, minimal delapan karakter\" termasuk ke dalam unsur kebahasaan teks prosedur berupa...", opts:["Kalimat tanya","Kalimat larangan","Kalimat perintah (imperatif) yang memuat syarat/batasan","Kalimat pengandaian","Kalimat pasif"], correct:2}
 ]
},
{
 title: "Teks 5 - Kaidah Kebahasaan Teks Prosedur",
 stimulus: "Teks prosedur memiliki ciri kebahasaan yang khas agar pembaca dapat melaksanakan langkah-langkah dengan tepat. Beberapa ciri tersebut antara lain: penggunaan kalimat imperatif (perintah) seperti \"potong\", \"aduk\", atau \"tekan\"; penggunaan konjungsi temporal seperti \"kemudian\", \"setelah itu\", dan \"selanjutnya\" untuk menunjukkan urutan langkah; penggunaan kata kerja aktif yang menyatakan tindakan konkret; penggunaan kalimat larangan yang biasanya ditandai kata \"jangan\" atau \"hindari\" untuk memberi peringatan; serta penggunaan keterangan alat, bahan, ukuran, atau waktu secara spesifik dan terukur.",
 questions: [
 {q:"Kata yang termasuk konjungsi temporal dalam teks prosedur adalah...", opts:["Karena","Kemudian","Meskipun","Walaupun","Namun"], correct:1},
 {q:"Kalimat \"Jangan menyentuh permukaan wajan yang masih panas\" merupakan contoh kalimat...", opts:["Kalimat tanya","Kalimat larangan","Kalimat berita","Kalimat pengandaian","Kalimat majemuk setara"], correct:1},
 {q:"Fungsi utama konjungsi temporal dalam teks prosedur adalah...", opts:["Menghubungkan dua kalimat yang bertentangan","Menunjukkan urutan atau tahapan pelaksanaan langkah","Menyatakan sebab akibat suatu tindakan","Menyatakan syarat sebuah tindakan","Menyatakan tujuan akhir prosedur"], correct:1},
 {q:"Manakah kalimat berikut yang menggunakan kata kerja imperatif dengan tepat?", opts:["Adonan itu diaduk oleh koki","Adonan akan diaduk hingga rata","Aduklah adonan hingga rata","Adonan telah diaduk hingga rata","Adonan sedang diaduk oleh asisten koki"], correct:2},
 {q:"Perhatikan kalimat berikut: \"Panaskan oven pada suhu 180 derajat Celsius selama sepuluh menit sebelum adonan dimasukkan.\" Unsur kebahasaan yang paling menonjol pada kalimat tersebut, selain kalimat imperatif, adalah...", opts:["Konjungsi pertentangan","Keterangan ukuran/suhu dan waktu yang spesifik dan terukur","Kalimat tanya retoris","Kata kerja pasif","Kalimat majemuk bertingkat sebab-akibat"], correct:1}
 ]
},
{
 title: "Teks 6 - Petunjuk Mengoperasikan Mesin Bubut Sederhana",
 stimulus: "Sebelum mengoperasikan mesin bubut, operator wajib memahami langkah keselamatan dan pengoperasian berikut.\n1) Kenakan alat pelindung diri berupa kacamata safety dan pakaian kerja yang pas di badan, hindari pakaian longgar yang dapat tersangkut komponen berputar.\n2) Periksa benda kerja dan pastikan tercekam kuat pada chuck sebelum mesin dinyalakan.\n3) Atur kecepatan putar (rpm) sesuai jenis material yang akan dibubut; gunakan kecepatan rendah untuk material keras dan kecepatan tinggi untuk material lunak.\n4) Nyalakan mesin dan dekatkan pahat secara perlahan ke benda kerja.\n5) Lakukan pemakanan (feeding) secara bertahap, jangan terlalu dalam agar pahat tidak patah.\n6) Setelah selesai, matikan mesin terlebih dahulu sebelum melepas benda kerja dari chuck.",
 questions: [
 {q:"Alat pelindung diri yang disebutkan dalam teks adalah...", opts:["Helm proyek","Kacamata safety","Sarung tangan karet","Masker respirator","Sepatu boots"], correct:1},
 {q:"Sebelum mesin dinyalakan, hal yang harus dipastikan operator adalah...", opts:["Kecepatan rpm sudah maksimal","Benda kerja tercekam kuat pada chuck","Pahat sudah patah","Mesin dalam keadaan mati total","Ruangan dalam keadaan gelap"], correct:1},
 {q:"Alasan pakaian longgar dilarang dikenakan saat mengoperasikan mesin bubut adalah...", opts:["Membuat operator terlihat kurang rapi","Dapat tersangkut pada komponen mesin yang berputar dan membahayakan keselamatan","Mengurangi kenyamanan gerak operator","Tidak sesuai dengan standar seragam sekolah","Menghambat proses pemakanan benda kerja"], correct:1},
 {q:"Pengaturan kecepatan putar (rpm) pada langkah ketiga bergantung pada...", opts:["Ukuran ruangan bengkel","Jenis material benda kerja","Warna benda kerja","Berat badan operator","Waktu pengerjaan yang tersedia"], correct:1},
 {q:"Mengapa mesin harus dimatikan terlebih dahulu sebelum benda kerja dilepas dari chuck?", opts:["Agar proses pelepasan lebih cepat","Untuk menghindari cedera akibat komponen mesin yang masih berputar","Karena chuck tidak dapat dibuka saat mesin menyala","Agar rpm dapat diatur ulang","Karena benda kerja akan otomatis terlepas saat mesin mati"], correct:1}
 ]
},
{
 title: "Teks 7 - Menyusun Teks Prosedur yang Baik",
 stimulus: "Sebuah teks prosedur yang baik disusun dengan struktur yang jelas, yaitu: (1) judul yang menggambarkan tujuan prosedur secara ringkas, (2) tujuan atau pengantar yang menjelaskan hasil akhir yang ingin dicapai, (3) daftar alat dan bahan (jika diperlukan), dan (4) langkah-langkah yang disusun secara runtut dan bernomor. Setiap langkah sebaiknya ditulis dengan kalimat yang singkat, jelas, dan tidak menimbulkan makna ganda. Penulis juga perlu memastikan bahwa tidak ada langkah penting yang terlewat, karena satu langkah yang hilang dapat menyebabkan pembaca gagal melaksanakan prosedur dengan benar.",
 questions: [
 {q:"Struktur teks prosedur yang disebutkan dalam teks tersebut terdiri atas empat bagian, salah satunya adalah...", opts:["Orientasi","Daftar alat dan bahan","Resolusi","Koda","Abstrak"], correct:1},
 {q:"Alasan setiap langkah dalam teks prosedur harus ditulis dengan kalimat yang tidak menimbulkan makna ganda adalah...", opts:["Agar teks terlihat lebih panjang","Supaya pembaca dapat melaksanakan langkah dengan tepat sesuai maksud penulis","Agar teks memenuhi syarat jumlah kata minimal","Supaya pembaca merasa terhibur","Agar teks dapat dikategorikan sebagai karya sastra"], correct:1},
 {q:"Akibat yang paling mungkin terjadi apabila satu langkah penting dalam teks prosedur terlewat adalah...", opts:["Pembaca akan lebih cepat menyelesaikan prosedur","Pembaca berpotensi gagal melaksanakan prosedur dengan benar","Teks menjadi lebih ringkas dan efektif","Tidak berpengaruh apa pun terhadap hasil akhir","Pembaca akan otomatis menambahkan langkah yang hilang"], correct:1},
 {q:"Seorang siswa menulis teks prosedur \"Cara Membuat Nasi Goreng\" namun langsung menuliskan langkah-langkah tanpa mencantumkan bahan yang diperlukan. Berdasarkan teks di atas, kekurangan tulisan siswa tersebut adalah...", opts:["Tidak memiliki judul","Tidak mencantumkan daftar alat dan bahan yang diperlukan sebelum langkah-langkah","Menggunakan kalimat imperatif","Langkah-langkah tidak diberi nomor","Menggunakan konjungsi temporal"], correct:1},
 {q:"Bagian yang menjelaskan hasil akhir yang ingin dicapai dalam teks prosedur disebut...", opts:["Daftar bahan","Tujuan/pengantar","Penutup","Judul","Simpulan"], correct:1}
 ]
},
{
 title: "Teks 8 - Dua Contoh Petunjuk Merakit Rak Buku (Perbandingan)",
 stimulus: "Teks A - Cara Merakit Rak Buku Knock-Down\nAlat dan bahan: obeng plus, papan rak (5 bagian), sekrup (20 buah), dowel kayu (8 buah).\nLangkah:\n1) Susun papan alas dan dua papan samping membentuk huruf U, lalu satukan menggunakan dowel kayu.\n2) Kencangkan sambungan menggunakan sekrup dan obeng plus.\n3) Pasang papan tengah sebagai sekat rak sesuai jarak yang diinginkan.\n4) Pasang papan atas sebagai penutup rak, lalu kencangkan seluruh sekrup hingga rak kokoh berdiri.\n\nTeks B - Cara Merakit Rak Buku\nSiapkan bahan-bahannya. Lalu pasang semua bagian. Kencangkan bagian yang perlu dikencangkan. Setelah itu rak sudah jadi dan siap digunakan.",
 questions: [
 {q:"Perbedaan mencolok antara Teks A dan Teks B adalah...", opts:["Teks A tidak memiliki langkah, Teks B memiliki langkah rinci","Teks A memuat langkah yang rinci dan terukur, sedangkan Teks B ditulis secara umum dan kurang jelas","Teks B mencantumkan alat dan bahan, Teks A tidak","Kedua teks sama-sama rinci dan mudah diikuti","Teks A lebih pendek daripada Teks B"], correct:1},
 {q:"Kalimat \"pasang semua bagian\" pada Teks B dinilai kurang efektif sebagai kalimat prosedur karena...", opts:["Menggunakan kalimat imperatif","Tidak menjelaskan bagian mana yang dipasang dan bagaimana cara memasangnya secara spesifik","Menggunakan konjungsi temporal","Terlalu panjang untuk dipahami","Tidak menggunakan huruf kapital di awal kalimat"], correct:1},
 {q:"Jika seseorang yang belum pernah merakit rak buku hanya diberikan Teks B, kemungkinan besar yang akan terjadi adalah...", opts:["Ia dapat merakit rak dengan sangat mudah karena instruksinya sudah lengkap","Ia akan kesulitan karena teks tidak menjelaskan alat, bahan, dan langkah secara rinci","Ia akan merakit rak lebih cepat dibandingkan menggunakan Teks A","Tidak ada perbedaan hasil rakitan antara menggunakan Teks A atau Teks B","Teks B lebih mudah dipahami karena lebih singkat"], correct:1},
 {q:"Berdasarkan perbandingan kedua teks tersebut, kesimpulan yang paling tepat mengenai kualitas sebuah teks prosedur adalah...", opts:["Semakin singkat sebuah teks prosedur, semakin baik kualitasnya","Kualitas teks prosedur ditentukan oleh kelengkapan alat/bahan serta kejelasan dan keterukuran setiap langkahnya","Teks prosedur tidak memerlukan daftar alat dan bahan","Teks prosedur yang baik tidak perlu menggunakan angka atau ukuran","Panjang pendeknya teks tidak berpengaruh pada pemahaman pembaca"], correct:1},
 {q:"Apabila Teks B ingin diperbaiki agar setara dengan kualitas Teks A, revisi yang paling tepat dilakukan adalah...", opts:["Menghapus seluruh langkah yang ada","Mengganti judul teks menjadi lebih panjang","Menambahkan daftar alat dan bahan serta merincikan setiap langkah secara spesifik dan berurutan","Mengubah seluruh kalimat menjadi kalimat tanya","Menghilangkan tanda baca pada setiap kalimat"], correct:2}
 ]
}
];

// Esai 1-5: materi "Membandingkan Teks Prosedur dengan Teks Lain (Eksplanasi)"
// Esai 6-10: materi "Evaluasi Informasi dalam Teks Prosedur"
// Setiap soal punya beberapa "konsep kunci"; tiap konsep berisi beberapa alternatif kata/frasa (OR).
// Skor esai per soal = (jumlah konsep yang ditemukan / total konsep) x 100, lalu dirata-rata dari 10 soal.
const ESSAYS = [
 { q: "Jelaskan pengertian teks eksplanasi dan bagaimana tujuannya berbeda dengan teks prosedur!",
 concepts: [["fenomena","proses alam","proses sosial","proses ilmiah"], ["sebab-akibat","sebab akibat","kausalitas"], ["memahami","paham"], ["tindakan","melakukan","diikuti"]] },
 { q: "Sebutkan struktur teks eksplanasi (3 bagian) dan jelaskan secara singkat masing-masing bagian tersebut!",
 concepts: [["pernyataan umum"], ["deretan penjelas","deret penjelas","penjelasan sebab akibat","rangkaian penjelas"], ["interpretasi","penutup"]] },
 { q: "Sebutkan minimal 3 persamaan antara teks prosedur dan teks eksplanasi!",
 concepts: [["proses","tahapan"], ["informatif"], ["faktual","fakta"], ["konjungsi"], ["sistematis"]] },
 { q: "Jelaskan perbedaan konjungsi dominan yang digunakan pada teks prosedur dan teks eksplanasi, berikan masing-masing satu contoh katanya!",
 concepts: [["temporal"], ["kausalitas"], ["kemudian","lalu","setelah itu"], ["karena","sebab","akibatnya"]] },
 { q: "Jelaskan langkah-langkah membandingkan teks prosedur dengan teks eksplanasi secara berurutan!",
 concepts: [["membaca"], ["tujuan"], ["struktur"], ["kebahasaan","bahasa"], ["simpulan","kesimpulan"]] },
 { q: "Jelaskan apa yang dimaksud dengan \"mengevaluasi informasi\" dalam teks prosedur, dan bagaimana bedanya dengan sekadar \"memahami\" isi teks!",
 concepts: [["menilai","penilaian"], ["dipercaya","layak"], ["logis","masuk akal"], ["kritis"]] },
 { q: "Sebutkan minimal 4 dari 6 kriteria evaluasi teks prosedur yang telah dipelajari!",
 concepts: [["keakuratan","akurat"], ["kelengkapan","lengkap"], ["kelogisan","logis","urutan"], ["kejelasan","jelas","ambigu"], ["kesesuaian","sesuai tujuan"], ["keselamatan","k3","kelayakan"]] },
 { q: "Jelaskan mengapa kemampuan mengevaluasi teks prosedur penting bagi siswa SMK, kaitkan dengan dunia kerja/kejuruan!",
 concepts: [["kegagalan kerja","gagal kerja"], ["kerusakan alat","rusak"], ["kecelakaan kerja","kecelakaan"], ["kesiapan kerja","job readiness"]] },
 { q: "Sebutkan langkah-langkah mengevaluasi teks prosedur secara berurutan!",
 concepts: [["membaca menyeluruh","membaca"], ["struktur"], ["kriteria"], ["temuan"], ["menyimpulkan","simpulan"], ["rekomendasi","saran perbaikan"]] },
 { q: "Perhatikan temuan berikut: sebuah teks prosedur menuliskan \"Tuang oli mesin baru sebanyak 5 liter\", padahal kapasitas oli mesin sepeda motor umumnya hanya sekitar 0,8-1,2 liter. Kriteria evaluasi apa yang dilanggar pada temuan tersebut, dan jelaskan alasannya!",
 concepts: [["keakuratan","akurat","tidak akurat"], ["spesifikasi","standar"], ["takaran","kapasitas"], ["5 liter","0,8","1,2","tidak sesuai"]] }
];
const ESSAY_MAXLEN = 500;
const PG_WEIGHT = 0.7;
const ESSAY_WEIGHT = 0.3;

function scoreEssayAnswer(answerText, concepts){
 const norm = (answerText || '').toLowerCase();
 let found = 0;
 concepts.forEach(altGroup =&gt; {
 const hit = altGroup.some(alt =&gt; norm.includes(alt.toLowerCase()));
 if(hit) found++;
 });
 return {found: found, total: concepts.length, pct: Math.round((found/concepts.length)*100)};
}

const LETTERS = ["A","B","C","D","E"];
const KKM = 75;
const MAX_ATTEMPT = 3;
let currentAbsen = null, currentNama = null, currentKelas = null, currentRecord = null;
let quizGroups = null;
let answers = {};
let essayAnswers = {};

function shuffle(arr){
 const a = arr.slice();
 for(let i=a.length-1;i&gt;0;i--){
 const j = Math.floor(Math.random()*(i+1));
 [a[i],a[j]] = [a[j],a[i]];
 }
 return a;
}

function buildShuffledQuiz(){
 const groupOrder = shuffle(GROUPS);
 return groupOrder.map(g =&gt; ({
 title: g.title,
 stimulus: g.stimulus,
 questions: shuffle(g.questions).map(q =&gt; {
 const optIdx = shuffle(q.opts.map((_,i)=&gt;i));
 return {
 q: q.q,
 opts: optIdx.map(i =&gt; q.opts[i]),
 correctLetter: LETTERS[optIdx.indexOf(q.correct)]
 };
 })
 }));
}

async function loadRecord(absen){
 try{
 const r = await window.storage.get('siswa:'+absen, true);
 return r ? JSON.parse(r.value) : null;
 }catch(e){
 return null;
 }
}
async function saveRecord(absen, record){
 try{
 await window.storage.set('siswa:'+absen, JSON.stringify(record), true);
 }catch(e){
 console.error('Gagal menyimpan data', e);
 }
}

document.getElementById('btnCekStatus').addEventListener('click', async () =&gt; {
 const absenEl = document.getElementById('absen');
 const namaEl = document.getElementById('nama');
 const kelasEl = document.getElementById('kelas');
 const absen = absenEl.value.trim();
 const nama = namaEl.value.trim();
 const kelas = kelasEl.value.trim();
 const err = document.getElementById('introErr');
 if(!absen || !nama || !kelas){
 err.style.display = 'block';
 return;
 }
 err.style.display = 'none';
 currentAbsen = absen;
 currentNama = nama;
 currentKelas = kelas;
 currentRecord = await loadRecord(absen);
 if(!currentRecord){
 currentRecord = {absen, nama, kelas, attempts: [], final: null};
 } else {
 currentRecord.nama = nama;
 currentRecord.kelas = kelas;
 }
 showStatusScreen();
});

document.getElementById('btnGanti').addEventListener('click', () =&gt; {
 document.getElementById('screen-status').classList.add('hidden');
 document.getElementById('screen-intro').classList.remove('hidden');
});

function showStatusScreen(){
 document.getElementById('screen-intro').classList.add('hidden');
 const statusScreen = document.getElementById('screen-status');
 statusScreen.classList.remove('hidden');
 document.getElementById('statusGreeting').textContent = 'Halo, ' + currentNama + ' (No. Absen ' + currentAbsen + ')';
 const body = document.getElementById('statusBody');
 const btnMulai = document.getElementById('btnMulai');
 const usedAttempts = currentRecord.attempts.length;
 const sisa = MAX_ATTEMPT - usedAttempts;

 const btnUnduhStatus = document.getElementById('btnUnduhStatus');
 if(currentRecord.final){
 const f = currentRecord.final;
 body.innerHTML =
 '&lt;p style="font-size:14px;"&gt;Nilai kamu sudah tersimpan sebagai nilai akhir.&lt;/p&gt;' +
 '&lt;div class="result-box ' + (f.score &gt;= KKM ? 'pass-bg' : 'fail-bg') + '" style="margin-top:10px;"&gt;' +
 '&lt;div style="font-size:13px;color:var(--ink-soft);"&gt;Nilai akhir tersimpan&lt;/div&gt;' +
 '&lt;div class="result-score ' + (f.score &gt;= KKM ? 'pass' : 'fail') + '"&gt;' + f.score + '&lt;/div&gt;' +
 '&lt;div style="font-size:13px;"&gt;' + (f.score &gt;= KKM ? 'Mencapai KKM (75)' : 'Nilai tertinggi dari 3 percobaan (belum mencapai KKM)') + '&lt;/div&gt;' +
 '&lt;div style="font-size:12px;color:var(--ink-soft);margin-top:6px;"&gt;Skor PG ' + f.pgScore + ' (bobot 70%) - Skor Esai ' + f.essayScorePct + ' (bobot 30%)&lt;/div&gt;' +
 '&lt;/div&gt;' +
 renderAttemptListHTML(currentRecord.attempts);
 btnMulai.classList.add('hidden');
 btnUnduhStatus.classList.remove('hidden');
 document.getElementById('instruksiStatus').classList.remove('hidden');
 } else if(sisa &lt;= 0){
 body.innerHTML = '&lt;p style="font-size:14px;"&gt;Kesempatanmu sudah habis. Sistem akan segera mencatat nilai tertinggimu.&lt;/p&gt;';
 btnMulai.classList.add('hidden');
 btnUnduhStatus.classList.add('hidden');
 document.getElementById('instruksiStatus').classList.add('hidden');
 } else {
 btnUnduhStatus.classList.add('hidden');
 document.getElementById('instruksiStatus').classList.add('hidden');
 body.innerHTML =
 '&lt;p style="font-size:14px;"&gt;Sisa kesempatan mengerjakan: &lt;b&gt;' + sisa + ' dari ' + MAX_ATTEMPT + '&lt;/b&gt;.&lt;/p&gt;' +
 (usedAttempts &gt; 0 ? renderAttemptListHTML(currentRecord.attempts) : '');
 btnMulai.classList.remove('hidden');
 btnMulai.textContent = usedAttempts === 0 ? 'Mulai Mengerjakan' : 'Coba Lagi (Percobaan ke-' + (usedAttempts+1) + ')';
 }
}

function renderAttemptListHTML(attempts){
 if(!attempts.length) return '';
 let html = '&lt;div class="attempt-log"&gt;&lt;b style="font-size:13px;"&gt;Riwayat percobaan:&lt;/b&gt;';
 attempts.forEach((a,i) =&gt; {
 html += '&lt;div&gt;&lt;span&gt;Percobaan ' + (i+1) + ' (PG ' + a.pgScore + ' - Esai ' + a.essayScorePct + ')&lt;/span&gt;&lt;span&gt;' + a.score + ' poin&lt;/span&gt;&lt;/div&gt;';
 });
 html += '&lt;/div&gt;';
 return html;
}

document.getElementById('btnMulai').addEventListener('click', startQuiz);

function startQuiz(){
 answers = {};
 essayAnswers = {};
 quizGroups = buildShuffledQuiz();
 document.getElementById('screen-status').classList.add('hidden');
 document.getElementById('screen-result').classList.add('hidden');
 document.getElementById('screen-quiz').classList.remove('hidden');
 renderQuiz();
 window.scrollTo({top:0, behavior:'smooth'});
}

function renderQuiz(){
 const body = document.getElementById('quizBody');
 let html = '';
 let counter = 0;
 quizGroups.forEach((g, gi) =&gt; {
 html += '&lt;div class="qgroup"&gt;&lt;div class="stimulus"&gt;&lt;div class="st-title"&gt;' + esc(g.title) + '&lt;/div&gt;' + esc(g.stimulus) + '&lt;/div&gt;';
 g.questions.forEach((q, qi) =&gt; {
 counter++;
 const qKey = gi + '-' + qi;
 html += '&lt;div class="q" data-qkey="' + qKey + '"&gt;';
 html += '&lt;div class="q-num"&gt;Soal ' + counter + '&lt;/div&gt;';
 html += '&lt;div class="q-text"&gt;' + esc(q.q) + '&lt;/div&gt;';
 q.opts.forEach((opt, oi) =&gt; {
 const letter = LETTERS[oi];
 html += '&lt;label class="opt" data-qkey="' + qKey + '" data-letter="' + letter + '"&gt;' +
 '&lt;input type="radio" name="q-' + qKey + '" value="' + letter + '"&gt;' +
 '&lt;span&gt;&lt;b&gt;' + letter + '.&lt;/b&gt; ' + esc(opt) + '&lt;/span&gt;&lt;/label&gt;';
 });
 html += '&lt;/div&gt;';
 });
 html += '&lt;/div&gt;';
 });

 html += '&lt;div class="sectionLabel"&gt;Bagian II - Esai (10 soal, dinilai otomatis berdasarkan kesesuaian konsep kunci jawabanmu - bobot Esai 30% dari nilai akhir - maksimal ' + ESSAY_MAXLEN + ' karakter per soal - jawaban wajib diketik sendiri, tempel/paste dinonaktifkan)&lt;/div&gt;';
 html += '&lt;div class="essaySection"&gt;';
 ESSAYS.forEach((item, ei) =&gt; {
 html += '&lt;div class="essayQ" data-ekey="' + ei + '"&gt;';
 html += '&lt;div class="essayQ-num"&gt;Esai ' + (ei+1) + '&lt;/div&gt;';
 html += '&lt;div class="essayQ-text"&gt;' + esc(item.q) + '&lt;/div&gt;';
 html += '&lt;textarea class="essayInput" data-ekey="' + ei + '" maxlength="' + ESSAY_MAXLEN + '" placeholder="Ketik jawabanmu di sini (tempel/paste tidak diperbolehkan)..."&gt;&lt;/textarea&gt;';
 html += '&lt;div class="charCount" data-ekey="' + ei + '"&gt;0 / ' + ESSAY_MAXLEN + '&lt;/div&gt;';
 html += '&lt;div class="pasteWarn" data-ekey="' + ei + '"&gt;Menempel (paste) tidak diperbolehkan. Silakan ketik sendiri jawabanmu.&lt;/div&gt;';
 html += '&lt;/div&gt;';
 });
 html += '&lt;/div&gt;';

 body.innerHTML = html;

 body.querySelectorAll('textarea.essayInput').forEach(ta =&gt; {
 ta.addEventListener('input', () =&gt; {
 const ekey = ta.getAttribute('data-ekey');
 essayAnswers[ekey] = ta.value;
 const counterEl = body.querySelector('.charCount[data-ekey="' + ekey + '"]');
 const len = ta.value.length;
 counterEl.textContent = len + ' / ' + ESSAY_MAXLEN;
 counterEl.classList.toggle('over', len &gt;= ESSAY_MAXLEN);
 updateProgress();
 });

 const showPasteWarn = () =&gt; {
 const ekey = ta.getAttribute('data-ekey');
 const warnEl = body.querySelector('.pasteWarn[data-ekey="' + ekey + '"]');
 if(!warnEl) return;
 warnEl.classList.add('show');
 clearTimeout(warnEl._hideTimer);
 warnEl._hideTimer = setTimeout(() =&gt; warnEl.classList.remove('show'), 3500);
 };

 ta.addEventListener('paste', (e) =&gt; { e.preventDefault(); showPasteWarn(); });
 ta.addEventListener('drop', (e) =&gt; { e.preventDefault(); showPasteWarn(); });
 ta.addEventListener('dragover', (e) =&gt; { e.preventDefault(); });
 ta.addEventListener('contextmenu', (e) =&gt; { e.preventDefault(); showPasteWarn(); });
 ta.addEventListener('keydown', (e) =&gt; {
 const key = (e.key || '').toLowerCase();
 const pasteCombo = (e.ctrlKey || e.metaKey) &amp;&amp; key === 'v';
 const shiftInsert = e.shiftKey &amp;&amp; key === 'insert';
 if(pasteCombo || shiftInsert){
 e.preventDefault();
 showPasteWarn();
 }
 });
 });

 body.querySelectorAll('.opt').forEach(optEl =&gt; {
 optEl.addEventListener('click', () =&gt; {
 const qKey = optEl.getAttribute('data-qkey');
 const letter = optEl.getAttribute('data-letter');
 answers[qKey] = letter;
 document.querySelectorAll('.opt[data-qkey="' + qKey + '"]').forEach(o =&gt; o.classList.remove('selected'));
 optEl.classList.add('selected');
 optEl.querySelector('input').checked = true;
 updateProgress();
 });
 });
 updateProgress();
}

function esc(s){
 const d = document.createElement('div');
 d.textContent = s;
 return d.innerHTML;
}

function totalPGQuestions(){
 return quizGroups.reduce((sum,g) =&gt; sum + g.questions.length, 0);
}

function totalQuestions(){
 return totalPGQuestions() + ESSAYS.length;
}

function essayAnsweredCount(){
 return ESSAYS.reduce((n,_,ei) =&gt; n + (essayAnswers[ei] &amp;&amp; essayAnswers[ei].trim() ? 1 : 0), 0);
}

function updateProgress(){
 const total = totalQuestions();
 const answered = Object.keys(answers).length + essayAnsweredCount();
 document.getElementById('progressFill').style.width = (answered/total*100) + '%';
 document.getElementById('progressText').textContent = answered + ' dari ' + total + ' soal terjawab (40 PG + 10 esai)';
}

document.getElementById('btnKirim').addEventListener('click', async () =&gt; {
 const total = totalQuestions();
 const answered = Object.keys(answers).length + essayAnsweredCount();
 const errEl = document.getElementById('quizErr');
 if(answered &lt; total){
 errEl.style.display = 'block';
 let target = null;
 const qEls = document.querySelectorAll('.q');
 for(let i=0;i&lt;qEls.length;i++){
 const qk = qEls[i].getAttribute('data-qkey');
 if(!answers[qk]){ target = qEls[i]; break; }
 }
 if(!target){
 const eEls = document.querySelectorAll('.essayQ');
 for(let i=0;i&lt;eEls.length;i++){
 const ek = eEls[i].getAttribute('data-ekey');
 if(!essayAnswers[ek] || !essayAnswers[ek].trim()){ target = eEls[i]; break; }
 }
 }
 if(target) target.scrollIntoView({behavior:'smooth', block:'center'});
 return;
 }
 errEl.style.display = 'none';

 let correctCount = 0;
 quizGroups.forEach((g, gi) =&gt; {
 g.questions.forEach((q, qi) =&gt; {
 const qKey = gi + '-' + qi;
 if(answers[qKey] === q.correctLetter) correctCount++;
 });
 });
 const pgScore = Math.round(correctCount * (100 / totalPGQuestions()));

 const essaySnapshot = ESSAYS.map((item, ei) =&gt; {
 const ans = (essayAnswers[ei] || '').trim();
 const result = scoreEssayAnswer(ans, item.concepts);
 return {q: item.q, a: ans, found: result.found, total: result.total, pct: result.pct};
 });
 const essayScorePct = Math.round(essaySnapshot.reduce((s,e) =&gt; s + e.pct, 0) / essaySnapshot.length);
 const score = Math.round(pgScore * PG_WEIGHT + essayScorePct * ESSAY_WEIGHT);

 currentRecord.attempts.push({score: score, pgScore: pgScore, essayScorePct: essayScorePct, essay: essaySnapshot, timestamp: new Date().toISOString()});
 const usedAttempts = currentRecord.attempts.length;

 if(score &gt;= KKM){
 currentRecord.final = {score: score, pgScore: pgScore, essayScorePct: essayScorePct, essay: essaySnapshot, source: 'kkm', attemptsUsed: usedAttempts, timestamp: new Date().toISOString()};
 } else if(usedAttempts &gt;= MAX_ATTEMPT){
 let best = currentRecord.attempts[0];
 currentRecord.attempts.forEach(a =&gt; { if(a.score &gt; best.score) best = a; });
 currentRecord.final = {score: best.score, pgScore: best.pgScore, essayScorePct: best.essayScorePct, essay: best.essay, source: 'tertinggi', attemptsUsed: usedAttempts, timestamp: new Date().toISOString()};
 }

 await saveRecord(currentAbsen, currentRecord);
 showResult(score, pgScore, essayScorePct);
});

function showResult(score, pgScore, essayScorePct){
 document.getElementById('screen-quiz').classList.add('hidden');
 const resultScreen = document.getElementById('screen-result');
 resultScreen.classList.remove('hidden');

 const passedNow = score &gt;= KKM;
 const outer = document.getElementById('resultBoxOuter');
 const usedAttempts = currentRecord.attempts.length;
 const sisa = MAX_ATTEMPT - usedAttempts;

 let statusLine;
 if(currentRecord.final &amp;&amp; currentRecord.final.source === 'kkm'){
 statusLine = 'Selamat! Nilai ini sudah mencapai KKM dan tersimpan sebagai nilai akhir.';
 } else if(currentRecord.final &amp;&amp; currentRecord.final.source === 'tertinggi'){
 statusLine = 'Kesempatan sudah habis. Nilai tertinggi dari 3 percobaanmu (' + currentRecord.final.score + ') tersimpan sebagai nilai akhir.';
 } else {
 statusLine = 'Nilai belum mencapai KKM (75). Sisa kesempatan: ' + sisa + '.';
 }

 outer.innerHTML =
 '&lt;div class="result-box ' + (passedNow ? 'pass-bg' : 'fail-bg') + '"&gt;' +
 '&lt;div style="font-size:13px;color:var(--ink-soft);"&gt;Nilai akhir percobaan ini&lt;/div&gt;' +
 '&lt;div class="result-score ' + (passedNow ? 'pass' : 'fail') + '"&gt;' + score + ' / 100&lt;/div&gt;' +
 '&lt;div style="font-size:12px;color:var(--ink-soft);margin-bottom:6px;"&gt;Skor PG ' + pgScore + ' (bobot 70%) + Skor Esai ' + essayScorePct + ' (bobot 30%)&lt;/div&gt;' +
 '&lt;div style="font-size:14px;"&gt;' + statusLine + '&lt;/div&gt;' +
 '&lt;/div&gt;';

 document.getElementById('attemptLog').innerHTML = renderAttemptListHTML(currentRecord.attempts);

 const btnCoba = document.getElementById('btnCobaLagi');
 const btnUnduhHasil = document.getElementById('btnUnduhHasil');
 if(!currentRecord.final &amp;&amp; sisa &gt; 0){
 btnCoba.classList.remove('hidden');
 } else {
 btnCoba.classList.add('hidden');
 }
 if(currentRecord.final){
 btnUnduhHasil.classList.remove('hidden');
 document.getElementById('instruksiHasil').classList.remove('hidden');
 } else {
 btnUnduhHasil.classList.add('hidden');
 document.getElementById('instruksiHasil').classList.add('hidden');
 }
 window.scrollTo({top:0, behavior:'smooth'});
}

document.getElementById('btnCobaLagi').addEventListener('click', startQuiz);

document.getElementById('btnSelesai').addEventListener('click', () =&gt; {
 document.getElementById('screen-result').classList.add('hidden');
 document.getElementById('screen-intro').classList.remove('hidden');
 document.getElementById('absen').value = '';
 document.getElementById('nama').value = '';
 document.getElementById('kelas').value = '';
});

function generatePrintArea(){
 const f = currentRecord.final;
 if(!f) return;
 const tgl = new Date(f.timestamp || Date.now()).toLocaleDateString('id-ID', {day:'numeric', month:'long', year:'numeric'});
 const statusTxt = f.score &gt;= KKM
 ? 'Mencapai KKM (75) pada percobaan ke-' + f.attemptsUsed
 : 'Nilai tertinggi dari 3 kali percobaan (belum mencapai KKM 75)';

 let html = '';
 html += '&lt;div class="pf-kop noBreak"&gt;';
 html += '&lt;h2&gt;Bukti Mengerjakan Ulangan Harian 1 - Petunjuk Kerja&lt;/h2&gt;';
 html += '&lt;p&gt;Mata Pelajaran Bahasa Indonesia - Kelas XI SMK - 40 Soal Pilihan Ganda + 10 Soal Esai - KKM 75&lt;/p&gt;';
 html += '&lt;/div&gt;';

 html += '&lt;table class="pf-identitas noBreak"&gt;';
 html += '&lt;tr&gt;&lt;td class="lbl"&gt;Nama Lengkap&lt;/td&gt;&lt;td&gt;: ' + esc(currentRecord.nama || '') + '&lt;/td&gt;' +
 '&lt;td class="lbl"&gt;No. Absen&lt;/td&gt;&lt;td&gt;: ' + esc(currentRecord.absen || '') + '&lt;/td&gt;&lt;/tr&gt;';
 html += '&lt;tr&gt;&lt;td class="lbl"&gt;Kelas&lt;/td&gt;&lt;td&gt;: ' + esc(currentRecord.kelas || '') + '&lt;/td&gt;' +
 '&lt;td class="lbl"&gt;Tanggal&lt;/td&gt;&lt;td&gt;: ' + tgl + '&lt;/td&gt;&lt;/tr&gt;';
 html += '&lt;/table&gt;';

 html += '&lt;div class="pf-skorbox noBreak"&gt;';
 html += '&lt;div class="pf-skoritem"&gt;Skor Pilihan Ganda (70%)&lt;b&gt;' + f.pgScore + ' / 100&lt;/b&gt;&lt;/div&gt;';
 html += '&lt;div class="pf-skoritem"&gt;Skor Esai Otomatis (30%)&lt;b&gt;' + f.essayScorePct + ' / 100&lt;/b&gt;&lt;/div&gt;';
 html += '&lt;div class="pf-skoritem"&gt;Nilai Akhir&lt;b&gt;' + f.score + ' / 100&lt;/b&gt;&lt;/div&gt;';
 html += '&lt;div class="pf-skoritem"&gt;Status&lt;b style="font-size:13px;"&gt;' + statusTxt + '&lt;/b&gt;&lt;/div&gt;';
 html += '&lt;/div&gt;';

 html += '&lt;div class="pf-kop noBreak" style="border-top:1px solid #000;border-bottom:none;padding-top:8px;margin-top:6px;"&gt;&lt;h2 style="font-size:14px;"&gt;Jawaban Esai (skor otomatis berdasarkan cakupan konsep kunci)&lt;/h2&gt;&lt;/div&gt;';

 f.essay.forEach((item, i) =&gt; {
 const breakClass = (i === 4 || i === 8) ? ' pbreak' : '';
 html += '&lt;div class="noBreak' + breakClass + '" style="margin-bottom:10px;"&gt;';
 html += '&lt;div class="pf-essay-q"&gt;' + (i+1) + '. ' + esc(item.q) + ' &lt;span style="font-weight:400;color:#555;"&gt;(cakupan konsep: ' + item.found + '/' + item.total + ' - skor ' + item.pct + ')&lt;/span&gt;&lt;/div&gt;';
 html += '&lt;div class="pf-essay-a"&gt;' + esc(item.a || '(tidak dijawab)') + '&lt;/div&gt;';
 html += '&lt;/div&gt;';
 });

 html += '&lt;div class="pf-ttd noBreak"&gt;';
 html += '&lt;div&gt;Peserta Didik&lt;div class="garis"&gt;' + esc(currentRecord.nama || '') + '&lt;/div&gt;&lt;/div&gt;';
 html += '&lt;div&gt;Guru Mata Pelajaran&lt;div class="garis"&gt;.............................&lt;/div&gt;&lt;/div&gt;';
 html += '&lt;/div&gt;';
 html += '&lt;p class="pf-note"&gt;&lt;b&gt;Dokumen ini wajib dicetak dan diserahkan kepada guru mata pelajaran Bahasa Indonesia&lt;/b&gt; sebagai bukti fisik pengerjaan ulangan. Nilai akhir = (Skor Pilihan Ganda x 70%) + (Skor Esai Otomatis x 30%). Skor esai dihitung berdasarkan cakupan konsep kunci pada jawaban, sehingga guru tetap disarankan meninjau ulang jawaban esai untuk memastikan kualitas dan kejujuran akademik.&lt;/p&gt;';

 document.getElementById('printArea').innerHTML = html;
}

function downloadProof(){
 if(!currentRecord.final){
 alert('Bukti PDF hanya tersedia setelah nilai akhir tersimpan (mencapai KKM atau kesempatan sudah habis).');
 return;
 }
 generatePrintArea();
 setTimeout(() =&gt; window.print(), 100);
}

document.getElementById('btnUnduhStatus').addEventListener('click', downloadProof);
document.getElementById('btnUnduhHasil').addEventListener('click', downloadProof);
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/ulangan-harian-1-petunjuk-kerja.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-9000860041661554971.post-5290013737713810938</guid><pubDate>Fri, 04 Sep 2026 01:41:45 +0000</pubDate><atom:updated>2026-09-04T08:46:30.437+07:00</atom:updated><title>Latihan Soal Tipe AKM: Pokok Bahasan Petunjuk Kerja</title><description>&lt;style&gt;
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&amp;family=Source+Sans+3:wght@400;500;600;700&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;display=swap');
.epb{
  --blueprint:#0F2A3D;
  --blueprint-deep:#0A1E2C;
  --panel:#4A2E12;
  --paper:#F3F0E6;
  --ink:#132029;
  --amber:#F5A623;
  --amber-deep:#C97F12;
  --mint:#6EC6CA;
  --line:rgba(234,242,245,0.16);
  --off-white:#EAF2F5;
  --muted:#9FB4C2;
}
.epb *{box-sizing:border-box;}
.epb{scroll-behavior:smooth; background:#0A1F2E;}
.epb{
  margin:0;
  position:relative;
  overflow:hidden;
  background:var(--blueprint);
  color:var(--off-white);
  font-family:'Source Sans 3',sans-serif;
  line-height:1.65;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 34px 34px;
}
.epb h1, .epb h2, .epb h3, .epb h4{font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.02em; margin:0 0 0.5em;}
.epb .mono{font-family:'IBM Plex Mono',monospace;}
.epb a{color:var(--amber);}
.epb .crop{position:absolute; width:22px; height:22px; z-index:5; opacity:.55; pointer-events:none;}
.epb .crop::before, .epb .crop::after{content:"";position:absolute;background:var(--mint);}
.epb .crop::before{width:100%;height:1px;top:50%;left:0;}
.epb .crop::after{width:1px;height:100%;left:50%;top:0;}
.epb .crop.tl{top:14px;left:14px;}
.epb .crop.tr{top:14px;right:14px;}
.epb .crop.bl{bottom:14px;left:14px;}
.epb .crop.br{bottom:14px;right:14px;}
.epb header.cover{
  position:relative;
  padding:40px 24px 36px;
  border-bottom:1px solid var(--line);
  overflow:hidden;
}
.epb .doc-code{font-family:'IBM Plex Mono',monospace; color:var(--amber); letter-spacing:0.15em; font-size:0.78rem; text-transform:uppercase; margin-bottom:18px; display:flex; gap:10px; align-items:center;}
.epb .doc-code::before{content:"◆"; color:var(--amber);}
.epb .cover h1{font-size:clamp(1.6rem,7vw,2.5rem); color:var(--off-white); max-width:16ch; line-height:1.05;}
.epb .cover .subtitle{color:var(--muted); font-size:1.05rem; max-width:60ch; margin-top:14px;}
.epb .meta-strip{
  margin-top:38px; display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px dashed var(--line); border-bottom:1px dashed var(--line);
}
.epb .meta-strip div{padding:14px 16px; border-right:1px dashed var(--line);}
.epb .meta-strip div:last-child{border-right:none;}
.epb .meta-strip .k{font-family:'IBM Plex Mono',monospace; font-size:0.68rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.1em;}
.epb .meta-strip .v{font-size:0.98rem; color:var(--off-white); margin-top:4px; font-weight:600;}
.epb .stamp{
  position:absolute; top:40px; right:20px; width:104px; height:104px;
  border:2.5px solid var(--amber); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  transform:rotate(-11deg);
  color:var(--amber); font-family:'IBM Plex Mono',monospace;
  text-align:center; font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase;
  opacity:0.9;
}
.epb .stamp .ring{position:absolute; inset:8px; border:1px dashed var(--amber); border-radius:50%;}
.epb .stamp b{display:block; font-family:'Oswald',sans-serif; font-size:1.05rem; letter-spacing:0.06em; margin-top:4px;}

@media (max-width:760px){.epb .stamp{display:none;}
.epb }


.epb main{max-width:920px; margin:0 auto; padding:0 20px 60px;}
.epb .section{padding-top:64px;}
.epb .eyebrow{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; margin-bottom:10px; display:flex; align-items:center; gap:10px;}
.epb .eyebrow::after{content:""; flex:1; height:1px; background:var(--line);}
.epb h2.section-title{font-size:1.7rem; color:var(--off-white);}
.epb p{color:#DDE7EC;}
.epb .lead{color:var(--muted); font-size:1.02rem; max-width:72ch;}
.epb .panel{
  background:var(--panel); border:1px solid var(--line); border-radius:2px;
  padding:26px 28px; margin:22px 0;
}
.epb .panel.tp{border-left:4px solid var(--mint);}
.epb .panel.warn{border-left:4px solid var(--amber);}
.epb .tp-box{display:grid; gap:14px; margin-top:18px;}
.epb .tp-item{display:flex; gap:14px; align-items:flex-start;}
.epb .tp-item .idx{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; min-width:34px;}
.epb .flow{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; margin-top:20px;}
.epb .flow .step{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px; position:relative;}
.epb .flow .step .tag{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:0.7rem; letter-spacing:0.1em; text-transform:uppercase;}
.epb .flow .step h4{font-size:1rem; margin-top:8px; color:var(--off-white);}
.epb .flow .step .time{position:absolute; top:16px; right:16px; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; color:var(--amber);}
.epb .grid-3{display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:14px; margin-top:16px;}
.epb .card{background:var(--blueprint-deep); border:1px solid var(--line); padding:18px;}
.epb .card h4{color:var(--amber); font-size:0.95rem;}
.epb .criteria{display:grid; gap:16px; margin-top:20px;}
.epb .crit{
  background:var(--blueprint-deep); border:1px solid var(--line);
  display:grid; grid-template-columns:64px 1fr; gap:0;
}
.epb .crit .num{
  background:var(--amber); color:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace;
  font-weight:700; display:flex; align-items:center; justify-content:center; font-size:1.05rem;
}
.epb .crit .body{padding:18px 20px;}
.epb .crit h4{color:var(--off-white); font-size:1.02rem; margin-bottom:6px;}
.epb .crit .q{margin-top:10px; padding-top:10px; border-top:1px dashed var(--line); font-family:'IBM Plex Mono',monospace; font-size:0.86rem; color:var(--amber);}
.epb .timeline{margin-top:24px; position:relative; padding-left:34px;}
.epb .timeline::before{content:""; position:absolute; left:11px; top:6px; bottom:6px; width:2px; background:var(--line);}
.epb .tl-item{position:relative; padding-bottom:26px;}
.epb .tl-item:last-child{padding-bottom:0;}
.epb .tl-item::before{content:""; position:absolute; left:-34px; top:2px; width:22px; height:22px; border-radius:50%; background:var(--blueprint); border:2px solid var(--amber);}
.epb .tl-item h4{color:var(--off-white); font-size:1rem; margin-bottom:4px;}
.epb .doc-sample{
  background:var(--paper); color:var(--ink); border-radius:2px; padding:0; margin-top:22px;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .doc-sample .doc-head{background:var(--ink); color:var(--paper); padding:12px 22px; font-family:'IBM Plex Mono',monospace; font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; display:flex; justify-content:space-between;}
.epb .doc-sample .doc-body{padding:26px 28px 30px;}
.epb .doc-sample h3{font-family:'Oswald',sans-serif; color:var(--ink); font-size:1.25rem; text-transform:none; letter-spacing:0;}
.epb .doc-sample ol{padding-left:22px;}
.epb .doc-sample li{margin-bottom:9px;}
.epb .flag{
  display:inline-block; font-family:'IBM Plex Mono',monospace; font-size:0.7rem; font-weight:600;
  background:#D94E2A; color:#fff; padding:1px 7px; border-radius:2px; margin-left:6px; vertical-align:middle;
}
.epb .doc-sample .note-margin{margin-top:6px; font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:#8A3A1F; padding-left:8px; border-left:2px solid #D94E2A;}
.epb .legend{display:flex; flex-wrap:wrap; gap:10px 20px; margin-top:16px;}
.epb .legend span{font-family:'IBM Plex Mono',monospace; font-size:0.78rem; color:var(--muted); display:flex; align-items:center; gap:8px;}
.epb .legend .dot{width:10px; height:10px; border-radius:50%; display:inline-block;}
.epb table{width:100%; border-collapse:collapse; margin-top:18px; font-size:0.94rem;}
.epb th, .epb td{border:1px solid var(--line); padding:10px 12px; text-align:left; vertical-align:top;}
.epb th{background:var(--blueprint-deep); font-family:'IBM Plex Mono',monospace; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.06em; color:var(--amber);}
.epb .summary-box{
  border:1px solid var(--amber); background:linear-gradient(180deg, rgba(245,166,35,0.08), transparent);
  padding:24px 26px; margin-top:24px;
}
.epb .summary-box ul{margin:10px 0 0; padding-left:20px;}
.epb .summary-box li{margin-bottom:8px;}
.epb .task-header{
  background:var(--amber); color:var(--blueprint-deep); padding:22px 26px; margin-top:20px;
  display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;
}
.epb .task-header h2{color:var(--blueprint-deep); font-size:1.4rem; margin:0;}
.epb .task-header .badge{font-family:'IBM Plex Mono',monospace; font-size:0.8rem; background:var(--blueprint-deep); color:var(--amber); padding:6px 12px; border-radius:2px;}
.epb .steps-num{counter-reset:step; margin-top:18px; list-style:none; padding:0;}
.epb .steps-num li{counter-increment:step; position:relative; padding-left:44px; margin-bottom:16px;}
.epb .steps-num li::before{content:counter(step,decimal-leading-zero); position:absolute; left:0; top:0; font-family:'IBM Plex Mono',monospace; color:var(--amber); font-weight:600; font-size:0.95rem; border:1px solid var(--amber); width:30px; height:26px; display:flex; align-items:center; justify-content:center;}
.epb .rubric-scale{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:16px;}
.epb .rubric-scale div{background:var(--blueprint-deep); border:1px solid var(--line); padding:12px; text-align:center;}
.epb .rubric-scale .n{font-family:'IBM Plex Mono',monospace; color:var(--amber); font-size:1.3rem; font-weight:700;}
.epb .rubric-scale .l{font-size:0.78rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.05em;}
.epb .notebook{
  background:repeating-linear-gradient(var(--paper), var(--paper) 33px, #D8D3C2 34px);
  color:var(--ink); border-radius:2px; padding:28px 30px; margin-top:20px; position:relative;
  box-shadow:0 20px 45px rgba(0,0,0,0.35);
}
.epb .notebook::before{content:""; position:absolute; left:46px; top:0; bottom:0; width:1.5px; background:#C0453A; opacity:0.5;}
.epb .notebook .content{padding-left:24px;}
.epb .notebook h3{color:var(--ink); font-family:'Oswald',sans-serif; letter-spacing:0.02em; margin-bottom:10px;}
.epb .notebook ul{padding-left:20px;}
.epb footer{border-top:1px solid var(--line); padding:24px 20px 40px; color:var(--muted); font-size:0.86rem;}
.epb footer .doc-code{margin-bottom:10px;}
.epb .divider-dash{border:none; border-top:1px dashed var(--line); margin:44px 0 0;}
.epb::selection{background:var(--amber); color:var(--blueprint-deep);}

&lt;/style&gt;
&lt;div class="epb"&gt;
&lt;div class="crop tl"&gt;&lt;/div&gt;&lt;div class="crop tr"&gt;&lt;/div&gt;&lt;div class="crop bl"&gt;&lt;/div&gt;&lt;div class="crop br"&gt;&lt;/div&gt;

&lt;header class="cover"&gt;
  &lt;div class="stamp"&gt;&lt;div class="ring"&gt;&lt;/div&gt;&lt;span&gt;Latihan Soal&lt;b&gt;Tipe AKM&lt;/b&gt;Fase F&lt;/span&gt;&lt;/div&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/PROSEDUR-AKM — Latihan Soal · 50 Butir&lt;/div&gt;
  &lt;h1&gt;Latihan Soal Tipe AKM: Pokok Bahasan Petunjuk Kerja&lt;/h1&gt;
  &lt;p class="subtitle"&gt;Latihan soal literasi membaca bertipe Asesmen Kompetensi Minimum (AKM) untuk Bahasa Indonesia Kelas XI SMK/MAK, mencakup seluruh materi pokok bahasan Petunjuk Kerja: evaluasi informasi, perbandingan teks, kaidah kebahasaan, serta penyusunan dan penyajian teks prosedur.&lt;/p&gt;

  &lt;div class="meta-strip"&gt;
    &lt;div&gt;&lt;div class="k"&gt;Mata Pelajaran&lt;/div&gt;&lt;div class="v"&gt;Bahasa Indonesia&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Jenjang&lt;/div&gt;&lt;div class="v"&gt;SMK/MAK — Kelas XI&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;Jumlah Soal&lt;/div&gt;&lt;div class="v"&gt;50 Butir · 5 Opsi&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;div class="k"&gt;KKM&lt;/div&gt;&lt;div class="v"&gt;75 · 3 Kali Kesempatan&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;main&gt;

  &lt;section class="section" id="petunjuk"&gt;
    &lt;div class="eyebrow"&gt;01 — Petunjuk Pengerjaan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Sebelum Mengerjakan, Baca Dahulu&lt;/h2&gt;

    &lt;div class="panel tp"&gt;
      &lt;p style="margin:0;"&gt;Soal berikut disusun mengikuti pola &lt;b style="color:var(--off-white);"&gt;Asesmen Kompetensi Minimum (AKM)&lt;/b&gt;: setiap kelompok soal diawali sebuah &lt;b style="color:var(--off-white);"&gt;teks stimulus&lt;/b&gt; (teks prosedur atau teks eksplanasi), lalu diikuti beberapa pertanyaan yang menguji kemampuan &lt;b style="color:var(--off-white);"&gt;menemukan informasi, menginterpretasi, membandingkan, dan mengevaluasi&lt;/b&gt; isi teks tersebut — bukan sekadar menghafal definisi.&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="grid-3"&gt;
      &lt;div class="card"&gt;&lt;h4&gt;50 Soal &amp;amp; 5 Opsi&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;20 soal mudah, 21 soal sedang, 9 soal sulit — pilihan jawaban A sampai E.&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;KKM = 75&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Skor dihitung otomatis dari 50 soal (setiap soal bernilai 2 poin, maksimal 100).&lt;/p&gt;&lt;/div&gt;
      &lt;div class="card"&gt;&lt;h4&gt;3 Kali Kesempatan&lt;/h4&gt;&lt;p style="font-size:0.9rem;"&gt;Jika belum mencapai KKM, kamu dapat mencoba lagi hingga maksimal 3 kali percobaan.&lt;/p&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="panel warn" style="margin-top:20px;"&gt;
      &lt;p style="margin:0;"&gt;&lt;b style="color:var(--off-white);"&gt;Aturan pengiriman nilai:&lt;/b&gt; Begitu skor kamu mencapai atau melebihi KKM (75), nilai tersebut langsung dikirim dan tercatat. Jika hingga percobaan ke-3 belum ada yang mencapai KKM, sistem akan otomatis mengirim &lt;b style="color:var(--off-white);"&gt;nilai tertinggi&lt;/b&gt; dari ketiga percobaanmu. Isi No. Absen dengan angka yang sama di setiap percobaan agar sistem dapat menghitung kesempatanmu dengan benar.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;!-- ============ PETUNJUK MENGERJAKAN ============ --&gt;
  &lt;section class="section" id="cara-mengerjakan"&gt;
    &lt;div class="eyebrow"&gt;02 — Langkah Pengerjaan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Cara Mengerjakan Latihan Ini&lt;/h2&gt;

    &lt;ol class="steps-num"&gt;
      &lt;li&gt;&lt;b&gt;Isi No. Absen dan Nama Lengkap&lt;/b&gt; pada kolom yang tersedia sebelum mulai menjawab. Gunakan No. Absen yang sama setiap kali mencoba, karena sistem memakainya untuk menghitung sisa kesempatan.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Baca teks stimulus&lt;/b&gt; (teks prosedur atau teks eksplanasi) yang muncul sebelum setiap kelompok soal dengan saksama, sebelum menjawab pertanyaan di bawahnya.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Jawab seluruh 50 soal&lt;/b&gt; dengan memilih salah satu opsi A–E. Perhatikan label tingkat kesulitan (Mudah/Sedang/Sulit) pada tiap soal sebagai gambaran bobotnya.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Tekan tombol "Kirim Jawaban"&lt;/b&gt; setelah semua soal terjawab. Jika masih ada yang kosong, sistem akan menunjukkan nomor yang belum dijawab.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Lihat skor yang muncul.&lt;/b&gt; Jika skor sudah mencapai KKM (75), nilai otomatis terkirim dan latihan selesai.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Jika skor belum mencapai KKM&lt;/b&gt;, tekan tombol "Coba Lagi" untuk mengulang (maksimal 3 kali percobaan). Jawaban akan direset, tetapi teks stimulus dan soal tetap sama.&lt;/li&gt;
      &lt;li&gt;&lt;b&gt;Setelah selesai&lt;/b&gt; (baik karena sudah mencapai KKM maupun kesempatan sudah habis), tunjukkan skor yang tertera di layar kepada guru sebagai bukti pengerjaan.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/section&gt;

  &lt;hr class="divider-dash"&gt;

  &lt;section class="section" id="kuis"&gt;
    &lt;div class="eyebrow"&gt;03 — Lembar Soal&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Kerjakan 50 Soal Berikut&lt;/h2&gt;

    &lt;div class="panel" id="epbq-form-wrap" style="margin-top:20px;"&gt;
      &lt;div style="display:grid; grid-template-columns:1fr 2fr; gap:14px; margin-bottom:10px;"&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;No. Absen&lt;/label&gt;
          &lt;input id="epbq-absen" type="text" inputmode="numeric" placeholder="Contoh: 12" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label class="mono" style="display:block; font-size:0.75rem; color:var(--amber); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:6px;"&gt;Nama Lengkap&lt;/label&gt;
          &lt;input id="epbq-nama" type="text" placeholder="Tulis nama lengkap" style="width:100%; padding:10px 12px; border-radius:2px; border:1px solid var(--line); background:var(--blueprint-deep); color:var(--off-white); font-family:'Source Sans 3',sans-serif; font-size:0.95rem;"&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;p id="epbq-attempt-info" class="mono" style="font-size:0.8rem; color:var(--muted); margin:4px 0 18px;"&gt;Masukkan No. Absen untuk melihat sisa kesempatan.&lt;/p&gt;

      &lt;div id="epbq-questions"&gt;&lt;/div&gt;

      &lt;div style="margin-top:24px; display:flex; align-items:center; gap:16px; flex-wrap:wrap;"&gt;
        &lt;button id="epbq-submit" type="button" style="background:var(--amber); color:var(--blueprint-deep); border:none; padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Kirim Jawaban&lt;/button&gt;
        &lt;button id="epbq-retry" type="button" style="display:none; background:transparent; color:var(--amber); border:1px solid var(--amber); padding:12px 26px; font-family:'Oswald',sans-serif; text-transform:uppercase; letter-spacing:0.05em; font-weight:600; border-radius:2px; cursor:pointer; font-size:0.95rem;"&gt;Coba Lagi&lt;/button&gt;
        &lt;span id="epbq-status" class="mono" style="font-size:0.85rem; color:var(--muted);"&gt;&lt;/span&gt;
      &lt;/div&gt;

      &lt;div id="epbq-result" style="display:none; margin-top:20px; padding:18px 20px; border:1px solid var(--amber); background:rgba(245,166,35,0.08);"&gt;
        &lt;p style="margin:0; font-size:1.05rem;"&gt;Skor percobaan ini: &lt;b id="epbq-score" style="color:var(--amber); font-size:1.3rem;"&gt;&lt;/b&gt; / 100&lt;/p&gt;
        &lt;p id="epbq-detail" style="margin:8px 0 0; font-size:0.9rem; color:var(--muted);"&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="doc-code"&gt;DOK/B.INDO/XI/PROSEDUR-AKM — Latihan Soal Tipe AKM, 50 Butir, Pokok Bahasan Petunjuk Kerja&lt;/div&gt;
  &lt;p style="margin:0;"&gt;Referensi: Kurikulum Merdeka — Capaian Pembelajaran Bahasa Indonesia Fase F; Pusat Asesmen dan Pembelajaran Kemendikbudristek — format Asesmen Kompetensi Minimum (AKM) Literasi Membaca.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
(function(){
  var EPBQ_ENDPOINT = "https://script.google.com/macros/s/AKfycbzM7t2qgAENzxQJXzSYJYL28R6nb3TxJVELLL3CopyEXb51t_gRtrkPOxTpI5mq6VCEKw/exec";
  var EPBQ_MATERI = "Latihan Soal AKM - Pokok Bahasan Petunjuk Kerja (50 Soal)";
  var KKM = 75;
  var MAX_ATTEMPTS = 3;

  var stimuli = [{"id": "A", "judul": "Cara Membuat Teh Hangat", "jenis": "Teks Prosedur", "teks": "Teh hangat adalah minuman yang mudah dibuat dan cocok dinikmati kapan saja. Berikut langkah membuatnya. Pertama, siapkan gelas dan satu kantong teh. Kedua, panaskan air hingga mendidih. Ketiga, masukkan kantong teh ke dalam gelas, lalu tuangkan air panas ke dalamnya. Setelah itu, tambahkan gula sesuai selera. Aduk hingga rata, dan teh hangat siap dinikmati."}, {"id": "B", "judul": "Prosedur Mengganti Baterai Remote TV", "jenis": "Teks Prosedur", "teks": "Remote TV yang tidak merespons biasanya disebabkan baterai yang sudah lemah. Berikut cara menggantinya. Buka penutup baterai di bagian belakang remote dengan menggeser atau menekannya sesuai petunjuk. Keluarkan baterai lama, lalu perhatikan tanda kutub positif (+) dan negatif (–) pada dudukan baterai. Pasang baterai baru sesuai posisi kutub tersebut. Tutup kembali penutup baterai hingga rapat, lalu coba tekan salah satu tombol untuk memastikan remote berfungsi."}, {"id": "C", "judul": "Proses Terjadinya Karat pada Besi", "jenis": "Teks Eksplanasi", "teks": "Karat adalah lapisan berwarna cokelat kemerahan yang muncul pada permukaan besi. Karat terjadi karena besi bereaksi dengan oksigen dan air melalui proses yang disebut oksidasi. Ketika permukaan besi terkena udara lembap dalam waktu lama, oksigen akan berikatan dengan atom besi membentuk senyawa besi oksida. Semakin lama besi terpapar kelembapan, semakin tebal lapisan karat yang terbentuk. Oleh karena itu, besi yang disimpan di tempat lembap akan lebih cepat berkarat dibandingkan besi yang disimpan di tempat kering."}, {"id": "D", "judul": "Prosedur Menyalakan Genset Portabel", "jenis": "Teks Prosedur", "teks": "Genset portabel berguna sebagai sumber listrik cadangan saat listrik padam. Berikut langkah menyalakannya. Pertama, letakkan genset di ruangan tertutup agar terhindar dari hujan. Kedua, periksa jumlah bahan bakar pada tangki genset. Ketiga, tekan tombol starter hingga mesin menyala. Keempat, tunggu beberapa menit sebelum menyambungkan kabel listrik ke genset. Genset kini siap digunakan untuk menyalakan peralatan listrik di rumah."}, {"id": "E", "judul": "Prosedur Membersihkan Filter AC Rumah secara Mandiri", "jenis": "Teks Prosedur", "teks": "Filter AC yang kotor dapat menurunkan kualitas udara dan kinerja pendinginan. Berikut cara membersihkannya. Matikan AC dan cabut sumber listriknya terlebih dahulu. Buka penutup unit indoor AC, lalu lepaskan filter udara secara perlahan. Cuci filter menggunakan air mengalir hingga debu dan kotoran luruh. Keringkan filter di tempat teduh, jangan di bawah sinar matahari langsung agar filter tidak melengkung. Pasang kembali filter setelah benar-benar kering, lalu nyalakan kembali AC seperti biasa."}, {"id": "F", "judul": "Proses Terjadinya Embun di Pagi Hari", "jenis": "Teks Eksplanasi", "teks": "Embun adalah titik-titik air yang muncul pada permukaan benda di pagi hari. Embun terbentuk karena udara di malam hari mengandung uap air yang mendingin seiring turunnya suhu. Ketika suhu udara turun hingga mencapai titik embun, uap air akan mengembun menjadi butiran air kecil pada permukaan benda seperti daun atau rumput. Proses ini biasanya terjadi pada malam hari yang cerah tanpa awan, karena panas bumi lebih cepat terlepas ke atmosfer. Dengan demikian, embun paling sering muncul pada pagi hari yang dingin dan cerah."}, {"id": "G", "judul": "Prosedur Mendaftar Akun Email Baru", "jenis": "Teks Prosedur", "teks": "Memiliki akun email sangat penting untuk berbagai keperluan digital. Berikut langkah mendaftarnya. Buka situs penyedia layanan email, lalu pilih menu 'Buat Akun Baru'. Isi formulir pendaftaran dengan nama lengkap, tanggal lahir, dan nomor telepon aktif. Selanjutnya, buatlah kata sandi yang kuat dengan kombinasi huruf, angka, dan simbol. Perlu diperhatikan, jangan gunakan kata sandi yang sama dengan akun media sosial lain agar lebih aman. Setelah semua data terisi, klik tombol 'Daftar' dan verifikasi akun melalui kode yang dikirim ke nomor telepon."}, {"id": "H", "judul": "Memilah Sampah Rumah Tangga (Sepasang Teks)", "jenis": "Teks Prosedur &amp; Teks Eksplanasi", "teks": "&lt;b&gt;Teks 1 — Prosedur Memilah Sampah Rumah Tangga:&lt;/b&gt; Memilah sampah bertujuan memudahkan proses daur ulang. Sediakan tiga wadah berbeda untuk sampah organik, anorganik, dan B3 (bahan berbahaya beracun). Pisahkan sisa makanan dan daun ke wadah organik. Masukkan plastik, kertas, dan logam ke wadah anorganik. Simpan baterai bekas dan lampu pecah pada wadah B3 secara terpisah. Buang sampah organik setiap hari, sedangkan sampah anorganik dapat dikumpulkan hingga penuh sebelum dibawa ke bank sampah.&lt;br&gt;&lt;br&gt;&lt;b&gt;Teks 2 — Proses Terjadinya Pencemaran akibat Sampah yang Tidak Dipilah:&lt;/b&gt; Sampah yang tidak dipilah akan bercampur antara bahan organik dan anorganik, sehingga proses penguraiannya menjadi lebih lambat. Sampah anorganik seperti plastik membutuhkan waktu ratusan tahun untuk terurai, sehingga menumpuk di tempat pembuangan akhir. Akibatnya, lahan yang tersedia untuk menampung sampah semakin berkurang. Selain itu, zat kimia dari sampah B3 yang tercampur dapat meresap ke dalam tanah dan mencemari sumber air di sekitarnya. Oleh karena itu, pemilahan sampah sejak dari rumah tangga menjadi langkah penting untuk mengurangi dampak pencemaran lingkungan."}];
  var questions = [{"stim": "A", "lvl": "Mudah", "q": "Apa tujuan utama teks prosedur tersebut?", "opts": ["Menjelaskan sejarah teh", "Membuat teh hangat siap diminum", "Membandingkan jenis teh", "Menceritakan pengalaman minum teh", "Menjual produk teh"], "correct": 1}, {"stim": "A", "lvl": "Mudah", "q": "Alat/bahan pertama yang disiapkan dalam teks adalah...", "opts": ["Gula dan sendok", "Air dan panci", "Gelas dan kantong teh", "Termos dan cangkir", "Susu dan kopi"], "correct": 2}, {"stim": "A", "lvl": "Mudah", "q": "Kata “setelah itu” pada teks berfungsi sebagai...", "opts": ["Konjungsi kausalitas", "Konjungsi temporal", "Konjungsi pertentangan", "Konjungsi pilihan", "Konjungsi penjumlahan"], "correct": 1}, {"stim": "A", "lvl": "Mudah", "q": "Kalimat “Aduk hingga rata” termasuk kalimat...", "opts": ["Deklaratif", "Interogatif", "Imperatif", "Persuasif", "Naratif"], "correct": 2}, {"stim": "B", "lvl": "Mudah", "q": "Menurut teks, apa penyebab remote TV tidak merespons?", "opts": ["Remote rusak permanen", "Baterai yang sudah lemah", "TV mati total", "Sinyal remote terganggu", "Remote basah"], "correct": 1}, {"stim": "B", "lvl": "Mudah", "q": "Hal yang harus diperhatikan saat memasang baterai baru adalah...", "opts": ["Warna baterai", "Ukuran remote", "Posisi kutub positif dan negatif", "Merek baterai", "Suhu ruangan"], "correct": 2}, {"stim": "B", "lvl": "Sedang", "q": "Langkah terakhir dalam prosedur tersebut adalah...", "opts": ["Membuka penutup baterai", "Mengeluarkan baterai lama", "Menutup penutup baterai", "Menekan tombol untuk memastikan remote berfungsi", "Membeli baterai baru"], "correct": 3}, {"stim": "B", "lvl": "Sedang", "q": "Mengapa penting memperhatikan tanda kutub baterai saat memasang?", "opts": ["Agar baterai terlihat rapi", "Agar remote berfungsi dengan benar", "Agar baterai lebih awet warnanya", "Karena aturan pabrik semata", "Tidak ada alasan khusus"], "correct": 1}, {"stim": "C", "lvl": "Mudah", "q": "Apa penyebab utama terjadinya karat pada besi menurut teks?", "opts": ["Suhu udara yang panas", "Reaksi besi dengan oksigen dan air", "Sinar matahari langsung", "Getaran mekanis", "Perubahan warna alami"], "correct": 1}, {"stim": "C", "lvl": "Sedang", "q": "Teks tersebut termasuk jenis teks eksplanasi karena...", "opts": ["Berisi langkah-langkah tindakan", "Menjelaskan proses terjadinya suatu fenomena", "Berisi cerita fiksi", "Berisi opini penulis", "Berisi iklan produk"], "correct": 1}, {"stim": "C", "lvl": "Sedang", "q": "Konjungsi “oleh karena itu” pada akhir teks berfungsi sebagai penanda...", "opts": ["Urutan", "Penambahan", "Penekanan", "Simpulan", "Pertentangan"], "correct": 3}, {"stim": "C", "lvl": "Mudah", "q": "Berdasarkan teks, besi akan lebih cepat berkarat jika disimpan di tempat...", "opts": ["Kering", "Lembap", "Dingin", "Bersuhu tinggi", "Tertutup rapat"], "correct": 1}, {"stim": "C", "lvl": "Sedang", "q": "Perbedaan mendasar teks ini dengan teks prosedur adalah...", "opts": ["Teks ini lebih pendek", "Teks ini bertujuan membuat pembaca memahami, bukan melakukan tindakan", "Teks ini tidak memiliki struktur", "Teks ini tidak menggunakan konjungsi", "Teks ini tidak informatif"], "correct": 1}, {"stim": "C", "lvl": "Sedang", "q": "Jika besi disimpan di tempat kering, berdasarkan informasi teks, proses karat akan...", "opts": ["Berlangsung lebih cepat", "Berlangsung lebih lambat", "Tidak berpengaruh sama sekali", "Terjadi secara instan", "Berubah menjadi proses lain"], "correct": 1}, {"stim": "D", "lvl": "Mudah", "q": "Apa tujuan dari prosedur tersebut?", "opts": ["Mematikan genset", "Menyalakan genset sebagai sumber listrik cadangan", "Memperbaiki genset rusak", "Menjual genset", "Menyimpan genset"], "correct": 1}, {"stim": "D", "lvl": "Mudah", "q": "Berapa langkah yang dijelaskan dalam prosedur tersebut?", "opts": ["2 langkah", "3 langkah", "4 langkah", "5 langkah", "6 langkah"], "correct": 2}, {"stim": "D", "lvl": "Sulit", "q": "Instruksi meletakkan genset di ruangan tertutup berpotensi membahayakan karena...", "opts": ["Genset akan cepat rusak", "Risiko keracunan gas karbon monoksida", "Suara genset menjadi lebih keras", "Genset akan kepanasan lebih cepat", "Bahan bakar lebih boros"], "correct": 1}, {"stim": "D", "lvl": "Sulit", "q": "Kekeliruan pada langkah pertama tersebut melanggar kriteria evaluasi teks prosedur, yaitu...", "opts": ["Keakuratan istilah", "Kelengkapan alat", "Keselamatan dan kelayakan praktik", "Kejelasan bahasa", "Kesesuaian judul"], "correct": 2}, {"stim": "D", "lvl": "Sulit", "q": "Rekomendasi perbaikan yang paling tepat untuk langkah pertama adalah...", "opts": ["Genset diletakkan di ruang terbuka atau berventilasi baik", "Genset dinyalakan tanpa bahan bakar", "Genset diletakkan di dalam lemari", "Genset dinyalakan di dalam kamar tidur", "Tidak perlu ada perubahan"], "correct": 0}, {"stim": "D", "lvl": "Mudah", "q": "Kalimat “Periksa jumlah bahan bakar pada tangki genset” termasuk kalimat...", "opts": ["Deklaratif", "Interogatif", "Imperatif", "Persuasif", "Naratif"], "correct": 2}, {"stim": "D", "lvl": "Sedang", "q": "Apakah urutan langkah dalam teks tersebut sudah logis?", "opts": ["Sudah, karena seluruh langkah aman dan berurutan", "Belum, karena langkah pertama membahayakan keselamatan meskipun urutan waktunya runtut", "Sudah, karena tidak ada langkah yang hilang", "Belum, karena tidak menggunakan bahasa baku", "Sudah, karena menggunakan konjungsi temporal"], "correct": 1}, {"stim": "D", "lvl": "Sedang", "q": "Selain keselamatan, aspek apa yang juga perlu diperiksa ulang agar prosedur ini benar-benar aman diikuti pembaca?", "opts": ["Kelengkapan langkah, misalnya instruksi soal ventilasi ruangan", "Jumlah huruf dalam judul", "Warna genset yang digunakan", "Harga bahan bakar", "Merek genset yang disebutkan"], "correct": 0}, {"stim": "E", "lvl": "Mudah", "q": "Apa tujuan prosedur membersihkan filter AC tersebut?", "opts": ["Mengganti unit AC", "Menjaga kualitas udara dan kinerja pendinginan", "Menghemat listrik semata", "Mengecat unit AC", "Memindahkan posisi AC"], "correct": 1}, {"stim": "E", "lvl": "Sedang", "q": "Mengapa filter harus dikeringkan di tempat teduh, bukan di bawah sinar matahari langsung?", "opts": ["Agar warnanya tidak pudar", "Agar filter tidak melengkung atau rusak", "Agar proses lebih cepat", "Karena aturan pabrik tanpa alasan teknis", "Agar debu tidak menempel"], "correct": 1}, {"stim": "E", "lvl": "Mudah", "q": "Kata “unit indoor” dan “filter” pada teks termasuk...", "opts": ["Konjungsi", "Kata teknis", "Kata ganti", "Kata sifat", "Kata seru"], "correct": 1}, {"stim": "E", "lvl": "Sedang", "q": "Kalimat “Matikan AC dan cabut sumber listriknya terlebih dahulu” termasuk kalimat perintah jenis...", "opts": ["Larangan", "Ajakan", "Perintah biasa", "Permintaan", "Persuasif"], "correct": 2}, {"stim": "E", "lvl": "Sedang", "q": "Manakah langkah yang menunjukkan alasan keselamatan sebelum membersihkan filter?", "opts": ["Mencuci filter dengan air mengalir", "Mematikan AC dan mencabut sumber listrik", "Mengeringkan filter di tempat teduh", "Memasang kembali filter", "Menyalakan kembali AC"], "correct": 1}, {"stim": "E", "lvl": "Mudah", "q": "Kalimat “Filter AC yang kotor dapat menurunkan kualitas udara...” termasuk bagian struktur...", "opts": ["Langkah-langkah", "Tujuan", "Alat dan bahan", "Penutup", "Interpretasi"], "correct": 1}, {"stim": "F", "lvl": "Mudah", "q": "Apa yang menyebabkan terbentuknya embun menurut teks?", "opts": ["Hujan yang turun di malam hari", "Penurunan suhu udara hingga titik embun", "Angin kencang di pagi hari", "Sinar matahari pagi", "Kelembapan tanah"], "correct": 1}, {"stim": "F", "lvl": "Sedang", "q": "Penanda wacana “dengan demikian” pada akhir teks berfungsi sebagai penanda...", "opts": ["Urutan", "Penambahan", "Penekanan", "Simpulan", "Pertentangan"], "correct": 3}, {"stim": "F", "lvl": "Mudah", "q": "Berdasarkan teks, embun paling sering muncul pada kondisi cuaca...", "opts": ["Berawan tebal", "Hujan deras", "Cerah tanpa awan", "Berkabut", "Berangin kencang"], "correct": 2}, {"stim": "F", "lvl": "Sedang", "q": "Konjungsi “karena” dalam teks tersebut menunjukkan hubungan...", "opts": ["Pertentangan", "Sebab-akibat", "Pilihan", "Waktu", "Penjumlahan"], "correct": 1}, {"stim": "F", "lvl": "Sedang", "q": "Apa perbedaan fungsi konjungsi kausalitas pada teks ini dengan konjungsi temporal pada teks prosedur?", "opts": ["Keduanya memiliki fungsi yang sama persis", "Kausalitas menjelaskan sebab-akibat proses alami, temporal menandai urutan langkah tindakan", "Kausalitas hanya dipakai dalam teks prosedur", "Temporal hanya dipakai dalam teks eksplanasi", "Keduanya tidak memiliki fungsi apa pun"], "correct": 1}, {"stim": "F", "lvl": "Sulit", "q": "Jika judul teks diubah menjadi “Cara Membuat Embun Buatan”, jenis teks tersebut kemungkinan besar akan berubah menjadi...", "opts": ["Teks deskripsi", "Teks prosedur", "Teks narasi", "Teks anekdot", "Tetap teks eksplanasi"], "correct": 1}, {"stim": "G", "lvl": "Mudah", "q": "Apa tujuan dari prosedur tersebut?", "opts": ["Menghapus akun email", "Mendaftar akun email baru", "Mengganti kata sandi", "Mengirim email", "Menutup akun media sosial"], "correct": 1}, {"stim": "G", "lvl": "Sedang", "q": "Frasa “perlu diperhatikan” pada teks berfungsi sebagai penanda wacana jenis...", "opts": ["Urutan", "Penambahan", "Penekanan", "Simpulan", "Pertentangan"], "correct": 2}, {"stim": "G", "lvl": "Mudah", "q": "Kata “selanjutnya” pada teks menandai...", "opts": ["Sebab-akibat", "Urutan langkah", "Pertentangan", "Pilihan", "Penekanan"], "correct": 1}, {"stim": "G", "lvl": "Sedang", "q": "Mengapa penulis menyarankan agar tidak menggunakan kata sandi yang sama dengan akun lain?", "opts": ["Agar mudah diingat", "Demi keamanan akun", "Agar proses pendaftaran lebih cepat", "Karena aturan pabrik", "Tidak ada alasan khusus"], "correct": 1}, {"stim": "G", "lvl": "Sulit", "q": "Jika seseorang mengikuti prosedur tersebut namun melewatkan langkah verifikasi kode, apa kemungkinan yang terjadi?", "opts": ["Akun langsung terhapus", "Akun belum aktif sepenuhnya", "Tidak terjadi apa-apa", "Kata sandi otomatis berubah", "Proses pendaftaran diulang dari awal secara otomatis"], "correct": 1}, {"stim": "G", "lvl": "Sedang", "q": "Bagian manakah dari teks yang termasuk kalimat persuasif?", "opts": ["“Buka situs penyedia layanan email”", "“Isi formulir pendaftaran dengan nama lengkap”", "“Perlu diperhatikan, jangan gunakan kata sandi yang sama dengan akun media sosial lain agar lebih aman”", "“Klik tombol Daftar”", "“Verifikasi akun melalui kode”"], "correct": 2}, {"stim": "H", "lvl": "Sedang", "q": "Apa persamaan mendasar antara kedua teks tersebut?", "opts": ["Keduanya bersifat fiksi", "Keduanya sama-sama menjelaskan proses/tahapan terkait sampah", "Keduanya tidak memiliki struktur", "Keduanya ditulis dalam bentuk puisi", "Keduanya tidak menggunakan konjungsi"], "correct": 1}, {"stim": "H", "lvl": "Sedang", "q": "Apa perbedaan tujuan utama kedua teks tersebut?", "opts": ["Teks 1 menjelaskan proses, Teks 2 mengarahkan tindakan", "Teks 1 mengarahkan tindakan, Teks 2 menjelaskan proses/dampak", "Keduanya bertujuan menghibur", "Keduanya bertujuan mempromosikan produk", "Keduanya tidak memiliki tujuan jelas"], "correct": 1}, {"stim": "H", "lvl": "Mudah", "q": "Manakah kalimat pada Teks 1 yang merupakan kalimat perintah?", "opts": ["“Memilah sampah bertujuan memudahkan proses daur ulang.”", "“Pisahkan sisa makanan dan daun ke wadah organik.”", "“Sampah yang tidak dipilah akan bercampur.”", "“Plastik membutuhkan waktu ratusan tahun untuk terurai.”", "“Pemilahan sampah menjadi langkah penting.”"], "correct": 1}, {"stim": "H", "lvl": "Sedang", "q": "Konjungsi “sehingga” pada Teks 2 menunjukkan hubungan...", "opts": ["Pertentangan", "Sebab-akibat", "Pilihan", "Waktu", "Penambahan"], "correct": 1}, {"stim": "H", "lvl": "Sedang", "q": "Berdasarkan Teks 2, apa dampak jangka panjang jika sampah tidak dipilah dari rumah tangga?", "opts": ["Sampah otomatis berkurang", "Lahan TPA berkurang dan sumber air tercemar", "Tidak ada dampak apa pun", "Sampah berubah menjadi kompos secara alami", "Proses daur ulang menjadi lebih cepat"], "correct": 1}, {"stim": "H", "lvl": "Sulit", "q": "Jika Teks 1 diikuti dengan benar oleh masyarakat, dampak yang dijelaskan pada Teks 2 kemungkinan akan...", "opts": ["Semakin parah", "Tidak berubah sama sekali", "Berkurang atau dapat dicegah", "Berubah menjadi masalah baru", "Tidak ada hubungannya sama sekali"], "correct": 2}, {"stim": "H", "lvl": "Sulit", "q": "Manakah pernyataan yang paling tepat menggambarkan hubungan antara kedua teks?", "opts": ["Teks 1 adalah solusi/tindakan pencegahan atas masalah yang dijelaskan Teks 2", "Teks 2 adalah solusi atas Teks 1", "Kedua teks membahas topik yang sama sekali berbeda", "Teks 1 dan Teks 2 saling bertentangan", "Teks 1 tidak berkaitan dengan Teks 2"], "correct": 0}, {"stim": "H", "lvl": "Mudah", "q": "Kata “B3” pada Teks 1 merupakan contoh dari...", "opts": ["Konjungsi", "Kata teknis/istilah khusus", "Kata ganti", "Kata sifat", "Kata seru"], "correct": 1}, {"stim": "H", "lvl": "Sulit", "q": "Apabila ingin mengevaluasi kelengkapan Teks 1, aspek apa yang paling perlu diperiksa?", "opts": ["Apakah seluruh jenis sampah rumah tangga sudah tercakup dalam tiga kategori wadah tersebut", "Apakah judul teks menarik", "Apakah teks menggunakan huruf kapital", "Apakah teks memiliki gambar", "Apakah teks ditulis dengan rapi"], "correct": 0}, {"stim": "H", "lvl": "Sulit", "q": "Berdasarkan kedua teks, simpulan yang paling tepat tentang pentingnya teks prosedur dalam menyelesaikan masalah lingkungan adalah...", "opts": ["Teks prosedur tidak ada kaitannya dengan masalah lingkungan", "Teks prosedur berperan sebagai panduan tindakan nyata untuk mencegah dampak negatif yang dijelaskan dalam teks eksplanasi", "Teks eksplanasi lebih penting daripada teks prosedur", "Teks prosedur hanya berguna untuk keperluan akademik", "Kedua jenis teks tidak memiliki hubungan sama sekali"], "correct": 1}];

  var optLabels = ["A","B","C","D","E"];
  var lvlColor = {"Mudah":"#6EC6CA","Sedang":"#F5A623","Sulit":"#D94E2A"};

  var container = document.getElementById("epbq-questions");
  var currentStim = null;
  var qCounter = 0;

  questions.forEach(function(item, idx){
    if(item.stim !== currentStim){
      currentStim = item.stim;
      var stimObj = stimuli.filter(function(s){ return s.id === currentStim; })[0];
      if(stimObj){
        var docSample = document.createElement("div");
        docSample.className = "doc-sample";
        docSample.style.margin = "24px 0 18px";
        docSample.innerHTML =
          '&lt;div class="doc-head"&gt;&lt;span&gt;Teks Stimulus \u00b7 ' + stimObj.jenis + '&lt;/span&gt;&lt;span&gt;Stimulus ' + stimObj.id + '&lt;/span&gt;&lt;/div&gt;' +
          '&lt;div class="doc-body"&gt;&lt;h3 style="color:#132029 !important;"&gt;' + stimObj.judul + '&lt;/h3&gt;' +
          '&lt;p style="color:#132029 !important; margin:8px 0 0;"&gt;' + stimObj.teks + '&lt;/p&gt;&lt;/div&gt;';
        container.appendChild(docSample);
      }
    }

    qCounter++;
    var block = document.createElement("div");
    block.style.marginBottom = "20px";
    block.style.paddingBottom = "16px";
    block.style.borderBottom = "1px dashed rgba(234,242,245,0.16)";

    var tag = document.createElement("span");
    tag.textContent = item.lvl;
    tag.style.display = "inline-block";
    tag.style.fontFamily = "'IBM Plex Mono', monospace";
    tag.style.fontSize = "0.68rem";
    tag.style.letterSpacing = "0.08em";
    tag.style.textTransform = "uppercase";
    tag.style.setProperty("color", lvlColor[item.lvl] || "#9FB4C2", "important");
    tag.style.setProperty("border", "1px solid " + (lvlColor[item.lvl] || "#9FB4C2"), "important");
    tag.style.padding = "2px 8px";
    tag.style.marginBottom = "6px";
    block.appendChild(tag);

    var qEl = document.createElement("p");
    qEl.style.setProperty("color", "#EAF2F5", "important");
    qEl.style.fontWeight = "600";
    qEl.style.margin = "6px 0 8px";
    qEl.textContent = qCounter + ". " + item.q;
    block.appendChild(qEl);

    item.opts.forEach(function(opt, oi){
      var label = document.createElement("label");
      label.style.display = "block";
      label.style.setProperty("color", "#DDE7EC", "important");
      label.style.fontSize = "0.92rem";
      label.style.margin = "4px 0";
      label.style.cursor = "pointer";

      var radio = document.createElement("input");
      radio.type = "radio";
      radio.name = "epbq-q" + idx;
      radio.value = oi;
      radio.style.marginRight = "8px";

      label.appendChild(radio);
      label.appendChild(document.createTextNode(optLabels[oi] + ". " + opt));
      block.appendChild(label);
    });

    container.appendChild(block);
  });

  function stateKey(absen){ return "epbq_akm_" + absen; }
  function loadState(absen){
    try{
      var raw = localStorage.getItem(stateKey(absen));
      if(!raw) return {attempts:0, bestScore:0, sent:false};
      return JSON.parse(raw);
    }catch(e){ return {attempts:0, bestScore:0, sent:false}; }
  }
  function saveState(absen, state){
    try{ localStorage.setItem(stateKey(absen), JSON.stringify(state)); }catch(e){}
  }

  var absenInput = document.getElementById("epbq-absen");
  var attemptInfo = document.getElementById("epbq-attempt-info");
  var submitBtn = document.getElementById("epbq-submit");
  var retryBtn = document.getElementById("epbq-retry");

  function refreshAttemptInfo(){
    var absen = absenInput.value.trim();
    if(!absen){
      attemptInfo.textContent = "Masukkan No. Absen untuk melihat sisa kesempatan.";
      submitBtn.style.display = "inline-block";
      retryBtn.style.display = "none";
      return;
    }
    var st = loadState(absen);
    if(st.sent){
      attemptInfo.textContent = "No. Absen " + absen + " sudah menyelesaikan latihan ini. Nilai tercatat: " + st.bestScore + ".";
      submitBtn.style.display = "none";
      retryBtn.style.display = "none";
    } else {
      attemptInfo.textContent = "No. Absen " + absen + " — kesempatan tersisa: " + (MAX_ATTEMPTS - st.attempts) + " dari " + MAX_ATTEMPTS + ".";
      submitBtn.style.display = "inline-block";
      retryBtn.style.display = "none";
    }
  }
  absenInput.addEventListener("input", refreshAttemptInfo);
  refreshAttemptInfo();

  function resetAnswers(){
    var radios = container.querySelectorAll('input[type="radio"]');
    radios.forEach(function(r){ r.checked = false; });
    window.scrollTo({top: document.getElementById("epbq-questions").offsetTop - 100, behavior:"smooth"});
  }

  retryBtn.addEventListener("click", function(){
    resetAnswers();
    document.getElementById("epbq-result").style.display = "none";
    retryBtn.style.display = "none";
    submitBtn.style.display = "inline-block";
    document.getElementById("epbq-status").textContent = "";
  });

  submitBtn.addEventListener("click", function(){
    var absen = absenInput.value.trim();
    var nama = document.getElementById("epbq-nama").value.trim();
    var statusEl = document.getElementById("epbq-status");

    if(!absen || !nama){
      statusEl.textContent = "Isi No. Absen dan Nama terlebih dahulu.";
      statusEl.style.color = "#F5A623";
      return;
    }

    var st = loadState(absen);
    if(st.sent){
      statusEl.textContent = "No. Absen ini sudah menyelesaikan latihan dan nilainya sudah tercatat.";
      statusEl.style.color = "#F5A623";
      return;
    }
    if(st.attempts &gt;= MAX_ATTEMPTS){
      statusEl.textContent = "Kesempatan sudah habis.";
      statusEl.style.color = "#F5A623";
      return;
    }

    var belumDijawab = [];
    var benar = 0;
    questions.forEach(function(item, idx){
      var checked = document.querySelector('input[name="epbq-q' + idx + '"]:checked');
      if(!checked){
        belumDijawab.push(idx+1);
      } else if(parseInt(checked.value,10) === item.correct){
        benar++;
      }
    });

    if(belumDijawab.length &gt; 0){
      statusEl.textContent = "Masih ada " + belumDijawab.length + " soal yang belum dijawab.";
      statusEl.style.color = "#F5A623";
      return;
    }

    var skor = benar * 2;
    st.attempts += 1;
    st.bestScore = Math.max(st.bestScore, skor);
    saveState(absen, st);

    document.getElementById("epbq-score").textContent = skor;
    document.getElementById("epbq-result").style.display = "block";

    var sisa = MAX_ATTEMPTS - st.attempts;

    if(skor &gt;= KKM){
      document.getElementById("epbq-detail").textContent = "Menjawab benar " + benar + " dari 50 soal. Skor mencapai KKM (75) \u2014 nilai ini akan dikirim ke guru.";
      kirimNilai(absen, nama, skor);
      st.sent = true;
      saveState(absen, st);
      submitBtn.style.display = "none";
      retryBtn.style.display = "none";
    } else if(sisa &gt; 0){
      document.getElementById("epbq-detail").textContent = "Menjawab benar " + benar + " dari 50 soal. Skor belum mencapai KKM (75). Kesempatan tersisa: " + sisa + ".";
      submitBtn.style.display = "none";
      retryBtn.style.display = "inline-block";
      statusEl.textContent = "";
    } else {
      document.getElementById("epbq-detail").textContent = "Menjawab benar " + benar + " dari 50 soal. Kesempatan telah habis. Nilai tertinggi dari 3 percobaan (" + st.bestScore + ") akan dikirim ke guru.";
      kirimNilai(absen, nama, st.bestScore);
      st.sent = true;
      saveState(absen, st);
      submitBtn.style.display = "none";
      retryBtn.style.display = "none";
    }
    refreshAttemptInfo();
  });

  function kirimNilai(absen, nama, skor){
    var statusEl = document.getElementById("epbq-status");
    statusEl.textContent = "Mengirim hasil...";
    statusEl.style.color = "#9FB4C2";

    var payload = new URLSearchParams();
    payload.append("noAbsen", absen);
    payload.append("nama", nama);
    payload.append("skor", skor);
    payload.append("materi", EPBQ_MATERI);
    payload.append("waktu", new Date().toISOString());

    fetch(EPBQ_ENDPOINT, {
      method: "POST",
      mode: "no-cors",
      headers: {"Content-Type":"application/x-www-form-urlencoded"},
      body: payload.toString()
    }).then(function(){
      statusEl.textContent = "Hasil berhasil dikirim ke guru.";
      statusEl.style.color = "#6EC6CA";
    }).catch(function(){
      statusEl.textContent = "Nilai tercatat di perangkat ini, namun pengiriman gagal. Tunjukkan skor ini ke guru.";
      statusEl.style.color = "#F5A623";
    });
  }
})();
&lt;/script&gt;
&lt;/div&gt;</description><link>http://kitab-investasi.blogspot.com/2026/09/latihan-soal-tipe-akm-pokok-bahasan.html</link><thr:total>0</thr:total><author>nof_rizal07@yahoo.co.id (abang_cool)</author></item></channel></rss>