<?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-3643028523075417648</atom:id><lastBuildDate>Sun, 09 Aug 2026 03:05:29 +0000</lastBuildDate><title>Tools Pdf</title><description></description><link>https://www.kholil.biz.id/</link><managingEditor>noreply@blogger.com (tews)</managingEditor><generator>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><language>en-us</language><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3147488496882368304</guid><pubDate>Fri, 10 Apr 2026 09:02:00 +0000</pubDate><atom:updated>2026-07-24T07:21:26.864-07:00</atom:updated><title>Compare pdf </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;meta name="description" content="Bandingkan dua file PDF secara online gratis. Ekstrak teks, lihat perbedaan highlight, 100% diproses di browser Anda secara aman."&gt;
    &lt;title&gt;Compare PDF Online Gratis (Ekstrak &amp; Sorot Perbedaan) – Kholil Tools&lt;/title&gt;
    
    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/compare-pdf" /&gt;
    &lt;meta property="og:title" content="Compare PDF Online Gratis" /&gt;
    &lt;meta property="og:description" content="Bandingkan file PDF dan lihat perbedaan teks secara instan tanpa upload ke server." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/compare-pdf" /&gt;
    &lt;meta property="og:type" content="website" /&gt;
    
    &lt;!-- Fonts --&gt;
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;!-- JSON-LD Schema untuk SEO --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Compare PDF Online",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/"
      }
    }
    &lt;/script&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Apakah file PDF saya dikirim ke server?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tidak. Seluruh pemrosesan dan perbandingan teks PDF dilakukan secara lokal di browser Anda (client-side) menggunakan PDF.js, tanpa mengunggah file ke server manapun."
          }
        },
        {
          "@type": "Question",
          "name": "Mengapa tabel atau tata letak PDF saya berantakan saat dibandingkan?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Alat ini mengekstrak teks murni dari PDF, sehingga elemen visual seperti tabel, gambar, dan format kolom tidak ikut dipertahankan dalam hasil perbandingan."
          }
        },
        {
          "@type": "Question",
          "name": "Bagaimana jika font dokumen A dan B berbeda?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Perbedaan jenis font, ukuran, atau warna tidak memengaruhi hasil karena algoritma hanya membandingkan karakter teks, bukan tampilan visualnya."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah PDF yang dilindungi password bisa dibandingkan?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tidak bisa secara langsung. PDF dengan proteksi password perlu dibuka dan disimpan ulang tanpa proteksi terlebih dahulu sebelum diunggah ke alat ini."
          }
        },
        {
          "@type": "Question",
          "name": "Berapa ukuran maksimal file PDF yang bisa diproses?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Karena diproses langsung di RAM perangkat, ukuran maksimal bergantung pada spesifikasi perangkat Anda. Umumnya file di bawah 20MB berjalan lancar di sebagian besar perangkat."
          }
        },
        {
          "@type": "Question",
          "name": "Kenapa hasil highlight kadang kurang akurat untuk paragraf panjang?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Alat ini membandingkan kata secara berurutan (word-by-word). Jika ada kalimat yang disisipkan di tengah dokumen, urutan pembacaan akan bergeser sehingga highlight bisa terlihat lebih banyak dari perubahan sebenarnya."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        /* ── VARIABEL &amp; RESET ── */
        :root {
            --blue-dark:  #0d47a1;
            --blue-mid:   #1565c0;
            --blue-light: #e3f2fd;
            --accent:     #00bcd4;
            --text-main:  #1a1a2e;
            --text-sub:   #4a5568;
            --border:     #dde3ec;
            --bg-page:    #f4f7fc;
            --green:      #2e7d32;
            --red:        #c62828;
            --radius:     14px;
        }
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-page);
            color: var(--text-main);
            line-height: 1.75;
        }

        /* ── NAVIGASI (BARU) ── */
        .main-nav { 
            background: #fff; 
            padding: 18px 25px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.05); 
            display: flex; 
            justify-content: space-between; 
            align-items: center;
            position: sticky;
            top: 0;
            z-index: 100;
        }
        .main-nav .logo a {
            text-decoration: none; 
            font-family: 'Sora', sans-serif;
            font-weight: 800; 
            font-size: 1.25rem; 
            color: var(--blue-dark);
        }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            text-decoration: none; 
            color: var(--text-sub); 
            font-weight: 500; 
            font-size: 0.95rem; 
            transition: color 0.2s;
        }
        .nav-links a:hover { color: var(--blue-mid); }

        /* ── HEADER ── */
        .site-header {
            background: linear-gradient(135deg, #0d47a1 0%, #1976d2 60%, #00bcd4 100%);
            padding: 60px 20px 70px;
            text-align: center;
            clip-path: ellipse(100% 100% at 50% 0%);
        }
        .site-header h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.2rem);
            font-weight: 800;
            color: #fff;
            letter-spacing: -0.5px;
            line-height: 1.2;
        }
        .site-header h1 span { color: #b3e5fc; }
        .site-header p {
            margin-top: 14px;
            font-size: clamp(1rem, 2vw, 1.2rem);
            color: rgba(255,255,255,.85);
            max-width: 680px;
            margin-left: auto;
            margin-right: auto;
        }

        /* ── CONTAINER &amp; TOOL CARD ── */
        .container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }
        .tool-card {
            background: #fff;
            border-radius: 20px;
            box-shadow: 0 12px 40px rgba(13,71,161,.1);
            padding: 40px 36px;
            margin: -36px auto 50px;
            max-width: 1000px;
            position: relative;
        }
        .upload-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-bottom: 28px;
        }
        .upload-box {
            border: 2.5px dashed var(--border);
            border-radius: var(--radius);
            padding: 36px 20px;
            background: #fafbff;
            text-align: center;
            transition: border-color .25s, background .25s;
            cursor: pointer;
        }
        .upload-box:hover, .upload-box.active {
            border-color: var(--blue-mid);
            background: var(--blue-light);
        }
        .upload-box .icon { font-size: 2.4rem; display: block; margin-bottom: 10px; }
        .upload-box strong { display: block; font-size: 1rem; color: var(--text-main); margin-bottom: 4px; }
        .upload-box small { color: var(--text-sub); font-size: .85rem; }
        
        .upload-label {
            display: inline-block;
            background: var(--blue-mid);
            color: #fff;
            padding: 11px 28px;
            border-radius: 50px;
            font-size: .95rem;
            font-weight: 600;
            margin-top: 14px;
            cursor: pointer;
            transition: background .2s;
        }
        .upload-label:hover { background: var(--blue-dark); }
        .upload-label input { display: none; }
        .file-name {
            margin-top: 12px;
            font-weight: 600;
            color: var(--blue-mid);
            font-size: .9rem;
            min-height: 20px;
            word-break: break-all;
        }

        .compare-btn {
            display: block;
            width: 100%;
            max-width: 380px;
            margin: 0 auto;
            background: linear-gradient(135deg, var(--blue-mid), var(--blue-dark));
            color: #fff;
            border: none;
            padding: 18px 0;
            font-size: 1.15rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            box-shadow: 0 6px 20px rgba(13,71,161,.35);
            transition: transform .15s, box-shadow .15s;
        }
        .compare-btn:hover:not(:disabled) {
            transform: translateY(-2px);
            box-shadow: 0 10px 28px rgba(13,71,161,.4);
        }
        .compare-btn:disabled { background: #9eb3cc; box-shadow: none; cursor: not-allowed; }

        #status { text-align: center; margin-top: 20px; font-weight: 600; font-size: 1rem; min-height: 24px; }

        /* ── RESULTS ── */
        .result-section { display: none; margin-top: 36px; }
        .result-header { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
        .result-header h2 { font-size: 1.3rem; color: var(--blue-dark); }
        .summary-box {
            background: #f0f7ff;
            border: 1px solid #bbdefb;
            border-radius: var(--radius);
            padding: 20px 24px;
            margin-bottom: 24px;
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
            gap: 16px;
        }
        .stat-item { text-align: center; }
        .stat-value { font-size: 1.8rem; font-weight: 800; color: var(--blue-mid); }
        .stat-label { font-size: .8rem; color: var(--text-sub); margin-top: 2px; }
        .panel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
        .panel { border: 1px solid var(--border); border-radius: var(--radius); background: #fff; overflow: hidden; }
        .panel-head {
            background: var(--blue-light);
            padding: 12px 18px;
            font-weight: 700;
            font-size: .95rem;
            color: var(--blue-dark);
            border-bottom: 1px solid #bbdefb;
        }
        .panel-body {
            padding: 18px;
            max-height: 420px;
            overflow-y: auto;
            font-size: .88rem;
            line-height: 1.7;
            white-space: pre-wrap;
            color: var(--text-main);
        }
        
        /* ── DIFF HIGHLIGHT STYLE (BARU) ── */
        mark.diff-add { background-color: #c8e6c9; color: #1b5e20; padding: 2px 4px; border-radius: 3px; font-weight: 600; }
        mark.diff-remove { background-color: #ffcdd2; color: #b71c1c; text-decoration: line-through; padding: 2px 4px; border-radius: 3px; font-weight: 600; }

        /* ── CONTENT SECTIONS ── */
        .content-wrap { max-width: 860px; margin: 0 auto; padding: 0 20px 80px; }
        .section-title {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.4rem, 3vw, 1.9rem);
            font-weight: 800;
            color: var(--text-main);
            margin-bottom: 16px;
        }
        .section-title span { color: var(--blue-mid); }
        .divider { width: 52px; height: 4px; background: linear-gradient(90deg, var(--blue-mid), var(--accent)); border-radius: 4px; margin-bottom: 28px; }
        
        /* Author Box */
        .author-box { display: flex; align-items: center; gap: 15px; background: #fff; padding: 20px; border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 30px; box-shadow: 0 2px 10px rgba(0,0,0,.02); }
        .author-img { width: 50px; height: 50px; background: linear-gradient(135deg, var(--blue-mid), var(--blue-dark)); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; font-size: 1.2rem; }
        .author-meta { font-size: 0.9rem; color: var(--text-sub); }
        .author-meta strong { color: var(--text-main); display: block; font-size: 1rem; }

        /* Steps */
        .steps { counter-reset: step; display: flex; flex-direction: column; gap: 20px; }
        .step {
            display: flex; gap: 20px; align-items: flex-start; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 22px 24px; box-shadow: 0 2px 10px rgba(0,0,0,.04);
        }
        .step-num {
            counter-increment: step; width: 44px; height: 44px; min-width: 44px; background: linear-gradient(135deg, var(--blue-mid), var(--blue-dark)); color: #fff; font-weight: 800; font-size: 1.1rem; display: flex; align-items: center; justify-content: center; border-radius: 50%;
        }
        .step-num::after { content: counter(step); }
        .step-content h3 { font-size: 1.05rem; font-weight: 700; margin-bottom: 5px; }
        .step-content p { color: var(--text-sub); font-size: .95rem; }

        /* Prose / Paragraf */
        .prose { color: var(--text-sub); font-size: .97rem; }
        .prose p { margin-bottom: 14px; }
        .prose a { color: var(--blue-mid); font-weight: 600; text-decoration: none; }
        .prose a:hover { text-decoration: underline; }
        .prose strong { color: var(--text-main); }

        /* Bukti / Proof Screenshot */
        .proof-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 10px; }
        .proof-item { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,.04); }
        .proof-item img { width: 100%; display: block; }
        .proof-caption { padding: 12px 16px; font-size: .85rem; color: var(--text-sub); text-align: center; border-top: 1px solid var(--border); }

        /* FAQ */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
        .faq-q {
            width: 100%; background: none; border: none; padding: 18px 22px; text-align: left; font-size: 1rem; font-weight: 600; color: var(--text-main); cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px;
        }
        .faq-q:hover { background: #f7faff; }
        .faq-q .arrow { transition: transform .25s; font-size: .8rem; margin-left: auto; }
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a { display: none; padding: 0 22px 18px; font-size: .93rem; color: var(--text-sub); border-top: 1px solid var(--border); line-height: 1.75; }
        .faq-a.visible { display: block; margin-top: 10px; }

        .content-section { margin-bottom: 60px; }

        /* Footer */
        footer {
            background: #0d2a5e; color: rgba(255,255,255,.7); text-align: center; padding: 36px 20px; font-size: .9rem;
        }
        footer a { color: #90caf9; text-decoration: none; margin: 0 5px; }
        footer a:hover { text-decoration: underline; }
        footer strong { color: #fff; }

        /* Responsive */
        @media (max-width: 680px) {
            .upload-grid, .panel-grid, .proof-grid { grid-template-columns: 1fr; }
            .tool-card { padding: 24px 18px; margin-top: -20px; }
            .site-header { clip-path: none; padding-bottom: 50px; }
            .nav-links { display: none; } /* Pada mobile, idealnya pakai hamburger menu, disembunyikan sementara untuk simplicity */
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- ══ NAVIGATION BAR ══ --&gt;
&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Kholil Tools&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/orc-pdf.html"&gt;OCR PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html"&gt;Scan to PDF&lt;/a&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;!-- ══ HEADER ══ --&gt;
&lt;header class="site-header"&gt;
    &lt;h1&gt;Compare &lt;span&gt;PDF&lt;/span&gt; Online Gratis&lt;/h1&gt;
    &lt;p&gt;Bandingkan dua file PDF dan temukan perbedaan teks secara instan — tanpa upload ke server, 100% aman berjalan di browser Anda.&lt;/p&gt;
&lt;/header&gt;

&lt;!-- ══ TOOL ══ --&gt;
&lt;div class="container"&gt;
    &lt;div class="tool-card"&gt;

        &lt;div class="upload-grid"&gt;
            &lt;!-- PDF 1 --&gt;
            &lt;div class="upload-box" id="box1"&gt;
                &lt;span class="icon"&gt;&#128196;&lt;/span&gt;
                &lt;strong&gt;PDF Pertama (Referensi)&lt;/strong&gt;
                &lt;small&gt;Dokumen asli atau versi lama&lt;/small&gt;
                &lt;label class="upload-label"&gt;
                    Pilih File PDF
                    &lt;input type="file" id="pdf1" accept="application/pdf"&gt;
                &lt;/label&gt;
                &lt;p class="file-name" id="name1"&gt;&lt;/p&gt;
            &lt;/div&gt;

            &lt;!-- PDF 2 --&gt;
            &lt;div class="upload-box" id="box2"&gt;
                &lt;span class="icon"&gt;&#128203;&lt;/span&gt;
                &lt;strong&gt;PDF Kedua (Pembanding)&lt;/strong&gt;
                &lt;small&gt;Dokumen revisi atau versi baru&lt;/small&gt;
                &lt;label class="upload-label"&gt;
                    Pilih File PDF
                    &lt;input type="file" id="pdf2" accept="application/pdf"&gt;
                &lt;/label&gt;
                &lt;p class="file-name" id="name2"&gt;&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;button class="compare-btn" id="compareBtn" disabled onclick="comparePDFs()"&gt;
            &#128269; Bandingkan Sekarang
        &lt;/button&gt;
        &lt;div id="status"&gt;&lt;/div&gt;

        &lt;!-- Result --&gt;
        &lt;div class="result-section" id="resultPanel"&gt;
            &lt;div class="result-header"&gt;
                &lt;span style="font-size:1.5rem;"&gt;✅&lt;/span&gt;
                &lt;h2&gt;Hasil Perbandingan&lt;/h2&gt;
            &lt;/div&gt;

            &lt;div class="summary-box" id="summaryBox"&gt;&lt;/div&gt;

            &lt;div class="panel-grid"&gt;
                &lt;div class="panel"&gt;
                    &lt;div class="panel-head" id="panelHead1"&gt;&#128196; PDF 1 — Teks Referensi&lt;/div&gt;
                    &lt;div class="panel-body" id="text1"&gt;&lt;/div&gt;
                &lt;/div&gt;
                &lt;div class="panel"&gt;
                    &lt;div class="panel-head" id="panelHead2"&gt;&#128203; PDF 2 — Teks Pembanding&lt;/div&gt;
                    &lt;div class="panel-body" id="text2"&gt;&lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ══ RICH CONTENT (OPTIMIZED UNTUK ADSENSE) ══ --&gt;
&lt;div class="content-wrap"&gt;
    
    &lt;!-- AUTHOR BOX (Sinyal E-E-A-T) --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
            Technical SEO &amp; Web Developer | Diperbarui: 21 Juli 2026
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- MAIN PROSE (Menghindari Thin Content &amp; Menambah Internal Linking) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Audit Perubahan Dokumen Tanpa &lt;span&gt;Upload ke Server&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Memeriksa revisi kontrak, draf skripsi, atau dokumen legal secara manual sangat memakan waktu. &lt;strong&gt;Compare PDF Online&lt;/strong&gt; dirancang untuk mengekstrak teks dari dua file berbeda dan menyorot perbedaan (penambahan atau penghapusan kata) menggunakan algoritma perbandingan langsung di browser Anda.&lt;/p&gt;
            &lt;p&gt;Karena kami menggunakan teknologi &lt;em&gt;client-side processing&lt;/em&gt; via PDF.js, tidak ada byte dari dokumen rahasia Anda yang ditransfer ke server eksternal. Semua terjadi di dalam RAM perangkat Anda secara instan dan 100% aman.&lt;/p&gt;
            &lt;p&gt;&lt;strong&gt;Catatan Penting:&lt;/strong&gt; Alat ini bekerja maksimal untuk PDF digital (seperti file yang diekspor dari Word/Docs). Jika dokumen Anda berupa hasil pindaian fisik (kertas yang difoto), alat ini tidak bisa membaca teksnya. Untuk kasus tersebut, silakan gunakan alat &lt;a href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html"&gt;Scan to PDF&lt;/a&gt; kami untuk merapikan file, lalu proses menggunakan teknologi &lt;a href="https://www.kholil.biz.id/2026/04/orc-pdf.html"&gt;OCR PDF&lt;/a&gt; untuk mengubah gambar menjadi teks digital sebelum membandingkannya di halaman ini.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI FUNGSI TOOL (SCREENSHOT NYATA) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Bukti &lt;span&gt;Alat Ini Bekerja&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Agar tidak sekadar klaim, berikut tangkapan layar langsung dari pengujian alat ini pada dua contoh dokumen (versi asli dan versi revisi):&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/0yvSyTZT/Screenshot-2026-07-21-01-36-59-390-com-android-chrome.jpg" alt="Tampilan proses ekstraksi teks pada alat Compare PDF" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;Proses ekstraksi &amp; cara kerja alat saat membaca kedua file PDF&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/1Hk2tPq/Screenshot-2026-07-21-01-36-31-719-com-android-chrome.jpg" alt="Hasil highlight perbedaan teks antara dua file PDF" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;Hasil akhir: kata dihapus ditandai merah, kata ditambah ditandai hijau&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- CARA MENGGUNAKAN --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Cara Kerja &lt;span&gt;Alat Ini&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="steps"&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;&lt;/div&gt;
                &lt;div class="step-content"&gt;
                    &lt;h3&gt;Unggah Referensi &amp; Revisi&lt;/h3&gt;
                    &lt;p&gt;Masukkan PDF versi asli di panel kiri dan PDF versi terbaru di panel kanan. Proses ekstraksi akan berjalan seketika saat Anda menekan tombol bandingkan.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;&lt;/div&gt;
                &lt;div class="step-content"&gt;
                    &lt;h3&gt;Analisis Highlight Perbedaan&lt;/h3&gt;
                    &lt;p&gt;Alat akan membaca struktur kata. Algoritma kami akan menandai kata yang dihapus dengan &lt;mark class="diff-remove"&gt;warna merah (dicoret)&lt;/mark&gt; dan kata baru yang ditambahkan dengan &lt;mark class="diff-add"&gt;warna hijau (disorot)&lt;/mark&gt;.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- FAQ (Aksesibel &amp; Natural) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;FAQ &amp; &lt;span&gt;Batasan Teknis&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128274;&lt;/span&gt; Apakah file PDF saya dikirim ke server?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Tidak. Kami menggunakan library PDF.js untuk memproses file murni di sisi klien (browser Anda). Server kami tidak pernah menerima, menyimpan, atau melihat dokumen Anda. Setelah Anda menutup tab browser, data langsung terhapus dari memori.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128202;&lt;/span&gt; Mengapa tabel atau tata letak PDF saya berantakan saat dibandingkan?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Secara teknis, alat ini adalah &lt;em&gt;Text Extractor&lt;/em&gt;. Format PDF tidak memiliki struktur paragraf yang baku seperti Microsoft Word (hanya koordinat posisi huruf). Kami mengekstrak teks mentahnya saja untuk membandingkan substansi isinya. Oleh karena itu, elemen visual seperti format tabel, gambar, dan warna font tidak akan dipertahankan dalam hasil perbandingan.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128292;&lt;/span&gt; Bagaimana jika font dokumen A dan B berbeda?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Perbedaan jenis font, ukuran, atau warna tidak memengaruhi hasil. Algoritma perbandingan kami hanya membandingkan karakter (string teks murni), sehingga dokumen tetap bisa dibandingkan secara presisi.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128273;&lt;/span&gt; Apakah PDF yang dilindungi password bisa dibandingkan?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Belum bisa secara langsung. Jika PDF Anda terkunci password, buka dulu filenya lalu simpan ulang tanpa proteksi (misalnya lewat aplikasi PDF reader biasa) sebelum diunggah ke alat ini.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128230;&lt;/span&gt; Berapa ukuran maksimal file PDF yang bisa diproses?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Tidak ada batas kaku dari sisi alat, karena semua diproses langsung di RAM perangkat Anda. Namun untuk pengalaman terbaik, file di bawah 20MB atau di bawah 100 halaman biasanya diproses dengan cepat di kebanyakan HP maupun laptop.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;⚠️&lt;/span&gt; Kenapa hasil highlight kadang terlihat lebih banyak dari perubahan aslinya?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Alat ini membandingkan kata secara berurutan dari awal sampai akhir dokumen. Jika ada satu kalimat baru yang disisipkan di tengah paragraf, semua kata setelahnya akan "bergeser" posisi, sehingga ikut ditandai sebagai perbedaan walau isinya sebenarnya sama. Ini adalah keterbatasan wajar dari metode perbandingan kata-per-kata, bukan bug.
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

&lt;/div&gt;&lt;!-- /content-wrap --&gt;

&lt;!-- ══ FOOTER ══ --&gt;
&lt;footer&gt;
  &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF Digital&lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/terms.html"&gt;Terms of Service&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Kontak&lt;/a&gt;
  &lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;&amp;copy; 2026 Kholil Tools. Semua hak dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- ══ SCRIPTS ══ --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';

let pdf1Bytes = null, pdf2Bytes = null;
let pdf1Name = "", pdf2Name = "";

const $ = id =&gt; document.getElementById(id);

function escapeHtml(str) {
    return str
        .replace(/&amp;/g, '&amp;amp;')
        .replace(/&lt;/g, '&amp;lt;')
        .replace(/&gt;/g, '&amp;gt;');
}

function setupUpload(inputId, nameId, boxId, byteSetter, nameSetter) {
    $(inputId).addEventListener('change', async e =&gt; {
        const file = e.target.files[0];
        if (!file) return;
        nameSetter(file.name);
        // Menggunakan innerHTML + entity numerik agar karakter centang selalu
        // tampil benar (tidak berubah jadi teks aneh saat disalin ke editor lain)
        $(nameId).innerHTML = '&amp;#10004; ' + escapeHtml(file.name);
        $(boxId).classList.add('active');
        byteSetter(await file.arrayBuffer());
        checkReady();
    });
}

setupUpload('pdf1', 'name1', 'box1', v =&gt; pdf1Bytes = v, v =&gt; pdf1Name = v);
setupUpload('pdf2', 'name2', 'box2', v =&gt; pdf2Bytes = v, v =&gt; pdf2Name = v);

function checkReady() {
    $('compareBtn').disabled = !(pdf1Bytes &amp;&amp; pdf2Bytes);
}

async function extractText(buffer) {
    const pdf = await pdfjsLib.getDocument({ data: buffer }).promise;
    let fullText = '';
    for (let i = 1; i &lt;= pdf.numPages; i++) {
        const page = await pdf.getPage(i);
        const content = await page.getTextContent();
        const pageText = content.items.map(item =&gt; item.str).join(' ');
        fullText += pageText + ' ';
    }
    return { text: fullText.replace(/\s+/g, ' ').trim(), pages: pdf.numPages };
}

function countWords(str) {
    return str.split(/\s+/).filter(Boolean).length;
}

// Algoritma Diff Kata Sederhana
function diffWords(text1, text2) {
    const arr1 = text1.split(/\s+/).filter(Boolean);
    const arr2 = text2.split(/\s+/).filter(Boolean);
    let result1 = "", result2 = "";
    
    // Logika linear ringan agar browser tidak freeze
    const maxLength = Math.max(arr1.length, arr2.length);
    for(let i=0; i &lt; maxLength; i++) {
        let w1 = arr1[i] || "";
        let w2 = arr2[i] || "";
        
        if (w1 === w2) {
            result1 += escapeHtml(w1) + " ";
            result2 += escapeHtml(w2) + " ";
        } else {
            if(w1) result1 += `&lt;mark class="diff-remove"&gt;${escapeHtml(w1)}&lt;/mark&gt; `;
            if(w2) result2 += `&lt;mark class="diff-add"&gt;${escapeHtml(w2)}&lt;/mark&gt; `;
        }
    }
    return [result1.trim(), result2.trim()];
}

async function comparePDFs() {
    const btn = $('compareBtn');
    btn.disabled = true;
    btn.textContent = '⏳ Memproses &amp; Menganalisis...';
    $('status').innerHTML = '&lt;span style="color:#1565c0;"&gt;&#128260; Mengekstrak &amp; menyorot perbedaan...&lt;/span&gt;';
    $('resultPanel').style.display = 'none';

    try {
        const [r1, r2] = await Promise.all([
            extractText(pdf1Bytes),
            extractText(pdf2Bytes)
        ]);

        const w1 = countWords(r1.text), w2 = countWords(r2.text);
        
        // Aplikasikan Diff Highlight
        const [highlightedText1, highlightedText2] = diffWords(r1.text, r2.text);

        $('panelHead1').innerHTML = '&#128196; ' + escapeHtml(pdf1Name) + ` (${r1.pages} hal.)`;
        $('panelHead2').innerHTML = '&#128203; ' + escapeHtml(pdf2Name) + ` (${r2.pages} hal.)`;
        
        // Menggunakan innerHTML agar tag &lt;mark&gt; dirender sebagai HTML
        $('text1').innerHTML = highlightedText1 || '(Tidak ada teks yang terdeteksi)';
        $('text2').innerHTML = highlightedText2 || '(Tidak ada teks yang terdeteksi)';

        const c1 = r1.text.length, c2 = r2.text.length;
        const diffPct = c1 &gt; 0 ? Math.round(Math.abs(c1 - c2) / Math.max(c1, c2) * 100) : 0;

        $('summaryBox').innerHTML = `
            &lt;div class="stat-item"&gt;
                &lt;div class="stat-value"&gt;${r1.pages}&lt;/div&gt;
                &lt;div class="stat-label"&gt;Halaman PDF 1&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-item"&gt;
                &lt;div class="stat-value"&gt;${r2.pages}&lt;/div&gt;
                &lt;div class="stat-label"&gt;Halaman PDF 2&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-item"&gt;
                &lt;div class="stat-value"&gt;${w1.toLocaleString()}&lt;/div&gt;
                &lt;div class="stat-label"&gt;Kata PDF 1&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-item"&gt;
                &lt;div class="stat-value"&gt;${w2.toLocaleString()}&lt;/div&gt;
                &lt;div class="stat-label"&gt;Kata PDF 2&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-item"&gt;
                &lt;div class="stat-value"&gt;${diffPct}%&lt;/div&gt;
                &lt;div class="stat-label"&gt;Estimasi Perubahan&lt;/div&gt;
            &lt;/div&gt;
        `;

        $('resultPanel').style.display = 'block';
        $('status').innerHTML = '&lt;span style="color:#2e7d32;"&gt;✅ Perbandingan selesai! (Merah: Dihapus, Hijau: Ditambah)&lt;/span&gt;';
        $('resultPanel').scrollIntoView({ behavior: 'smooth', block: 'start' });

    } catch (err) {
        console.error(err);
        $('status').innerHTML = '&lt;span style="color:#c62828;"&gt;❌ Gagal memproses. Pastikan file adalah PDF yang valid.&lt;/span&gt;';
    }

    btn.disabled = false;
    btn.textContent = '&#128269; Bandingkan Sekarang';
    checkReady();
}

// Aksesibilitas FAQ accordion
function toggleFaq(btn) {
    const answer = btn.nextElementSibling;
    const isOpen = answer.classList.contains('visible');
    
    // Tutup semua FAQ yang terbuka
    document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('visible'));
    document.querySelectorAll('.faq-q').forEach(b =&gt; {
        b.classList.remove('open');
        b.setAttribute('aria-expanded', 'false');
    });
    
    // Buka yang di-klik
    if (!isOpen) {
        answer.classList.add('visible');
        btn.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
    }
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/compare-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3052938064500506637</guid><pubDate>Fri, 10 Apr 2026 09:01:00 +0000</pubDate><atom:updated>2026-07-20T11:32:34.764-07:00</atom:updated><title>Orc pdf </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;meta name="description" content="Ekstrak teks dari PDF scan secara online gratis. Ubah dokumen pindaian, faktur, atau buku menjadi teks digital secara aman langsung di browser Anda."&gt;
    &lt;title&gt;OCR PDF Online Gratis – Ekstrak Teks dari Scan | Kholil Tools&lt;/title&gt;
    
    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/orc-pdf.html" /&gt;
    &lt;meta property="og:title" content="OCR PDF Online Gratis – Ekstrak Teks dari Scan" /&gt;
    &lt;meta property="og:description" content="Ekstrak teks dari PDF scan secara online gratis tanpa upload ke server. 100% diproses di browser Anda." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/orc-pdf.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;
    
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;!-- Schema Markup untuk WebApp &amp; E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "OCR PDF Online",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/"
      }
    }
    &lt;/script&gt;
    
    &lt;!-- Schema Markup untuk FAQ (SEO) --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Apakah alat ini benar-benar tidak menyimpan data saya?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Ya. Berbeda dengan layanan lain, kami menggunakan teknologi Client-Side Processing. Mesin OCR Tesseract.js berjalan menggunakan memori (RAM) di perangkat Anda. Server kami tidak pernah menerima paket file maupun melihat isi dokumen Anda."
          }
        },
        {
          "@type": "Question",
          "name": "Jenis dokumen PDF apa saja yang didukung?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Alat ini dirancang untuk membaca PDF hasil pindaian (scan) atau gambar yang dikonversi menjadi PDF. Contoh dokumen yang sangat didukung meliputi faktur cetak, kwitansi, halaman jurnal fisik, majalah, atau surat cetak."
          }
        },
        {
          "@type": "Question",
          "name": "Mengapa teks hasil ekstraksi saya berantakan atau banyak salah eja?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Akurasi mesin OCR bergantung penuh pada kualitas dokumen PDF. Jika dokumen dipindai dengan resolusi rendah (di bawah 150 DPI), posisi kertas miring, atau menggunakan font dekoratif (seperti tulisan tangan), mesin akan kesulitan membaca pola piksel. Pastikan dokumen Anda dipindai dengan pencahayaan yang terang dan kontras yang tajam."
          }
        },
        {
          "@type": "Question",
          "name": "Apa spesifikasi perangkat (device) yang dibutuhkan?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Karena alat ini memanfaatkan komputasi lokal, kami menyarankan penggunaan browser modern (Google Chrome, Microsoft Edge, atau Safari versi terbaru). Untuk kelancaran pemrosesan dokumen yang panjang, pastikan perangkat keras (PC/Smartphone) Anda memiliki minimal RAM 4GB."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah ada batas maksimal ukuran file PDF?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Secara sistem, tidak ada batasan ukuran file baku karena kami tidak mengunggah file Anda ke cloud. Namun, demi mencegah crash atau tab browser tertutup paksa (out of memory), kami sangat merekomendasikan untuk memproses file berukuran di bawah 10 MB atau kurang dari 20 halaman sekaligus."
          }
        },
        {
          "@type": "Question",
          "name": "Berapa lama proses ekstraksi teks biasanya berlangsung?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Kecepatan pemrosesan bergantung pada spesifikasi CPU dan RAM perangkat Anda. Untuk dokumen beresolusi standar (huruf teks yang jelas), ekstraksi per halamannya rata-rata memakan waktu sekitar 5 hingga 10 detik."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        /* ── VARIABEL &amp; RESET ── */
        :root {
            --blue-dark:  #0d47a1;
            --blue-mid:   #1565c0;
            --blue-light: #e3f2fd;
            --accent:     #00bcd4;
            --green:      #2e7d32;
            --text-main:  #1a1a2e;
            --text-sub:   #4a5568;
            --border:     #dde3ec;
            --bg-page:    #f4f7fc;
            --radius:     14px;
        }
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-page);
            color: var(--text-main);
            line-height: 1.75;
        }

        /* ── NAVIGASI ── */
        .main-nav { 
            background: #fff; 
            padding: 18px 25px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.05); 
            display: flex; 
            justify-content: space-between; 
            align-items: center; 
            position: sticky; 
            top: 0; 
            z-index: 100; 
        }
        .main-nav .logo a {
            text-decoration: none; 
            font-family: 'Sora', sans-serif;
            font-weight: 800; 
            font-size: 1.25rem; 
            color: var(--blue-dark);
        }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            text-decoration: none; 
            color: var(--text-sub); 
            font-weight: 500; 
            font-size: 0.95rem; 
            transition: color 0.2s;
        }
        .nav-links a:hover { color: var(--blue-mid); }

        /* ── HEADER ── */
        .site-header {
            background: linear-gradient(135deg, #0d47a1 0%, #1976d2 60%, #00bcd4 100%);
            padding: 60px 20px 70px;
            text-align: center;
            clip-path: ellipse(100% 100% at 50% 0%);
        }
        .site-header h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.2rem);
            font-weight: 800;
            color: #fff;
            letter-spacing: -0.5px;
            line-height: 1.2;
        }
        .site-header h1 span { color: #b3e5fc; }
        .site-header p {
            margin-top: 14px;
            font-size: clamp(1rem, 2vw, 1.2rem);
            color: rgba(255,255,255,.85);
            max-width: 680px;
            margin-left: auto;
            margin-right: auto;
        }
        .badge-row {
            display: flex;
            justify-content: center;
            gap: 12px;
            flex-wrap: wrap;
            margin-top: 24px;
        }
        .badge {
            background: #fff;
            color: var(--blue-dark);
            padding: 6px 16px;
            border-radius: 50px;
            font-size: .85rem;
            font-weight: 700;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        /* ── CONTAINER &amp; TOOL CARD ── */
        .container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }
        .tool-card {
            background: #fff;
            border-radius: 20px;
            box-shadow: 0 12px 40px rgba(13,71,161,.1);
            padding: 40px 36px;
            margin: -36px auto 50px;
            max-width: 900px;
            position: relative;
        }

        /* Drop zone */
        .drop-zone {
            border: 2.5px dashed var(--border);
            border-radius: var(--radius);
            padding: 50px 30px;
            background: #fafbff;
            text-align: center;
            transition: border-color .25s, background .25s;
            cursor: pointer;
            margin-bottom: 28px;
        }
        .drop-zone.dragging, .drop-zone:hover {
            border-color: var(--blue-mid);
            background: var(--blue-light);
        }
        .drop-zone .dz-icon { font-size: 3rem; display: block; margin-bottom: 14px; }
        .drop-zone strong { font-size: 1.05rem; display: block; margin-bottom: 6px; }
        .drop-zone small { color: var(--text-sub); font-size: .88rem; }
        
        .upload-label {
            display: inline-block;
            background: var(--blue-mid);
            color: #fff;
            padding: 12px 30px;
            border-radius: 50px;
            font-size: .95rem;
            font-weight: 600;
            margin-top: 16px;
            cursor: pointer;
            transition: background .2s;
        }
        .upload-label:hover { background: var(--blue-dark); }
        .upload-label input { display: none; }
        .file-name {
            margin-top: 14px;
            font-weight: 600;
            color: var(--blue-mid);
            font-size: .9rem;
            min-height: 20px;
        }

        /* Progress bar */
        .progress-wrap { display: none; margin-bottom: 20px; }
        .progress-label {
            font-size: .88rem;
            color: var(--text-sub);
            margin-bottom: 6px;
            display: flex;
            justify-content: space-between;
        }
        .progress-bar-bg {
            background: #e0e0e0;
            border-radius: 50px;
            height: 10px;
            overflow: hidden;
        }
        .progress-bar-fill {
            height: 100%;
            background: linear-gradient(90deg, var(--blue-mid), var(--accent));
            border-radius: 50px;
            width: 0%;
            transition: width .3s ease;
        }

        /* Buttons */
        .btn-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 10px; }
        .ocr-btn {
            background: linear-gradient(135deg, var(--blue-mid), var(--blue-dark));
            color: #fff;
            border: none;
            padding: 16px 40px;
            font-size: 1.1rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            box-shadow: 0 6px 20px rgba(13,71,161,.3);
            transition: transform .15s, box-shadow .15s;
        }
        .ocr-btn:hover:not(:disabled) {
            transform: translateY(-2px);
            box-shadow: 0 10px 28px rgba(13,71,161,.4);
        }
        .ocr-btn:disabled { background: #9eb3cc; box-shadow: none; cursor: not-allowed; }
        .dl-btn {
            display: none;
            background: linear-gradient(135deg, #388e3c, #2e7d32);
            color: #fff;
            border: none;
            padding: 16px 36px;
            font-size: 1rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            text-decoration: none;
            box-shadow: 0 6px 20px rgba(46,125,50,.3);
            transition: transform .15s;
        }
        .dl-btn:hover { transform: translateY(-2px); }
        .copy-btn {
            display: none;
            background: #fff;
            color: var(--blue-mid);
            border: 2px solid var(--blue-mid);
            padding: 15px 30px;
            font-size: 1rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            transition: background .2s;
        }
        .copy-btn:hover { background: var(--blue-light); }

        #status {
            text-align: center;
            margin: 16px 0;
            font-weight: 600;
            font-size: .97rem;
            min-height: 24px;
        }

        /* Result box */
        .result-box {
            border: 1px solid var(--border);
            border-radius: var(--radius);
            overflow: hidden;
            margin-top: 24px;
        }
        .result-head {
            background: var(--blue-light);
            padding: 12px 18px;
            font-weight: 700;
            font-size: .95rem;
            color: var(--blue-dark);
            border-bottom: 1px solid #bbdefb;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .result-stats { font-size: .8rem; color: var(--text-sub); font-weight: 400; }
        #resultText {
            white-space: pre-wrap;
            font-family: 'Courier New', monospace;
            font-size: .88rem;
            background: #fafafa;
            padding: 20px;
            min-height: 180px;
            max-height: 420px;
            overflow-y: auto;
            color: var(--text-main);
            line-height: 1.75;
        }

        /* ── CONTENT ── */
        .content-wrap { max-width: 860px; margin: 0 auto; padding: 0 20px 80px; }
        .section-title {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.4rem, 3vw, 1.9rem);
            font-weight: 800;
            color: var(--text-main);
            margin-bottom: 16px;
        }
        .section-title span { color: var(--blue-mid); }
        .divider { width: 52px; height: 4px; background: linear-gradient(90deg, var(--blue-mid), var(--accent)); border-radius: 4px; margin-bottom: 28px; }
        .content-section { margin-bottom: 60px; }

        /* Author Box */
        .author-box { display: flex; align-items: center; gap: 15px; background: #fff; padding: 20px; border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 30px; box-shadow: 0 2px 10px rgba(0,0,0,.02); }
        .author-img { width: 50px; height: 50px; background: linear-gradient(135deg, var(--blue-mid), var(--blue-dark)); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; font-size: 1.2rem; }
        .author-meta { font-size: 0.9rem; color: var(--text-sub); }
        .author-meta strong { color: var(--text-main); display: block; font-size: 1rem; }

        /* Prose */
        .prose { color: var(--text-sub); font-size: .97rem; }
        .prose p { margin-bottom: 14px; }
        .prose a { color: var(--blue-mid); font-weight: 600; text-decoration: none; }
        .prose a:hover { text-decoration: underline; }
        .prose strong { color: var(--text-main); }

        /* FAQ */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
        .faq-q {
            width: 100%; background: none; border: none; padding: 18px 22px; text-align: left; font-size: 1rem; font-weight: 600; color: var(--text-main); cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px;
        }
        .faq-q:hover { background: #f7faff; }
        .faq-q .arrow { transition: transform .25s; font-size: .8rem; margin-left: auto;}
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a { display: none; padding: 0 22px 18px; font-size: .93rem; color: var(--text-sub); border-top: 1px solid var(--border); line-height: 1.75; }
        .faq-a.visible { display: block; margin-top: 10px; }

        footer {
            background: #0d2a5e; color: rgba(255,255,255,.7); text-align: center; padding: 36px 20px; font-size: .9rem;
        }
        footer a { color: #90caf9; text-decoration: none; margin: 0 5px; }
        footer a:hover { text-decoration: underline; }
        footer strong { color: #fff; }

        @media (max-width: 680px) {
            .tool-card { padding: 24px 18px; margin-top: -20px; }
            .site-header { clip-path: none; padding-bottom: 50px; }
            .btn-row { flex-direction: column; align-items: center; }
            .nav-links { display: none; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- ══ NAVIGATION BAR ══ --&gt;
&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Kholil Tools&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html"&gt;Scan to PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;!-- ══ HEADER ══ --&gt;
&lt;header class="site-header"&gt;
    &lt;h1&gt;OCR &lt;span&gt;PDF&lt;/span&gt; Online Gratis&lt;/h1&gt;
    &lt;p&gt;Ekstrak teks dari PDF scan atau gambar secara instan — tanpa upload ke server, 100% berjalan aman di browser Anda.&lt;/p&gt;
    &lt;div class="badge-row"&gt;
        &lt;span class="badge"&gt;&#128274; Privasi Terjamin&lt;/span&gt;
        &lt;span class="badge"&gt;⚡ Proses di Browser&lt;/span&gt;
        &lt;span class="badge"&gt;&#127379; Gratis Selamanya&lt;/span&gt;
        &lt;span class="badge"&gt;&#128229; Download .TXT&lt;/span&gt;
    &lt;/div&gt;
&lt;/header&gt;

&lt;!-- ══ TOOL ══ --&gt;
&lt;div class="container"&gt;
    &lt;div class="tool-card"&gt;

        &lt;!-- Drop zone --&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            &lt;span class="dz-icon"&gt;&#128444;️&lt;/span&gt;
            &lt;strong&gt;Seret &amp; Lepas file PDF scan di sini&lt;/strong&gt;
            &lt;small&gt;atau klik tombol di bawah untuk memilih file&lt;/small&gt;
            &lt;label class="upload-label"&gt;
                &#128196; Pilih PDF Scan
                &lt;input type="file" id="pdfFile" accept="application/pdf"&gt;
            &lt;/label&gt;
            &lt;p class="file-name" id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;!-- Progress (Aksesibilitas aria-live) --&gt;
        &lt;div class="progress-wrap" id="progressWrap"&gt;
            &lt;div class="progress-label"&gt;
                &lt;span id="progressLabel" aria-live="polite"&gt;Memproses...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar-bg"&gt;
                &lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Buttons --&gt;
        &lt;div class="btn-row"&gt;
            &lt;button class="ocr-btn" id="ocrBtn" disabled onclick="runOCR()"&gt;&#128269; Jalankan OCR&lt;/button&gt;
            &lt;button class="copy-btn" id="copyBtn" onclick="copyText()"&gt;&#128203; Salin Teks&lt;/button&gt;
            &lt;a class="dl-btn" id="downloadBtn" href="#" download="hasil_ocr.txt"&gt;⬇️ Download .TXT&lt;/a&gt;
        &lt;/div&gt;

        &lt;!-- Status Box --&gt;
        &lt;div id="status" aria-live="polite"&gt;&lt;/div&gt;

        &lt;!-- Result --&gt;
        &lt;div class="result-box"&gt;
            &lt;div class="result-head"&gt;
                &lt;span&gt;&#128221; Hasil Ekstraksi Teks OCR&lt;/span&gt;
                &lt;span class="result-stats" id="resultStats"&gt;&lt;/span&gt;
            &lt;/div&gt;
            &lt;div id="resultText"&gt;Hasil teks akan muncul di sini setelah proses OCR selesai...&lt;/div&gt;
        &lt;/div&gt;

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

&lt;!-- ══ RICH CONTENT (OPTIMIZED) ══ --&gt;
&lt;div class="content-wrap"&gt;
    
    &lt;!-- E-E-A-T Author Box --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
            &lt;span style="font-size:0.85rem; color:var(--text-sub);"&gt;Technical SEO &amp; Web Developer | Terakhir Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- Panduan Integrasi Alat --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Solusi Digitalisasi Dokumen &lt;span&gt;Tanpa Upload Server&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Mengekstrak teks dari dokumen hasil pindaian (seperti faktur, jurnal fisik, atau kwitansi) seringkali membutuhkan perangkat lunak berbayar yang mahal. Dengan alat OCR (&lt;em&gt;Optical Character Recognition&lt;/em&gt;) kami, Anda dapat mengubah gambar teks menjadi format digital langsung dari browser.&lt;/p&gt;
            &lt;p&gt;&lt;strong&gt;Alur Kerja Optimal:&lt;/strong&gt; Jika Anda memiliki banyak gambar dokumen fisik yang terpisah, kami merekomendasikan untuk menyatukannya terlebih dahulu menggunakan alat &lt;a href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html"&gt;Scan to PDF&lt;/a&gt;. Setelah teks berhasil diekstrak dan dokumen PDF dirapikan, Anda juga dapat memberikan penomoran halaman yang konsisten melalui fitur &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt; kami agar arsip digital Anda lebih profesional.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Bukti Nyata &lt;span&gt;Kinerja Ekstraksi&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Kami sangat transparan dengan kemampuan alat ini. Berdasarkan pengujian langsung pada browser standar (Chrome Android), mesin OCR kami mampu membaca dan mengekstrak teks dengan presisi tinggi.&lt;/p&gt;
            &lt;p&gt;Lihat bagaimana alat ini berhasil mendeteksi dan mengekstrak &lt;strong&gt;77 kata dan 475 karakter&lt;/strong&gt; dari dokumen PDF percobaan (&lt;em&gt;test_ocr.pdf&lt;/em&gt;) dalam hitungan detik, lengkap dengan format spasi dan baris yang terjaga secara akurat.&lt;/p&gt;
            
            &lt;div style="display:flex; gap:15px; flex-wrap:wrap; margin-top:20px;"&gt;
                &lt;!-- Gambar Bukti 1: Proses --&gt;
                &lt;div style="flex:1; min-width:280px; border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/6RRKdsYR/Screenshot-2026-07-21-01-21-33-831-com-android-chrome.webp" alt="Bukti proses upload OCR PDF di Chrome" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;Proses identifikasi &amp; pemuatan file&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 2: Hasil --&gt;
                &lt;div style="flex:1; min-width:280px; border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/DHKnnmj9/Screenshot-2026-07-21-01-22-03-122-com-android-chrome.webp" alt="Bukti hasil ekstraksi teks 77 kata menggunakan alat OCR" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;Hasil teks terekstrak siap disalin&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- FAQ 6 POIN --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;FAQ &amp; &lt;span&gt;Batasan Teknis&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128274;&lt;/span&gt; Apakah alat ini benar-benar tidak menyimpan data saya?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Ya. Berbeda dengan layanan lain, kami menggunakan teknologi &lt;em&gt;Client-Side Processing&lt;/em&gt;. Mesin OCR Tesseract.js berjalan menggunakan memori (RAM) di perangkat Anda. Server kami tidak pernah menerima paket file maupun melihat isi dokumen Anda.
                &lt;/div&gt;
            &lt;/div&gt;
            
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128196;&lt;/span&gt; Jenis dokumen PDF apa saja yang didukung?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Alat ini dirancang untuk membaca PDF hasil pindaian (&lt;em&gt;scan&lt;/em&gt;) atau gambar yang dikonversi menjadi PDF. Contoh dokumen yang sangat didukung meliputi faktur cetak, kwitansi, halaman jurnal fisik, majalah, atau surat cetak.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;⚠️&lt;/span&gt; Mengapa teks hasil ekstraksi saya berantakan atau banyak salah eja?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Akurasi mesin OCR bergantung penuh pada kualitas dokumen PDF. Jika dokumen dipindai dengan resolusi rendah (di bawah 150 DPI), posisi kertas miring, atau menggunakan font dekoratif (seperti tulisan tangan), mesin akan kesulitan membaca pola piksel. Pastikan dokumen Anda dipindai dengan pencahayaan yang terang dan kontras yang tajam.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128187;&lt;/span&gt; Apa spesifikasi perangkat (device) yang dibutuhkan?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Karena alat ini memanfaatkan komputasi lokal, kami menyarankan penggunaan browser modern (Google Chrome, Microsoft Edge, atau Safari versi terbaru). Untuk kelancaran pemrosesan dokumen yang panjang, pastikan perangkat keras (PC/Smartphone) Anda memiliki minimal RAM 4GB.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128190;&lt;/span&gt; Apakah ada batas maksimal ukuran file PDF?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Secara sistem, tidak ada batasan ukuran file baku karena kami tidak mengunggah file Anda ke &lt;em&gt;cloud&lt;/em&gt;. Namun, demi mencegah &lt;em&gt;crash&lt;/em&gt; atau tab browser tertutup paksa (&lt;em&gt;out of memory&lt;/em&gt;), kami sangat merekomendasikan untuk memproses file berukuran di bawah 10 MB atau kurang dari 20 halaman sekaligus.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;⏳&lt;/span&gt; Berapa lama proses ekstraksi teks biasanya berlangsung?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Kecepatan pemrosesan bergantung pada spesifikasi CPU dan RAM perangkat Anda. Untuk dokumen beresolusi standar (huruf teks yang jelas), ekstraksi per halamannya rata-rata memakan waktu sekitar 5 hingga 10 detik.
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

&lt;/div&gt;&lt;!-- /content-wrap --&gt;

&lt;!-- ══ FOOTER ══ --&gt;
&lt;footer&gt;
  &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF Digital&lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/contact.html"&gt;Hubungi Kami&lt;/a&gt;
  &lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;&amp;copy; 2026 Kholil Tools. Semua hak dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- ══ SCRIPTS ══ --&gt;
&lt;!-- PDF.js di-load di awal karena ringan dan dibutuhkan untuk preview --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';

const $ = id =&gt; document.getElementById(id);
let selectedFile = null;
let extractedText = '';

// Fungsi Lazy Load Tesseract OCR (Meningkatkan Core Web Vitals)
function loadTesseract() {
    return new Promise((resolve, reject) =&gt; {
        if (window.Tesseract) return resolve();
        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@v5.0.0/dist/tesseract.min.js';
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
}

const dropZone = $('dropZone');
dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('dragging'); });
dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('dragging'));
dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('dragging');
    const file = e.dataTransfer.files[0];
    if (file?.type === 'application/pdf') handleFile(file);
    else setStatus('❌ File harus berformat PDF.', 'error');
});

$('pdfFile').addEventListener('change', e =&gt; {
    const file = e.target.files[0];
    if (file) handleFile(file);
});

function handleFile(file) {
    selectedFile = file;
    // Menggunakan Unicode Emoji asli di JS untuk menghindari karakter aneh
    $('fileName').textContent = '✔️ ' + file.name;
    dropZone.classList.add('dragging');
    $('ocrBtn').disabled = false;
    $('downloadBtn').style.display = 'none';
    $('copyBtn').style.display = 'none';
    $('resultText').textContent = 'File siap diproses. Klik "Jalankan OCR" untuk memulai.';
    $('resultStats').textContent = '';
    extractedText = '';
    setStatus('&#128196; File dipilih: ' + file.name, 'info');
}

function setStatus(msg, type) {
    const colors = { info: '#1565c0', success: '#2e7d32', error: '#c62828', warn: '#f57c00' };
    $('status').innerHTML = `&lt;span style="color:${colors[type] || '#333'}"&gt;${msg}&lt;/span&gt;`;
}

function setProgress(label, pct) {
    $('progressWrap').style.display = 'block';
    $('progressLabel').textContent = label;
    $('progressPct').textContent = pct + '%';
    $('progressFill').style.width = pct + '%';
}

async function runOCR() {
    if (!selectedFile) return;
    $('ocrBtn').disabled = true;
    $('ocrBtn').textContent = '⏳ Memuat Mesin OCR...';
    $('downloadBtn').style.display = 'none';
    $('copyBtn').style.display = 'none';
    extractedText = '';

    setStatus('&#128260; Mengunduh modul OCR (Hanya dilakukan sekali)...', 'info');
    setProgress('Menyiapkan Tesseract.js', 5);

    try {
        // Panggil Lazy Load
        await loadTesseract();
        
        $('ocrBtn').textContent = '⏳ Memproses Dokumen...';
        setStatus('&#128269; Memulai pemindaian piksel dokumen...', 'info');

        const arrayBuffer = await selectedFile.arrayBuffer();
        const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
        const totalPages = pdf.numPages;

        const worker = await Tesseract.createWorker('ind+eng', 1, {
            logger: m =&gt; {
                // Logika progres halus bisa ditangani di sini jika perlu
            }
        });

        let fullText = '';

        for (let i = 1; i &lt;= totalPages; i++) {
            const pct = Math.round(5 + ((i - 1) / totalPages) * 90);
            setProgress(`Memproses halaman ${i} dari ${totalPages}...`, pct);
            setStatus(`&#128269; Mengekstrak teks dari halaman ${i}...`, 'info');

            const page = await pdf.getPage(i);
            // Scale diatur ke 2.0 untuk menyeimbangkan kualitas OCR dan performa RAM
            const viewport = page.getViewport({ scale: 2.0 });
            const canvas = document.createElement('canvas');
            canvas.width = viewport.width;
            canvas.height = viewport.height;

            await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise;

            const imageData = canvas.toDataURL('image/png');
            const { data: { text } } = await worker.recognize(imageData);
            fullText += `── Halaman ${i} ──\n${text.trim()}\n\n`;
        }

        await worker.terminate();

        extractedText = fullText.trim();
        $('resultText').textContent = extractedText || '(Tidak ada teks yang terdeteksi)';

        // Kalkulasi Statistik
        const words = extractedText.split(/\s+/).filter(Boolean).length;
        const chars = extractedText.length;
        $('resultStats').textContent = `${totalPages} hal. • ${words.toLocaleString()} kata • ${chars.toLocaleString()} karakter`;

        // Siapkan File Download
        const blob = new Blob([extractedText], { type: 'text/plain;charset=utf-8' });
        $('downloadBtn').href = URL.createObjectURL(blob);
        $('downloadBtn').download = selectedFile.name.replace(/\.pdf$/i, '') + '_ocr.txt';
        $('downloadBtn').style.display = 'inline-block';
        $('copyBtn').style.display = 'inline-block';

        setProgress('Selesai!', 100);
        setStatus(`✅ Ekstraksi selesai! Berhasil membaca ${words.toLocaleString()} kata.`, 'success');
        
        $('resultText').scrollIntoView({ behavior: 'smooth', block: 'start' });

    } catch (err) {
        console.error(err);
        setStatus('❌ Terjadi kesalahan teknis. Pastikan file bukan PDF terenkripsi.', 'error');
        $('progressWrap').style.display = 'none';
    }

    $('ocrBtn').disabled = false;
    $('ocrBtn').textContent = '&#128269; Jalankan OCR';
}

function copyText() {
    if (!extractedText) return;
    navigator.clipboard.writeText(extractedText).then(() =&gt; {
        const btn = $('copyBtn');
        btn.textContent = '✔️ Tersalin!';
        setTimeout(() =&gt; btn.textContent = '&#128203; Salin Teks', 2000);
    });
}

// Aksesibilitas WCAG: aria-expanded untuk screen reader
function toggleFaq(btn) {
    const answer = btn.nextElementSibling;
    const isOpen = answer.classList.contains('visible');
    
    // Tutup semua Accordion
    document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('visible'));
    document.querySelectorAll('.faq-q').forEach(b =&gt; {
        b.classList.remove('open');
        b.setAttribute('aria-expanded', 'false');
    });
    
    // Buka Accordion yang ditekan
    if (!isOpen) { 
        answer.classList.add('visible'); 
        btn.classList.add('open'); 
        btn.setAttribute('aria-expanded', 'true');
    }
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/orc-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-7214532549337378298</guid><pubDate>Fri, 10 Apr 2026 09:00:00 +0000</pubDate><atom:updated>2026-07-24T07:25:40.971-07:00</atom:updated><title>Scan to PDF </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;meta name="description" content="Ubah foto scan dokumen, surat, atau gambar apa pun menjadi file PDF berkualitas tinggi secara online gratis. 100% diproses aman di browser Anda tanpa upload server."&gt;
    &lt;title&gt;Scan to PDF Online Gratis – Ubah Foto Menjadi PDF | Kholil Tools&lt;/title&gt;
    
    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html" /&gt;
    &lt;meta property="og:title" content="Scan to PDF Online Gratis – Ubah Foto Menjadi PDF" /&gt;
    &lt;meta property="og:description" content="Convert foto atau gambar scan menjadi PDF berkualitas tinggi. Privasi aman tanpa upload ke server." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/scan-to-pdf.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;
    
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;!-- Schema Markup untuk WebApp &amp; E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Scan to PDF Online",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/"
      }
    }
    &lt;/script&gt;
    
    &lt;!-- Schema Markup untuk FAQ (SEO) --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Apakah foto pribadi saya diunggah ke server?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Sama sekali tidak. Proses penyatuan foto ke PDF berjalan 100% secara lokal di browser Anda menggunakan JavaScript (jsPDF). Setelah halaman ditutup, tidak ada jejak gambar Anda yang tersisa di internet."
          }
        },
        {
          "@type": "Question",
          "name": "Format gambar apa saja yang didukung?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tools ini mendukung format gambar JPG/JPEG, PNG, dan WebP — tiga format yang paling umum dihasilkan oleh kamera smartphone maupun scanner digital."
          }
        },
        {
          "@type": "Question",
          "name": "Mengapa browser saya menjadi lambat saat konversi?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Karena alat ini tidak menggunakan server eksternal, alat ini sangat bergantung pada RAM perangkat Anda. Mengunggah puluhan foto beresolusi sangat tinggi sekaligus dapat membebani memori smartphone Anda."
          }
        },
        {
          "@type": "Question",
          "name": "Apa fungsi opsi 'Sesuai Gambar' pada pengaturan ukuran?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Opsi 'Sesuai Gambar' mencegah foto Anda terpotong atau dipaksakan masuk ke rasio A4. Ini berguna jika Anda ingin membuat portofolio dari gambar yang memiliki dimensi bervariasi."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah ada watermark pada PDF yang dihasilkan?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tidak ada watermark sama sekali. PDF yang dihasilkan adalah file bersih tanpa tanda air atau logo."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        /* ── VARIABEL &amp; RESET ── */
        :root {
            --green-dark:  #1b5e20;
            --green-mid:   #2e7d32;
            --green-light: #e8f5e9;
            --blue-mid:    #1565c0;
            --accent:      #00bcd4;
            --text-main:   #1a1a2e;
            --text-sub:    #4a5568;
            --border:      #dde3ec;
            --bg-page:     #f4f7fc;
            --radius:      14px;
        }
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-page);
            color: var(--text-main);
            line-height: 1.75;
        }

        /* ── NAVIGASI ── */
        .main-nav { 
            background: #fff; 
            padding: 18px 25px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.05); 
            display: flex; 
            justify-content: space-between; 
            align-items: center; 
            position: sticky; 
            top: 0; 
            z-index: 100; 
        }
        .main-nav .logo a {
            text-decoration: none; 
            font-family: 'Sora', sans-serif;
            font-weight: 800; 
            font-size: 1.25rem; 
            color: var(--green-dark);
        }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            text-decoration: none; 
            color: var(--text-sub); 
            font-weight: 500; 
            font-size: 0.95rem; 
            transition: color 0.2s;
        }
        .nav-links a:hover { color: var(--green-mid); }

        /* ── HEADER ── */
        .site-header {
            background: linear-gradient(135deg, #1b5e20 0%, #2e7d32 55%, #00bcd4 100%);
            padding: 60px 20px 70px;
            text-align: center;
            clip-path: ellipse(100% 100% at 50% 0%);
        }
        .site-header h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.2rem);
            font-weight: 800;
            color: #fff;
            letter-spacing: -0.5px;
            line-height: 1.2;
        }
        .site-header h1 span { color: #b9f6ca; }
        .site-header p {
            margin-top: 14px;
            font-size: clamp(1rem, 2vw, 1.2rem);
            color: rgba(255,255,255,.85);
            max-width: 680px;
            margin-left: auto;
            margin-right: auto;
        }
        .badge-row {
            display: flex;
            justify-content: center;
            gap: 12px;
            flex-wrap: wrap;
            margin-top: 24px;
        }
        .badge {
            background: #fff;
            color: var(--green-dark);
            padding: 6px 16px;
            border-radius: 50px;
            font-size: .85rem;
            font-weight: 700;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        /* ── LAYOUT ── */
        .container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }

        /* ── TOOL CARD ── */
        .tool-card {
            background: #fff;
            border-radius: 20px;
            box-shadow: 0 12px 40px rgba(27,94,32,.12);
            padding: 40px 36px;
            margin: -36px auto 50px;
            max-width: 900px;
            position: relative;
        }

        /* Drop zone */
        .drop-zone {
            border: 2.5px dashed var(--border);
            border-radius: var(--radius);
            padding: 46px 24px;
            background: #fafbff;
            text-align: center;
            transition: border-color .25s, background .25s;
            cursor: pointer;
            margin-bottom: 24px;
        }
        .drop-zone.active {
            border-color: var(--green-mid);
            background: var(--green-light);
        }
        .drop-zone .dz-icon { font-size: 3rem; display: block; margin-bottom: 12px; }
        .drop-zone strong { font-size: 1.05rem; display: block; margin-bottom: 4px; }
        .drop-zone small { color: var(--text-sub); font-size: .88rem; }
        .upload-label {
            display: inline-block;
            background: var(--green-mid);
            color: #fff;
            padding: 12px 30px;
            border-radius: 50px;
            font-size: .95rem;
            font-weight: 600;
            margin-top: 16px;
            cursor: pointer;
            transition: background .2s;
        }
        .upload-label:hover { background: var(--green-dark); }
        .upload-label input { display: none; }
        .file-name {
            margin-top: 12px;
            font-weight: 600;
            color: var(--green-mid);
            font-size: .9rem;
            min-height: 20px;
        }

        /* Settings row */
        .settings-row {
            display: flex;
            gap: 16px;
            flex-wrap: wrap;
            align-items: center;
            justify-content: center;
            margin-bottom: 24px;
        }
        .setting-group {
            display: flex;
            flex-direction: column;
            gap: 4px;
            min-width: 160px;
        }
        .setting-group label {
            font-size: .82rem;
            font-weight: 600;
            color: var(--text-sub);
            text-transform: uppercase;
            letter-spacing: .5px;
        }
        .setting-group select {
            padding: 9px 14px;
            border: 1.5px solid var(--border);
            border-radius: 8px;
            font-size: .92rem;
            color: var(--text-main);
            background: #fff;
            cursor: pointer;
        }
        .setting-group select:focus {
            outline: none;
            border-color: var(--green-mid);
        }

        /* Progress */
        .progress-wrap { display: none; margin-bottom: 18px; }
        .progress-label {
            font-size: .85rem;
            color: var(--text-sub);
            margin-bottom: 6px;
            display: flex;
            justify-content: space-between;
        }
        .progress-bar-bg {
            background: #e0e0e0;
            border-radius: 50px;
            height: 10px;
            overflow: hidden;
        }
        .progress-bar-fill {
            height: 100%;
            background: linear-gradient(90deg, var(--green-mid), var(--accent));
            border-radius: 50px;
            width: 0%;
            transition: width .3s ease;
        }

        /* Buttons */
        .btn-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
        .convert-btn {
            background: linear-gradient(135deg, var(--green-mid), var(--green-dark));
            color: #fff;
            border: none;
            padding: 16px 40px;
            font-size: 1.1rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            box-shadow: 0 6px 20px rgba(27,94,32,.3);
            transition: transform .15s, box-shadow .15s;
        }
        .convert-btn:hover:not(:disabled) {
            transform: translateY(-2px);
            box-shadow: 0 10px 28px rgba(27,94,32,.4);
        }
        .convert-btn:disabled { background: #9eb3aa; box-shadow: none; cursor: not-allowed; }
        .dl-btn {
            display: none;
            background: linear-gradient(135deg, #1565c0, #0d47a1);
            color: #fff;
            border: none;
            padding: 16px 36px;
            font-size: 1rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            text-decoration: none;
            box-shadow: 0 6px 20px rgba(13,71,161,.3);
            transition: transform .15s;
        }
        .dl-btn:hover { transform: translateY(-2px); }
        .clear-btn {
            display: none;
            background: #fff;
            color: #c62828;
            border: 2px solid #ef9a9a;
            padding: 15px 24px;
            font-size: .9rem;
            font-weight: 700;
            border-radius: 50px;
            cursor: pointer;
            transition: background .2s;
        }
        .clear-btn:hover { background: #ffebee; }

        #status {
            text-align: center;
            margin: 14px 0;
            font-weight: 600;
            font-size: .97rem;
            min-height: 24px;
        }

        /* Preview grid */
        .preview-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
            gap: 14px;
            margin-top: 24px;
        }
        .preview-item {
            border: 2px solid #a5d6a7;
            border-radius: 10px;
            overflow: hidden;
            background: #fff;
            position: relative;
            box-shadow: 0 2px 8px rgba(0,0,0,.07);
        }
        .preview-item img {
            width: 100%;
            height: 160px;
            object-fit: cover;
            display: block;
        }
        .preview-label {
            padding: 7px 10px;
            font-size: .78rem;
            color: var(--text-sub);
            background: #f9fafb;
            border-top: 1px solid #e0e0e0;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .preview-remove {
            position: absolute;
            top: 6px; right: 6px;
            background: rgba(198,40,40,.85);
            color: #fff;
            border: none;
            border-radius: 50%;
            width: 22px; height: 22px;
            font-size: .75rem;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
        }

        /* ── CONTENT ── */
        .content-wrap { max-width: 860px; margin: 0 auto; padding: 0 20px 80px; }
        .section-title {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.4rem, 3vw, 1.9rem);
            font-weight: 800;
            color: var(--text-main);
            margin-bottom: 16px;
        }
        .section-title span { color: var(--green-mid); }
        .divider { width: 52px; height: 4px; background: linear-gradient(90deg, var(--green-mid), var(--accent)); border-radius: 4px; margin-bottom: 28px; }
        .content-section { margin-bottom: 60px; }

        /* Author Box */
        .author-box { display: flex; align-items: center; gap: 15px; background: #fff; padding: 20px; border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 30px; box-shadow: 0 2px 10px rgba(0,0,0,.02); }
        .author-img { width: 50px; height: 50px; background: linear-gradient(135deg, var(--green-mid), var(--green-dark)); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; font-size: 1.2rem; }
        .author-meta { font-size: 0.9rem; color: var(--text-sub); }
        .author-meta strong { color: var(--text-main); display: block; font-size: 1rem; }

        /* Steps */
        .steps { counter-reset: step; display: flex; flex-direction: column; gap: 20px; }
        .step {
            display: flex; gap: 20px; align-items: flex-start;
            background: #fff; border: 1px solid var(--border);
            border-radius: var(--radius); padding: 22px 24px;
            box-shadow: 0 2px 10px rgba(0,0,0,.04);
        }
        .step-num {
            counter-increment: step; width: 44px; height: 44px; min-width: 44px;
            background: linear-gradient(135deg, var(--green-mid), var(--green-dark));
            color: #fff; font-weight: 800; font-size: 1.1rem;
            display: flex; align-items: center; justify-content: center;
            border-radius: 50%;
        }
        .step-num::after { content: counter(step); }
        .step-content h3 { font-size: 1.05rem; font-weight: 700; margin-bottom: 5px; }
        .step-content p { color: var(--text-sub); font-size: .95rem; }

        /* Prose &amp; Lists */
        .prose { color: var(--text-sub); font-size: .97rem; }
        .prose p { margin-bottom: 14px; }
        .prose a { color: var(--blue-mid); font-weight: 600; text-decoration: none; }
        .prose a:hover { text-decoration: underline; }
        .prose ul { padding-left: 22px; margin-bottom: 14px; }
        .prose li { margin-bottom: 6px; }
        .prose strong { color: var(--text-main); }

        /* Info boxes */
        .info-box {
            background: #e8f5e9; border-left: 4px solid #43a047;
            border-radius: 0 var(--radius) var(--radius) 0;
            padding: 16px 20px; font-size: .93rem; color: #1b5e20;
            margin: 20px 0;
        }

        /* FAQ */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
        .faq-q {
            width: 100%; background: none; border: none; padding: 18px 22px; text-align: left; font-size: 1rem; font-weight: 600; color: var(--text-main); cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px;
        }
        .faq-q:hover { background: #f7fff8; }
        .faq-q .arrow { transition: transform .25s; font-size: .8rem; margin-left: auto; }
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a { display: none; padding: 0 22px 18px; font-size: .93rem; color: var(--text-sub); border-top: 1px solid var(--border); line-height: 1.75; }
        .faq-a.visible { display: block; margin-top: 10px; }

        footer {
            background: #0d2a5e; color: rgba(255,255,255,.7); text-align: center; padding: 36px 20px; font-size: .9rem;
        }
        footer a { color: #90caf9; text-decoration: none; margin: 0 5px; }
        footer a:hover { text-decoration: underline; }
        footer strong { color: #fff; }

        @media (max-width: 680px) {
            .tool-card { padding: 24px 18px; margin-top: -20px; }
            .site-header { clip-path: none; padding-bottom: 50px; }
            .btn-row { flex-direction: column; align-items: center; }
            .settings-row { flex-direction: column; align-items: stretch; }
            .nav-links { display: none; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- ══ NAVIGASI ══ --&gt;
&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Kholil Tools&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;!-- ══ HEADER ══ --&gt;
&lt;header class="site-header"&gt;
    &lt;h1&gt;Scan to &lt;span&gt;PDF&lt;/span&gt; Online Gratis&lt;/h1&gt;
    &lt;p&gt;Ubah foto atau gambar scan menjadi file PDF siap pakai — tanpa upload ke server, 100% berjalan aman di browser Anda.&lt;/p&gt;
    &lt;div class="badge-row"&gt;
        &lt;span class="badge"&gt;&#128274; Privasi Terjamin&lt;/span&gt;
        &lt;span class="badge"&gt;&#128248; Multi-Foto Sekaligus&lt;/span&gt;
        &lt;span class="badge"&gt;&#127379; Gratis Selamanya&lt;/span&gt;
        &lt;span class="badge"&gt;⚙️ Tanpa Kompresi Paksa&lt;/span&gt;
    &lt;/div&gt;
&lt;/header&gt;

&lt;!-- ══ TOOL ══ --&gt;
&lt;div class="container"&gt;
    &lt;div class="tool-card"&gt;

        &lt;!-- Drop zone --&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            &lt;span class="dz-icon"&gt;&#128248;&lt;/span&gt;
            &lt;strong&gt;Seret &amp; Lepas foto scan di sini&lt;/strong&gt;
            &lt;small&gt;Bisa banyak foto sekaligus — JPG, PNG, WebP didukung&lt;/small&gt;
            &lt;label class="upload-label"&gt;
                &#128444;️ Pilih Foto / Gambar
                &lt;input type="file" id="imageFiles" accept="image/jpeg,image/png,image/jpg,image/webp" multiple&gt;
            &lt;/label&gt;
            &lt;p class="file-name" id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;!-- Settings --&gt;
        &lt;div class="settings-row"&gt;
            &lt;div class="setting-group"&gt;
                &lt;label&gt;Ukuran Halaman&lt;/label&gt;
                &lt;select id="pageSize"&gt;
                    &lt;option value="a4"&gt;A4 (210 × 297 mm)&lt;/option&gt;
                    &lt;option value="letter"&gt;Letter (216 × 279 mm)&lt;/option&gt;
                    &lt;option value="a3"&gt;A3 (297 × 420 mm)&lt;/option&gt;
                    &lt;option value="auto"&gt;Sesuai Gambar (Auto)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label&gt;Orientasi&lt;/label&gt;
                &lt;select id="orientation"&gt;
                    &lt;option value="portrait"&gt;Potret (Portrait)&lt;/option&gt;
                    &lt;option value="landscape"&gt;Lanskap (Landscape)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label&gt;Margin&lt;/label&gt;
                &lt;select id="margin"&gt;
                    &lt;option value="10"&gt;Normal (10 mm)&lt;/option&gt;
                    &lt;option value="0"&gt;Tanpa Margin&lt;/option&gt;
                    &lt;option value="20"&gt;Lebar (20 mm)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Progress --&gt;
        &lt;div class="progress-wrap" id="progressWrap"&gt;
            &lt;div class="progress-label"&gt;
                &lt;span id="progressLabel" aria-live="polite"&gt;Memproses...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar-bg"&gt;
                &lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Buttons --&gt;
        &lt;div class="btn-row"&gt;
            &lt;button class="convert-btn" id="convertBtn" disabled onclick="convertToPDF()"&gt;&#128196; Buat PDF Sekarang&lt;/button&gt;
            &lt;a class="dl-btn" id="downloadBtn" href="#" download="hasil_scan.pdf"&gt;⬇️ Download PDF&lt;/a&gt;
            &lt;button class="clear-btn" id="clearBtn" onclick="clearAll()"&gt;&#128465;️ Hapus Semua&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- Status Box --&gt;
        &lt;div id="status" aria-live="polite"&gt;&lt;/div&gt;

        &lt;!-- Preview --&gt;
        &lt;div class="preview-grid" id="previewGrid"&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ══ RICH CONTENT (OPTIMIZED) ══ --&gt;
&lt;div class="content-wrap"&gt;
    
    &lt;!-- E-E-A-T Author Box --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
            &lt;span style="font-size:0.85rem; color:var(--text-sub);"&gt;Technical SEO &amp; Web Developer | Terakhir Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- Integrasi Alur Kerja &amp; Internal Linking --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Merakit Dokumen Digital Secara &lt;span&gt;Aman&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Mengonversi foto dokumen fisik (seperti surat tugas, formulir lamaran kerja, atau laporan keuangan) ke dalam satu file PDF sangat penting untuk menjaga integritas format agar tidak pecah saat dicetak. Karena alat ini berbasis &lt;em&gt;Client-Side&lt;/em&gt; menggunakan teknologi jsPDF, seluruh proses penyatuan gambar beresolusi tinggi dieksekusi murni di dalam memori (RAM) komputer atau &lt;em&gt;smartphone&lt;/em&gt; Anda.&lt;/p&gt;
            
            &lt;p&gt;&lt;strong&gt;Rekomendasi Alur Kerja:&lt;/strong&gt; Setelah seluruh foto berhasil digabungkan menjadi PDF yang rapi, ada kalanya Anda memerlukan organisasi dokumen yang lebih baik. Anda dapat langsung membubuhkan nomor urut di bagian bawah dokumen menggunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt; kami. Sebaliknya, jika di kemudian hari file PDF hasil konversi Anda terkorupsi saat ditransfer atau dikirim via email, Anda dapat memulihkannya menggunakan layanan &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt; tanpa perlu mengonversi ulang dari awal.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- CARA PENGGUNAAN --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Cara Praktis &lt;span&gt;Mengonversi Foto&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="steps"&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;&lt;/div&gt;
                &lt;div class="step-content"&gt;
                    &lt;h3&gt;Upload Gambar Anda&lt;/h3&gt;
                    &lt;p&gt;Seret dan jatuhkan foto ke area unggah, atau klik tombol pilih. Anda bisa memasukkan puluhan gambar secara bersamaan dari galeri Anda.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;&lt;/div&gt;
                &lt;div class="step-content"&gt;
                    &lt;h3&gt;Pratinjau dan Sesuaikan&lt;/h3&gt;
                    &lt;p&gt;Halaman akan memunculkan pratinjau (&lt;em&gt;thumbnail&lt;/em&gt;). Pilih ukuran kertas standar (A4/Letter) atau gunakan mode "Sesuai Gambar" untuk mempertahankan dimensi asli. Anda juga bisa menghapus gambar yang salah pilih langsung dari daftar.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;&lt;/div&gt;
                &lt;div class="step-content"&gt;
                    &lt;h3&gt;Unduh File PDF&lt;/h3&gt;
                    &lt;p&gt;Klik tombol konversi dan tunggu &lt;em&gt;progress bar&lt;/em&gt; hingga penuh. Alat ini akan menyatukan gambar-gambar Anda menjadi dokumen digital yang ringan dan siap dibagikan.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="info-box" style="margin-top:28px;"&gt;
            &#128161; &lt;strong&gt;Tips Resolusi:&lt;/strong&gt; Pastikan Anda memotret dokumen di ruangan dengan pencahayaan merata. Hindari pantulan kilat (&lt;em&gt;flash&lt;/em&gt;) kamera karena akan membuat teks di dalam gambar menjadi tidak terbaca saat dicetak ulang.
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Bukti Kinerja: &lt;span&gt;Konversi Berhasil&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Kami menjamin transparansi kinerja alat ini. Berikut adalah bukti pengujian langsung menggunakan browser standar (Chrome di perangkat seluler). Alat kami terbukti mampu memproses banyak gambar sekaligus, mengonversinya menjadi PDF resolusi tinggi, dan siap diunduh dalam hitungan detik tanpa menguras kuota internet Anda.&lt;/p&gt;
            
            &lt;div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap:15px; margin-top:20px;"&gt;
                &lt;!-- Gambar Bukti 1: Upload --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/ymQnwc3M/Screenshot-2026-07-21-06-15-19-607-com-android-chrome.jpg" alt="Bukti upload banyak foto sekaligus ke tool Scan to PDF" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;1. Upload &amp; Pratinjau Foto&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 2: Proses --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/ccFPG3F0/Screenshot-2026-07-21-06-15-26-205-com-android-chrome.jpg" alt="Proses konversi gambar menjadi file PDF berjalan instan" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;2. Pemrosesan di Memori Browser&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 3: Download --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/tT6tb8m0/Screenshot-2026-07-21-06-15-29-633-com-android-chrome.jpg" alt="Tombol download PDF berfungsi sempurna setelah konversi" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;3. Konversi Sukses, File Siap Unduh&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 4: Hasil --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/GrjfnxB/Screenshot-2026-07-21-06-17-56-773-com-google-android-apps-docs.jpg" alt="Hasil akhir file PDF berhasil dibuka di aplikasi Google Docs" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;4. Hasil Akhir (Tidak Pecah)&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- FAQ --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;FAQ &amp; &lt;span&gt;Pertanyaan Teknis&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="faq-list"&gt;
            
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128274;&lt;/span&gt; Apakah foto pribadi saya diunggah ke server?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Sama sekali tidak. Proses penyatuan foto ke PDF berjalan 100% secara lokal di browser Anda menggunakan JavaScript. Setelah tab ditutup, tidak ada jejak gambar Anda yang tersisa di internet, sehingga sangat aman untuk file rahasia.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128444;️&lt;/span&gt; Format gambar apa saja yang didukung?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Alat ini membaca semua format citra web standar, yaitu ekstensi JPG/JPEG, PNG, dan WebP. Ketiganya adalah format keluaran (&lt;em&gt;output&lt;/em&gt;) utama dari sebagian besar kamera gawai pintar saat ini.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;⚠️&lt;/span&gt; Mengapa browser HP saya tertutup sendiri (Force Close)?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Karena alat ini beroperasi secara lokal, ia memakan ruang RAM (Memori Akses Acak). Memproses puluhan foto beresolusi 4K (di atas 10MB per foto) sekaligus bisa menguras memori ponsel. Kami sarankan Anda mengunggah dokumen dalam jumlah sedang (maksimal 20 berkas per sesi).
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128207;&lt;/span&gt; Apa kegunaan pengaturan ukuran "Sesuai Gambar"?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Opsi "Sesuai Gambar" (Auto) berguna untuk mencegah sistem memaksakan gambar lebar Anda masuk ke dalam bingkai kertas standar A4 yang dapat menyebabkan peregangan atau pemotongan (&lt;em&gt;cropping&lt;/em&gt;) yang merusak tata letak foto.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128271;&lt;/span&gt; Apakah terdapat logo tempelan (watermark)?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Tidak ada atribut merek maupun logo bayangan (&lt;em&gt;watermark&lt;/em&gt;). PDF yang tercetak sepenuhnya murni berisikan halaman foto milik Anda.
                &lt;/div&gt;
            &lt;/div&gt;
            
        &lt;/div&gt;
    &lt;/section&gt;

&lt;/div&gt;&lt;!-- /content-wrap --&gt;

&lt;!-- ══ FOOTER ══ --&gt;
&lt;footer&gt;
  &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF Digital&lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Hubungi Kami&lt;/a&gt;
  &lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;&amp;copy; 2026 Kholil Tools. Semua hak dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- ══ SCRIPTS ══ --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
const $ = id =&gt; document.getElementById(id);
let selectedImages = [];

// ARIA Live untuk status Screen Reader
$('status').setAttribute('aria-live', 'polite');
$('progressLabel').setAttribute('aria-live', 'polite');

// ── Drop zone ──
const dropZone = $('dropZone');
dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('active'); });
dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('active'));
dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('active');
    const files = Array.from(e.dataTransfer.files).filter(f =&gt; f.type.startsWith('image/'));
    if (files.length) addImages(files);
    else setStatus('❌ Harap pilih file gambar (JPG, PNG, WebP).', 'error');
});

$('imageFiles').addEventListener('change', e =&gt; {
    const files = Array.from(e.target.files);
    if (files.length) addImages(files);
});

function addImages(files) {
    selectedImages = [...selectedImages, ...files];
    renderPreviews();
    $('convertBtn').disabled = false;
    $('clearBtn').style.display = 'inline-block';
    $('downloadBtn').style.display = 'none';
    setStatus(`&#128248; ${selectedImages.length} foto dipilih — siap dikonversi ke PDF.`, 'info');
}

function renderPreviews() {
    const grid = $('previewGrid');
    grid.innerHTML = '';
    $('fileName').innerHTML = selectedImages.length
        ? `✔️ ${selectedImages.length} foto dipilih`
        : '';

    selectedImages.forEach((file, idx) =&gt; {
        const reader = new FileReader();
        reader.onload = e =&gt; {
            const div = document.createElement('div');
            div.className = 'preview-item';
            // Hindari injeksi karakter berbahaya di nama file
            const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
            div.innerHTML = `
                &lt;img src="${e.target.result}" alt="Foto ${idx + 1}"&gt;
                &lt;button class="preview-remove" onclick="removeImage(${idx})" title="Hapus"&gt;✕&lt;/button&gt;
                &lt;div class="preview-label"&gt;Hal. ${idx + 1} — ${safeName}&lt;/div&gt;
            `;
            grid.appendChild(div);
        };
        reader.readAsDataURL(file);
    });
}

function removeImage(idx) {
    selectedImages.splice(idx, 1);
    renderPreviews();
    if (!selectedImages.length) {
        $('convertBtn').disabled = true;
        $('clearBtn').style.display = 'none';
        setStatus('', 'info');
    } else {
        setStatus(`&#128248; ${selectedImages.length} foto tersisa.`, 'info');
    }
}

function clearAll() {
    selectedImages = [];
    $('previewGrid').innerHTML = '';
    $('fileName').innerHTML = '';
    $('convertBtn').disabled = true;
    $('clearBtn').style.display = 'none';
    $('downloadBtn').style.display = 'none';
    $('progressWrap').style.display = 'none';
    $('imageFiles').value = '';
    setStatus('', 'info');
}

function setStatus(msg, type) {
    const colors = { info: '#1565c0', success: '#2e7d32', error: '#c62828' };
    $('status').innerHTML = msg
        ? `&lt;span style="color:${colors[type] || '#333'}"&gt;${msg}&lt;/span&gt;`
        : '';
}

function setProgress(label, pct) {
    $('progressWrap').style.display = 'block';
    $('progressLabel').textContent = label;
    $('progressPct').textContent = pct + '%';
    $('progressFill').style.width = pct + '%';
}

async function convertToPDF() {
    if (!selectedImages.length) return;

    $('convertBtn').disabled = true;
    $('convertBtn').innerHTML = '⏳ Membuat PDF...';
    $('downloadBtn').style.display = 'none';

    const pageSize  = $('pageSize').value;
    const orient    = $('orientation').value;
    const margin    = parseInt($('margin').value);
    const total     = selectedImages.length;

    setStatus('&#128260; Memproses foto...', 'info');
    setProgress('Menyiapkan...', 0);

    try {
        const { jsPDF } = window.jspdf;

        // Load semua gambar ke memori
        const imgObjects = [];
        for (let i = 0; i &lt; total; i++) {
            setProgress(`Memuat foto ${i + 1} dari ${total}...`, Math.round((i / total) * 40));
            const dataUrl = await readFileAsDataURL(selectedImages[i]);
            const img = await loadImage(dataUrl);
            imgObjects.push({ dataUrl, img });
        }

        // Tentukan ukuran format halaman pertama
        const firstImg = imgObjects[0].img;
        let pdfFormat = pageSize === 'auto'
            ? [firstImg.naturalWidth * 0.264583, firstImg.naturalHeight * 0.264583]
            : pageSize;

        const pdf = new jsPDF({
            orientation: pageSize === 'auto'
                ? (firstImg.naturalWidth &gt; firstImg.naturalHeight ? 'landscape' : 'portrait')
                : orient,
            unit: 'mm',
            format: pdfFormat
        });

        for (let i = 0; i &lt; imgObjects.length; i++) {
            setProgress(`Menambahkan halaman ${i + 1} dari ${total}...`, Math.round(40 + (i / total) * 55));
            setStatus(`&#128444;️ Menyusun halaman ${i + 1} dari ${total}...`, 'info');

            const { dataUrl, img } = imgObjects[i];

            if (i &gt; 0) {
                if (pageSize === 'auto') {
                    const w = img.naturalWidth * 0.264583;
                    const h = img.naturalHeight * 0.264583;
                    pdf.addPage([w, h], img.naturalWidth &gt; img.naturalHeight ? 'landscape' : 'portrait');
                } else {
                    pdf.addPage();
                }
            }

            const pw = pdf.internal.pageSize.getWidth();
            const ph = pdf.internal.pageSize.getHeight();

            let fw = pw - margin * 2;
            let fh = (img.naturalHeight / img.naturalWidth) * fw;
            
            // Logika Resizing untuk menyesuaikan dengan batas halaman (Margin)
            if (fh &gt; ph - margin * 2) {
                fh = ph - margin * 2;
                fw = (img.naturalWidth / img.naturalHeight) * fh;
            }

            const x = (pw - fw) / 2;
            const y = (ph - fh) / 2;

            const fmt = selectedImages[i].type === 'image/png' ? 'PNG' : 'JPEG';
            pdf.addImage(dataUrl, fmt, x, y, fw, fh, undefined, 'FAST');
        }

        setProgress('Menyimpan file PDF...', 98);

        const blob = pdf.output('blob');
        const url  = URL.createObjectURL(blob);
        const sizeMB = (blob.size / 1024 / 1024).toFixed(2);

        $('downloadBtn').href = url;
        $('downloadBtn').download = `Scan_to_PDF_${Date.now()}.pdf`;
        $('downloadBtn').style.display = 'inline-block';

        setProgress('Selesai!', 100);
        setStatus(`✅ PDF berhasil dibuat! ${total} halaman • Ukuran: ${sizeMB} MB`, 'success');

    } catch (err) {
        console.error(err);
        setStatus('❌ Gagal membuat PDF. Memori perangkat mungkin tidak mencukupi untuk file sebesar ini.', 'error');
        $('progressWrap').style.display = 'none';
    }

    $('convertBtn').disabled = false;
    $('convertBtn').innerHTML = '&#128196; Buat PDF Sekarang';
}

function readFileAsDataURL(file) {
    return new Promise(resolve =&gt; {
        const r = new FileReader();
        r.onload = e =&gt; resolve(e.target.result);
        r.readAsDataURL(file);
    });
}

function loadImage(src) {
    return new Promise(resolve =&gt; {
        const img = new Image();
        img.onload = () =&gt; resolve(img);
        img.src = src;
    });
}

// Aksesibilitas WCAG: aria-expanded untuk FAQ Accordion
function toggleFaq(btn) {
    const answer = btn.nextElementSibling;
    const isOpen = answer.classList.contains('visible');
    
    // Reset area lain
    document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('visible'));
    document.querySelectorAll('.faq-q').forEach(b =&gt; {
        b.classList.remove('open');
        b.setAttribute('aria-expanded', 'false');
    });
    
    // Toggle elemen terpilih
    if (!isOpen) { 
        answer.classList.add('visible'); 
        btn.classList.add('open'); 
        btn.setAttribute('aria-expanded', 'true');
    }
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/scan-to-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-8683495718394607725</guid><pubDate>Fri, 10 Apr 2026 08:59:00 +0000</pubDate><atom:updated>2026-07-24T07:27:38.377-07:00</atom:updated><title>Page numbers </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;meta name="description" content="Tambah nomor halaman ke PDF secara online gratis. Kustomisasi posisi, format, warna, dan ukuran secara aman tanpa upload ke server."&gt;
    &lt;title&gt;Add Page Number PDF Online Gratis – Nomor Halaman Otomatis | Kholil Tools&lt;/title&gt;
    
    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/page-numbers.html" /&gt;
    &lt;meta property="og:title" content="Add Page Number PDF Online Gratis – Nomor Halaman Otomatis" /&gt;
    &lt;meta property="og:description" content="Tambah nomor halaman ke PDF dengan posisi dan warna kustom. 100% aman diproses di browser tanpa upload." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/page-numbers.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;
    
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;!-- Schema Markup untuk WebApp &amp; E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Add Page Number PDF",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/"
      }
    }
    &lt;/script&gt;
    
    &lt;!-- Schema Markup untuk FAQ (SEO) --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Bagaimana alat ini memproses PDF dengan ukuran halaman campuran?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Sistem kami mendeteksi rasio dan dimensi secara individual per halaman. Penempatan titik kordinat nomor akan dikalkulasi ulang secara dinamis untuk setiap halaman, sehingga posisinya selalu akurat meskipun dokumen Anda campur aduk."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah saya bisa menimpa file yang sudah ada nomor halamannya?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Kami tidak menyarankan hal tersebut. Skrip kami tidak menghapus piksel lama, melainkan mencetak lapisan teks baru di atas halaman. Jika dokumen sudah bernomor, Anda akan melihat angka yang tumpang tindih."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah format nomor halaman memengaruhi ukuran akhir file PDF?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tidak secara signifikan. Penambahan nomor halaman menggunakan metode injeksi teks vektor, bukan sebagai gambar. Artinya, penambahan ukuran filenya sangat minim (hanya beberapa kilobyte) meskipun dokumen Anda memiliki ratusan halaman."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        /* ── VARIABEL &amp; RESET ── */
        :root {
            --purple-dark:  #4c1d95;
            --purple-mid:   #6d28d9;
            --purple-light: #f5f3ff;
            --accent:       #00bcd4;
            --text-main:    #1a1a2e;
            --text-sub:     #4a5568;
            --border:       #dde3ec;
            --bg-page:      #f4f7fc;
            --green:        #2e7d32;
            --radius:       14px;
        }
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-page);
            color: var(--text-main);
            line-height: 1.75;
        }

        /* ── NAVIGASI ── */
        .main-nav { 
            background: #fff; 
            padding: 18px 25px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.05); 
            display: flex; 
            justify-content: space-between; 
            align-items: center; 
            position: sticky; 
            top: 0; 
            z-index: 100; 
        }
        .main-nav .logo a { 
            text-decoration: none; 
            font-family: 'Sora', sans-serif; 
            font-weight: 800; 
            font-size: 1.25rem; 
            color: var(--purple-dark); 
        }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            text-decoration: none; 
            color: var(--text-sub); 
            font-weight: 500; 
            font-size: 0.95rem; 
            transition: color 0.2s; 
        }
        .nav-links a:hover { color: var(--purple-mid); }

        /* ── HEADER ── */
        .site-header {
            background: linear-gradient(135deg, #4c1d95 0%, #6d28d9 55%, #00bcd4 100%);
            padding: 60px 20px 70px;
            text-align: center;
            clip-path: ellipse(100% 100% at 50% 0%);
        }
        .site-header h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.2rem);
            font-weight: 800;
            color: #fff;
            letter-spacing: -0.5px;
            line-height: 1.2;
        }
        .site-header h1 span { color: #e9d5ff; }
        .site-header p {
            margin-top: 14px;
            font-size: clamp(1rem, 2vw, 1.2rem);
            color: rgba(255,255,255,.85);
            max-width: 680px;
            margin-left: auto;
            margin-right: auto;
        }
        .badge-row {
            display: flex;
            justify-content: center;
            gap: 12px;
            flex-wrap: wrap;
            margin-top: 24px;
        }
        .badge {
            background: #fff;
            color: var(--purple-dark);
            padding: 6px 16px;
            border-radius: 50px;
            font-size: .85rem;
            font-weight: 700;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        /* ── LAYOUT ── */
        .container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }

        /* ── TOOL CARD ── */
        .tool-card {
            background: #fff;
            border-radius: 20px;
            box-shadow: 0 12px 40px rgba(76,29,149,.12);
            padding: 40px 36px;
            margin: -36px auto 50px;
            max-width: 860px;
        }

        /* Drop zone */
        .drop-zone {
            border: 2.5px dashed var(--border);
            border-radius: var(--radius);
            padding: 46px 24px;
            background: #fafbff;
            text-align: center;
            transition: border-color .25s, background .25s;
            cursor: pointer;
            margin-bottom: 28px;
        }
        .drop-zone.active { border-color: var(--purple-mid); background: var(--purple-light); }
        .drop-zone .dz-icon { font-size: 3rem; display: block; margin-bottom: 12px; }
        .drop-zone strong { font-size: 1.05rem; display: block; margin-bottom: 4px; }
        .drop-zone small { color: var(--text-sub); font-size: .88rem; }
        .upload-label {
            display: inline-block;
            background: var(--purple-mid);
            color: #fff;
            padding: 12px 30px;
            border-radius: 50px;
            font-size: .95rem;
            font-weight: 600;
            margin-top: 16px;
            cursor: pointer;
            transition: background .2s;
        }
        .upload-label:hover { background: var(--purple-dark); }
        .upload-label input { display: none; }
        .file-name {
            margin-top: 12px;
            font-weight: 600;
            color: var(--purple-mid);
            font-size: .9rem;
            min-height: 20px;
        }

        /* Settings grid */
        .settings-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px;
            margin-bottom: 24px;
        }
        .setting-group { display: flex; flex-direction: column; gap: 6px; }
        .setting-group.full { grid-column: 1 / -1; }
        .setting-group label {
            font-size: .82rem;
            font-weight: 700;
            color: var(--purple-dark);
            text-transform: uppercase;
            letter-spacing: .5px;
        }
        .setting-group select,
        .setting-group input[type="text"],
        .setting-group input[type="number"] {
            padding: 10px 14px;
            border: 1.5px solid var(--border);
            border-radius: 8px;
            font-size: .92rem;
            color: var(--text-main);
            background: #fff;
            transition: border-color .2s;
        }
        .setting-group select:focus,
        .setting-group input:focus {
            outline: none;
            border-color: var(--purple-mid);
        }

        /* Color picker row */
        .color-row { display: flex; gap: 10px; align-items: center; }
        .color-row input[type="color"] {
            width: 44px; height: 40px;
            border: 1.5px solid var(--border);
            border-radius: 8px; cursor: pointer;
            padding: 2px; background: #fff;
        }
        .color-row input[type="text"] { flex: 1; }

        /* Preview strip */
        .page-preview {
            background: #f8f9fa;
            border: 1px solid var(--border);
            border-radius: var(--radius);
            padding: 18px 20px;
            margin-bottom: 24px;
            text-align: center;
        }
        .page-preview p { font-size: .85rem; color: var(--text-sub); margin-bottom: 10px; }
        .preview-page {
            display: inline-block;
            width: 120px;
            height: 160px;
            background: #fff;
            border: 1.5px solid #ccc;
            border-radius: 4px;
            position: relative;
            box-shadow: 2px 3px 8px rgba(0,0,0,.1);
        }
        .preview-page .page-lines {
            padding: 14px 10px;
            display: flex; flex-direction: column; gap: 5px;
        }
        .preview-page .line { height: 5px; background: #e0e0e0; border-radius: 2px; }
        .preview-num {
            position: absolute; font-size: 9px; font-weight: 700;
            color: #555; white-space: nowrap;
        }

        /* Buttons */
        .btn-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 14px; }
        .add-btn {
            background: linear-gradient(135deg, var(--purple-mid), var(--purple-dark));
            color: #fff; border: none; padding: 16px 40px;
            font-size: 1.1rem; font-weight: 700; border-radius: 50px;
            cursor: pointer; box-shadow: 0 6px 20px rgba(76,29,149,.3);
            transition: transform .15s, box-shadow .15s;
        }
        .add-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 28px rgba(76,29,149,.4); }
        .add-btn:disabled { background: #b0a3cc; box-shadow: none; cursor: not-allowed; }
        .dl-btn {
            display: none;
            background: linear-gradient(135deg, #388e3c, #2e7d32);
            color: #fff; border: none; padding: 16px 36px;
            font-size: 1rem; font-weight: 700; border-radius: 50px;
            text-decoration: none; box-shadow: 0 6px 20px rgba(46,125,50,.3);
            transition: transform .15s;
        }
        .dl-btn:hover { transform: translateY(-2px); }

        #status {
            text-align: center;
            margin: 14px 0;
            font-weight: 600;
            font-size: .97rem;
            min-height: 24px;
        }

        /* ── CONTENT ── */
        .content-wrap { max-width: 860px; margin: 0 auto; padding: 0 20px 80px; }
        .section-title {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.4rem, 3vw, 1.9rem);
            font-weight: 800;
            color: var(--text-main);
            margin-bottom: 16px;
        }
        .section-title span { color: var(--purple-mid); }
        .divider {
            width: 52px; height: 4px;
            background: linear-gradient(90deg, var(--purple-mid), var(--accent));
            border-radius: 4px; margin-bottom: 28px;
        }
        .content-section { margin-bottom: 60px; }

        /* Author Box */
        .author-box { 
            display: flex; align-items: center; gap: 15px; 
            background: #fff; padding: 20px; border-radius: var(--radius); 
            border: 1px solid var(--border); margin-bottom: 30px; 
            border-left: 4px solid var(--purple-mid);
            box-shadow: 0 2px 10px rgba(0,0,0,.02); 
        }
        .author-img { 
            width: 50px; height: 50px; background: var(--purple-mid); 
            border-radius: 50%; display: flex; align-items: center; 
            justify-content: center; color: #fff; font-weight: bold; font-size: 1.2rem; 
        }
        .author-meta { font-size: 0.9rem; color: var(--text-sub); }
        .author-meta strong { color: var(--text-main); display: block; font-size: 1rem; }

        /* Prose */
        .prose { color: var(--text-sub); font-size: .97rem; }
        .prose p { margin-bottom: 14px; }
        .prose ul { padding-left: 22px; margin-bottom: 14px; }
        .prose li { margin-bottom: 6px; }
        .prose strong { color: var(--text-main); }
        .prose a { color: var(--purple-mid); font-weight: 600; text-decoration: none; }
        .prose a:hover { text-decoration: underline; }

        /* FAQ */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
        .faq-q {
            width: 100%; background: none; border: none;
            padding: 18px 22px; text-align: left;
            font-size: 1rem; font-weight: 600; color: var(--text-main);
            cursor: pointer; display: flex; justify-content: space-between;
            align-items: center; gap: 12px;
        }
        .faq-q:hover { background: #faf7ff; }
        .faq-q .arrow { transition: transform .25s; font-size: .8rem; margin-left: auto; }
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a {
            display: none; padding: 0 22px 18px;
            font-size: .93rem; color: var(--text-sub);
            border-top: 1px solid var(--border); line-height: 1.75;
        }
        .faq-a.visible { display: block; margin-top: 10px; }

        footer {
            background: #0d2a5e;
            color: rgba(255,255,255,.7);
            text-align: center;
            padding: 36px 20px;
            font-size: .9rem;
        }
        footer a { color: #90caf9; text-decoration: none; margin: 0 5px; }
        footer a:hover { text-decoration: underline; }
        footer strong { color: #fff; }

        @media (max-width: 680px) {
            .tool-card { padding: 24px 18px; margin-top: -20px; }
            .site-header { clip-path: none; padding-bottom: 50px; }
            .settings-grid { grid-template-columns: 1fr; }
            .btn-row { flex-direction: column; align-items: center; }
            .nav-links { display: none; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- ══ NAVIGASI ══ --&gt;
&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Kholil Tools&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-pdfa.html"&gt;PDF to PDF/A&lt;/a&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;!-- ══ HEADER ══ --&gt;
&lt;header class="site-header"&gt;
    &lt;h1&gt;Add Page Number &lt;span&gt;PDF&lt;/span&gt; Online&lt;/h1&gt;
    &lt;p&gt;Tambahkan nomor halaman ke PDF dengan posisi, format, dan warna sesuai keinginan — tanpa upload ke server, 100% aman.&lt;/p&gt;
    &lt;div class="badge-row"&gt;
        &lt;span class="badge"&gt;&#128274; Privasi Terjamin&lt;/span&gt;
        &lt;span class="badge"&gt;&#127912; Kustom Posisi &amp; Warna&lt;/span&gt;
        &lt;span class="badge"&gt;&#127379; Gratis Selamanya&lt;/span&gt;
        &lt;span class="badge"&gt;⚡ Proses Instan&lt;/span&gt;
    &lt;/div&gt;
&lt;/header&gt;

&lt;!-- ══ TOOL ══ --&gt;
&lt;div class="container"&gt;
    &lt;div class="tool-card"&gt;

        &lt;!-- Drop zone --&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            &lt;span class="dz-icon"&gt;&#128196;&lt;/span&gt;
            &lt;strong&gt;Seret &amp; Lepas file PDF di sini&lt;/strong&gt;
            &lt;small&gt;atau klik tombol di bawah untuk memilih file&lt;/small&gt;
            &lt;label class="upload-label" for="pdfFile"&gt;
                &#128194; Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf"&gt;
            &lt;/label&gt;
            &lt;p class="file-name" id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;!-- Settings --&gt;
        &lt;div class="settings-grid"&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="position"&gt;Posisi Nomor Halaman&lt;/label&gt;
                &lt;select id="position" onchange="updatePreview()"&gt;
                    &lt;option value="footer-center"&gt;Footer — Tengah&lt;/option&gt;
                    &lt;option value="footer-left"&gt;Footer — Kiri&lt;/option&gt;
                    &lt;option value="footer-right"&gt;Footer — Kanan&lt;/option&gt;
                    &lt;option value="header-center"&gt;Header — Tengah&lt;/option&gt;
                    &lt;option value="header-left"&gt;Header — Kiri&lt;/option&gt;
                    &lt;option value="header-right"&gt;Header — Kanan&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;

            &lt;div class="setting-group"&gt;
                &lt;label for="format"&gt;Format Nomor Halaman&lt;/label&gt;
                &lt;select id="format" onchange="updatePreview()"&gt;
                    &lt;option value="num"&gt;1, 2, 3 ...&lt;/option&gt;
                    &lt;option value="page-of"&gt;Page 1 of X&lt;/option&gt;
                    &lt;option value="num-of"&gt;1 of X&lt;/option&gt;
                    &lt;option value="halaman"&gt;Halaman 1&lt;/option&gt;
                    &lt;option value="halaman-dari"&gt;Halaman 1 dari X&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;

            &lt;div class="setting-group"&gt;
                &lt;label for="fontSize"&gt;Ukuran Font&lt;/label&gt;
                &lt;select id="fontSize" onchange="updatePreview()"&gt;
                    &lt;option value="10"&gt;Sangat Kecil (10 pt)&lt;/option&gt;
                    &lt;option value="12"&gt;Kecil (12 pt)&lt;/option&gt;
                    &lt;option value="14" selected&gt;Sedang (14 pt)&lt;/option&gt;
                    &lt;option value="16"&gt;Besar (16 pt)&lt;/option&gt;
                    &lt;option value="18"&gt;Sangat Besar (18 pt)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;

            &lt;div class="setting-group"&gt;
                &lt;label for="startNum"&gt;Mulai dari Nomor&lt;/label&gt;
                &lt;input type="number" id="startNum" value="1" min="1" max="999" onchange="updatePreview()"&gt;
            &lt;/div&gt;

            &lt;div class="setting-group full"&gt;
                &lt;label for="colorPicker"&gt;Warna Teks&lt;/label&gt;
                &lt;div class="color-row"&gt;
                    &lt;input type="color" id="colorPicker" value="#333333" onchange="syncColor(this.value)"&gt;
                    &lt;input type="text" id="colorHex" value="#333333" maxlength="7" onchange="syncColorFromHex(this.value)"&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="setting-group full"&gt;
                &lt;label for="prefix"&gt;Teks Awalan (Opsional)&lt;/label&gt;
                &lt;input type="text" id="prefix" placeholder="Contoh: - atau | (kosongkan jika tidak perlu)" onchange="updatePreview()"&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Live preview --&gt;
        &lt;div class="page-preview"&gt;
            &lt;p&gt;&#128065;️ Preview posisi nomor halaman&lt;/p&gt;
            &lt;div class="preview-page" id="previewPage"&gt;
                &lt;div class="page-lines"&gt;
                    &lt;div class="line" style="width:80%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:100%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:65%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:90%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:75%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:55%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:85%"&gt;&lt;/div&gt;
                    &lt;div class="line" style="width:70%"&gt;&lt;/div&gt;
                &lt;/div&gt;
                &lt;span class="preview-num" id="previewNum" aria-live="polite" style="bottom:6px; left:50%; transform:translateX(-50%);"&gt;1&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Buttons --&gt;
        &lt;div class="btn-row"&gt;
            &lt;button class="add-btn" id="addBtn" disabled onclick="addPageNumbers()"&gt;
                &#128290; Tambah Nomor Halaman
            &lt;/button&gt;
            &lt;a class="dl-btn" id="downloadBtn" href="#" download="pdf_bernomor.pdf"&gt;
                ⬇️ Download PDF
            &lt;/a&gt;
        &lt;/div&gt;

        &lt;div id="status" aria-live="polite"&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ══ RICH CONTENT (OPTIMIZED) ══ --&gt;
&lt;div class="content-wrap"&gt;
    
    &lt;!-- E-E-A-T Author Box --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
            &lt;span style="display:block;"&gt;Technical SEO &amp; Archiving Specialist | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- Narasi Unik &amp; Internal Linking --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Standardisasi &lt;span&gt;Dokumen Profesional&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Berbeda dengan sekadar membaca dokumen kasual, dokumen resmi seperti kontrak bisnis, laporan keuangan, atau manuskrip akademis sangat bergantung pada hierarki halaman. Tanpa penomoran yang presisi, proses audit atau referensi silang saat presentasi akan menjadi sangat kacau.&lt;/p&gt;
            &lt;p&gt;Alat ini mengeksekusi penyisipan nomor halaman menggunakan &lt;em&gt;library&lt;/em&gt; sisi klien. Artinya, tidak ada satu byte pun dari dokumen sensitif Anda yang diunggah ke server kami.&lt;/p&gt;
            
            &lt;h3 style="margin-top:20px; font-size:1.1rem; color:var(--purple-dark);"&gt;Rekomendasi Alur Kearsipan Digital&lt;/h3&gt;
            &lt;p&gt;Untuk memastikan dokumen akhir Anda lolos standar ISO pengarsipan jangka panjang, kami sangat menyarankan Anda untuk mengikuti alur ini:&lt;/p&gt;
            &lt;ul style="margin-top:10px;"&gt;
                &lt;li&gt;&lt;strong&gt;Pemulihan File:&lt;/strong&gt; Jika file PDF awal Anda rusak atau &lt;em&gt;corrupt&lt;/em&gt; sehingga tidak bisa dibaca oleh sistem penomoran ini, pulihkan terlebih dahulu menggunakan protokol &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt; kami.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Penggabungan Terstruktur:&lt;/strong&gt; Jika Anda memiliki banyak bab terpisah, satukan file tersebut menggunakan fitur penggabungan sebelum memberikan nomor halaman agar urutannya berkesinambungan secara otomatis.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Penguncian Arsip:&lt;/strong&gt; Setelah nomor halaman berhasil dibubuhkan dan dokumen siap didistribusikan secara permanen, konversikan file tersebut menggunakan alat &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-pdfa.html"&gt;PDF to PDF/A&lt;/a&gt; untuk mematuhi standar kearsipan elektronik global.&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Bukti Kinerja: &lt;span&gt;Penyisipan Berhasil&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Kami sangat menjaga komitmen atas fungsionalitas riil &lt;em&gt;tool&lt;/em&gt; kami. Di bawah ini adalah dokumentasi nyata pengujian sistem kami pada peramban seluler standar. Alat ini terbukti secara transparan mampu membaca dokumen, menyisipkan teks numerik ke koordinat yang tepat, dan langsung dapat diunduh tanpa perantara server.&lt;/p&gt;
            
            &lt;div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap:15px; margin-top:20px;"&gt;
                &lt;!-- Gambar Bukti 1: Upload --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/Myn0LWnx/Screenshot-2026-07-21-06-29-06-806-com-android-chrome.jpg" alt="Bukti upload dokumen PDF ke sistem penomoran" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;1. Upload &amp; Identifikasi File&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 2: Proses --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/VWv8pd5Z/Screenshot-2026-07-21-06-29-25-468-com-android-chrome.jpg" alt="Proses injeksi nomor halaman menggunakan library JS lokal" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;2. Rendering Nomor Secara Lokal&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 3: Download --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/DfGRcGDg/Screenshot-2026-07-21-06-30-25-543-com-android-chrome.jpg" alt="Tombol download PDF bernomor berfungsi sempurna" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;3. Konversi Sukses, File Siap Unduh&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar Bukti 4: Hasil --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/GQGNFHQz/Screenshot-2026-07-21-06-29-36-854-com-google-android-apps-docs.jpg" alt="Hasil akhir dokumen PDF bernomor otomatis presisi" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;4. Hasil Akhir (Presisi &amp; Rapi)&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- FAQ Kustom &amp; Unik (3 Poin) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Informasi &lt;span&gt;Teknis Alat&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" aria-controls="faq-1" onclick="toggleFaq(this)"&gt;
                    Bagaimana alat ini memproses PDF dengan ukuran halaman campuran?
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div id="faq-1" class="faq-a"&gt;
                    Sistem kami mendeteksi rasio dan dimensi (A4, Letter, atau Landscape) secara individual per halaman. Penempatan titik kordinat nomor (misal: "Footer Tengah") akan dikalkulasi ulang secara dinamis untuk setiap halaman, sehingga posisinya selalu akurat meskipun dokumen Anda campur aduk.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" aria-controls="faq-2" onclick="toggleFaq(this)"&gt;
                    Apakah saya bisa menimpa file yang sudah ada nomor halamannya?
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div id="faq-2" class="faq-a"&gt;
                    Kami tidak menyarankan hal tersebut. Skrip kami tidak menghapus piksel lama, melainkan mencetak lapisan teks baru di atas halaman. Jika dokumen sudah bernomor, Anda akan melihat angka yang tumpang tindih. Sebaiknya hapus nomor aslinya terlebih dahulu melalui editor bawaan perangkat Anda.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" aria-controls="faq-3" onclick="toggleFaq(this)"&gt;
                    Apakah format nomor halaman memengaruhi ukuran akhir file PDF?
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div id="faq-3" class="faq-a"&gt;
                    Tidak secara signifikan. Penambahan nomor halaman menggunakan metode injeksi teks vektor, bukan sebagai gambar. Artinya, penambahan ukuran filenya sangat minim (hanya beberapa kilobyte) meskipun dokumen Anda memiliki ratusan halaman.
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;
&lt;/div&gt;

&lt;!-- ══ FOOTER ══ --&gt;
&lt;footer&gt;
  &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF Digital&lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Hubungi Kami&lt;/a&gt;
  &lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;&amp;copy; 2026 Kholil Tools. Semua hak dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- ══ SCRIPTS ══ --&gt;
&lt;script&gt;
const $ = id =&gt; document.getElementById(id);
let originalBytes = null;
let originalFileName = '';

// ── Fungsi Lazy Load Library (Meningkatkan Performa Load Halaman) ──
function loadPdfLib() {
    return new Promise((resolve, reject) =&gt; {
        if (window.PDFLib) return resolve();
        const script = document.createElement('script');
        script.src = 'https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js';
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
}

// ── Drop zone ──
const dropZone = $('dropZone');
dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('active'); });
dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('active'));
dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('active');
    const file = e.dataTransfer.files[0];
    if (file?.type === 'application/pdf') handleFile(file);
    else setStatus('❌ File harus berformat PDF.', 'error');
});

$('pdfFile').addEventListener('change', e =&gt; {
    if (e.target.files[0]) handleFile(e.target.files[0]);
});

function handleFile(file) {
    const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
    originalFileName = file.name;
    $('fileName').innerHTML = '✔️ ' + safeName;
    dropZone.classList.add('active');
    $('addBtn').disabled = false;
    $('downloadBtn').style.display = 'none';
    setStatus(`&#128196; File siap diproses: ${safeName}`, 'info');

    const reader = new FileReader();
    reader.onload = e =&gt; originalBytes = e.target.result;
    reader.readAsArrayBuffer(file);
}

function setStatus(msg, type) {
    const colors = { info: '#6d28d9', success: '#2e7d32', error: '#c62828' };
    $('status').innerHTML = msg ? `&lt;span style="color:${colors[type]||'#333'}"&gt;${msg}&lt;/span&gt;` : '';
}

// ── Color sync ──
function syncColor(val) { $('colorHex').value = val; }
function syncColorFromHex(val) {
    if (/^#[0-9a-fA-F]{6}$/.test(val)) $('colorPicker').value = val;
}

// ── Live preview ──
function updatePreview() {
    const pos    = $('position').value;
    const fmt    = $('format').value;
    const start  = parseInt($('startNum').value) || 1;
    const prefix = $('prefix').value.trim();

    let label = '';
    if (fmt === 'num')           label = prefix ? `${prefix} ${start}` : `${start}`;
    else if (fmt === 'page-of')  label = `Page ${start} of X`;
    else if (fmt === 'num-of')   label = `${start} of X`;
    else if (fmt === 'halaman')  label = `Halaman ${start}`;
    else                         label = `Halaman ${start} dari X`;

    const el = $('previewNum');
    // Escape XSS potential on prefix
    el.innerHTML = label.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");

    el.style.cssText = 'position:absolute; font-size:9px; font-weight:700; color:#555; white-space:nowrap;';

    if (pos.includes('footer')) {
        el.style.bottom = '6px'; el.style.top = 'auto';
    } else {
        el.style.top = '6px'; el.style.bottom = 'auto';
    }
    if (pos.includes('center')) {
        el.style.left = '50%'; el.style.transform = 'translateX(-50%)'; el.style.right = 'auto';
    } else if (pos.includes('left')) {
        el.style.left = '8px'; el.style.transform = 'none'; el.style.right = 'auto';
    } else {
        el.style.right = '8px'; el.style.left = 'auto'; el.style.transform = 'none';
    }
}

updatePreview();

// ── Main function ──
async function addPageNumbers() {
    if (!originalBytes) return;

    const addBtn = $('addBtn');
    addBtn.disabled = true;
    addBtn.innerHTML = '⏳ Mengunduh Mesin Pemroses...';
    $('downloadBtn').style.display = 'none';
    setStatus('&#128260; Menyiapkan sistem PDF...', 'info');

    try {
        // Eksekusi Lazy Load PDF-Lib saat tombol diklik
        await loadPdfLib();
        addBtn.innerHTML = '⏳ Menyisipkan Nomor...';

        const pos      = $('position').value;
        const fmt      = $('format').value;
        const fontSize = parseInt($('fontSize').value);
        const startNum = parseInt($('startNum').value) || 1;
        const prefix   = $('prefix').value.trim();
        const hexColor = $('colorHex').value;

        // Parse hex color to RGB
        const r = parseInt(hexColor.slice(1,3),16)/255;
        const g = parseInt(hexColor.slice(3,5),16)/255;
        const b = parseInt(hexColor.slice(5,7),16)/255;

        const pdfDoc = await PDFLib.PDFDocument.load(originalBytes);
        const pages  = pdfDoc.getPages();
        const total  = pages.length;
        const font   = await pdfDoc.embedFont(PDFLib.StandardFonts.Helvetica);

        for (let i = 0; i &lt; total; i++) {
            const page   = pages[i];
            const { width, height } = page.getSize();
            const pageNum = startNum + i;

            let text = '';
            if (fmt === 'num')          text = prefix ? `${prefix} ${pageNum}` : `${pageNum}`;
            else if (fmt === 'page-of') text = `Page ${pageNum} of ${startNum + total - 1}`;
            else if (fmt === 'num-of')  text = `${pageNum} of ${startNum + total - 1}`;
            else if (fmt === 'halaman') text = `Halaman ${pageNum}`;
            else                        text = `Halaman ${pageNum} dari ${startNum + total - 1}`;

            const textWidth = font.widthOfTextAtSize(text, fontSize);

            let x, y;
            const margin = 28;

            y = pos.includes('footer') ? margin - 10 : height - margin;

            if (pos.includes('center'))      x = (width - textWidth) / 2;
            else if (pos.includes('left'))   x = margin;
            else                             x = width - textWidth - margin;

            page.drawText(text, {
                x, y,
                size: fontSize,
                font,
                color: PDFLib.rgb(r, g, b),
                opacity: 0.9
            });
        }

        const pdfBytes = await pdfDoc.save();
        const blob = new Blob([pdfBytes], { type: 'application/pdf' });
        const url  = URL.createObjectURL(blob);

        $('downloadBtn').href = url;
        $('downloadBtn').download = originalFileName.replace(/\.pdf$/i, '') + '_bernomor.pdf';
        $('downloadBtn').style.display = 'inline-block';

        setStatus(`✅ Selesai! Nomor halaman ditambahkan ke ${total} halaman.`, 'success');

    } catch (err) {
        console.error(err);
        setStatus('❌ Gagal memproses PDF. Pastikan file valid dan tidak terenkripsi.', 'error');
    }

    addBtn.disabled = false;
    addBtn.innerHTML = '&#128290; Tambah Nomor Halaman';
}

function toggleFaq(btn) {
    const a = btn.nextElementSibling;
    const open = a.classList.contains('visible');
    
    document.querySelectorAll('.faq-a').forEach(x =&gt; x.classList.remove('visible'));
    document.querySelectorAll('.faq-q').forEach(x =&gt; {
        x.classList.remove('open');
        x.setAttribute('aria-expanded', 'false');
    });
    
    if (!open) { 
        a.classList.add('visible'); 
        btn.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
    }
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/page-numbers.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3142451605292881700</guid><pubDate>Fri, 10 Apr 2026 08:57:44 +0000</pubDate><atom:updated>2026-07-24T07:39:28.779-07:00</atom:updated><title>Repair PDF</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;meta name="description" content="Pulihkan file PDF yang corrupt, error, atau gagal dibuka menggunakan skrip rekonstruksi data secara online. Tanpa upload ke server, gratis, dan sangat privat."&gt;
    &lt;title&gt;Repair PDF Online Gratis – Pulihkan Dokumen Corrupt | Kholil Tools&lt;/title&gt;
    
    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/repair-pdf.html" /&gt;
    &lt;meta property="og:title" content="Repair PDF Online Gratis – Pulihkan Dokumen Corrupt" /&gt;
    &lt;meta property="og:description" content="Perbaiki file PDF yang rusak secara lokal di browser Anda. Mengembalikan cross-reference yang korup secara aman." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/repair-pdf.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;
    
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;!-- Schema Markup untuk E-E-A-T &amp; WebApp --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Repair PDF Tool",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/"
      }
    }
    &lt;/script&gt;
    
    &lt;!-- Schema Markup untuk FAQ --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Apakah ini dapat mengatasi PDF yang terkunci sandi (password)?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tidak. Jika kerusakan terjadi pada file yang dilapisi algoritma enkripsi AES-256, maka struktur heksadesimalnya sama sekali tidak bisa direkonstruksi oleh skrip pemulihan standar. Alat ini hanya efektif untuk dokumen tanpa sandi."
          }
        },
        {
          "@type": "Question",
          "name": "Mengapa halaman terakhir saya tetap kosong setelah diperbaiki?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Ini adalah fenomena Data Truncation (Pemotongan Data). Jika dokumen Anda terputus tepat saat Anda mengunduhnya dari server di masa lalu, maka byte untuk halaman-halaman terakhir tersebut secara fisik tidak pernah ada di dalam file Anda."
          }
        },
        {
          "@type": "Question",
          "name": "Seberapa aman file perusahaan saya saat diproses?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Sangat aman. Kami menggunakan metode pemrosesan Client-Side. File perusahaan Anda tidak pernah diunggah ke cloud. Seluruh proses perbaikan terjadi di dalam memori (RAM) perangkat Anda sendiri."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        /* ── VARIABEL &amp; RESET ── */
        :root {
            --orange-dark:  #e65100;
            --orange-mid:   #f57c00;
            --orange-light: #fff8f0;
            --accent:       #00bcd4;
            --text-main:    #1a1a2e;
            --text-sub:     #4a5568;
            --border:       #dde3ec;
            --bg-page:      #f4f7fc;
            --green:        #2e7d32;
            --radius:       14px;
        }
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-page);
            color: var(--text-main);
            line-height: 1.75;
        }

        /* ── NAVIGASI ── */
        .main-nav { 
            background: #fff; 
            padding: 18px 25px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.05); 
            display: flex; 
            justify-content: space-between; 
            align-items: center; 
            position: sticky; 
            top: 0; 
            z-index: 100; 
        }
        .main-nav .logo a { 
            text-decoration: none; 
            font-family: 'Sora', sans-serif; 
            font-weight: 800; 
            font-size: 1.25rem; 
            color: var(--orange-dark); 
        }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            text-decoration: none; 
            color: var(--text-sub); 
            font-weight: 500; 
            font-size: 0.95rem; 
            transition: color 0.2s; 
        }
        .nav-links a:hover { color: var(--orange-mid); }

        /* ── HEADER ── */
        .site-header {
            background: linear-gradient(135deg, #bf360c 0%, #f57c00 55%, #ffb300 100%);
            padding: 60px 20px 70px;
            text-align: center;
            clip-path: ellipse(100% 100% at 50% 0%);
        }
        .site-header h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.2rem);
            font-weight: 800;
            color: #fff;
            letter-spacing: -0.5px;
            line-height: 1.2;
        }
        .site-header h1 span { color: #ffe0b2; }
        .site-header p {
            margin-top: 14px;
            font-size: clamp(1rem, 2vw, 1.2rem);
            color: rgba(255,255,255,.88);
            max-width: 680px;
            margin-left: auto;
            margin-right: auto;
        }

        /* ── LAYOUT &amp; TOOL CARD ── */
        .container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }
        .tool-card {
            background: #fff;
            border-radius: 20px;
            box-shadow: 0 12px 40px rgba(245,124,0,.12);
            padding: 40px 36px;
            margin: -36px auto 50px;
            max-width: 820px;
        }

        /* Drop zone */
        .drop-zone {
            border: 2.5px dashed var(--border);
            border-radius: var(--radius);
            padding: 50px 24px;
            background: #fafbff;
            text-align: center;
            transition: border-color .25s, background .25s;
            cursor: pointer;
            margin-bottom: 28px;
        }
        .drop-zone.active { border-color: var(--orange-mid); background: var(--orange-light); }
        .drop-zone .dz-icon { font-size: 3rem; display: block; margin-bottom: 12px; }
        .drop-zone strong { font-size: 1.05rem; display: block; margin-bottom: 4px; }
        .drop-zone small { color: var(--text-sub); font-size: .88rem; }
        
        .upload-label {
            display: inline-block;
            background: var(--orange-mid);
            color: #fff;
            padding: 12px 30px;
            border-radius: 50px;
            font-size: .95rem;
            font-weight: 600;
            margin-top: 16px;
            cursor: pointer;
            transition: background .2s;
        }
        .upload-label:hover { background: var(--orange-dark); }
        .upload-label input { display: none; }
        .file-name {
            margin-top: 12px;
            font-weight: 600;
            color: var(--orange-mid);
            font-size: .9rem;
            min-height: 20px;
        }

        /* Diagnostic panel */
        .diag-panel {
            display: none;
            background: #f8f9fa;
            border: 1px solid var(--border);
            border-radius: var(--radius);
            padding: 18px 20px;
            margin-bottom: 24px;
            font-size: .88rem;
        }
        .diag-panel h4 { font-size: .9rem; font-weight: 700; margin-bottom: 10px; color: var(--orange-dark); }
        .diag-row { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #eee; }
        .diag-row:last-child { border-bottom: none; }
        .diag-label { color: var(--text-sub); }
        .diag-val { font-weight: 600; }
        .ok  { color: #2e7d32; }
        .bad { color: #c62828; }
        .warn{ color: #f57c00; }

        /* Buttons &amp; Progress */
        .btn-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 14px; }
        .repair-btn {
            background: linear-gradient(135deg, var(--orange-mid), var(--orange-dark));
            color: #fff; border: none; padding: 16px 40px;
            font-size: 1.1rem; font-weight: 700; border-radius: 50px;
            cursor: pointer; box-shadow: 0 6px 20px rgba(245,124,0,.35);
            transition: transform .15s, box-shadow .15s;
        }
        .repair-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 28px rgba(245,124,0,.45); }
        .repair-btn:disabled { background: #cbb89a; box-shadow: none; cursor: not-allowed; }
        .dl-btn {
            display: none;
            background: linear-gradient(135deg, #388e3c, #2e7d32);
            color: #fff; border: none; padding: 16px 36px;
            font-size: 1rem; font-weight: 700; border-radius: 50px;
            text-decoration: none; box-shadow: 0 6px 20px rgba(46,125,50,.3);
            transition: transform .15s;
        }
        .dl-btn:hover { transform: translateY(-2px); }

        #status {
            text-align: center; margin: 14px 0;
            font-weight: 600; font-size: .97rem;
            min-height: 44px; line-height: 1.6;
        }

        .progress-wrap { display: none; margin-bottom: 18px; }
        .progress-label {
            font-size: .85rem; color: var(--text-sub);
            margin-bottom: 6px; display: flex; justify-content: space-between;
        }
        .progress-bar-bg { background: #e0e0e0; border-radius: 50px; height: 10px; overflow: hidden; }
        .progress-bar-fill {
            height: 100%; background: linear-gradient(90deg, var(--orange-mid), #ffb300);
            border-radius: 50px; width: 0%; transition: width .3s ease;
        }

        .limit-note {
            background: #fff8e1; border: 1px solid #ffe082;
            border-radius: var(--radius); padding: 16px 20px;
            font-size: .88rem; color: #5d4037; margin-top: 20px; line-height: 1.7;
        }
        .limit-note strong { color: #e65100; }

        /* ── CONTENT AREA ── */
        .content-wrap { max-width: 860px; margin: 0 auto; padding: 0 20px 80px; }
        .section-title {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.4rem, 3vw, 1.9rem);
            font-weight: 800; color: var(--text-main); margin-bottom: 16px;
        }
        .section-title span { color: var(--orange-mid); }
        .divider {
            width: 52px; height: 4px;
            background: linear-gradient(90deg, var(--orange-mid), var(--accent));
            border-radius: 4px; margin-bottom: 28px;
        }
        .content-section { margin-bottom: 60px; }

        /* Author Box E-E-A-T */
        .author-box { 
            display: flex; align-items: center; gap: 15px; 
            background: #fff; padding: 20px; border-radius: var(--radius); 
            border: 1px solid var(--border); margin-bottom: 30px; 
            border-left: 4px solid var(--orange-mid);
            box-shadow: 0 2px 10px rgba(0,0,0,.02); 
        }
        .author-img { 
            width: 50px; height: 50px; background: var(--orange-mid); 
            border-radius: 50%; display: flex; align-items: center; 
            justify-content: center; color: #fff; font-weight: bold; font-size: 1.2rem; 
        }
        .author-meta { font-size: 0.9rem; color: var(--text-sub); }
        .author-meta strong { color: var(--text-main); display: block; font-size: 1rem; }

        /* Prose */
        .prose { color: var(--text-sub); font-size: .97rem; }
        .prose p { margin-bottom: 14px; }
        .prose ul { padding-left: 22px; margin-bottom: 14px; }
        .prose li { margin-bottom: 6px; }
        .prose strong { color: var(--text-main); }
        .prose a { color: var(--orange-mid); font-weight: 600; text-decoration: none; }
        .prose a:hover { text-decoration: underline; }

        /* FAQ */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
        .faq-q {
            width: 100%; background: none; border: none;
            padding: 18px 22px; text-align: left;
            font-size: 1rem; font-weight: 600; color: var(--text-main);
            cursor: pointer; display: flex; justify-content: space-between;
            align-items: center; gap: 12px;
        }
        .faq-q:hover { background: #fffaf5; }
        .faq-q .arrow { transition: transform .25s; font-size: .8rem; margin-left: auto; }
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a {
            display: none; padding: 0 22px 18px;
            font-size: .93rem; color: var(--text-sub);
            border-top: 1px solid var(--border); line-height: 1.75;
        }
        .faq-a.visible { display: block; margin-top: 10px; }

        footer {
            background: #0d2a5e; color: rgba(255,255,255,.7);
            text-align: center; padding: 36px 20px; font-size: .9rem;
        }
        footer a { color: #90caf9; text-decoration: none; margin: 0 5px; }
        footer a:hover { text-decoration: underline; }
        footer strong { color: #fff; }

        @media (max-width: 680px) {
            .tool-card { padding: 24px 18px; margin-top: -20px; }
            .site-header { clip-path: none; padding-bottom: 50px; }
            .btn-row { flex-direction: column; align-items: center; }
            .nav-links { display: none; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- ══ NAVIGASI E-E-A-T ══ --&gt;
&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;&lt;a href="https://www.kholil.biz.id/"&gt;Kholil Tools&lt;/a&gt;&lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/compare-pdf.html"&gt;Compare Pdf&lt;/a&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;!-- ══ HEADER ══ --&gt;
&lt;header class="site-header"&gt;
    &lt;h1&gt;Repair &lt;span&gt;PDF&lt;/span&gt; Online Gratis&lt;/h1&gt;
    &lt;p&gt;Pulihkan struktur PDF yang corrupt menggunakan perbaikan cross-reference secara mandiri secara aman di browser Anda.&lt;/p&gt;
&lt;/header&gt;

&lt;!-- ══ TOOL ══ --&gt;
&lt;div class="container"&gt;
    &lt;div class="tool-card"&gt;

        &lt;!-- Drop zone --&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            &lt;span class="dz-icon"&gt;&#128736;️&lt;/span&gt;
            &lt;strong&gt;Seret &amp; Lepas file PDF yang rusak di sini&lt;/strong&gt;
            &lt;small&gt;atau klik tombol di bawah untuk memilih file&lt;/small&gt;
            &lt;label class="upload-label" for="pdfFile"&gt;
                &#128194; Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf,.pdf"&gt;
            &lt;/label&gt;
            &lt;p class="file-name" id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;!-- Diagnostic panel --&gt;
        &lt;div class="diag-panel" id="diagPanel"&gt;
            &lt;h4&gt;&#128269; Hasil Analisis File&lt;/h4&gt;
            &lt;div class="diag-row"&gt;&lt;span class="diag-label"&gt;Nama file&lt;/span&gt;&lt;span class="diag-val" id="dName"&gt;—&lt;/span&gt;&lt;/div&gt;
            &lt;div class="diag-row"&gt;&lt;span class="diag-label"&gt;Ukuran file&lt;/span&gt;&lt;span class="diag-val" id="dSize"&gt;—&lt;/span&gt;&lt;/div&gt;
            &lt;div class="diag-row"&gt;&lt;span class="diag-label"&gt;Header PDF&lt;/span&gt;&lt;span class="diag-val" id="dHeader"&gt;—&lt;/span&gt;&lt;/div&gt;
            &lt;div class="diag-row"&gt;&lt;span class="diag-label"&gt;Status awal&lt;/span&gt;&lt;span class="diag-val" id="dStatus"&gt;—&lt;/span&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Progress (Aksesibilitas aria-live) --&gt;
        &lt;div class="progress-wrap" id="progressWrap"&gt;
            &lt;div class="progress-label"&gt;
                &lt;span id="progressLabel" aria-live="polite"&gt;Memproses...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar-bg"&gt;&lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Buttons --&gt;
        &lt;div class="btn-row"&gt;
            &lt;button class="repair-btn" id="repairBtn" disabled onclick="repairPDF()"&gt;
                &#128736;️ Perbaiki PDF Sekarang
            &lt;/button&gt;
            &lt;a class="dl-btn" id="downloadBtn" href="#" download="pdf_repaired.pdf"&gt;
                ⬇️ Download PDF yang Sudah Diperbaiki
            &lt;/a&gt;
        &lt;/div&gt;

        &lt;!-- Status Box --&gt;
        &lt;div id="status" aria-live="polite"&gt;&lt;/div&gt;

        &lt;div class="limit-note"&gt;
            ⚠️ &lt;strong&gt;Catatan Teknis:&lt;/strong&gt; Tools ini menggunakan teknik rekonstruksi struktur PDF klien-sisi yang efektif untuk kasus kerusakan ringan hingga sedang (seperti terputusnya transfer file). File dengan kerusakan parah pada &lt;em&gt;byte&lt;/em&gt; inti atau yang terenkripsi oleh algoritma AES mungkin tidak dapat dipulihkan.
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ══ RICH CONTENT (OPTIMIZED) ══ --&gt;
&lt;div class="content-wrap"&gt;

    &lt;!-- Kotak Penulis E-E-A-T --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
            &lt;span style="display:block;"&gt;Technical SEO &amp; Archiving Specialist | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- Analisis Mendalam &amp; Internal Linking --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Anatomi &lt;span&gt;Kerusakan PDF&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Sebuah file PDF bukanlah sekadar gambar datar; dokumen ini pada dasarnya adalah sebuah basis data mini yang berisi teks, lapisan grafis vektor, instruksi tata letak, dan yang terpenting: &lt;em&gt;cross-reference table&lt;/em&gt; (XREF). Ketika file gagal diunduh seutuhnya, ditransfer menggunakan koneksi jaringan yang buruk, atau sebagian datanya hilang saat penyimpanan, tabel XREF ini menjadi terputus atau berantakan.&lt;/p&gt;
            
            &lt;p&gt;Jika perangkat lunak pembaca seperti Acrobat Reader mendeteksi tabel XREF yang rusak ini, ia akan menolak untuk membukanya dan menampilkan &lt;em&gt;error&lt;/em&gt; &lt;strong&gt;"Format error: Not a PDF or corrupted"&lt;/strong&gt;.&lt;/p&gt;
            
            &lt;h3 style="margin-top:20px; font-size:1.1rem; color:var(--orange-dark);"&gt;Alur Pemulihan (Recovery) &amp; Pengarsipan&lt;/h3&gt;
            &lt;p&gt;Aplikasi web ini menggunakan dua pendekatan teknis secara bertahap untuk membangun ulang dokumen Anda:&lt;/p&gt;
            &lt;ul style="margin-top:10px;"&gt;
                &lt;li&gt;&lt;strong&gt;Rebuild PDF-Lib:&lt;/strong&gt; Kami memeriksa baris heksadesimal file Anda secara mandiri di &lt;em&gt;browser&lt;/em&gt;. Skrip kami kemudian membangun ulang tabel referensi yang hilang dengan "menebak" posisi setiap objek secara logis.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;PDF.js Fallback:&lt;/strong&gt; Jika PDF kehilangan terlalu banyak struktur dasarnya, skrip kami beralih ke mode ekstraksi dengan mencoba merender elemen grafiknya secara paksa untuk dirakit kembali menjadi halaman dokumen baru.&lt;/li&gt;
            &lt;/ul&gt;
            
            &lt;p&gt;&lt;strong&gt;Rekomendasi Pascaperbaikan:&lt;/strong&gt; Jika Anda sedang menjelajahi &lt;a href="https://www.kholil.biz.id/"&gt;beranda alat utama kami&lt;/a&gt;, Anda akan menyadari bahwa pemulihan file ini seringkali memengaruhi metadata aslinya. Jika alat &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt; ini berhasil memulihkan file penting Anda, ada kemungkinan susunan meta halamannya perlu ditata ulang.&lt;/p&gt;
            &lt;p&gt;Untuk dokumen standar, Anda bisa mulai merapikannya kembali dengan sistem &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt;. Selanjutnya, agar struktur dokumen tidak mudah &lt;em&gt;corrupt&lt;/em&gt; lagi di masa depan dan sah secara hukum kearsipan digital, konversikan formatnya melalui alat &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-pdfa.html"&gt;PDF to PDF/A&lt;/a&gt; milik kami.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Bukti Kinerja: &lt;span&gt;Pemulihan Berhasil&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="prose"&gt;
            &lt;p&gt;Kami menjamin transparansi fungsionalitas alat ini. Berikut adalah dokumentasi pengujian langsung dari perangkat seluler standar terhadap sebuah file PDF yang rusak (corrupt). Sistem kami secara mandiri berhasil menganalisis, memulihkan data, dan menyajikan file yang utuh untuk diunduh.&lt;/p&gt;
            
            &lt;div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap:15px; margin-top:20px;"&gt;
                &lt;!-- Gambar 1: Bukti File Korup --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/j99Yzfxy/Screenshot-2026-07-21-16-29-36-533-com-android-providers-downloads-ui.jpg" alt="Bukti file PDF asli yang corrupt dan tidak bisa dibuka" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;1. File Asli Korup&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar 2: Upload dan Analisis --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/XkJkcTqF/Screenshot-2026-07-21-16-30-06-549-com-android-chrome.jpg" alt="File corrupt diunggah dan dianalisis oleh sistem" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;2. Analisis &amp; Unggah&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar 3: Perbaikan Berhasil --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/v87Scbw/Screenshot-2026-07-21-16-30-15-264-com-android-chrome.jpg" alt="Proses perbaikan PDF 100% berhasil" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;3. Pemulihan 100% Sukses&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar 4: Tombol Download --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/x87K7kg5/Screenshot-2026-07-21-16-30-19-688-com-android-chrome.jpg" alt="Tombol download muncul dan berfungsi setelah repair" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;4. Siap Diunduh&lt;/p&gt;
                &lt;/div&gt;
                &lt;!-- Gambar 5: Hasil Akhir --&gt;
                &lt;div style="border:1px solid var(--border); border-radius:var(--radius); padding:10px; background:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                    &lt;img src="https://i.ibb.co.com/4wJLNjF1/Screenshot-2026-07-21-16-30-26-672-com-google-android-apps-docs.jpg" alt="Hasil akhir dokumen PDF berhasil dibuka tanpa error" style="width:100%; height:auto; border-radius:8px;"&gt;
                    &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--text-sub);"&gt;5. Hasil Akhir (File Terbuka)&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- FAQ Teknis --&gt;
    &lt;section class="content-section"&gt;
        &lt;h2 class="section-title"&gt;Tanya Jawab &lt;span&gt;Pemulihan Data&lt;/span&gt;&lt;/h2&gt;
        &lt;div class="divider"&gt;&lt;/div&gt;
        &lt;div class="faq-list"&gt;
            
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" aria-controls="faq-1" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128274;&lt;/span&gt; Apakah ini dapat mengatasi PDF yang terkunci sandi?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div id="faq-1" class="faq-a"&gt;
                    Tidak. Jika kerusakan terjadi pada file yang dilapisi algoritma enkripsi (membutuhkan kata sandi yang sah untuk dibuka), maka struktur heksadesimalnya sama sekali tidak bisa direkonstruksi oleh skrip pemulihan standar karena isinya telah diacak. Alat ini hanya efektif untuk dokumen yang tidak dilindungi sandi.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" aria-controls="faq-2" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;⚠️&lt;/span&gt; Mengapa halaman terakhir saya tetap kosong setelah diperbaiki?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div id="faq-2" class="faq-a"&gt;
                    Ini adalah fenomena "Data Truncation" (Pemotongan Data). Jika dokumen Anda terputus tepat saat Anda mengunduhnya dari server di masa lalu, maka byte informasi untuk halaman-halaman terakhir tersebut secara fisik tidak pernah ada di dalam file Anda. Kami tidak bisa memulihkan data yang sejak awal tidak terbentuk.
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" aria-controls="faq-3" onclick="toggleFaq(this)"&gt;
                    &lt;span style="display:flex; gap:12px; align-items:center;"&gt;
                        &lt;span aria-hidden="true"&gt;&#128737;️&lt;/span&gt; Seberapa aman file perusahaan saya saat diproses?
                    &lt;/span&gt;
                    &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;
                &lt;/button&gt;
                &lt;div id="faq-3" class="faq-a"&gt;
                    Sangat aman. Kami menggunakan metode pemrosesan murni secara &lt;em&gt;Client-Side&lt;/em&gt;. File perusahaan, faktur, maupun laporan yang rusak tidak pernah diunggah (upload) ke server eksternal mana pun. Seluruh operasi penyelamatan piksel dan teks terjadi di dalam ruang memori perangkat Anda sendiri.
                &lt;/div&gt;
            &lt;/div&gt;
            
        &lt;/div&gt;
    &lt;/section&gt;

&lt;/div&gt;&lt;!-- /content-wrap --&gt;

&lt;!-- ══ FOOTER ══ --&gt;
&lt;footer&gt;
  &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF Digital&lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt; |
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Hubungi Kami&lt;/a&gt;
  &lt;/p&gt;
  &lt;p style="margin-top:8px;"&gt;&amp;copy; 2026 Kholil Tools. Semua hak dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- ══ SCRIPTS ══ --&gt;
&lt;script&gt;
const $ = id =&gt; document.getElementById(id);
let originalBytes = null;
let originalFileName = '';

// Fungsi Lazy Loading Pustaka JS
function loadLibrary(url, objName) {
    return new Promise((resolve, reject) =&gt; {
        if (window[objName]) return resolve();
        const script = document.createElement('script');
        script.src = url;
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
}

// ── Drop zone ──
const dropZone = $('dropZone');
dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('active'); });
dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('active'));
dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('active');
    const file = e.dataTransfer.files[0];
    if (file) handleFile(file);
    else setStatus('❌ Harap lepaskan file PDF.', 'error');
});
$('pdfFile').addEventListener('change', e =&gt; { if (e.target.files[0]) handleFile(e.target.files[0]); });

async function handleFile(file) {
    // Sanitasi nama file untuk mencegah XSS
    const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
    originalFileName = file.name;
    
    $('fileName').innerHTML = '✔️ ' + safeName;
    dropZone.classList.add('active');
    $('downloadBtn').style.display = 'none';
    $('progressWrap').style.display = 'none';
    setStatus('&#128269; Mengunduh Pustaka Mesin &amp; Menganalisis file...', 'info');

    const reader = new FileReader();
    reader.onload = async e =&gt; {
        originalBytes = e.target.result;
        
        try {
            // Lazy load PDF.js engine untuk analisis awal
            await loadLibrary('https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js', 'pdfjsLib');
            pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
            analyzeFile(file, originalBytes, safeName);
        } catch (err) {
            setStatus('❌ Gagal memuat library pemroses PDF. Periksa koneksi internet Anda.', 'error');
        }
    };
    reader.readAsArrayBuffer(file);
}

function analyzeFile(file, buffer, safeName) {
    const bytes = new Uint8Array(buffer);
    const header = String.fromCharCode(...bytes.slice(0, 5));
    const isValidHeader = header === '%PDF-';
    const sizeMB = (file.size / 1024 / 1024).toFixed(2);

    $('dName').innerHTML = safeName;
    $('dSize').innerHTML = `${sizeMB} MB`;
    $('dHeader').innerHTML = isValidHeader ? '✔️ Valid (%PDF-)' : '❌ Tidak valid / bukan PDF';
    $('dHeader').className = 'diag-val ' + (isValidHeader ? 'ok' : 'bad');

    if (!isValidHeader) {
        $('dStatus').innerHTML = 'File sangat corrupt';
        $('dStatus').className = 'diag-val bad';
        setStatus('⚠️ Header PDF rusak. Proses perbaikan tetap akan dipaksa.', 'warn');
    } else {
        $('dStatus').innerHTML = 'Header Valid, siap diperbaiki';
        $('dStatus').className = 'diag-val warn';
        setStatus('&#128196; File terdeteksi. Tekan tombol Perbaiki sekarang.', 'info');
    }
    
    $('diagPanel').style.display = 'block';
    $('repairBtn').disabled = false;
}

function setStatus(msg, type) {
    const colors = { info: '#f57c00', success: '#2e7d32', error: '#c62828', warn: '#e65100' };
    $('status').innerHTML = msg ? `&lt;span style="color:${colors[type]||'#333'}"&gt;${msg}&lt;/span&gt;` : '';
}

function setProgress(label, pct) {
    $('progressWrap').style.display = 'block';
    $('progressLabel').textContent = label;
    $('progressPct').textContent = pct + '%';
    $('progressFill').style.width = pct + '%';
}

async function repairPDF() {
    if (!originalBytes) return;

    const btn = $('repairBtn');
    btn.disabled = true;
    btn.innerHTML = '⏳ Menyelamatkan Data...';
    $('downloadBtn').style.display = 'none';

    setProgress('Memanggil pustaka PDF-lib...', 5);
    setStatus('&#128260; Mencoba rekonstruksi XREF (Tabel Indeks)...', 'info');

    let repairedBytes = null;
    let method = '';

    try {
        // Panggil library PDF-lib saat tombol repair ditekan
        await loadLibrary('https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js', 'PDFLib');
        
        setProgress('Menyusun struktur heksadesimal...', 30);
        
        // Metode 1: Rekonstruksi struktur dasar
        const pdfDoc = await PDFLib.PDFDocument.load(originalBytes, {
            ignoreEncryption: true,
            updateMetadata: true
        });
        
        setProgress('Menyimpan hasil pemulihan...', 60);
        repairedBytes = await pdfDoc.save({ useObjectStreams: false, addDefaultPage: false });
        method = 'Rekonstruksi Dasar';
        setProgress('Metode pertama berhasil', 80);
        
    } catch (e1) {
        setStatus('&#128260; XREF terlalu hancur. Mencoba ekstraksi halaman paksa...', 'warn');
        setProgress('Mencoba pemaksaan ekstraksi...', 40);

        try {
            // Metode 2: Ekstraksi grafik paksa menggunakan PDF.js
            const pdfJs = await pdfjsLib.getDocument({
                data: originalBytes, stopAtErrors: false
            }).promise;
            
            const newPdf = await PDFLib.PDFDocument.create();
            setProgress('Memindahkan grafik halaman...', 60);

            let srcDoc;
            try {
                srcDoc = await PDFLib.PDFDocument.load(originalBytes, {
                    ignoreEncryption: true, throwOnInvalidObject: false
                });
            } catch(_) { srcDoc = null; }

            if (srcDoc) {
                const indices = Array.from({ length: srcDoc.getPageCount() }, (_, i) =&gt; i);
                const copied  = await newPdf.copyPages(srcDoc, indices);
                copied.forEach(p =&gt; newPdf.addPage(p));
            } else {
                for (let i = 0; i &lt; pdfJs.numPages; i++) { newPdf.addPage([595, 842]); }
            }

            repairedBytes = await newPdf.save();
            method = 'Pemaksaan Ekstraksi Grafis';
            setProgress('Penyelamatan grafis selesai...', 90);
            
        } catch (e2) {
            setProgress('', 0);
            $('progressWrap').style.display = 'none';
            setStatus('❌ Gagal. Dokumen hancur secara total atau terenkripsi AES.', 'error');
            btn.disabled = false;
            btn.innerHTML = '&#128736;️ Perbaiki PDF Sekarang';
            return;
        }
    }

    setProgress('Tuntas!', 100);
    const blob = new Blob([repairedBytes], { type: 'application/pdf' });
    const url  = URL.createObjectURL(blob);
    
    $('downloadBtn').href = url;
    $('downloadBtn').download = originalFileName.replace(/\.pdf$/i, '') + '_pulih.pdf';
    $('downloadBtn').style.display = 'inline-block';
    
    setStatus(`✅ Pemulihan sukses via algoritma: &lt;em&gt;${method}&lt;/em&gt;.&lt;br&gt;&lt;small&gt;Silakan klik tombol download di atas.&lt;/small&gt;`, 'success');
    
    btn.disabled = false;
    btn.innerHTML = '&#128736;️ Perbaiki PDF Sekarang';
}

// Aksesibilitas WCAG: aria-expanded
function toggleFaq(btn) {
    const a = btn.nextElementSibling;
    const open = a.classList.contains('visible');
    
    // Tutup seluruh panel lain
    document.querySelectorAll('.faq-a').forEach(x =&gt; x.classList.remove('visible'));
    document.querySelectorAll('.faq-q').forEach(x =&gt; {
        x.classList.remove('open');
        x.setAttribute('aria-expanded', 'false');
    });
    
    // Buka panel yang dituju
    if (!open) { 
        a.classList.add('visible'); 
        btn.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
    }
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/repair-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3716422621916782911</guid><pubDate>Fri, 10 Apr 2026 08:56:34 +0000</pubDate><atom:updated>2026-07-24T07:43:26.289-07:00</atom:updated><title>PDF to PDF/A Converter</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;meta name="description" content="Konversi PDF ke PDF/A-1b, PDF/A-2b, atau PDF/A-3b untuk kebutuhan pengarsipan jangka panjang standar ISO 19005. 100% online gratis, diproses aman di memori browser."&gt;
    &lt;title&gt;PDF ke PDF/A Converter Online Gratis | Standar ISO 19005&lt;/title&gt;

    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/pdf-to-pdfa.html" /&gt;
    &lt;meta property="og:title" content="PDF ke PDF/A Converter Online Gratis | Standar ISO 19005" /&gt;
    &lt;meta property="og:description" content="Konversi PDF biasa ke PDF/A untuk pengarsipan jangka panjang tanpa upload ke server. Aman dan 100% gratis." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/pdf-to-pdfa.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;

    &lt;!-- Preconnect &amp; Fonts --&gt;
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema Markup untuk WebApp &amp; E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "PDF to PDF/A Converter",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" },
      "author": { "@type": "Person", "name": "Kholil", "url": "https://www.kholil.biz.id/" }
    }
    &lt;/script&gt;
    
    &lt;!-- Schema Markup untuk FAQ --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Apakah hasil konversi ini benar-benar valid sebagai PDF/A ISO 19005?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Konverter ini menambahkan metadata XMP yang divalidasi dan mengoptimalkan struktur dokumen sesuai spesifikasi dasar PDF/A. Namun, karena kami tidak memodifikasi objek multimedia ekstrem di dalam dokumen Anda, kami sangat menyarankan Anda untuk memverifikasi ulang hasil unduhan dengan perangkat lunak open-source seperti veraPDF untuk jaminan ISO 19005 penuh."
          }
        },
        {
          "@type": "Question",
          "name": "Kenapa dokumen PDF/A saya tidak bisa diedit setelah diunduh?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Sifat dasar format PDF/A (Archive) adalah Read-Only secara esensi. Format ini dirancang agar dokumen tidak dapat diubah (tamper-proof) puluhan tahun ke depan. Jika Anda perlu mengubah isinya, gunakan file PDF aslinya."
          }
        },
        {
          "@type": "Question",
          "name": "Kenapa saya gagal mengonversi file yang corrupt?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Proses penyisipan metadata XMP PDF/A membutuhkan tabel referensi dokumen yang utuh. Jika file Anda berstatus corrupt atau strukturnya hancur, skrip kami akan menolaknya. Silakan pulihkan dokumen Anda terlebih dahulu menggunakan layanan Repair PDF kami sebelum mengonversinya menjadi format arsip."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        :root {
            --teal-900: #00352c;
            --teal-700: #00695c;
            --teal-500: #26a69a;
            --teal-100: #e0f2f1;
            --teal-50:  #f0faf9;
            --ink:      #1a1f2e;
            --muted:    #5a6270;
            --border:   #d4e9e7;
            --white:    #ffffff;
            --green:    #2e7d32;
            --red:      #c62828;
            --radius-lg: 18px;
            --radius-md: 12px;
            --radius-sm: 8px;
            --shadow-card: 0 4px 24px rgba(0,53,44,0.10);
            --shadow-lg:   0 12px 48px rgba(0,53,44,0.14);
        }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--teal-50);
            color: var(--ink);
            line-height: 1.75;
        }

        /* ── NAV ── */
        nav {
            background: var(--white);
            border-bottom: 1px solid var(--border);
            padding: 0 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            height: 58px;
            position: sticky;
            top: 0;
            z-index: 99;
        }
        .nav-brand {
            font-family: 'Sora', sans-serif;
            font-size: 1.18rem;
            font-weight: 800;
            color: var(--teal-700);
            text-decoration: none;
            letter-spacing: -0.5px;
        }
        .nav-brand span { color: var(--teal-500); }
        .nav-links { display: flex; gap: 24px; }
        .nav-links a {
            font-size: 0.9rem;
            font-weight: 500;
            color: var(--muted);
            text-decoration: none;
        }
        .nav-links a:hover { color: var(--teal-700); }

        /* Hamburger Button */
        .nav-toggle {
            display: none;
            flex-direction: column;
            gap: 5px;
            cursor: pointer;
            padding: 4px;
            background: none;
            border: none;
        }
        .nav-toggle span {
            display: block;
            width: 24px;
            height: 2px;
            background: var(--teal-700);
            border-radius: 2px;
        }

        /* Mobile menu */
        .mobile-menu {
            display: none;
            position: fixed;
            top: 58px;
            left: 0;
            right: 0;
            background: var(--white);
            border-bottom: 1px solid var(--border);
            z-index: 98;
            padding: 16px 24px;
            flex-direction: column;
            gap: 0;
            box-shadow: 0 8px 24px rgba(0,53,44,0.10);
        }
        .mobile-menu.open { display: flex; }
        .mobile-menu a {
            font-size: 0.95rem;
            font-weight: 500;
            color: var(--ink);
            text-decoration: none;
            padding: 12px 0;
            border-bottom: 1px solid var(--border);
        }
        .mobile-menu a:last-child { border-bottom: none; }
        .mobile-menu a:hover { color: var(--teal-700); }

        /* ── HERO ── */
        .hero {
            background: linear-gradient(135deg, var(--teal-900) 0%, var(--teal-700) 100%);
            color: white;
            text-align: center;
            padding: 64px 24px 56px;
        }
        .hero-eyebrow {
            display: inline-block;
            background: rgba(255,255,255,0.15);
            border: 1px solid rgba(255,255,255,0.3);
            border-radius: 50px;
            padding: 5px 18px;
            font-size: 0.82rem;
            font-weight: 600;
            letter-spacing: 0.8px;
            text-transform: uppercase;
            margin-bottom: 20px;
        }
        .hero h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.2rem);
            font-weight: 800;
            line-height: 1.15;
            margin-bottom: 16px;
            letter-spacing: -1px;
        }
        .hero p {
            font-size: 1.1rem;
            opacity: 0.88;
            max-width: 600px;
            margin: 0 auto;
        }

        /* ── MAIN LAYOUT ── */
        .page-body {
            max-width: 860px;
            margin: 0 auto;
            padding: 40px 20px 80px;
        }

        /* ── AUTHOR BOX E-E-A-T ── */
        .author-box { 
            display: flex; align-items: center; gap: 15px; 
            background: var(--white); padding: 20px; 
            border-radius: var(--radius-md); border: 1px solid var(--border); 
            margin-bottom: 30px; border-left: 4px solid var(--teal-700); 
            box-shadow: var(--shadow-card); 
        }
        .author-img { 
            width: 50px; height: 50px; background: var(--teal-700); 
            border-radius: 50%; display: flex; align-items: center; 
            justify-content: center; color: white; font-weight: bold; font-size: 1.2rem; 
        }
        .author-meta { font-size: 0.9rem; color: var(--muted); }
        .author-meta strong { color: var(--ink); display: block; font-size: 1rem; }

        /* ── TOOL CARD ── */
        .tool-card {
            background: var(--white);
            border-radius: var(--radius-lg);
            box-shadow: var(--shadow-lg);
            padding: 36px;
            margin-bottom: 40px;
        }

        .drop-zone {
            border: 2.5px dashed var(--teal-500);
            border-radius: var(--radius-md);
            padding: 48px 24px;
            background: var(--teal-50);
            text-align: center;
            transition: background 0.2s, border-color 0.2s;
            cursor: pointer;
            margin-bottom: 24px;
        }
        .drop-zone:hover, .drop-zone.dragover {
            background: var(--teal-100);
            border-color: var(--teal-700);
        }
        .drop-icon { font-size: 2.6rem; margin-bottom: 12px; }
        .drop-zone h3 {
            font-size: 1.05rem;
            font-weight: 600;
            color: var(--teal-900);
            margin-bottom: 6px;
        }
        .drop-zone p { font-size: 0.9rem; color: var(--muted); }
        .file-label {
            display: inline-block;
            background: var(--teal-700);
            color: white;
            padding: 10px 28px;
            border-radius: 50px;
            font-size: 0.92rem;
            font-weight: 600;
            cursor: pointer;
            margin-top: 14px;
            transition: background 0.2s;
        }
        .file-label:hover { background: var(--teal-900); }
        input[type="file"] { display: none; }

        #fileName {
            font-size: 0.95rem;
            font-weight: 600;
            color: var(--teal-700);
            margin-top: 12px;
            min-height: 24px;
        }

        .version-row { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 24px; }
        .version-label { font-size: 0.85rem; font-weight: 600; color: var(--muted); margin-bottom: 10px; display: block; }
        .ver-btn {
            padding: 9px 22px;
            border: 2px solid var(--border);
            border-radius: 50px;
            background: white;
            color: var(--ink);
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s;
        }
        .ver-btn:hover { border-color: var(--teal-500); color: var(--teal-700); }
        .ver-btn.active { background: var(--teal-700); border-color: var(--teal-700); color: white; }

        .btn-convert {
            width: 100%;
            background: linear-gradient(135deg, var(--teal-700), var(--teal-900));
            color: white;
            border: none;
            padding: 16px;
            font-size: 1.05rem;
            font-weight: 700;
            border-radius: var(--radius-md);
            cursor: pointer;
            transition: opacity 0.2s;
            letter-spacing: 0.2px;
        }
        .btn-convert:hover { opacity: 0.9; }
        .btn-convert:disabled { opacity: 0.6; cursor: not-allowed; }

        #downloadBtn {
            display: none;
            width: 100%;
            background: linear-gradient(135deg, #388e3c, #1b5e20);
            color: white;
            padding: 16px;
            font-size: 1.05rem;
            font-weight: 700;
            border-radius: var(--radius-md);
            text-decoration: none;
            text-align: center;
            margin-top: 12px;
            transition: opacity 0.2s;
        }
        #downloadBtn:hover { opacity: 0.9; }

        #status {
            margin-top: 16px;
            font-size: 0.95rem;
            font-weight: 500;
            min-height: 24px;
            text-align: center;
        }
        .status-ok  { color: var(--green); }
        .status-err { color: var(--red); }

        #progressWrap { display: none; margin-top: 16px; }
        #progressBar { height: 6px; background: var(--teal-100); border-radius: 99px; overflow: hidden; }
        #progressFill {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, var(--teal-500), var(--teal-700));
            border-radius: 99px;
            transition: width 0.4s ease;
        }

        .badge-row { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border); }
        .badge { display: flex; align-items: center; gap: 7px; font-size: 0.82rem; color: var(--muted); font-weight: 500; }
        .badge-icon { font-size: 1rem; }

        /* ── ARTICLE CONTENT ── */
        .content-section {
            background: var(--white);
            border-radius: var(--radius-lg);
            box-shadow: var(--shadow-card);
            padding: 40px;
            margin-bottom: 28px;
        }
        .content-section h2 {
            font-family: 'Sora', sans-serif;
            font-size: 1.45rem;
            font-weight: 800;
            color: var(--teal-900);
            margin-bottom: 16px;
            letter-spacing: -0.4px;
            padding-bottom: 10px;
            border-bottom: 3px solid var(--teal-100);
        }
        .content-section h3 { font-size: 1.05rem; font-weight: 700; color: var(--teal-700); margin: 24px 0 8px; }
        .content-section p { font-size: 0.97rem; color: #3d4452; margin-bottom: 14px; line-height: 1.8; }
        .content-section ul, .content-section ol { padding-left: 22px; margin-bottom: 14px; }
        .content-section li { font-size: 0.97rem; color: #3d4452; margin-bottom: 8px; line-height: 1.75; }

        .compare-table { width: 100%; border-collapse: collapse; margin: 20px 0; font-size: 0.9rem; }
        .compare-table th { background: var(--teal-700); color: white; padding: 12px 16px; text-align: left; font-weight: 600; }
        .compare-table th:first-child { border-radius: var(--radius-sm) 0 0 0; }
        .compare-table th:last-child  { border-radius: 0 var(--radius-sm) 0 0; }
        .compare-table td { padding: 11px 16px; border-bottom: 1px solid var(--border); color: var(--ink); vertical-align: top; }
        .compare-table tr:nth-child(even) td { background: var(--teal-50); }
        .compare-table tr:last-child td { border-bottom: none; }
        .tag-yes { color: var(--green); font-weight: 700; }
        .tag-no  { color: var(--red);   font-weight: 700; }

        .steps { counter-reset: step; padding: 0; list-style: none; }
        .steps li { counter-increment: step; display: flex; gap: 16px; margin-bottom: 20px; align-items: flex-start; }
        .steps li::before {
            content: counter(step);
            flex-shrink: 0;
            width: 32px;
            height: 32px;
            background: var(--teal-700);
            color: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: 700;
            font-size: 0.88rem;
            margin-top: 2px;
        }
        .steps li strong { display: block; font-size: 0.97rem; margin-bottom: 3px; }
        .steps li span { font-size: 0.9rem; color: var(--muted); }

        /* ── FAQ – FIXED BUTTONS ── */
        .faq-item { border-bottom: 1px solid var(--border); padding: 18px 0; }
        .faq-item:last-child { border-bottom: none; }
        .faq-q {
            width: 100%;
            text-align: left;
            background: none;
            border: none;
            font-family: inherit;
            font-weight: 700;
            font-size: 0.97rem;
            color: var(--teal-900);
            padding: 0;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 12px;
            user-select: none;
        }
        .faq-q:focus { outline: 2px solid var(--teal-500); outline-offset: 4px; border-radius: 4px; }
        .faq-icon {
            flex-shrink: 0;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            background: var(--teal-100);
            color: var(--teal-700);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1rem;
            font-weight: 700;
            line-height: 1;
            transition: background 0.2s, transform 0.2s;
        }
        .faq-q.open .faq-icon { background: var(--teal-700); color: white; transform: rotate(45deg); }
        .faq-a { font-size: 0.93rem; color: #4a5260; line-height: 1.8; display: none; padding-top: 10px; }
        .faq-a.open { display: block; }

        .highlight-box {
            background: var(--teal-50);
            border-left: 4px solid var(--teal-500);
            border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
            padding: 16px 20px;
            margin: 20px 0;
        }
        .highlight-box p { margin: 0; font-size: 0.93rem; color: var(--teal-900); }

        footer {
            background: var(--teal-900);
            color: rgba(255,255,255,0.7);
            text-align: center;
            padding: 36px 24px;
            font-size: 0.88rem;
        }
        footer a { color: rgba(255,255,255,0.85); text-decoration: none; }
        footer a:hover { color: white; }
        footer .footer-logo { font-family: 'Sora', sans-serif; font-size: 1.3rem; font-weight: 800; color: white; margin-bottom: 10px; }
        footer .footer-logo span { color: var(--teal-500); }
        .footer-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 20px; margin-top: 16px; }

        @media (max-width: 600px) {
            .tool-card, .content-section { padding: 24px 18px; }
            .nav-links { display: none; }
            .nav-toggle { display: flex; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;nav&gt;
    &lt;a class="nav-brand" href="https://www.kholil.biz.id/"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html"&gt;Scan to PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt;
    &lt;/div&gt;
    &lt;!-- Tombol Hamburger dengan Aksesibilitas --&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
&lt;/nav&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/scan-to-pdf.html"&gt;&#128444;️ Scan to PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;&#128290; Page Numbers&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;&#128736;️ Repair PDF&lt;/a&gt;
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-eyebrow"&gt;&#128450; Standar ISO 19005 &amp;middot; 100% Aman&lt;/div&gt;
    &lt;h1&gt;Konversi PDF ke PDF/A&lt;br&gt;Secara Instan&lt;/h1&gt;
    &lt;p&gt;Ubah dokumen PDF biasa menjadi format PDF/A untuk arsip elektronik jangka panjang. Proses langsung di dalam RAM perangkat Anda &amp;mdash; tidak ada dokumen yang diunggah ke jaringan publik.&lt;/p&gt;
&lt;/div&gt;

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

    &lt;!-- AUTHOR BOX E-E-A-T --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
            &lt;span style="display:block;"&gt;Technical SEO &amp; Archiving Specialist | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- TOOL --&gt;
    &lt;div class="tool-card"&gt;
        &lt;div class="drop-zone" id="dropArea"&gt;
            &lt;div class="drop-icon"&gt;&#128196;&lt;/div&gt;
            &lt;h3&gt;Seret &amp;amp; Jatuhkan File PDF ke Sini&lt;/h3&gt;
            &lt;p&gt;atau klik tombol di bawah untuk memilih file&lt;/p&gt;
            &lt;label class="file-label"&gt;
                Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf"&gt;
            &lt;/label&gt;
            &lt;p id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;span class="version-label"&gt;Pilih Spesifikasi PDF/A:&lt;/span&gt;
        &lt;div class="version-row"&gt;
            &lt;button class="ver-btn active" data-version="2b" onclick="selectVersion(this)"&gt;PDF/A-2b &lt;small style="opacity:0.7;font-weight:400"&gt;(Disarankan)&lt;/small&gt;&lt;/button&gt;
            &lt;button class="ver-btn" data-version="1b" onclick="selectVersion(this)"&gt;PDF/A-1b&lt;/button&gt;
            &lt;button class="ver-btn" data-version="3b" onclick="selectVersion(this)"&gt;PDF/A-3b&lt;/button&gt;
        &lt;/div&gt;

        &lt;button class="btn-convert" id="convertBtn" onclick="convertToPDFA()"&gt;&amp;#x1F504; Susun Metadata PDF/A Sekarang&lt;/button&gt;
        &lt;a id="downloadBtn" href="#"&gt;&amp;#x2B07;&amp;#xFE0F; Unduh Hasil Konversi PDF/A&lt;/a&gt;

        &lt;div id="progressWrap"&gt;
            &lt;div id="progressBar"&gt;&lt;div id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
        &lt;/div&gt;
        &lt;div id="status"&gt;&lt;/div&gt;

        &lt;div class="badge-row"&gt;
            &lt;div class="badge"&gt;&lt;span class="badge-icon"&gt;&#128274;&lt;/span&gt; Privasi Tanpa Upload&lt;/div&gt;
            &lt;div class="badge"&gt;&lt;span class="badge-icon"&gt;⚡&lt;/span&gt; Eksekusi Client-Side&lt;/div&gt;
            &lt;div class="badge"&gt;&lt;span class="badge-icon"&gt;&#127379;&lt;/span&gt; 100% Gratis&lt;/div&gt;
            &lt;div class="badge"&gt;&lt;span class="badge-icon"&gt;&#128421;️&lt;/span&gt; Stabil Lintas OS&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- PANDUAN --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Metode Konversi Standar Arsip&lt;/h2&gt;
        &lt;p&gt;Proses penyelarasan dokumen ke standar ISO sangat lugas menggunakan infrastruktur kami tanpa memerlukan instalasi perangkat lunak desktop. Berikut alurnya:&lt;/p&gt;
        &lt;ol class="steps"&gt;
            &lt;li&gt;
                &lt;div&gt;
                    &lt;strong&gt;Unggah Dokumen Inti&lt;/strong&gt;
                    &lt;span&gt;Seret dokumen dari peramban Anda. Pastikan dokumen tersebut bukanlah file corrupt. Jika file Anda tidak valid, silakan lakukan proses &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt; terlebih dahulu di utilitas kami sebelum melanjutkan pengarsipan.&lt;/span&gt;
                &lt;/div&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;div&gt;
                    &lt;strong&gt;Pilih Spesifikasi Kepatuhan&lt;/strong&gt;
                    &lt;span&gt;Terdapat tiga arsitektur: PDF/A-1b, PDF/A-2b, dan PDF/A-3b. Untuk kebutuhan manajemen arsip publik, &lt;strong&gt;PDF/A-2b&lt;/strong&gt; merupakan fondasi terbaik karena kemampuannya merender transparansi lapisan dan menangani kompresi resolusi yang efisien.&lt;/span&gt;
                &lt;/div&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;div&gt;
                    &lt;strong&gt;Suntikkan Metadata XMP&lt;/strong&gt;
                    &lt;span&gt;Tekan tombol eksekusi. Skrip kami akan membongkar &lt;em&gt;byte&lt;/em&gt; file Anda secara lokal untuk menyisipkan paket deskriptor XML (XMP) yang menyatakan kepatuhan terhadap standar ISO.&lt;/span&gt;
                &lt;/div&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;div&gt;
                    &lt;strong&gt;Unduh Arsip Final&lt;/strong&gt;
                    &lt;span&gt;Sistem akan memunculkan file PDF baru. Dokumen ini telah terisolasi agar siap disimpan untuk jangka panjang (Read-Only) sehingga tidak mudah diutak-atik.&lt;/span&gt;
                &lt;/div&gt;
            &lt;/li&gt;
        &lt;/ol&gt;
        &lt;div class="highlight-box"&gt;
            &lt;p&gt;&#128161; &lt;strong&gt;Catatan Teknis:&lt;/strong&gt; Jika dokumen Anda dilindungi oleh metode kriptografi kata sandi, lapisan XMP tidak akan bisa disisipkan. Bukalah gembok perlindungan dokumen Anda sebelum melanjutkan.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Bukti Kinerja: &lt;span&gt;Konversi Berhasil&lt;/span&gt;&lt;/h2&gt;
        &lt;p&gt;Kami menjamin transparansi fungsionalitas alat ini. Berikut adalah dokumentasi pengujian langsung dari perangkat seluler standar terhadap sebuah file PDF. Sistem kami secara mandiri berhasil memvalidasi, menyuntikkan metadata ISO, dan menyajikan file PDF/A yang siap diunduh.&lt;/p&gt;
        
        &lt;div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap:15px; margin-top:20px;"&gt;
            &lt;!-- Gambar 1: Upload --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/MD3tjWxW/Screenshot-2026-07-21-16-54-13-364-com-android-chrome.jpg" alt="Bukti upload dokumen PDF berjalan mulus ditandai nama file" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;1. Upload File Berhasil&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 2: Proses --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/zTn4JrtS/Screenshot-2026-07-21-16-54-29-420-com-android-chrome.jpg" alt="Proses konversi file PDF ke PDF/A berhasil" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;2. Konversi Selesai&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 3: Download --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/dC3kPXv/Screenshot-2026-07-21-16-54-35-471-com-android-chrome.jpg" alt="Tombol download berfungsi dan responsif" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;3. Tombol Download Aktif&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 4: Hasil --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/rKZd1x9k/Screenshot-2026-07-21-16-54-41-509-com-google-android-apps-docs.jpg" alt="Hasil akhir perbaikan file PDF/A" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;4. Hasil Dokumen Valid&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- APA ITU PDF/A --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Apa Itu PDF/A dan Mengapa Penting?&lt;/h2&gt;
        &lt;p&gt;PDF/A adalah singkatan dari &lt;strong&gt;Portable Document Format / Archive&lt;/strong&gt;, sebuah versi khusus dari format PDF yang dirancang dan distandarisasi secara ketat oleh ISO melalui &lt;strong&gt;ISO 19005&lt;/strong&gt;. Diperkenalkan pertama kali pada tahun 2005, format ini kini menjadi pondasi hukum internasional untuk pengarsipan rekaman elektronik.&lt;/p&gt;
        &lt;p&gt;PDF biasa mungkin akan kehilangan font atau warna jika dibuka sepuluh tahun dari sekarang di komputer yang berbeda. PDF/A mengunci seluruh entitas (font, profil warna, tabel) ke dalam tubuh file, guna memastikan dokumen tersebut &lt;strong&gt;pasti dapat dibaca secara identik puluhan hingga ratusan tahun kemudian&lt;/strong&gt; terlepas dari perangkat lunak yang digunakan.&lt;/p&gt;

        &lt;h3&gt;Perbedaan Arsitektural PDF Biasa vs PDF/A&lt;/h3&gt;
        &lt;table class="compare-table"&gt;
            &lt;thead&gt;
                &lt;tr&gt;&lt;th&gt;Spesifikasi&lt;/th&gt;&lt;th&gt;PDF Biasa&lt;/th&gt;&lt;th&gt;PDF/A&lt;/th&gt;&lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                &lt;tr&gt;&lt;td&gt;Font tertanam utuh (Embedded)&lt;/td&gt;&lt;td&gt;&lt;span class="tag-no"&gt;Opsional&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="tag-yes"&gt;Wajib&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Profil warna ICC terkalibrasi&lt;/td&gt;&lt;td&gt;&lt;span class="tag-no"&gt;Tidak wajib&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="tag-yes"&gt;Wajib&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Konten multimedia (Audio/Video)&lt;/td&gt;&lt;td&gt;&lt;span class="tag-yes"&gt;Diizinkan&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="tag-no"&gt;Dilarang secara ketat&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Enkripsi Kata Sandi (Password)&lt;/td&gt;&lt;td&gt;&lt;span class="tag-yes"&gt;Bisa diterapkan&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="tag-no"&gt;Tidak sah / Dilarang&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Eksekusi JavaScript&lt;/td&gt;&lt;td&gt;&lt;span class="tag-yes"&gt;Diizinkan&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="tag-no"&gt;Dilarang&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Lapisan Metadata XMP Standar&lt;/td&gt;&lt;td&gt;&lt;span class="tag-no"&gt;Opsional&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="tag-yes"&gt;Wajib Terdapat di Header&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
    &lt;/div&gt;

    &lt;!-- VERSI --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Klasifikasi PDF/A: Evolusi ISO 19005&lt;/h2&gt;

        &lt;h3&gt;PDF/A-1b (Tahun Rilis 2005)&lt;/h3&gt;
        &lt;p&gt;Generasi pertama. Format ini sangat ketat dan tidak mengizinkan transparansi objek (seperti logo watermark berbayang) maupun efek grafis berlapis.&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Ideal untuk:&lt;/strong&gt; Dokumen teks murni hasil pindaian (scan), atau surat keterangan statis tanpa grafik kompleks.&lt;/p&gt;

        &lt;h3&gt;PDF/A-2b (Tahun Rilis 2011) ⭐ Sangat Direkomendasikan&lt;/h3&gt;
        &lt;p&gt;Pembaharuan besar untuk memenuhi kebutuhan dokumen modern. Ini memperbolehkan transparansi, lapisan (OCG), dan yang paling vital, dukungan kompresi JPEG 2000 untuk mengecilkan ukuran arsip gambar.&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Ideal untuk:&lt;/strong&gt; Hampir segala jenis dokumen kontemporer (laporan grafis, proposal tata letak visual, brosur desain).&lt;/p&gt;

        &lt;h3&gt;PDF/A-3b (Tahun Rilis 2012)&lt;/h3&gt;
        &lt;p&gt;Menghadirkan fleksibilitas pengarsipan ganda. Versi ini memperbolehkan penyematan file eksternal (seperti XML, format Excel mentah) terbungkus di dalam wadah file PDF.&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Ideal untuk:&lt;/strong&gt; Faktur elektronik (E-Invoicing) pemerintah yang membutuhkan lampiran data tagihan terstruktur.&lt;/p&gt;
    &lt;/div&gt;

    &lt;!-- KEAMANAN --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Keamanan &amp;amp; Protokol Pemrosesan Privat&lt;/h2&gt;
        &lt;p&gt;Skrip kami menghindari jalur arsitektur SaaS tradisional demi mengamankan kepemilikan data Anda:&lt;/p&gt;
        &lt;ul&gt;
            &lt;li&gt;✅ &lt;strong&gt;Zero-Transmission:&lt;/strong&gt; Data Anda tidak berpindah melintasi kabel internet. Berbeda dengan platform lainnya, kami merakit metadata arsip PDF/A langsung menggunakan CPU peramban lokal.&lt;/li&gt;
            &lt;li&gt;✅ &lt;strong&gt;Non-Retention:&lt;/strong&gt; Memori peramban disapu bersih segera setelah dokumen selesai diunduh.&lt;/li&gt;
            &lt;li&gt;✅ &lt;strong&gt;Kesesuaian Data Sensitif:&lt;/strong&gt; Karena bebas peretasan Man-in-the-Middle (MitM), alat ini secara legal sangat aman bagi akta perseroan, paten, dan catatan medis.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/div&gt;

    &lt;!-- FAQ TEKNIS --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Pertanyaan Teknis Pengarsipan (FAQ)&lt;/h2&gt;

        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                Apakah hasil konversi ini benar-benar valid sebagai PDF/A ISO 19005?
                &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class="faq-a"&gt;Konverter ini menambahkan metadata XMP yang divalidasi dan mengoptimalkan struktur dokumen sesuai spesifikasi dasar PDF/A. Namun, karena kami tidak memodifikasi objek multimedia ekstrem di dalam dokumen Anda, kami sangat menyarankan Anda untuk memverifikasi ulang hasil unduhan dengan perangkat lunak open-source seperti veraPDF untuk jaminan kepatuhan ISO 19005 secara menyeluruh sebelum diserahkan ke sistem pemerintahan.&lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                Kenapa dokumen PDF/A saya tidak bisa diedit setelah diunduh?
                &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class="faq-a"&gt;Sifat dasar format PDF/A (Archive) adalah "Read-Only" secara esensi. Format ini dirancang agar dokumen tidak dapat diubah (tamper-proof) puluhan tahun ke depan, yang melindunginya dari modifikasi pihak tidak bertanggung jawab. Jika Anda perlu mengubah isinya, gunakan file PDF aslinya lalu simpan ulang sebagai PDF/A.&lt;/div&gt;
        &lt;/div&gt;
        
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                Kenapa saya gagal mengonversi file yang corrupt?
                &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class="faq-a"&gt;Proses penyisipan metadata XMP PDF/A membutuhkan tabel referensi dokumen (XREF) yang utuh agar tidak menabrak baris byte vital. Jika file Anda berstatus corrupt atau strukturnya hancur, skrip kami akan menolaknya. Silakan pulihkan dokumen Anda terlebih dahulu menggunakan layanan &lt;a href="https://www.kholil.biz.id/2026/04/repair-pdf.html"&gt;Repair PDF&lt;/a&gt; kami sebelum mengonversinya menjadi format arsip resmi.&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- TIPS --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Prosedur Praktik Terbaik Pengarsipan ISO&lt;/h2&gt;
        &lt;h3&gt;1. Persiapan Dokumen Pra-Arsip&lt;/h3&gt;
        &lt;p&gt;Banyak instansi pemerintahan tidak menerima hasil pindaian (scan) biasa jika tidak disertai penomoran logis. Tambahkan penomoran berurutan menggunakan &lt;a href="https://www.kholil.biz.id/2026/04/page-numbers.html"&gt;Page Numbers&lt;/a&gt; sebelum melapisinya dengan PDF/A.&lt;/p&gt;
        &lt;h3&gt;2. Tatanan Penamaan Arsip&lt;/h3&gt;
        &lt;p&gt;Gunakan tata nama yang kaku dan kronologis. Hindari penamaan &lt;code&gt;dokumen_akhir.pdf&lt;/code&gt;. Gunakan sintaks seperti &lt;code&gt;Laporan_Keuangan_Kuartal_III_2026.pdfa.pdf&lt;/code&gt;.&lt;/p&gt;
        &lt;h3&gt;3. Hindari Penumpukan Redundansi Enkripsi&lt;/h3&gt;
        &lt;p&gt;Ingatlah bahwa ISO 19005 melarang keras penggunaan enkripsi bawaan PDF untuk alasan pemeliharaan jangka panjang. Jangan pernah memaksakan injeksi password jika tujuan Anda adalah arsip historis permanen.&lt;/p&gt;
    &lt;/div&gt;

&lt;/div&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
    &lt;div class="footer-logo"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/div&gt;
    &lt;p&gt;Solusi Utilitas PDF Digital Standar Kearsipan&lt;/p&gt;
    &lt;div class="footer-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Kontak&lt;/a&gt;
    &lt;/div&gt;
    &lt;p style="margin-top:16px;opacity:0.55;"&gt;&amp;#169; 2026 Kholil Tools. Seluruh hak cipta dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- LIBRARIES --&gt;
&lt;script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"&gt;&lt;/script&gt;

&lt;script&gt;
    let originalBytes = null;
    let originalFileName = "";
    let selectedVersion = "2b";

    const dropArea    = document.getElementById('dropArea');
    const fileInput   = document.getElementById('pdfFile');
    const fileNameEl  = document.getElementById('fileName');
    const statusEl    = document.getElementById('status');
    const downloadBtn = document.getElementById('downloadBtn');
    const progressWrap = document.getElementById('progressWrap');
    const progressFill = document.getElementById('progressFill');

    /* Drag &amp; Drop */
    dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('dragover'); });
    dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('dragover'));
    dropArea.addEventListener('drop', e =&gt; {
        e.preventDefault();
        dropArea.classList.remove('dragover');
        const file = e.dataTransfer.files[0];
        if (file &amp;&amp; file.type === "application/pdf") handleFile(file);
        else setStatus("Format tidak dikenali. Harap pilih file PDF (.pdf).", "err");
    });

    fileInput.addEventListener('change', e =&gt; {
        if (e.target.files[0]) handleFile(e.target.files[0]);
    });

    function selectVersion(btn) {
        document.querySelectorAll('.ver-btn').forEach(b =&gt; b.classList.remove('active'));
        btn.classList.add('active');
        selectedVersion = btn.dataset.version;
    }

    function handleFile(file) {
        // Mencegah XSS pada render nama file
        const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
        originalFileName = file.name;
        fileNameEl.innerHTML = "✔️ " + safeName + " (" + (file.size / 1024).toFixed(1) + " KB)";
        setStatus("File siap dikonversi.", "");
        downloadBtn.style.display = "none";
        const reader = new FileReader();
        reader.onload = e =&gt; { originalBytes = e.target.result; };
        reader.readAsArrayBuffer(file);
    }

    function setStatus(msg, type) {
        statusEl.className = type === "ok" ? "status-ok" : type === "err" ? "status-err" : "";
        statusEl.innerHTML = msg;
    }

    async function convertToPDFA() {
        if (!originalBytes) { alert("Silakan pilih file PDF terlebih dahulu."); return; }

        const btn = document.getElementById('convertBtn');
        btn.disabled = true;
        btn.innerHTML = "⏳ Menginjeksikan Metadata...";
        downloadBtn.style.display = "none";

        progressWrap.style.display = "block";
        let prog = 0;
        const interval = setInterval(() =&gt; {
            prog = Math.min(prog + 12, 85);
            progressFill.style.width = prog + "%";
        }, 200);

        setStatus("Memproses arsitektur PDF/A-" + selectedVersion + "...", "");

        try {
            const pdfDoc = await PDFLib.PDFDocument.load(originalBytes, { ignoreEncryption: false });

            pdfDoc.setTitle(originalFileName.replace('.pdf',''));
            pdfDoc.setProducer("Kholil Tools – Sistem ISO 19005");
            pdfDoc.setCreator("Kholil Tools Engine");
            pdfDoc.setCreationDate(new Date());
            pdfDoc.setModificationDate(new Date());

            const conformanceLevel = selectedVersion === "1b" ? "1" : selectedVersion === "3b" ? "3" : "2";
            const now = new Date().toISOString();
            const xmpMeta = `&lt;?xpacket begin="\uFEFF" id="W5M0MpCehiHzreSzNTczkc9d"?&gt;
&lt;x:xmpmeta xmlns:x="adobe:ns:meta/"&gt;
  &lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;
    &lt;rdf:Description rdf:about=""
        xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"&gt;
      &lt;pdfaid:part&gt;${conformanceLevel}&lt;/pdfaid:part&gt;
      &lt;pdfaid:conformance&gt;B&lt;/pdfaid:conformance&gt;
    &lt;/rdf:Description&gt;
    &lt;rdf:Description rdf:about=""
        xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;
      &lt;dc:format&gt;application/pdf&lt;/dc:format&gt;
    &lt;/rdf:Description&gt;
    &lt;rdf:Description rdf:about=""
        xmlns:xmp="http://ns.adobe.com/xap/1.0/"&gt;
      &lt;xmp:CreatorTool&gt;Kholil Tools - Standar ISO&lt;/xmp:CreatorTool&gt;
      &lt;xmp:CreateDate&gt;${now}&lt;/xmp:CreateDate&gt;
      &lt;xmp:ModifyDate&gt;${now}&lt;/xmp:ModifyDate&gt;
    &lt;/rdf:Description&gt;
  &lt;/rdf:RDF&gt;
&lt;/x:xmpmeta&gt;
&lt;?xpacket end="w"?&gt;`;

            const xmpBytes = new TextEncoder().encode(xmpMeta);
            const metadataStream = pdfDoc.context.stream(xmpBytes, {
                Type: 'Metadata',
                Subtype: 'XML',
                Length: xmpBytes.length,
            });
            const metadataRef = pdfDoc.context.register(metadataStream);
            pdfDoc.catalog.set(PDFLib.PDFName.of('Metadata'), metadataRef);

            const pdfBytes = await pdfDoc.save({
                useObjectStreams: selectedVersion !== "1b",
                addDefaultPage: false,
            });

            clearInterval(interval);
            progressFill.style.width = "100%";

            const blob = new Blob([pdfBytes], { type: 'application/pdf' });
            const url  = URL.createObjectURL(blob);

            downloadBtn.href = url;
            downloadBtn.download = originalFileName.replace(/\.pdf$/i, '_PDFA-' + selectedVersion + '.pdf');
            downloadBtn.style.display = "block";

            setStatus("✔️ Tuntas! File berhasil dikonfigurasi ke standar PDF/A-" + selectedVersion + ".", "ok");

        } catch (err) {
            clearInterval(interval);
            progressFill.style.width = "0%";
            console.error(err);
            if (err.message &amp;&amp; err.message.includes('encrypt')) {
                setStatus("❌ File PDF dilindungi kata sandi. Hapus enkripsi sandi sebelum pengarsipan.", "err");
            } else {
                setStatus("❌ Konversi tertolak. File Anda mungkin corrupt atau mengalami anomali byte.", "err");
            }
        }

        btn.disabled = false;
        btn.innerHTML = "&amp;#x1F504; Susun Metadata PDF/A Sekarang";
        setTimeout(() =&gt; { progressWrap.style.display = "none"; progressFill.style.width = "0%"; }, 2500);
    }

    /* Perbaikan FAQ Aksesibilitas (WCAG) */
    function toggleFaq(btn) {
        const isOpen = btn.classList.contains('open');
        // Tutup semua Accordion
        document.querySelectorAll('.faq-q').forEach(q =&gt; {
            q.classList.remove('open');
            q.setAttribute('aria-expanded', 'false');
            q.querySelector('.faq-icon').textContent = '+';
            q.nextElementSibling.classList.remove('open');
        });
        // Buka elemen yang ditekan
        if (!isOpen) {
            btn.classList.add('open');
            btn.setAttribute('aria-expanded', 'true');
            btn.querySelector('.faq-icon').textContent = '×';
            btn.nextElementSibling.classList.add('open');
        }
    }

    /* Perbaikan Mobile Menu Toggle (WCAG) */
    function toggleMenu() {
        const menu = document.getElementById('mobileMenu');
        const toggle = document.getElementById('navToggle');
        const isMenuOpen = menu.classList.toggle('open');
        toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
    }

    // Menutup menu seluler ketika layar disentuh di luarnya
    document.addEventListener('click', function(e) {
        const menu = document.getElementById('mobileMenu');
        const toggle = document.getElementById('navToggle');
        if (!menu.contains(e.target) &amp;&amp; !toggle.contains(e.target) &amp;&amp; menu.classList.contains('open')) {
            menu.classList.remove('open');
            toggle.setAttribute('aria-expanded', 'false');
        }
    });
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/pdf-to-pdfa.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-6443838256065603888</guid><pubDate>Fri, 10 Apr 2026 08:55:14 +0000</pubDate><atom:updated>2026-07-24T07:45:07.312-07:00</atom:updated><title>Organize pdf </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;meta name="description" content="Organize PDF online gratis. Gabungkan, hapus, dan susun ulang halaman PDF dengan sistem drag &amp; drop yang 100% aman diproses di memori browser Anda."&gt;
    &lt;title&gt;Organize PDF Online Gratis – Gabung, Hapus &amp; Susun Ulang Halaman | Kholil Tools&lt;/title&gt;

    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/organize-pdf.html" /&gt;
    &lt;meta property="og:title" content="Organize PDF Online Gratis – Gabung, Hapus &amp; Susun Ulang Halaman" /&gt;
    &lt;meta property="og:description" content="Atur halaman dokumen PDF secara visual. Aman, tanpa upload ke server, dan 100% gratis." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/organize-pdf.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;

    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema Markup untuk WebApp &amp; E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Organize PDF Tool",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "All",
      "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" },
      "author": { "@type": "Person", "name": "Kholil", "url": "https://www.kholil.biz.id/" }
    }
    &lt;/script&gt;

    &lt;!-- Schema Markup untuk FAQ --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Bagaimana sistem ini memproses PDF berukuran sangat besar?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Sistem kami mengekstrak dokumen Anda ke dalam bentuk ArrayBuffer di RAM perangkat Anda. Jika Anda mengunggah PDF sebesar 500MB, Anda memerlukan setidaknya 1GB RAM kosong pada peramban Anda."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah metadata asli dan Bookmark dokumen tetap terbawa?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Jika Anda mengatur ulang halaman, tautan internal kemungkinan besar rusak karena koordinat objek berubah. Namun, kualitas teks vektor dan gambar asli tidak akan pernah dikompresi (lossless)."
          }
        },
        {
          "@type": "Question",
          "name": "Mengapa beberapa dokumen gagal dibongkar halamannya?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Dokumen yang dienkripsi secara penuh (AES-256) dengan kata sandi wajib tidak dapat dibedah oleh skrip sisi-klien kami. Anda harus menghapus perlindungan kriptografi tersebut secara mandiri terlebih dahulu."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        :root {
            --green-900: #1b3a1f;
            --green-700: #2e7d32;
            --green-500: #4caf50;
            --green-100: #e8f5e9;
            --green-50:  #f1faf2;
            --ink:       #1a1f2e;
            --muted:     #5a6270;
            --border:    #d3e8d4;
            --white:     #ffffff;
            --red:       #c62828;
            --blue:      #1565c0;
            --radius-lg: 18px;
            --radius-md: 12px;
            --radius-sm: 8px;
            --shadow-card: 0 4px 24px rgba(27,58,31,0.09);
            --shadow-lg:   0 12px 48px rgba(27,58,31,0.13);
        }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--green-50);
            color: var(--ink);
            line-height: 1.75;
        }

        /* ── NAVIGASI ── */
        nav { 
            background: var(--white); 
            border-bottom: 1px solid var(--border); 
            padding: 0 24px; 
            display: flex; 
            align-items: center; 
            justify-content: space-between; 
            height: 58px; 
            position: sticky; 
            top: 0; 
            z-index: 99; 
        }
        .nav-brand { 
            font-family: 'Sora', sans-serif; 
            font-size: 1.18rem; 
            font-weight: 800; 
            color: var(--green-700); 
            text-decoration: none; 
        }
        .nav-brand span { color: var(--green-500); }
        .nav-links { display: flex; gap: 24px; }
        .nav-links a { 
            font-size: 0.9rem; 
            font-weight: 500; 
            color: var(--muted); 
            text-decoration: none; 
            transition: color 0.2s;
        }
        .nav-links a:hover { color: var(--green-700); }

        /* Hamburger Button */
        .nav-toggle { 
            display: none; 
            flex-direction: column; 
            gap: 5px; 
            cursor: pointer; 
            padding: 4px; 
            background: none; 
            border: none; 
        }
        .nav-toggle span { 
            display: block; 
            width: 24px; 
            height: 2px; 
            background: var(--green-700); 
            border-radius: 2px; 
        }

        /* Mobile menu */
        .mobile-menu { 
            display: none; 
            position: fixed; 
            top: 58px; left: 0; right: 0; 
            background: var(--white); 
            border-bottom: 1px solid var(--border); 
            z-index: 98; 
            padding: 16px 24px; 
            flex-direction: column; 
            gap: 0; 
            box-shadow: 0 8px 24px rgba(0,0,0,0.1); 
        }
        .mobile-menu.open { display: flex; }
        .mobile-menu a { 
            font-size: 0.95rem; 
            font-weight: 500; 
            color: var(--ink); 
            text-decoration: none; 
            padding: 12px 0; 
            border-bottom: 1px solid var(--border); 
        }
        .mobile-menu a:last-child { border-bottom: none; }

        /* ── HERO ── */
        .hero {
            background: linear-gradient(135deg, var(--green-900) 0%, var(--green-700) 100%);
            color: white;
            text-align: center;
            padding: 64px 24px 56px;
        }
        .hero-eyebrow {
            display: inline-block;
            background: rgba(255,255,255,0.15);
            border: 1px solid rgba(255,255,255,0.28);
            border-radius: 50px;
            padding: 5px 18px;
            font-size: 0.82rem;
            font-weight: 600;
            letter-spacing: 0.8px;
            text-transform: uppercase;
            margin-bottom: 20px;
        }
        .hero h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(2rem, 5vw, 3.1rem);
            font-weight: 800;
            line-height: 1.15;
            margin-bottom: 16px;
            letter-spacing: -1px;
        }
        .hero p { font-size: 1.05rem; opacity: 0.88; max-width: 580px; margin: 0 auto; }

        /* ── LAYOUT ── */
        .page-body {
            max-width: 920px;
            margin: 0 auto;
            padding: 40px 20px 80px;
        }

        /* ── AUTHOR BOX E-E-A-T ── */
        .author-box { 
            display: flex; align-items: center; gap: 15px; 
            background: var(--white); padding: 20px; 
            border-radius: var(--radius-md); border: 1px solid var(--border); 
            margin-bottom: 30px; border-left: 4px solid var(--green-700); 
            box-shadow: var(--shadow-card); 
        }
        .author-img { 
            width: 50px; height: 50px; background: var(--green-700); 
            border-radius: 50%; display: flex; align-items: center; 
            justify-content: center; color: white; font-weight: bold; font-size: 1.2rem; 
        }
        .author-meta { font-size: 0.9rem; color: var(--muted); }
        .author-meta strong { color: var(--ink); display: block; font-size: 1rem; }

        /* ── TOOL CARD ── */
        .tool-card {
            background: var(--white);
            border-radius: var(--radius-lg);
            box-shadow: var(--shadow-lg);
            padding: 36px;
            margin-bottom: 40px;
        }

        /* drop zone */
        .drop-zone {
            border: 2.5px dashed var(--green-500);
            border-radius: var(--radius-md);
            padding: 44px 24px;
            background: var(--green-50);
            text-align: center;
            transition: background 0.2s, border-color 0.2s;
            cursor: pointer;
            margin-bottom: 24px;
        }
        .drop-zone:hover, .drop-zone.dragover {
            background: var(--green-100);
            border-color: var(--green-700);
        }
        .drop-icon { font-size: 2.4rem; margin-bottom: 12px; }
        .drop-zone h3 { font-size: 1rem; font-weight: 600; color: var(--green-900); margin-bottom: 5px; }
        .drop-zone p  { font-size: 0.88rem; color: var(--muted); }
        .file-label {
            display: inline-block;
            background: var(--green-700);
            color: white;
            padding: 10px 26px;
            border-radius: 50px;
            font-size: 0.92rem;
            font-weight: 600;
            cursor: pointer;
            margin-top: 14px;
            transition: background 0.2s;
        }
        .file-label:hover { background: var(--green-900); }
        input[type="file"] { display: none; }

        #fileNames {
            font-size: 0.88rem;
            color: var(--green-700);
            font-weight: 600;
            margin-top: 10px;
            min-height: 20px;
        }

        /* toolbar */
        .toolbar {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 20px;
            align-items: center;
        }
        .toolbar-label {
            font-size: 0.82rem;
            font-weight: 600;
            color: var(--muted);
            text-transform: uppercase;
            letter-spacing: 0.5px;
            margin-right: 4px;
        }
        .t-btn {
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 8px 18px;
            border: 1.5px solid var(--border);
            border-radius: 50px;
            background: white;
            color: var(--ink);
            font-size: 0.88rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.18s;
        }
        .t-btn:hover { border-color: var(--green-500); color: var(--green-700); background: var(--green-50); }
        .t-btn.danger { color: var(--red); border-color: #ffcdd2; }
        .t-btn.danger:hover { background: #fff5f5; border-color: var(--red); }
        .t-btn:disabled { opacity: 0.45; cursor: not-allowed; }

        /* pages grid */
        .pages-grid {
            display: flex;
            flex-wrap: wrap;
            gap: 14px;
            min-height: 220px;
            padding: 20px;
            background: var(--green-50);
            border-radius: var(--radius-md);
            border: 2px dashed var(--border);
            margin-bottom: 24px;
            transition: background 0.2s;
            align-content: flex-start;
        }
        .pages-grid.drag-active { background: var(--green-100); border-color: var(--green-500); }
        .empty-state {
            width: 100%;
            text-align: center;
            padding: 40px 20px;
            color: var(--muted);
            font-size: 0.95rem;
        }
        .empty-state .big-icon { font-size: 3rem; margin-bottom: 12px; }

        /* page thumbnail */
        .page-thumb {
            width: 140px;
            border: 2px solid var(--border);
            border-radius: var(--radius-sm);
            background: white;
            box-shadow: var(--shadow-card);
            position: relative;
            cursor: grab;
            transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s;
            user-select: none;
        }
        .page-thumb:hover { transform: translateY(-3px); box-shadow: 0 8px 28px rgba(27,58,31,0.15); }
        .page-thumb.selected { border-color: var(--green-700); box-shadow: 0 0 0 3px rgba(46,125,50,0.25); }
        .page-thumb.dragging { opacity: 0.35; transform: scale(0.96); }
        .page-thumb.drag-over { border-color: var(--green-500); background: var(--green-50); }

        .thumb-canvas { width: 100%; height: auto; display: block; border-radius: 6px 6px 0 0; }

        .thumb-footer {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 5px 8px;
            background: var(--green-50);
            border-top: 1px solid var(--border);
            border-radius: 0 0 6px 6px;
        }
        .thumb-num { font-size: 0.78rem; font-weight: 700; color: var(--green-700); }
        .thumb-del {
            background: none;
            border: none;
            color: #aaa;
            cursor: pointer;
            font-size: 0.85rem;
            padding: 0 2px;
            line-height: 1;
            transition: color 0.15s;
        }
        .thumb-del:hover { color: var(--red); }

        .select-overlay {
            position: absolute;
            top: 6px; left: 6px;
            width: 20px; height: 20px;
            border-radius: 50%;
            border: 2px solid white;
            background: rgba(0,0,0,0.35);
            display: flex; align-items: center; justify-content: center;
            font-size: 0.65rem;
            color: white;
            transition: background 0.15s;
        }
        .page-thumb.selected .select-overlay { background: var(--green-700); }

        /* progress */
        #progressWrap { display: none; margin-bottom: 16px; }
        #progressBar { height: 5px; background: var(--green-100); border-radius: 99px; overflow: hidden; }
        #progressFill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--green-500), var(--green-700)); border-radius: 99px; transition: width 0.4s ease; }

        #status {
            font-size: 0.93rem;
            font-weight: 600;
            min-height: 22px;
            text-align: center;
            margin-bottom: 12px;
        }
        .s-ok  { color: var(--green-700); }
        .s-err { color: var(--red); }
        .s-inf { color: var(--muted); }

        /* download */
        .btn-download {
            display: none;
            width: 100%;
            background: linear-gradient(135deg, var(--green-700), var(--green-900));
            color: white;
            padding: 15px;
            font-size: 1.05rem;
            font-weight: 700;
            border-radius: var(--radius-md);
            text-decoration: none;
            text-align: center;
            transition: opacity 0.2s;
            margin-bottom: 12px;
        }
        .btn-download:hover { opacity: 0.9; }

        .btn-save {
            width: 100%;
            background: linear-gradient(135deg, var(--green-700), var(--green-900));
            color: white;
            border: none;
            padding: 15px;
            font-size: 1.05rem;
            font-weight: 700;
            border-radius: var(--radius-md);
            cursor: pointer;
            transition: opacity 0.2s;
        }
        .btn-save:hover { opacity: 0.9; }
        .btn-save:disabled { opacity: 0.55; cursor: not-allowed; }

        /* badge row */
        .badge-row {
            display: flex;
            gap: 16px;
            flex-wrap: wrap;
            margin-top: 20px;
            padding-top: 18px;
            border-top: 1px solid var(--border);
        }
        .badge { display: flex; align-items: center; gap: 6px; font-size: 0.82rem; color: var(--muted); font-weight: 500; }

        /* ARTICLE */
        .content-section {
            background: var(--white);
            border-radius: var(--radius-lg);
            box-shadow: var(--shadow-card);
            padding: 40px;
            margin-bottom: 28px;
        }
        .content-section h2 {
            font-family: 'Sora', sans-serif;
            font-size: 1.42rem;
            font-weight: 800;
            color: var(--green-900);
            margin-bottom: 16px;
            padding-bottom: 10px;
            border-bottom: 3px solid var(--green-100);
            letter-spacing: -0.3px;
        }
        .content-section h3 {
            font-size: 1.03rem;
            font-weight: 700;
            color: var(--green-700);
            margin: 22px 0 8px;
        }
        .content-section p {
            font-size: 0.97rem;
            color: #3d4452;
            margin-bottom: 14px;
            line-height: 1.8;
        }
        .prose li {
            font-size: 0.97rem;
            color: #3d4452;
            margin-bottom: 8px;
            line-height: 1.75;
        }
        .prose a { color: var(--blue); font-weight: 600; text-decoration: none; }
        .prose a:hover { text-decoration: underline; }

        /* ── FAQ – ACCESSIBLE VERSION ── */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
        .faq-q { 
            width: 100%; background: none; border: none; padding: 18px 22px; 
            text-align: left; font-size: 1rem; font-weight: 700; color: var(--green-900); 
            cursor: pointer; display: flex; justify-content: space-between; 
            align-items: center; gap: 12px; font-family: inherit; 
        }
        .faq-q:focus { outline: 2px solid var(--green-500); outline-offset: -2px; }
        .faq-q:hover { background: var(--green-50); }
        .faq-icon { 
            flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%; 
            background: var(--green-100); color: var(--green-700); display: flex; 
            align-items: center; justify-content: center; font-size: 1rem; 
            font-weight: 700; transition: transform 0.3s; 
        }
        .faq-q.open .faq-icon { background: var(--green-700); color: white; transform: rotate(45deg); }
        .faq-a { display: none; padding: 0 22px 18px; font-size: 0.93rem; color: var(--muted); line-height: 1.75; }
        .faq-a.open { display: block; }

        /* footer */
        footer {
            background: var(--green-900);
            color: rgba(255,255,255,0.7);
            text-align: center;
            padding: 36px 24px;
            font-size: 0.88rem;
        }
        .footer-logo { font-family: 'Sora', sans-serif; font-size: 1.3rem; font-weight: 800; color: white; margin-bottom: 10px; }
        .footer-logo span { color: var(--green-500); }
        footer a { color: rgba(255,255,255,0.8); text-decoration: none; }

        @media (max-width: 600px) {
            .tool-card, .content-section { padding: 22px 16px; }
            .nav-links { display: none; }
            .nav-toggle { display: flex; }
            .page-thumb { width: 110px; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- NAV --&gt;
&lt;nav&gt;
    &lt;a class="nav-brand" href="https://www.kholil.biz.id/"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/protect-pdf.html"&gt;Protect PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/unlock-pdf.html"&gt;Unlock PDF&lt;/a&gt;
    &lt;/div&gt;
    &lt;!-- Hamburger Button Aksesibel --&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu Utama"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
&lt;/nav&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/protect-pdf.html"&gt;&#128274; Protect PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/unlock-pdf.html"&gt;&#128275; Unlock PDF&lt;/a&gt;
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-eyebrow"&gt;Visual Drag &amp;amp; Drop &amp;middot; 100% Client-Side&lt;/div&gt;
    &lt;h1&gt;Organize PDF Online —&lt;br&gt;Gabung, Hapus &amp;amp; Susun Ulang&lt;/h1&gt;
    &lt;p&gt;Atur struktur halaman PDF Anda secara visual. Susun ulang urutan arsip, singkirkan halaman kosong, dan satukan beberapa dokumen dengan aman di dalam browser.&lt;/p&gt;
&lt;/div&gt;

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

    &lt;!-- TOOL --&gt;
    &lt;div class="tool-card"&gt;

        &lt;div class="drop-zone" id="dropArea"&gt;
            &lt;div class="drop-icon"&gt;&amp;#128194;&lt;/div&gt;
            &lt;h3&gt;Seret File PDF ke Sini&lt;/h3&gt;
            &lt;p&gt;Bisa beberapa file sekaligus untuk digabung&lt;/p&gt;
            &lt;label class="file-label"&gt;
                Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf" multiple&gt;
            &lt;/label&gt;
            &lt;p id="fileNames"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;div class="toolbar"&gt;
            &lt;span class="toolbar-label"&gt;Tindakan:&lt;/span&gt;
            &lt;button class="t-btn" id="btnSelAll" onclick="selectAll()" disabled&gt;Pilih Semua&lt;/button&gt;
            &lt;button class="t-btn danger" id="btnDelSel" onclick="deleteSelected()" disabled&gt;Hapus Terpilih&lt;/button&gt;
            &lt;button class="t-btn" id="btnRotate" onclick="rotateSelected()" disabled&gt;Putar 90&amp;deg;&lt;/button&gt;
            &lt;button class="t-btn" id="btnClear" onclick="clearAll()" disabled&gt;Bersihkan Semua&lt;/button&gt;
        &lt;/div&gt;

        &lt;div id="pagesGrid" class="pages-grid"&gt;
            &lt;div class="empty-state"&gt;
                &lt;div class="big-icon"&gt;&amp;#128196;&lt;/div&gt;
                &lt;p&gt;Halaman PDF akan muncul di sini.&lt;br&gt;Upload file untuk memulai.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div id="progressWrap"&gt;
            &lt;div id="progressBar"&gt;&lt;div id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;div id="status" class="s-inf" aria-live="polite"&gt;&lt;/div&gt;

        &lt;a id="downloadBtn" class="btn-download" href="#"&gt;&amp;#11015; Download PDF yang Sudah Diatur&lt;/a&gt;
        &lt;button class="btn-save" id="saveBtn" onclick="buildAndSave()" disabled&gt;&amp;#128190; Simpan sebagai PDF Baru&lt;/button&gt;

        &lt;div class="badge-row"&gt;
            &lt;div class="badge"&gt;&amp;#128274; File tidak dikirim ke server&lt;/div&gt;
            &lt;div class="badge"&gt;&amp;#9889; Drag &amp;amp; drop responsif&lt;/div&gt;
            &lt;div class="badge"&gt;&amp;#127379; Gratis, tanpa batas halaman&lt;/div&gt;
            &lt;div class="badge"&gt;&amp;#128241; Kompatibel lintas perangkat&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- AUTHOR BOX E-E-A-T --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
            &lt;span style="display:block;"&gt;Spesialis Tata Kelola Dokumen Digital | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- NARASI UNIK (Studi Kasus Kearsipan) --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Manajemen Halaman Dokumen Legal Tanpa Jejak Server&lt;/h2&gt;
        &lt;p&gt;Dalam pengelolaan dokumen tingkat tinggi—seperti penyusunan bundel bukti persidangan, lampiran audit keuangan, atau penggabungan portofolio tender—kesalahan penyusunan satu lembar halaman saja dapat memicu penolakan berkas. Menggunakan aplikasi desktop seringkali memberatkan perangkat, sementara mengunggah dokumen hukum ke aplikasi &lt;em&gt;online&lt;/em&gt; biasa berisiko fatal terhadap kebocoran informasi pihak ketiga.&lt;/p&gt;
        
        &lt;p&gt;Skrip "Organize PDF" kami menjawab dilema tersebut dengan menggeser paradigma pemrosesan. Kami tidak mengoperasikan &lt;em&gt;server&lt;/em&gt; pengolah gambar. Sebaliknya, saat Anda menyeret file ke dalam area di atas, mesin analisis dokumen akan diunduh secara instan ke dalam memori peramban Anda untuk mengeksekusi penyusunan secara lokal.&lt;/p&gt;
        
        &lt;h3&gt;Alur Kerja Kearsipan (Best Practice)&lt;/h3&gt;
        &lt;ul class="prose"&gt;
            &lt;li&gt;&lt;strong&gt;Tahap Pengecekan Orientasi:&lt;/strong&gt; Dokumen yang dipindai (&lt;em&gt;scan&lt;/em&gt;) secara massal seringkali memiliki halaman yang terbalik. Manfaatkan fitur "Pilih Semua" yang berdekatan dengan halaman terbalik tersebut, lalu tekan "Putar 90°" untuk merapikan orientasinya tanpa harus membuka aplikasi pengedit foto.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Tahap Pemangkasan (Trimming):&lt;/strong&gt; Hapus halaman sampul atau halaman kosong (&lt;em&gt;blank page&lt;/em&gt;) hasil pemindaian langsung dari papan pratinjau (&lt;em&gt;preview board&lt;/em&gt;).&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Keamanan Lanjutan:&lt;/strong&gt; Jika dokumen yang Anda susun mengandung informasi personal yang sangat sensitif, Anda dapat menguncinya menggunakan &lt;a href="https://www.kholil.biz.id/2026/04/protect-pdf.html"&gt;Protect PDF&lt;/a&gt; kami setelah dokumen diunduh. Sebaliknya, jika Anda perlu membongkar dan menyusun ulang dokumen yang sudah terkunci, manfaatkan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/unlock-pdf.html"&gt;Unlock PDF&lt;/a&gt; sebelum Anda memasukkannya ke halaman ini.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/div&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Bukti Kinerja: &lt;span&gt;Manajemen Multi-File Berhasil&lt;/span&gt;&lt;/h2&gt;
        &lt;p&gt;Kami memastikan bahwa alat ini mampu menangani beban kerja berat secara stabil. Berikut adalah dokumentasi pengujian saat kami mengunggah &lt;strong&gt;4 file PDF sekaligus&lt;/strong&gt; melalui peramban ponsel. Sistem berhasil membongkar seluruh halamannya, menyusunnya dalam satu papan kerja, dan merakit ulang menjadi satu dokumen utuh secara instan.&lt;/p&gt;
        
        &lt;div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap:15px; margin-top:20px;"&gt;
            &lt;!-- Gambar 1: Upload 4 PDF --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/B54j7LnJ/Screenshot-2026-07-22-01-30-50-872-com-android-chrome.jpg" alt="Upload 4 file PDF sekaligus ke Organize PDF" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;1. Multi-Upload File&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 2: Halaman Muncul --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/60hq4CWH/Screenshot-2026-07-22-01-31-04-791-com-android-chrome.jpg" alt="Halaman muncul berurutan di papan pratinjau" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;2. Pratinjau Terpadu&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 3: Download Button --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/Rk2JCr9h/Screenshot-2026-07-22-01-31-17-147-com-android-chrome.jpg" alt="Tombol download muncul setelah PDF berhasil dirakit" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;3. Proses Perakitan&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 4: Hasil 1 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/QvsbS7xF/Screenshot-2026-07-22-01-31-30-258-com-google-android-apps-docs.jpg" alt="Hasil akhir PDF halaman 1" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;4. Dokumen Utuh (Hal 1)&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 5: Hasil 2 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/HfJYjpG5/Screenshot-2026-07-22-01-31-35-114-com-google-android-apps-docs.jpg" alt="Hasil akhir PDF halaman 2" style="width:100%; height:auto; border-radius:4px;"&gt;
                &lt;p style="font-size:0.85rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;5. Dokumen Utuh (Hal 2)&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- FAQ TEKNIS --&gt;
    &lt;div class="content-section"&gt;
        &lt;h2&gt;Tanya Jawab Teknis Eksekusi PDF&lt;/h2&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    Bagaimana sistem ini memproses PDF berukuran sangat besar?
                    &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Sistem kami mengekstrak dokumen Anda ke dalam bentuk &lt;em&gt;ArrayBuffer&lt;/em&gt; di RAM komputer/ponsel Anda. Jika Anda mengunggah PDF sebesar 500MB, Anda memerlukan setidaknya 1GB RAM kosong pada peramban Anda. Kami merender &lt;em&gt;thumbnail&lt;/em&gt; dengan kualitas rendah secara sengaja untuk menghemat beban CPU Anda selama proses pratinjau grafis.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    Apakah metadata asli dan Bookmark dokumen tetap terbawa?
                    &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Tergantung pada modifikasi Anda. Jika Anda mengatur ulang halaman secara agresif, tautan internal kemungkinan besar rusak karena koordinat objek aslinya berubah. Namun, kualitas teks vektor dan gambar inti (&lt;em&gt;source rendering&lt;/em&gt;) tidak akan pernah dikompresi (&lt;em&gt;lossless&lt;/em&gt;) selama proses penyusunan ulang.
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                    Mengapa beberapa dokumen gagal dibongkar halamannya?
                    &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
                &lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    Dokumen yang dienkripsi secara penuh (AES-256) dengan kata sandi wajib tidak dapat dibedah oleh skrip sisi-klien kami. Anda harus menghapus perlindungan kriptografi tersebut secara mandiri terlebih dahulu (menggunakan alat &lt;em&gt;Unlock&lt;/em&gt;) sebelum dapat menyusun ulang halamannya di panel aplikasi ini.
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/div&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
    &lt;div class="footer-logo"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/div&gt;
    &lt;p&gt;Solusi Utilitas PDF Digital yang Aman dan Andal&lt;/p&gt;
    &lt;p style="margin-top:14px;"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt; &amp;nbsp;&amp;middot;&amp;nbsp;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt; &amp;nbsp;&amp;middot;&amp;nbsp;
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Kontak&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style="margin-top:14px; opacity:0.5;"&gt;&amp;copy; 2026 Kholil Tools. Seluruh hak cipta dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- SCRIPTS DENGAN LAZY LOADING --&gt;
&lt;script&gt;
    let pages = [];
    let pdfLibDocs = [];
    let selectedSet = new Set();
    let dragSrc = null;

    const dropArea     = document.getElementById('dropArea');
    const fileInput    = document.getElementById('pdfFile');
    const fileNames    = document.getElementById('fileNames');
    const grid         = document.getElementById('pagesGrid');
    const statusEl     = document.getElementById('status');
    const downloadBtn  = document.getElementById('downloadBtn');
    const saveBtn      = document.getElementById('saveBtn');
    const progressWrap = document.getElementById('progressWrap');
    const progressFill = document.getElementById('progressFill');

    // ── Lazy Load Core Libraries ──
    function loadLibraries() {
        return new Promise((resolve) =&gt; {
            if (window.pdfjsLib &amp;&amp; window.PDFLib) return resolve();
            
            setStatus('Mengunduh Pustaka Mesin PDF...', 'inf');
            const script1 = document.createElement('script');
            script1.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js';
            
            const script2 = document.createElement('script');
            script2.src = 'https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js';
            
            script1.onload = () =&gt; {
                pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
                document.head.appendChild(script2);
            };
            script2.onload = resolve;
            document.head.appendChild(script1);
        });
    }

    /* Drag &amp; Drop Events */
    dropArea.addEventListener('dragover', function(e){ e.preventDefault(); dropArea.classList.add('dragover'); });
    dropArea.addEventListener('dragleave', function(){ dropArea.classList.remove('dragover'); });
    dropArea.addEventListener('drop', function(e){
        e.preventDefault();
        dropArea.classList.remove('dragover');
        const dropped = Array.from(e.dataTransfer.files).filter(function(f){ return f.type === 'application/pdf'; });
        handleFiles(dropped);
    });
    
    fileInput.addEventListener('change', function(e){ handleFiles(Array.from(e.target.files)); });

    async function handleFiles(files) {
        if (!files.length) return;
        
        // Render nama aman tanpa XSS
        fileNames.innerHTML = files.map(function(f){ 
            return f.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;"); 
        }).join(', ');
        
        showProgress(true);
        
        // Panggil Lazy Load sebelum memproses
        await loadLibraries();

        setStatus('Menganalisis ' + files.length + ' dokumen...', 'inf');
        
        let loaded = 0;
        for (let i = 0; i &lt; files.length; i++) {
            let file = files[i];
            try {
                let buf = await file.arrayBuffer();
                let pdfJS  = await pdfjsLib.getDocument({ data: buf.slice(0) }).promise;
                let pdfLib = await PDFLib.PDFDocument.load(buf, { ignoreEncryption: true });
                let fIdx = pdfLibDocs.length;
                pdfLibDocs.push(pdfLib);
                
                for (let p = 0; p &lt; pdfLib.getPageCount(); p++) {
                    let pjsPage = await pdfJS.getPage(p + 1);
                    await addPageCard(pdfLib, fIdx, p, pjsPage);
                }
            } catch(err) {
                console.error('Load error', file.name, err);
                setStatus(`Gagal memuat ${file.name}. Dokumen mungkin terenkripsi kuat.`, 'err');
            }
            loaded++;
            progressFill.style.width = (loaded / files.length * 90) + '%';
        }
        
        progressFill.style.width = '100%';
        setTimeout(function(){ showProgress(false); }, 800);
        updateUI();
        if (pages.length &gt; 0) {
            setStatus(pages.length + ' halaman siap diatur. Seret (Drag) untuk mengubah urutan.', 'inf');
        }
    }

    async function addPageCard(pdfLib, fIdx, pageIdx, pdfJSPage) {
        let wrapper = document.createElement('div');
        wrapper.className = 'page-thumb';

        let canvas = document.createElement('canvas');
        canvas.className = 'thumb-canvas';
        // ARIA label untuk aksesibilitas screen reader
        canvas.setAttribute('aria-label', `Pratinjau halaman ${pageIdx + 1}`);
        wrapper.appendChild(canvas);

        if (pdfJSPage) {
            let vp = pdfJSPage.getViewport({ scale: 0.45 });
            canvas.width  = vp.width;
            canvas.height = vp.height;
            await pdfJSPage.render({ canvasContext: canvas.getContext('2d'), viewport: vp }).promise;
        } else {
            canvas.width = 120; canvas.height = 160;
            let ctx = canvas.getContext('2d');
            ctx.fillStyle = '#f0f0f0'; ctx.fillRect(0,0,120,160);
            ctx.fillStyle = '#aaa'; ctx.font = '13px sans-serif';
            ctx.fillText('Page', 40, 85);
        }

        let ov = document.createElement('div');
        ov.className = 'select-overlay';
        ov.innerHTML = '&amp;#10003;'; // Checkmark
        wrapper.appendChild(ov);

        let foot = document.createElement('div');
        foot.className = 'thumb-footer';
        let numEl = document.createElement('span');
        numEl.className = 'thumb-num';
        let delBtn = document.createElement('button');
        delBtn.className = 'thumb-del';
        delBtn.title = 'Hapus halaman ini';
        delBtn.innerHTML = '&amp;times;';
        delBtn.onclick = function(e){ e.stopPropagation(); removePage(wrapper); };
        
        foot.appendChild(numEl);
        foot.appendChild(delBtn);
        wrapper.appendChild(foot);

        let state = { pdfLibDoc: pdfLib, fIdx: fIdx, pageIdx: pageIdx, rotation: 0, element: wrapper };
        pages.push(state);

        wrapper.addEventListener('click', function(){ toggleSelect(wrapper); });

        // Logic Drag &amp; Drop Interaktif
        wrapper.draggable = true;
        wrapper.addEventListener('dragstart', function(e){
            dragSrc = wrapper;
            wrapper.classList.add('dragging');
            e.dataTransfer.effectAllowed = 'move';
        });
        wrapper.addEventListener('dragend', function(){
            dragSrc = null;
            wrapper.classList.remove('dragging');
            document.querySelectorAll('.page-thumb').forEach(function(t){ t.classList.remove('drag-over'); });
            renumber();
        });
        wrapper.addEventListener('dragover', function(e){
            e.preventDefault();
            if (dragSrc &amp;&amp; dragSrc !== wrapper) {
                document.querySelectorAll('.page-thumb').forEach(function(t){ t.classList.remove('drag-over'); });
                wrapper.classList.add('drag-over');
                let allThumbs = Array.from(grid.querySelectorAll('.page-thumb'));
                let srcIdx = allThumbs.indexOf(dragSrc);
                let tgtIdx = allThumbs.indexOf(wrapper);
                if (srcIdx &lt; tgtIdx) wrapper.after(dragSrc);
                else wrapper.before(dragSrc);
            }
        });
        wrapper.addEventListener('dragleave', function(){ wrapper.classList.remove('drag-over'); });
        wrapper.addEventListener('drop', function(e){ e.preventDefault(); wrapper.classList.remove('drag-over'); });

        let es = grid.querySelector('.empty-state');
        if (es) es.remove();
        grid.appendChild(wrapper);
        renumber();
    }

    function renumber() {
        let thumbs = Array.from(grid.querySelectorAll('.page-thumb'));
        thumbs.forEach(function(t, i){
            let num = t.querySelector('.thumb-num');
            if (num) num.textContent = 'Hal. ' + (i + 1);
        });
        let newOrder = [];
        thumbs.forEach(function(t){
            let found = pages.find(function(p){ return p.element === t; });
            if (found) newOrder.push(found);
        });
        pages = newOrder;
    }

    function toggleSelect(el) {
        if (selectedSet.has(el)) {
            selectedSet.delete(el);
            el.classList.remove('selected');
        } else {
            selectedSet.add(el);
            el.classList.add('selected');
        }
        updateToolbar();
    }

    function selectAll() {
        grid.querySelectorAll('.page-thumb').forEach(function(t){
            selectedSet.add(t);
            t.classList.add('selected');
        });
        updateToolbar();
    }

    function removePage(el) {
        pages = pages.filter(function(p){ return p.element !== el; });
        selectedSet.delete(el);
        el.remove();
        renumber();
        updateUI();
        setStatus(pages.length + ' halaman tersisa.', 'inf');
        if (pages.length === 0) showEmpty();
    }

    function deleteSelected() {
        if (!selectedSet.size) return;
        if (!confirm('Hapus ' + selectedSet.size + ' halaman terpilih?')) return;
        selectedSet.forEach(function(el){
            pages = pages.filter(function(p){ return p.element !== el; });
            el.remove();
        });
        selectedSet.clear();
        renumber();
        updateUI();
        setStatus(pages.length + ' halaman tersisa.', 'inf');
        if (pages.length === 0) showEmpty();
    }

    function rotateSelected() {
        selectedSet.forEach(function(el){
            let pg = pages.find(function(p){ return p.element === el; });
            if (!pg) return;
            pg.rotation = (pg.rotation + 90) % 360;
            let canvas = el.querySelector('canvas');
            if (canvas) {
                canvas.style.transform = 'rotate(' + pg.rotation + 'deg)';
                if (pg.rotation % 180 !== 0) {
                    let w = canvas.width, h = canvas.height;
                    canvas.style.marginTop    = ((w - h) / 2) + 'px';
                    canvas.style.marginBottom = ((w - h) / 2) + 'px';
                } else {
                    canvas.style.marginTop = '0';
                    canvas.style.marginBottom = '0';
                }
            }
        });
    }

    function clearAll() {
        if (!confirm('Hapus semua halaman dan mulai dari awal?')) return;
        pages = []; pdfLibDocs = []; selectedSet.clear();
        grid.innerHTML = '';
        showEmpty();
        fileNames.innerHTML = '';
        downloadBtn.style.display = 'none';
        fileInput.value = '';
        updateUI();
        setStatus('', '');
    }

    function showEmpty() {
        grid.innerHTML = '&lt;div class="empty-state"&gt;&lt;div class="big-icon"&gt;&amp;#128196;&lt;/div&gt;&lt;p&gt;Halaman PDF akan muncul di sini.&lt;br&gt;Upload file untuk memulai.&lt;/p&gt;&lt;/div&gt;';
    }

    async function buildAndSave() {
        if (!pages.length) return;
        saveBtn.disabled = true;
        saveBtn.textContent = 'Membangun PDF...';
        downloadBtn.style.display = 'none';
        setStatus('Menyusun PDF baru secara lokal...', 'inf');
        showProgress(true);
        
        try {
            let newDoc = await PDFLib.PDFDocument.create();
            let done = 0;
            for (let i = 0; i &lt; pages.length; i++) {
                let pg = pages[i];
                let copied = (await newDoc.copyPages(pg.pdfLibDoc, [pg.pageIdx]))[0];
                if (pg.rotation) {
                    let existing = copied.getRotation().angle;
                    copied.setRotation(PDFLib.degrees((existing + pg.rotation) % 360));
                }
                newDoc.addPage(copied);
                done++;
                progressFill.style.width = (done / pages.length * 95) + '%';
            }
            let bytes = await newDoc.save();
            progressFill.style.width = '100%';
            
            setTimeout(function(){ showProgress(false); }, 800);
            
            let url = URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));
            downloadBtn.href = url;
            downloadBtn.download = 'organized-kholiltools.pdf';
            downloadBtn.style.display = 'block';
            
            setStatus('Selesai! PDF baru dengan ' + pages.length + ' halaman siap didownload.', 'ok');
        } catch(err) {
            console.error(err);
            setStatus('Gagal membuat PDF. Coba segarkan halaman.', 'err');
            showProgress(false);
        }
        saveBtn.disabled = false;
        saveBtn.innerHTML = '&amp;#128190; Simpan sebagai PDF Baru';
    }

    function setStatus(msg, type) {
        statusEl.className = type === 'ok' ? 's-ok' : type === 'err' ? 's-err' : 's-inf';
        statusEl.textContent = msg;
    }
    
    function showProgress(show) {
        progressWrap.style.display = show ? 'block' : 'none';
        if (!show) progressFill.style.width = '0%';
    }
    
    function updateToolbar() {
        document.getElementById('btnDelSel').disabled = selectedSet.size === 0;
        document.getElementById('btnRotate').disabled = selectedSet.size === 0;
    }
    
    function updateUI() {
        let has = pages.length &gt; 0;
        saveBtn.disabled = !has;
        document.getElementById('btnClear').disabled = !has;
        document.getElementById('btnSelAll').disabled = !has;
        if(has) {
            document.getElementById('btnSelAll').disabled = false;
            document.getElementById('btnClear').disabled = false;
        }
    }

    /* Fungsi Mobile Menu &amp; FAQ dengan Aksesibilitas WCAG */
    function toggleMenu() {
        const menu = document.getElementById('mobileMenu');
        const toggle = document.getElementById('navToggle');
        const isMenuOpen = menu.classList.toggle('open');
        toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
    }

    function toggleFaq(btn) {
        const a = btn.nextElementSibling;
        const open = btn.classList.contains('open');
        
        document.querySelectorAll('.faq-a').forEach(x =&gt; x.classList.remove('open'));
        document.querySelectorAll('.faq-q').forEach(x =&gt; {
            x.classList.remove('open');
            x.setAttribute('aria-expanded', 'false');
        });
        
        if (!open) { 
            a.classList.add('open'); 
            btn.classList.add('open');
            btn.setAttribute('aria-expanded', 'true');
        }
    }
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/organize-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3279905420349485449</guid><pubDate>Fri, 10 Apr 2026 08:54:04 +0000</pubDate><atom:updated>2026-07-24T07:46:21.609-07:00</atom:updated><title>Protect pdf </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;meta name="description" content="Kunci file PDF Anda dengan kata sandi (Password) standar enkripsi AES-128. Atur hak cetak dan hak salin dokumen 100% aman di peramban (browser) Anda."&gt;
    &lt;title&gt;Protect PDF Online – Kunci File PDF Secara Aman | Kholil Tools&lt;/title&gt;
    
    &lt;!-- Canonical &amp; Schema --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/protect-pdf.html" /&gt;
    &lt;meta property="og:title" content="Protect PDF Online – Kunci File PDF Secara Aman" /&gt;
    &lt;meta property="og:description" content="Beri lapisan kata sandi ke dokumen PDF Anda. 100% diproses di RAM tanpa upload ke server eksternal." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/protect-pdf.html" /&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&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Protect PDF Tool",
      "applicationCategory": "SecurityApplication",
      "operatingSystem": "All",
      "author": { "@type": "Person", "name": "Kholil", "url": "https://www.kholil.biz.id/" }
    }
    &lt;/script&gt;
    
    &lt;style&gt;
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        :root {
            --purple-dark: #4a1d96;
            --purple-main: #6d28d9;
            --purple-mid: #7c3aed;
            --purple-light: #ede9fe;
            --purple-pale: #f5f3ff;
            --green-dark: #166534;
            --green-main: #16a34a;
            --text-dark: #1e1b2e;
            --text-mid: #4b4568;
            --text-light: #6b6b8a;
            --border: #e2ddf5;
            --white: #ffffff;
            --shadow-sm: 0 2px 8px rgba(109,40,217,0.08);
            --shadow-md: 0 8px 32px rgba(109,40,217,0.12);
            --shadow-lg: 0 20px 60px rgba(109,40,217,0.15);
        }

        body {
            font-family: 'Inter', sans-serif;
            background: #f8f6ff;
            color: var(--text-dark);
            line-height: 1.7;
        }

        /* ── NAV ── */
        .main-nav { 
            background: var(--white); 
            border-bottom: 1px solid var(--border); 
            padding: 16px 24px; 
            display: flex; 
            align-items: center; 
            justify-content: space-between; 
            position: sticky; 
            top: 0; 
            z-index: 100; 
            box-shadow: var(--shadow-sm); 
        }
        .main-nav .logo a { 
            font-family: 'Sora', sans-serif; 
            font-size: 1.25rem; 
            font-weight: 800; 
            color: var(--purple-main); 
            text-decoration: none; 
        }
        .main-nav .logo span { color: var(--text-dark); }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            font-size: 0.95rem; 
            font-weight: 500; 
            color: var(--text-mid); 
            text-decoration: none; 
            transition: color 0.2s; 
        }
        .nav-links a:hover { color: var(--purple-main); }
        
        .nav-toggle {
            display: none; flex-direction: column; gap: 5px; cursor: pointer; padding: 4px; background: none; border: none;
        }
        .nav-toggle span { display: block; width: 24px; height: 2px; background: var(--purple-main); border-radius: 2px; }
        
        .mobile-menu {
            display: none; position: fixed; top: 65px; left: 0; right: 0; background: var(--white); border-bottom: 1px solid var(--border); z-index: 98; padding: 16px 24px; flex-direction: column; gap: 0; box-shadow: var(--shadow-md);
        }
        .mobile-menu.open { display: flex; }
        .mobile-menu a { font-size: 0.95rem; font-weight: 500; color: var(--text-dark); text-decoration: none; padding: 12px 0; border-bottom: 1px solid var(--border); }
        .mobile-menu a:last-child { border-bottom: none; }

        /* ── LAYOUT ── */
        .page-wrap { max-width: 860px; margin: 0 auto; padding: 48px 24px 80px; }

        /* ── HERO ── */
        .hero { text-align: center; margin-bottom: 48px; }
        .hero-badge {
            display: inline-flex; align-items: center; gap: 6px;
            background: var(--purple-light); color: var(--purple-dark);
            font-size: 0.8rem; font-weight: 600; padding: 6px 14px;
            border-radius: 999px; margin-bottom: 20px;
            letter-spacing: 0.04em; text-transform: uppercase;
        }
        h1 {
            font-family: 'Sora', sans-serif; font-size: clamp(2rem, 5vw, 2.8rem);
            font-weight: 800; color: var(--text-dark); line-height: 1.2; margin-bottom: 16px;
        }
        h1 em { font-style: normal; color: var(--purple-main); }
        .hero-sub { font-size: 1.05rem; color: var(--text-mid); max-width: 580px; margin: 0 auto 28px; }
        
        .trust-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
        .trust-pill {
            display: flex; align-items: center; gap: 6px;
            background: var(--white); border: 1px solid var(--border);
            border-radius: 999px; padding: 6px 14px;
            font-size: 0.82rem; font-weight: 500; color: var(--text-mid);
            box-shadow: var(--shadow-sm);
        }

        /* ── AUTHOR BOX E-E-A-T ── */
        .author-box { 
            display: flex; align-items: center; gap: 15px; 
            background: var(--white); padding: 20px; border-radius: 12px; 
            border: 1px solid var(--border); margin-bottom: 30px; 
            border-left: 4px solid var(--purple-main); box-shadow: var(--shadow-sm); 
        }
        .author-img { 
            width: 50px; height: 50px; background: var(--purple-main); 
            border-radius: 50%; display: flex; align-items: center; 
            justify-content: center; color: white; font-weight: bold; font-size: 1.2rem; 
        }

        /* ── TOOL CARD ── */
        .tool-card {
            background: var(--white); border-radius: 20px; padding: 40px 36px;
            box-shadow: var(--shadow-lg); border: 1px solid var(--border); margin-bottom: 56px;
        }

        .drop-zone {
            border: 2.5px dashed var(--border); border-radius: 14px;
            padding: 44px 24px; text-align: center; background: var(--purple-pale);
            transition: all 0.25s; cursor: pointer; margin-bottom: 28px;
        }
        .drop-zone:hover, .drop-zone.over { border-color: var(--purple-main); background: var(--purple-light); }
        .drop-icon { font-size: 2.4rem; display: block; margin-bottom: 10px; }
        .drop-label { font-size: 1rem; color: var(--text-mid); margin-bottom: 16px; }
        .drop-label strong { color: var(--text-dark); }
        
        .btn-choose {
            display: inline-flex; align-items: center; gap: 8px;
            background: var(--purple-light); color: var(--purple-dark);
            border: 2px solid var(--purple-main); padding: 11px 28px;
            font-size: 0.95rem; font-weight: 600; border-radius: 999px;
            cursor: pointer; transition: all 0.2s;
        }
        .btn-choose:hover { background: var(--purple-main); color: var(--white); }
        .btn-choose input { display: none; }
        #fileName { margin-top: 14px; font-size: 0.95rem; font-weight: 600; color: var(--purple-dark); min-height: 22px; }

        /* Form fields */
        .fields { display: flex; flex-direction: column; gap: 18px; margin-bottom: 28px; }
        .field-group label { display: block; font-size: 0.88rem; font-weight: 600; color: var(--text-dark); margin-bottom: 7px; }
        .field-group .field-hint { font-size: 0.78rem; color: var(--text-light); margin-bottom: 7px; font-weight: 400; }
        
        .password-wrapper { position: relative; width: 100%; display: flex; align-items: center; }
        .password-wrapper input[type="password"], .password-wrapper input[type="text"] {
            width: 100%; padding: 13px 45px 13px 16px;
            border: 2px solid var(--border); border-radius: 10px;
            font-size: 1rem; font-family: 'Inter', sans-serif;
            color: var(--text-dark); transition: border-color 0.2s; background: var(--purple-pale);
        }
        .password-wrapper input:focus { outline: none; border-color: var(--purple-main); background: var(--white); }
        .toggle-pwd { position: absolute; right: 12px; background: none; border: none; cursor: pointer; font-size: 1.1rem; color: var(--text-light); padding: 5px; }

        /* Permissions */
        .perm-section { background: var(--purple-pale); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 28px; }
        .perm-title { font-size: 0.88rem; font-weight: 700; color: var(--text-dark); margin-bottom: 14px; }
        .perm-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
        .perm-item { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; color: var(--text-mid); cursor:pointer; }
        .perm-item input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--purple-main); cursor: pointer; }

        /* Buttons */
        .btn-protect {
            width: 100%; background: linear-gradient(135deg, var(--purple-main) 0%, var(--purple-dark) 100%);
            color: var(--white); border: none; padding: 17px 24px;
            font-size: 1.05rem; font-weight: 700; border-radius: 12px;
            cursor: pointer; transition: all 0.25s; letter-spacing: 0.02em;
            box-shadow: 0 4px 18px rgba(109,40,217,0.35);
        }
        .btn-protect:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(109,40,217,0.4); }
        .btn-protect:disabled { background: #b0a8c8; box-shadow: none; cursor: not-allowed; }

        #downloadBtn {
            display: none; width: 100%;
            background: linear-gradient(135deg, var(--green-main) 0%, var(--green-dark) 100%);
            color: var(--white); padding: 17px 24px;
            font-size: 1.05rem; font-weight: 700; border-radius: 12px;
            text-decoration: none; text-align: center; margin-top: 14px;
            box-shadow: 0 4px 18px rgba(22,163,74,0.3); transition: all 0.25s;
        }
        #downloadBtn:hover { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(22,163,74,0.4); }

        #status { margin-top: 18px; font-size: 0.95rem; font-weight: 500; min-height: 28px; text-align: center; }

        /* Safety note */
        .safety-note {
            display: flex; gap: 12px; align-items: flex-start;
            background: #fff8e1; border: 1px solid #ffd54f;
            border-radius: 10px; padding: 16px; margin-top: 22px;
            font-size: 0.84rem; color: #5f4400; line-height: 1.6;
        }
        .safety-note .icon { font-size: 1.2rem; flex-shrink: 0; }

        /* ── ARTICLE SECTIONS ── */
        .article { margin-bottom: 56px; }
        .article h2 {
            font-family: 'Sora', sans-serif; font-size: 1.5rem;
            font-weight: 800; color: var(--text-dark); margin-bottom: 16px;
            padding-bottom: 10px; border-bottom: 2px solid var(--purple-light);
        }
        .article h3 { font-size: 1.1rem; font-weight: 700; color: var(--purple-dark); margin: 24px 0 10px; }
        .article p { font-size: 0.97rem; color: var(--text-mid); margin-bottom: 14px; line-height: 1.8; }
        .article ul { padding-left: 22px; margin-bottom: 16px; color: var(--text-mid); font-size: 0.97rem; }
        .article li { margin-bottom: 7px; line-height: 1.6; }
        .article strong { color: var(--text-dark); }
        .article a { color: var(--purple-main); text-decoration: none; font-weight: 600; }
        .article a:hover { text-decoration: underline; }

        /* FAQ Semantik */
        .faq-list { display: flex; flex-direction: column; gap: 14px; }
        .faq-item { background: var(--white); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
        .faq-q { 
            width: 100%; text-align: left; background: none; border: none; 
            padding: 18px 20px; font-size: 0.97rem; font-weight: 600; color: var(--text-dark); 
            cursor: pointer; display: flex; justify-content: space-between; align-items: center; 
            gap: 12px; transition: background 0.15s; font-family: 'Inter', sans-serif; 
        }
        .faq-q:focus { outline: 2px solid var(--purple-main); outline-offset: -2px; }
        .faq-q:hover { background: var(--purple-pale); }
        .faq-q .arrow { transition: transform 0.25s; font-size: 0.7rem; color: var(--purple-main); }
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a { 
            padding: 0 20px; max-height: 0; overflow: hidden; 
            transition: max-height 0.3s ease, padding 0.3s; 
            font-size: 0.92rem; color: var(--text-mid); background: var(--purple-pale); 
        }
        .faq-a.open { max-height: 400px; padding: 16px 20px; }

        /* Bukti Screenshot Grid */
        .proof-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 16px;
        }
        .proof-item {
            background: var(--white); border: 1px solid var(--border);
            border-radius: 14px; overflow: hidden; box-shadow: var(--shadow-sm);
            transition: transform 0.2s, box-shadow 0.2s;
        }
        .proof-item:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
        .proof-item img {
            width: 100%; aspect-ratio: 9 / 16; object-fit: cover;
            display: block; background: var(--purple-pale);
        }
        .proof-caption {
            display: flex; align-items: center; gap: 6px;
            padding: 10px 12px; font-size: 0.8rem; font-weight: 600;
            color: var(--text-dark); line-height: 1.4;
        }
        .proof-caption .num {
            flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%;
            background: var(--purple-main); color: var(--white);
            font-size: 0.72rem; display: flex; align-items: center; justify-content: center;
        }

        /* Footer */
        footer {
            background: var(--white); border-top: 1px solid var(--border);
            padding: 32px 24px; text-align: center; color: var(--text-light); font-size: 0.85rem;
        }
        footer a { color: var(--text-mid); text-decoration: none; font-weight: 500; transition: color 0.2s; }
        footer a:hover { color: var(--purple-main); }

        @media (max-width: 600px) {
            .nav-links { display: none; }
            .nav-toggle { display: flex; }
            .tool-card { padding: 28px 20px; }
            .perm-grid { grid-template-columns: 1fr; }
            h1 { font-size: 1.8rem; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;&lt;a href="https://www.kholil.biz.id/"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/unlock-pdf.html"&gt;Unlock PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;HTML to PDF&lt;/a&gt;
    &lt;/div&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
&lt;/nav&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/unlock-pdf.html"&gt;&#128275; Unlock PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;&#127760; HTML to PDF&lt;/a&gt;
&lt;/div&gt;

&lt;div class="page-wrap"&gt;
    
    &lt;!-- HERO --&gt;
    &lt;header class="hero"&gt;
        &lt;span class="hero-badge"&gt;&#128274; Keamanan Dokumen&lt;/span&gt;
        &lt;h1&gt;Kunci File PDF dengan &lt;em&gt;Sandi&lt;/em&gt;&lt;br&gt;— 100% Pemrosesan Privat&lt;/h1&gt;
        &lt;p class="hero-sub"&gt;Lumpuhkan akses modifikasi dan pembacaan dengan algoritma enkripsi sisi klien. Tanpa jejak riwayat di server eksternal.&lt;/p&gt;
        &lt;div class="trust-row"&gt;
            &lt;span class="trust-pill"&gt;✅ Client-Side Memory&lt;/span&gt;
            &lt;span class="trust-pill"&gt;&#128272; Enkripsi Algoritma AES-128&lt;/span&gt;
            &lt;span class="trust-pill"&gt;⚡ Kontrol Izin Lanjut&lt;/span&gt;
        &lt;/div&gt;
    &lt;/header&gt;

    &lt;!-- TOOL --&gt;
    &lt;div class="tool-card"&gt;
        &lt;div class="drop-zone" id="dropArea"&gt;
            &lt;span class="drop-icon"&gt;&#128196;&lt;/span&gt;
            &lt;p class="drop-label"&gt;&lt;strong&gt;Seret &amp; lepas file PDF Anda di sini&lt;/strong&gt;&lt;br&gt;atau klik tombol di bawah untuk memilih file&lt;/p&gt;
            &lt;label class="btn-choose"&gt;
                &#128193; Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf"&gt;
            &lt;/label&gt;
            &lt;p id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;div class="fields"&gt;
            &lt;div class="field-group"&gt;
                &lt;label for="userPassword"&gt;Kata Sandi Pengguna (Wajib) &lt;span style="color:#dc2626"&gt;*&lt;/span&gt;&lt;/label&gt;
                &lt;div id="hint-user" class="field-hint"&gt;Penerima dokumen harus memasukkan sandi ini untuk sekadar "Membuka" file.&lt;/div&gt;
                &lt;div class="password-wrapper"&gt;
                    &lt;input type="password" id="userPassword" aria-describedby="hint-user" placeholder="Ketik kata sandi..." autocomplete="new-password"&gt;
                    &lt;button type="button" class="toggle-pwd" onclick="togglePassword('userPassword', this)" aria-label="Tampilkan kata sandi"&gt;&#128065;&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="field-group"&gt;
                &lt;label for="ownerPassword"&gt;Kata Sandi Administrator (Opsional)&lt;/label&gt;
                &lt;div id="hint-owner" class="field-hint"&gt;Gunakan sandi berbeda agar hak izin (Mencetak/Mengopi) di bawah ini terkunci.&lt;/div&gt;
                &lt;div class="password-wrapper"&gt;
                    &lt;input type="password" id="ownerPassword" aria-describedby="hint-owner" placeholder="Kosongkan jika tak perlu kontrol lanjutan" autocomplete="new-password"&gt;
                    &lt;button type="button" class="toggle-pwd" onclick="togglePassword('ownerPassword', this)" aria-label="Tampilkan kata sandi"&gt;&#128065;&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="perm-section"&gt;
            &lt;p class="perm-title"&gt;⚙️ Manajemen Izin Dokumen Ekstra&lt;/p&gt;
            &lt;div class="perm-grid"&gt;
                &lt;label class="perm-item" for="permPrint"&gt;&lt;input type="checkbox" id="permPrint" checked&gt; Mengizinkan pencetakan file&lt;/label&gt;
                &lt;label class="perm-item" for="permCopy"&gt;&lt;input type="checkbox" id="permCopy"&gt; Mengizinkan penyalinan teks&lt;/label&gt;
                &lt;label class="perm-item" for="permModify"&gt;&lt;input type="checkbox" id="permModify"&gt; Mengizinkan pengubahan teks&lt;/label&gt;
                &lt;label class="perm-item" for="permForms"&gt;&lt;input type="checkbox" id="permForms" checked&gt; Membuka pengisian formulir&lt;/label&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;button class="btn-protect" id="protectBtn" onclick="protectPDF()"&gt;&#128274; Protect PDF Sekarang&lt;/button&gt;
        &lt;a id="downloadBtn" href="#"&gt;⬇️ Download PDF yang Sudah Diproteksi&lt;/a&gt;
        &lt;div id="status"&gt;&lt;/div&gt;

        &lt;div class="safety-note"&gt;
            &lt;span class="icon"&gt;⚠️&lt;/span&gt;
            &lt;div&gt;&lt;strong&gt;Peringatan Keamanan:&lt;/strong&gt; Skrip enkripsi ini bersifat destruktif searah. Jika Anda kehilangan "User Password", file tidak akan pernah bisa dibuka kembali oleh siapapun. Simpan kata sandi Anda dengan baik.&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- AUTHOR BOX E-E-A-T --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div&gt;
            &lt;strong style="color:var(--text-dark); display:block; font-size:1rem;"&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
            &lt;span style="font-size:0.85rem; color:var(--text-light);"&gt;Spesialis Kriptografi Berkas Digital | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- NARASI UNIK --&gt;
    &lt;section class="article"&gt;
        &lt;h2&gt;Melindungi Hak Cipta &amp; Dokumen Sensitif&lt;/h2&gt;
        &lt;p&gt;Berbagi laporan medikal, kontrak hak cipta independen (freelance), atau portofolio desain visual tanpa pembatasan hak akses memiliki risiko besar terjadinya plagiasi (&lt;em&gt;Copy-Paste&lt;/em&gt;). Daripada mengirimkan dokumen kosong dan mencemaskan apakah data Anda dicuri, skrip kriptografi kami memungkinkan Anda mencetak lapisan perlindungan 128-bit langsung dari memori gawai pintar atau PC Anda.&lt;/p&gt;
        
        &lt;h3&gt;Arsitektur Sandi "Owner" vs "User"&lt;/h3&gt;
        &lt;p&gt;Aplikasi ini memiliki hierarki dua tingkat pertahanan (&lt;em&gt;Double-layer defense&lt;/em&gt;):&lt;/p&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;User Password:&lt;/strong&gt; Ini adalah kunci pintu masuk. Jika klien tidak mengetahui sandi ini, file akan menampilkan jendela error saat di klik.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Owner Password (Administrator):&lt;/strong&gt; Jika Anda menetapkan sandi Master yang berbeda, maka Anda mengaktifkan fungsi &lt;em&gt;Digital Rights Management (DRM)&lt;/em&gt; ringan. Misalnya: Klien bisa membuka file dengan &lt;em&gt;User Password&lt;/em&gt;, namun jika mereka mencoba memblok teks untuk disalin, tombol "Copy" di PDF Reader mereka akan mati dan dikunci dengan gembok berwarna abu-abu.&lt;/li&gt;
        &lt;/ul&gt;
        
        &lt;p&gt;&lt;strong&gt;Siklus Pemrosesan Lanjutan:&lt;/strong&gt; Apabila suatu hari Anda tidak lagi membutuhkan batasan pada file yang telah Anda buat, Anda wajib mencopot algoritma pelindungnya menggunakan fitur &lt;a href="https://www.kholil.biz.id/2026/04/unlock-pdf.html"&gt;Unlock PDF&lt;/a&gt; kami. Sebagai tambahan, jika sumber asli dokumen Anda adalah web page yang ingin Anda amankan menjadi berkas digital, kami juga menyediakan layanan &lt;a href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;HTML to PDF&lt;/a&gt; yang cepat dan aman.&lt;/p&gt;
    &lt;/section&gt;

    &lt;!-- FAQ TEKNIS --&gt;
    &lt;section class="article"&gt;
        &lt;h2&gt;Tanya Jawab Kriptografi File&lt;/h2&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Mengapa menggunakan enkripsi standar AES-128? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Pustaka standar industri yang digunakan alat berbasis peramban (JavaScript) kami membangun tingkat komputasi tangguh pada protokol &lt;em&gt;Advanced Encryption Standard (AES)&lt;/em&gt;. Tipe 128-bit kami pilih karena telah diverifikasi tangguh untuk dokumen publik komersial, memastikan proses berjalan instan, dan mencegah peramban Anda mengalami &lt;em&gt;Crash/Freeze&lt;/em&gt; pada RAM ponsel berkapasitas rendah.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah Tim Admin bisa membuka kunci file saya jika saya lupa? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Secara absolut, tidak bisa. File PDF tersebut tidak pernah ditransfer melintasi &lt;em&gt;server&lt;/em&gt; pengolahan kami. Jika Anda kehilangan "User Password", file tersebut tidak memiliki fungsi pemulihan (&lt;em&gt;Backdoor&lt;/em&gt;). Kami menghimbau Anda menggunakan pengelola kata sandi (&lt;em&gt;Password Manager&lt;/em&gt;) agar data tidak terkunci secara permanen.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah saya wajib mengisi Kata Sandi Administrator? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Tidak wajib. Kolom ini bersifat opsional. Jika dikosongkan, sistem otomatis menyamakannya dengan "User Password" sehingga hanya ada satu kunci untuk membuka sekaligus mengubah izin dokumen. Isi kolom ini hanya jika Anda ingin memisahkan hak "membuka file" dari hak "mengatur izin cetak/salin".&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Mengapa proses "Protect PDF" gagal atau macet di tombol "Menyuntikkan Enkripsi"? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Penyebab paling umum adalah koneksi internet yang gagal memuat pustaka enkripsi saat halaman pertama kali dibuka, atau file PDF yang sudah terenkripsi/rusak sebelumnya. Coba muat ulang (refresh) halaman dengan koneksi yang stabil, lalu ulangi proses. Pesan error yang tampil di layar akan menunjukkan penyebab pastinya.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah ukuran atau jumlah halaman file PDF saya akan berubah? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Tidak. Proses ini hanya menambahkan lapisan sandi dan aturan izin pada struktur file, tanpa mengubah konten, urutan halaman, maupun kualitas gambar di dalam dokumen. Ukuran file biasanya bertambah sangat sedikit karena adanya data enkripsi tambahan.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI FUNGSIONALITAS --&gt;
    &lt;section class="article"&gt;
        &lt;h2&gt;Bukti Tool Ini Berfungsi 100%&lt;/h2&gt;
        &lt;p&gt;Berikut tangkapan layar nyata dari proses pengujian alat ini, mulai dari kata sandi terenkripsi hingga file PDF hasil unduhan yang benar-benar meminta password saat dibuka.&lt;/p&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/Wpt9HD6p/Screenshot-2026-07-22-02-01-17-294-com-android-chrome.jpg" alt="Kata sandi terenkripsi" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;1&lt;/span&gt; Kata sandi terenkripsi&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/hJ1rsm7D/Screenshot-2026-07-22-02-01-19-764-com-android-chrome.jpg" alt="Kata sandi terlihat" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;2&lt;/span&gt; Kata sandi terlihat&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/jZZ3THmG/Screenshot-2026-07-22-02-01-24-386-com-android-chrome.jpg" alt="Password berhasil diatur" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;3&lt;/span&gt; Password berhasil diatur&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/ZR6BHRTT/Screenshot-2026-07-22-02-01-27-603-com-android-chrome.jpg" alt="Tombol download berfungsi normal" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;4&lt;/span&gt; Tombol download berfungsi normal&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/hxpgKt7s/Screenshot-2026-07-22-02-01-34-683-com-google-android-apps-docs.jpg" alt="Hasil PDF meminta password" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;5&lt;/span&gt; Hasil PDF ada password-nya&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

&lt;/div&gt;&lt;!-- end page-wrap --&gt;

&lt;footer&gt;
    &lt;p&gt;&amp;copy; 2026 &lt;strong&gt;Kholil Tools&lt;/strong&gt; &amp;nbsp;·&amp;nbsp; Sistem Pemrosesan Client-Side&lt;/p&gt;
    &lt;p style="margin-top:12px; font-size:0.9rem;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html" style="margin: 0 8px;"&gt;Kebijakan Privasi&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="margin: 0 8px;"&gt;Kontak Kami&lt;/a&gt;
    &lt;/p&gt;
&lt;/footer&gt;

&lt;!-- Libraries --&gt;
&lt;script src="https://unpkg.com/@cantoo/pdf-lib@2.7.1/dist/pdf-lib.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
    let originalBytes = null;
    let originalFileName = "";

    const pdfFileInput = document.getElementById('pdfFile');
    const dropArea    = document.getElementById('dropArea');
    const fileNameEl  = document.getElementById('fileName');
    const statusEl    = document.getElementById('status');
    const protectBtn  = document.getElementById('protectBtn');
    const downloadBtn = document.getElementById('downloadBtn');

    // ARIA Live untuk Notifikasi Status (Aksesibilitas)
    statusEl.setAttribute('aria-live', 'polite');

    dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('over'); });
    dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('over'));
    dropArea.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropArea.classList.remove('over');
        const f = e.dataTransfer.files[0];
        if (f &amp;&amp; f.type === 'application/pdf') handleFile(f);
        else alert('Silakan pilih file PDF yang valid.');
    });
    
    pdfFileInput.addEventListener('change', e =&gt; {
        if (e.target.files[0]) handleFile(e.target.files[0]);
    });

    function handleFile(file) {
        // Anti-XSS Sanitization
        const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
        originalFileName = file.name;
        fileNameEl.innerHTML = '✅ ' + safeName;
        statusEl.innerHTML = '';
        downloadBtn.style.display = 'none';
        
        const reader = new FileReader();
        reader.onload = e =&gt; { originalBytes = e.target.result; };
        reader.readAsArrayBuffer(file);
    }

    // Toggle Hide/Show Password
    function togglePassword(inputId, btn) {
        const input = document.getElementById(inputId);
        if (input.type === 'password') {
            input.type = 'text';
            btn.textContent = '&#128584;';
        } else {
            input.type = 'password';
            btn.textContent = '&#128065;';
        }
    }

    async function protectPDF() {
        if (!originalBytes) { alert('Silakan pilih file PDF terlebih dahulu!'); return; }
        
        const userPass  = document.getElementById('userPassword').value;
        let   ownerPass = document.getElementById('ownerPassword').value.trim();
        
        if (!userPass)  { alert('Kata sandi utama tidak boleh kosong!'); return; }
        if (!ownerPass) ownerPass = userPass;

        const perm = {
            printing:              document.getElementById('permPrint').checked ? 'highResolution' : 'none',
            modifying:             document.getElementById('permModify').checked,
            copying:               document.getElementById('permCopy').checked,
            annotating:            false,
            fillingForms:          document.getElementById('permForms').checked,
            contentAccessibility:  true,
            documentAssembly:      false,
        };

        protectBtn.disabled    = true;
        protectBtn.textContent = '⏳ Menyuntikkan Enkripsi AES...';
        statusEl.innerHTML     = '';

        try {
            if (typeof PDFLib === 'undefined' || !PDFLib.PDFDocument) {
                throw new Error('Pustaka PDF gagal dimuat. Periksa koneksi internet Anda lalu muat ulang halaman.');
            }

            const pdfDoc = await PDFLib.PDFDocument.load(originalBytes, { ignoreEncryption: true });

            if (typeof pdfDoc.encrypt !== 'function') {
                throw new Error('Fungsi enkripsi tidak tersedia pada pustaka yang dimuat.');
            }

            // Enkripsi AES-128 dengan kata sandi pengguna &amp; administrator
            await pdfDoc.encrypt({ userPassword: userPass, ownerPassword: ownerPass, permissions: perm });
            const bytes  = await pdfDoc.save();
            const blob   = new Blob([bytes], { type: 'application/pdf' });
            const url    = URL.createObjectURL(blob);

            downloadBtn.href     = url;
            downloadBtn.download = originalFileName.replace(/\.pdf$/i, '_Terlindungi.pdf');
            downloadBtn.style.display = 'block';

            statusEl.innerHTML = '&lt;span style="color:#16a34a;font-weight:700;"&gt;✅ PDF berhasil dikunci secara kriptografis! Silakan unduh dokumen Anda.&lt;/span&gt;';
        } catch (err) {
            console.error(err);
            const safeMsg = String(err &amp;&amp; err.message ? err.message : err).replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
            statusEl.innerHTML = '&lt;span style="color:#dc2626;font-weight:600;"&gt;❌ Gagal memproses enkripsi: ' + safeMsg + '&lt;/span&gt;';
        }

        protectBtn.disabled    = false;
        protectBtn.textContent = '&#128274; Protect PDF Sekarang';
    }

    // Toggle FAQ Aksesibel
    function toggleFaq(btn) {
        const answer = btn.nextElementSibling;
        const isOpen = btn.classList.contains('open');
        
        document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('open'));
        document.querySelectorAll('.faq-q').forEach(b =&gt; {
            b.classList.remove('open');
            b.setAttribute('aria-expanded', 'false');
        });
        
        if (!isOpen) {
            answer.classList.add('open');
            btn.classList.add('open');
            btn.setAttribute('aria-expanded', 'true');
        }
    }

    // Toggle Mobile Menu
    function toggleMenu() {
        const menu = document.getElementById('mobileMenu');
        const toggle = document.getElementById('navToggle');
        const isMenuOpen = menu.classList.toggle('open');
        toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
    }
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/protect-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-1891026793471657977</guid><pubDate>Fri, 10 Apr 2026 08:53:00 +0000</pubDate><atom:updated>2026-07-24T07:49:47.049-07:00</atom:updated><title>Unlock pdf </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;meta name="description" content="Hapus proteksi dan buka kunci file PDF yang terkunci sandi secara online gratis. Proses dekripsi terjadi di dalam memori browser Anda, privasi dijamin."&gt;
    &lt;title&gt;Unlock PDF Online – Buka &amp; Hapus Kunci Sandi PDF | Kholil Tools&lt;/title&gt;

    &lt;!-- Canonical &amp; OG --&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/unlock-pdf.html" /&gt;
    &lt;meta property="og:title" content="Unlock PDF Online – Buka &amp; Hapus Kunci Sandi PDF" /&gt;
    &lt;meta property="og:description" content="Hapus batasan cetak/salin pada file PDF Anda. Proses 100% Client-Side yang dijamin aman." /&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/unlock-pdf.html" /&gt;
    &lt;meta property="og:type" content="website" /&gt;

    &lt;!-- Fonts --&gt;
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema E-E-A-T &amp; WebApp --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Unlock PDF Tool",
      "applicationCategory": "SecurityApplication",
      "operatingSystem": "All",
      "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" },
      "author": { "@type": "Person", "name": "Kholil", "url": "https://www.kholil.biz.id/" }
    }
    &lt;/script&gt;

    &lt;!-- Schema FAQ --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Apakah ini bisa menembus paksa (Hack) PDF orang lain?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Secara absolut, TIDAK. Alat ini bukan cracker dan tidak memiliki mekanisme Brute-Force Attack. Anda tetap wajib memiliki Sandi Valid untuk dapat membongkar kunci tersebut."
          }
        },
        {
          "@type": "Question",
          "name": "Apakah resolusi gambar di dalam dokumen ikut berubah?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Tidak. Mesin dekripsi QPDF yang kami terapkan hanya menghapus lapisan enkripsi pada dictionary keamanan dokumen, tanpa membongkar ulang atau mengompres ulang stream gambar di dalamnya. Kualitas grafis tetap identik dengan berkas asli."
          }
        }
      ]
    }
    &lt;/script&gt;

    &lt;style&gt;
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        :root {
            --red-dark:   #991b1b;
            --red-main:   #dc2626;
            --red-light:  #fee2e2;
            --red-pale:   #fff5f5;
            --green-dark: #166534;
            --green-main: #16a34a;
            --text-dark:  #1a1a2e;
            --text-mid:   #4b4568;
            --text-light: #6b6b8a;
            --border:     #f0d5d5;
            --border-neu: #e8e0f0;
            --white:      #ffffff;
            --purple-main: #6d28d9;
            --shadow-sm:  0 2px 8px rgba(220,38,38,0.07);
            --shadow-lg:  0 20px 60px rgba(220,38,38,0.13);
        }

        body {
            font-family: 'Inter', sans-serif;
            background: #fdf5f5;
            color: var(--text-dark);
            line-height: 1.7;
        }

        /* ── NAVIGASI ── */
        .main-nav {
            background: var(--white);
            border-bottom: 1px solid var(--border-neu);
            padding: 16px 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: sticky;
            top: 0;
            z-index: 100;
            box-shadow: var(--shadow-sm);
        }
        .main-nav .logo a {
            font-family: 'Sora', sans-serif;
            font-size: 1.25rem;
            font-weight: 800;
            color: var(--purple-main);
            text-decoration: none;
        }
        .main-nav .logo span { color: var(--text-dark); }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a {
            font-size: 0.95rem;
            font-weight: 500;
            color: var(--text-mid);
            text-decoration: none;
            transition: color 0.2s;
        }
        .nav-links a:hover { color: var(--purple-main); }

        .nav-toggle {
            display: none; flex-direction: column; gap: 5px; cursor: pointer; padding: 4px; background: none; border: none;
        }
        .nav-toggle span { display: block; width: 24px; height: 2px; background: var(--purple-main); border-radius: 2px; }

        .mobile-menu {
            display: none; position: fixed; top: 65px; left: 0; right: 0; background: var(--white); border-bottom: 1px solid var(--border-neu); z-index: 98; padding: 16px 24px; flex-direction: column; gap: 0; box-shadow: 0 8px 32px rgba(0,0,0,0.1);
        }
        .mobile-menu.open { display: flex; }
        .mobile-menu a { font-size: 0.95rem; font-weight: 500; color: var(--text-dark); text-decoration: none; padding: 12px 0; border-bottom: 1px solid var(--border-neu); }
        .mobile-menu a:last-child { border-bottom: none; }

        /* ── LAYOUT ── */
        .page-wrap { max-width: 860px; margin: 0 auto; padding: 48px 24px 80px; }

        /* ── HERO ── */
        .hero { text-align: center; margin-bottom: 48px; }
        .hero-badge {
            display: inline-flex; align-items: center; gap: 6px; background: var(--red-light); color: var(--red-dark); font-size: 0.8rem; font-weight: 600; padding: 6px 14px; border-radius: 999px; margin-bottom: 20px; letter-spacing: 0.04em; text-transform: uppercase;
        }
        h1 { font-family: 'Sora', sans-serif; font-size: clamp(2rem, 5vw, 2.8rem); font-weight: 800; color: var(--text-dark); line-height: 1.2; margin-bottom: 16px; }
        h1 em { font-style: normal; color: var(--red-main); }
        .hero-sub { font-size: 1.05rem; color: var(--text-mid); max-width: 580px; margin: 0 auto 28px; }

        .trust-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
        .trust-pill { display: flex; align-items: center; gap: 6px; background: var(--white); border: 1px solid var(--border-neu); border-radius: 999px; padding: 6px 14px; font-size: 0.82rem; font-weight: 500; color: var(--text-mid); box-shadow: var(--shadow-sm); }

        /* ── TOOL CARD ── */
        .tool-card { background: var(--white); border-radius: 20px; padding: 40px 36px; box-shadow: var(--shadow-lg); border: 1px solid var(--border); margin-bottom: 40px; }

        .drop-zone { border: 2.5px dashed var(--border); border-radius: 14px; padding: 44px 24px; text-align: center; background: var(--red-pale); transition: all 0.25s; cursor: pointer; margin-bottom: 28px; }
        .drop-zone:hover, .drop-zone.over { border-color: var(--red-main); background: var(--red-light); }
        .drop-icon { font-size: 2.4rem; display: block; margin-bottom: 10px; }
        .drop-label { font-size: 1rem; color: var(--text-mid); margin-bottom: 16px; }
        .drop-label strong { color: var(--text-dark); }
        .btn-choose { display: inline-flex; align-items: center; gap: 8px; background: var(--red-light); color: var(--red-dark); border: 2px solid var(--red-main); padding: 11px 28px; font-size: 0.95rem; font-weight: 600; border-radius: 999px; cursor: pointer; transition: all 0.2s; }
        .btn-choose:hover { background: var(--red-main); color: var(--white); }
        .btn-choose input { display: none; }
        #fileName { margin-top: 14px; font-size: 0.95rem; font-weight: 600; color: var(--red-dark); min-height: 22px; }

        /* Password Form */
        .field-group { margin-bottom: 20px; }
        .field-group label { display: block; font-size: 0.88rem; font-weight: 600; color: var(--text-dark); margin-bottom: 7px; }
        .field-group .field-hint { font-size: 0.78rem; color: var(--text-light); margin-bottom: 7px; font-weight: 400; }

        .password-wrapper { position: relative; display: flex; align-items: center; width: 100%; }
        .password-wrapper input { width: 100%; padding: 13px 45px 13px 16px; border: 2px solid var(--border-neu); border-radius: 10px; font-size: 1rem; font-family: 'Inter', sans-serif; color: var(--text-dark); transition: border-color 0.2s; background: var(--red-pale); }
        .password-wrapper input:focus { outline: none; border-color: var(--red-main); background: var(--white); }
        .toggle-pwd { position: absolute; right: 12px; background: none; border: none; font-size: 1.1rem; cursor: pointer; color: var(--text-light); padding: 5px; }

        /* Buttons */
        .btn-unlock { width: 100%; background: linear-gradient(135deg, var(--red-main) 0%, var(--red-dark) 100%); color: var(--white); border: none; padding: 17px 24px; font-size: 1.05rem; font-weight: 700; border-radius: 12px; cursor: pointer; transition: all 0.25s; letter-spacing: 0.02em; box-shadow: 0 4px 18px rgba(220,38,38,0.35); }
        .btn-unlock:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(220,38,38,0.4); }
        .btn-unlock:disabled { background: #b0a8a8; box-shadow: none; cursor: not-allowed; }

        #downloadBtn { display: none; width: 100%; background: linear-gradient(135deg, var(--green-main) 0%, var(--green-dark) 100%); color: var(--white); padding: 17px 24px; font-size: 1.05rem; font-weight: 700; border-radius: 12px; text-decoration: none; text-align: center; margin-top: 14px; box-shadow: 0 4px 18px rgba(22,163,74,0.3); transition: all 0.25s; }
        #downloadBtn:hover { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(22,163,74,0.4); }

        #status { margin-top: 18px; font-size: 0.95rem; font-weight: 500; min-height: 28px; text-align: center; }

        .warning-note { display: flex; gap: 12px; align-items: flex-start; background: #fffbeb; border: 1px solid #fcd34d; border-radius: 10px; padding: 16px; margin-top: 22px; font-size: 0.84rem; color: #78350f; line-height: 1.6; }
        .warning-note .icon { font-size: 1.2rem; flex-shrink: 0; }

        /* ── BUKTI SCREENSHOT (PROOF GRID) ── */
        .proof-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
            gap: 14px;
            margin: 20px 0;
        }
        .proof-item {
            background: var(--white);
            border: 1px solid var(--border-neu);
            border-radius: 12px;
            overflow: hidden;
            box-shadow: var(--shadow-sm);
        }
        .proof-item img {
            width: 100%;
            aspect-ratio: 9 / 18;
            object-fit: cover;
            display: block;
            background: var(--red-pale);
        }
        .proof-caption {
            display: flex;
            align-items: flex-start;
            gap: 8px;
            padding: 10px 12px;
            font-size: 0.8rem;
            color: var(--text-mid);
            font-weight: 500;
            line-height: 1.4;
        }
        .proof-num {
            flex-shrink: 0;
            width: 20px; height: 20px;
            background: var(--green-main);
            color: var(--white);
            border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            font-size: 0.72rem;
            font-weight: 800;
        }

        /* ── AUTHOR BOX E-E-A-T ── */
        .author-box { display: flex; align-items: center; gap: 15px; background: var(--white); padding: 20px; border-radius: 12px; border: 1px solid var(--border-neu); margin-bottom: 30px; border-left: 4px solid var(--purple-main); box-shadow: var(--shadow-sm); }
        .author-img { width: 50px; height: 50px; background: var(--purple-main); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 1.2rem; }

        /* ── ARTICLE SECTIONS ── */
        .article { margin-bottom: 56px; }
        .article h2 { font-family: 'Sora', sans-serif; font-size: 1.5rem; font-weight: 800; color: var(--text-dark); margin-bottom: 16px; padding-bottom: 10px; border-bottom: 2px solid var(--red-light); }
        .article h3 { font-size: 1.1rem; font-weight: 700; color: var(--red-dark); margin: 24px 0 10px; }
        .article p { font-size: 0.97rem; color: var(--text-mid); margin-bottom: 14px; }
        .article ul { padding-left: 22px; margin-bottom: 16px; color: var(--text-mid); font-size: 0.97rem; }
        .article li { margin-bottom: 7px; }
        .article strong { color: var(--text-dark); }
        .article a { color: var(--purple-main); text-decoration: none; font-weight: 600; }
        .article a:hover { text-decoration: underline; }

        /* FAQ Accessible */
        .faq { display: flex; flex-direction: column; border: 1px solid var(--border-neu); border-radius: 14px; overflow: hidden; background: var(--white); }
        .faq-item { border-bottom: 1px solid var(--border-neu); }
        .faq-item:last-child { border-bottom: none; }
        .faq-q { width: 100%; text-align: left; background: none; border: none; padding: 18px 20px; font-size: 0.97rem; font-weight: 600; color: var(--text-dark); cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px; transition: background 0.15s; font-family: 'Inter', sans-serif; }
        .faq-q:focus { outline: 2px solid var(--red-main); outline-offset: -2px; }
        .faq-q:hover { background: var(--red-pale); }
        .faq-q .arrow { transition: transform 0.25s; font-size: 0.7rem; color: var(--red-main); }
        .faq-q.open .arrow { transform: rotate(180deg); }
        .faq-a { padding: 0 20px; max-height: 0; overflow: hidden; transition: max-height 0.3s ease, padding 0.3s; font-size: 0.92rem; color: var(--text-mid); background: var(--red-pale); }
        .faq-a.open { max-height: 400px; padding: 16px 20px; }
        .faq-a p { margin-bottom: 8px; }
        .faq-a p:last-child { margin-bottom: 0; }

        /* Footer */
        footer { background: var(--white); border-top: 1px solid var(--border-neu); padding: 32px 24px; text-align: center; color: var(--text-light); font-size: 0.85rem; }
        footer a { color: var(--purple-main); text-decoration: none; font-weight: 500; transition: color 0.2s; }
        footer a:hover { text-decoration: underline; }

        @media (max-width: 600px) {
            .nav-links { display: none; }
            .nav-toggle { display: flex; }
            .tool-card { padding: 28px 20px; }
            h1 { font-size: 1.8rem; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;&lt;a href="https://www.kholil.biz.id/"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/rotate-pdf.html "&gt;Rotate PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;HTML to PDF&lt;/a&gt;
    &lt;/div&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu Utama"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
&lt;/nav&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/rotate-pdf.html"&gt;&#128260; Rotate PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;&#127760; HTML to PDF&lt;/a&gt;
&lt;/div&gt;

&lt;div class="page-wrap"&gt;

    &lt;!-- HERO --&gt;
    &lt;header class="hero"&gt;
        &lt;span class="hero-badge"&gt;&#128275; Akses Dokumen Terbuka&lt;/span&gt;
        &lt;h1&gt;Unlock PDF — &lt;em&gt;Lumpuhkan Sandi&lt;/em&gt;&lt;br&gt;Langsung di Browser Anda&lt;/h1&gt;
        &lt;p class="hero-sub"&gt;Cabut batasan cetak, salin, atau enkripsi sandi utama dokumen PDF Anda. 100% diproses tanpa melalui peladen (server) eksternal.&lt;/p&gt;
        &lt;div class="trust-row"&gt;
            &lt;span class="trust-pill"&gt;✅ 100% Client-Side&lt;/span&gt;
            &lt;span class="trust-pill"&gt;&#128274; Privasi RAM Terjamin&lt;/span&gt;
            &lt;span class="trust-pill"&gt;⚡ Mendukung AES-256 &amp; RC4&lt;/span&gt;
        &lt;/div&gt;
    &lt;/header&gt;

    &lt;!-- TOOL --&gt;
    &lt;div class="tool-card"&gt;
        &lt;div class="drop-zone" id="dropArea"&gt;
            &lt;span class="drop-icon"&gt;&#128274;&lt;/span&gt;
            &lt;p class="drop-label"&gt;&lt;strong&gt;Seret &amp; lepas file PDF yang terkunci di sini&lt;/strong&gt;&lt;br&gt;atau klik tombol di bawah untuk memilih file&lt;/p&gt;
            &lt;label class="btn-choose"&gt;
                &#128193; Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf"&gt;
            &lt;/label&gt;
            &lt;p id="fileName"&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;div class="field-group"&gt;
            &lt;label for="pdfPassword"&gt;Sandi Master PDF (Jika Dokumen Terkunci Penuh)&lt;/label&gt;
            &lt;div id="pwd-hint" class="field-hint"&gt;Kosongkan kolom ini jika dokumen Anda hanya mengalami pembatasan fitur (tidak bisa di-copy/di-print) namun masih bisa dibuka/dibaca.&lt;/div&gt;
            &lt;div class="password-wrapper"&gt;
                &lt;input type="password" id="pdfPassword" aria-describedby="pwd-hint" placeholder="Ketik kata sandi (Case-sensitive)..." autocomplete="current-password"&gt;
                &lt;button type="button" class="toggle-pwd" aria-label="Lihat kata sandi" onclick="togglePassword()"&gt;&#128065;️&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;button class="btn-unlock" id="unlockBtn" onclick="unlockPDF()"&gt;&#128275; Unlock &amp; Hapus Sandi PDF&lt;/button&gt;
        &lt;a id="downloadBtn" href="#"&gt;⬇️ Download PDF Tanpa Kunci&lt;/a&gt;
        &lt;div id="status" aria-live="polite"&gt;&lt;/div&gt;

        &lt;div class="warning-note"&gt;
            &lt;span class="icon"&gt;⚠️&lt;/span&gt;
            &lt;div&gt;
                &lt;strong&gt;Catatan Penting:&lt;/strong&gt; Alat ini bukan mesin peretas (&lt;em&gt;hack&lt;/em&gt;). Alat ini didesain agar Anda dapat mencopot sandi gembok secara permanen dari dokumen legal &lt;strong&gt;milik Anda sendiri&lt;/strong&gt; (yang Anda ketahui sandinya) demi mempermudah urusan kearsipan digital.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- E-E-A-T AUTHOR BOX --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div&gt;
            &lt;strong style="color:var(--text-dark); display:block; font-size:1rem;"&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
            &lt;span style="font-size:0.85rem; color:var(--text-light);"&gt;Praktisi Keamanan Siber Dokumen | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- NARASI ARTIKEL UNIK --&gt;
    &lt;section class="article"&gt;
        &lt;h2&gt;Dekonstruksi Kunci Algoritma PDF Secara Lokal&lt;/h2&gt;
        &lt;p&gt;Terkadang kita terhambat oleh birokrasi keamanan sistem kita sendiri. Misalnya, Anda mengunduh tagihan mutasi dari pihak bank yang dikunci secara massal menggunakan format tanggal lahir. Membuka dokumen tersebut berulang kali untuk proses audit pembukuan tentu sangat menyita waktu. Hal yang sama berlaku untuk dokumen yang posisi halamannya terbalik, yang mengharuskan Anda menggunakan &lt;a href="https://www.kholil.biz.id/2026/04/rotate-pdf.html"&gt;Rotate PDF&lt;/a&gt;, namun operasi tersebut ditolak akibat adanya enkripsi sandi.&lt;/p&gt;

        &lt;p&gt;Aplikasi web "Unlock PDF" kami bekerja dengan menjalankan mesin &lt;strong&gt;QPDF&lt;/strong&gt; yang dikompilasi ke WebAssembly langsung di dalam browser Anda. Ketika Anda memasukkan kata sandi yang sah, mesin ini membaca struktur enkripsi asli dokumen (RC4 maupun AES-128/256), mendekripsi setiap stream kontennya, lalu menyusunnya kembali menjadi file PDF baru yang benar-benar bersih dari lapisan sandi — bukan sekadar menyembunyikan metadata proteksi.&lt;/p&gt;

        &lt;h3&gt;Mengatasi "Owner Permission Lock"&lt;/h3&gt;
        &lt;p&gt;Dalam skenario lain, Anda mungkin menerima materi studi atau e-Book yang bisa dibuka tanpa kata sandi, namun sistem melumpuhkan fungsi "Copy" (salin teks) atau "Print" (cetak). Pembatasan izin ini (&lt;em&gt;Owner Lock&lt;/em&gt;) umumnya tidak menggunakan enkripsi sandi tingkat tinggi. Jika ini yang terjadi, Anda dapat langsung menekan tombol eksekusi tanpa mengisi kolom sandi — mesin kami akan menghapus dictionary enkripsi tersebut secara otomatis.&lt;/p&gt;

        &lt;h3&gt;Rekomendasi Manajemen Berkas Lanjutan&lt;/h3&gt;
        &lt;p&gt;Setelah dokumen berhasil dibuka kuncinya, file tersebut menjadi sangat rentan jika didistribusikan ke publik atau situs &lt;em&gt;web&lt;/em&gt;. Jika Anda ingin menyebarkannya kembali sebagai format situs web yang lebih responsif, pertimbangkan utilitas konversi &lt;a href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;HTML to PDF&lt;/a&gt; kami (dan arah kebalikannya) sesuai dengan alur pengembangan Anda.&lt;/p&gt;
    &lt;/section&gt;

    &lt;!-- BUKTI NYATA HASIL UNLOCK --&gt;
    &lt;section class="article"&gt;
        &lt;h2&gt;Bukti Nyata: Hasil Uji Coba Unlock PDF&lt;/h2&gt;
        &lt;p&gt;Berikut tangkapan layar langsung dari pengujian alat ini pada dokumen PDF ber-password, dari proses unggah hingga hasil akhir bebas sandi.&lt;/p&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/23xGWpsp/Screenshot-2026-07-22-16-39-24-869-com-google-android-apps-docs.jpg" alt="File PDF yang masih terkunci sandi, meminta password saat dibuka" loading="lazy" width="360" height="720"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;1&lt;/span&gt;&lt;span&gt;File PDF terkunci sebelum diproses&lt;/span&gt;&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/NqkG24Y/Screenshot-2026-07-22-16-46-51-012-com-android-chrome.jpg" alt="Status berhasil menghapus sandi enkripsi PDF di halaman Unlock PDF" loading="lazy" width="360" height="720"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;2&lt;/span&gt;&lt;span&gt;Sandi enkripsi berhasil dihapus&lt;/span&gt;&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/Gyr9cXQ/Screenshot-2026-07-22-16-40-17-951-com-android-chrome.jpg" alt="Tombol download PDF tanpa kunci berfungsi normal dan file terunduh" loading="lazy" width="360" height="720"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;3&lt;/span&gt;&lt;span&gt;Tombol download berfungsi normal&lt;/span&gt;&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/kstcXmG9/Screenshot-2026-07-22-16-46-58-652-com-google-android-apps-docs.jpg" alt="Hasil PDF yang sudah tidak meminta password lagi saat dibuka" loading="lazy" width="360" height="720"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;4&lt;/span&gt;&lt;span&gt;Hasil akhir: PDF terbuka tanpa password&lt;/span&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- FAQ TEKNIS &amp; Aksesibilitas --&gt;
    &lt;section class="article"&gt;
        &lt;h2&gt;Tanya Jawab Penghapusan Hak Akses&lt;/h2&gt;
        &lt;div class="faq" id="faqContainer"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah ini bisa menembus paksa (Hack) PDF orang lain? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Secara absolut, &lt;strong&gt;TIDAK&lt;/strong&gt;. Alat ini tidak memiliki mekanisme &lt;em&gt;Brute-Force Attack&lt;/em&gt; atau &lt;em&gt;Dictionary Attack&lt;/em&gt;. Anda tetap wajib memasukkan "Sandi Valid" pada kolom yang disediakan untuk dapat membongkar kunci dokumen. Alat ini ditujukan untuk mempermudah Anda mencopot sandi gembok secara permanen dari dokumen legal milik Anda sendiri.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah resolusi gambar di dalam dokumen ikut berubah? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Tidak. Mesin dekripsi QPDF yang kami terapkan hanya menargetkan lapisan enkripsi dokumen tanpa mengompres ulang stream gambar atau font di dalamnya. Kualitas resolusi grafis dan vektor teks akan tetap identik (&lt;em&gt;lossless&lt;/em&gt;) dengan dokumen master Anda.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Bagaimana jika saya lupa kata sandi dokumen saya sendiri? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;
                    &lt;p&gt;Jika itu adalah &lt;em&gt;User Password&lt;/em&gt; (di mana dokumen sama sekali tidak bisa dibuka tanpa sandi), Anda sayangnya tidak akan dapat memulihkannya menggunakan alat ini. Dekripsi menuntut parameter kunci yang akurat.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

&lt;/div&gt;&lt;!-- end page-wrap --&gt;

&lt;footer&gt;
    &lt;p&gt;&amp;copy; 2026 &lt;strong&gt;Kholil Tools&lt;/strong&gt; &amp;nbsp;·&amp;nbsp; Sistem Pemrosesan Client-Side Teraman&lt;/p&gt;
    &lt;p style="margin-top:12px; font-size:0.9rem;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html" style="margin: 0 8px;"&gt;Kebijakan Privasi&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/terms.html" style="margin: 0 8px;"&gt;Ketentuan Layanan&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="margin: 0 8px;"&gt;Tentang Kami&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="margin: 0 8px;"&gt;Kontak Kami&lt;/a&gt;
    &lt;/p&gt;
&lt;/footer&gt;

&lt;script type="module"&gt;
    // ── Mesin dekripsi: QPDF dikompilasi ke WebAssembly ──
    // Library ini BENAR-BENAR mendekripsi RC4 &amp; AES-128/256, berbeda dari pdf-lib
    // yang hanya mengabaikan metadata enkripsi tanpa membongkar isinya.
    import createModule from "https://cdn.jsdelivr.net/npm/@neslinesli93/qpdf-wasm@0.3.0/+esm";

    const WASM_URL = "https://cdn.jsdelivr.net/npm/@neslinesli93/qpdf-wasm@0.3.0/dist/qpdf.wasm";

    let originalFile = null;

    const pdfFileInput = document.getElementById('pdfFile');
    const dropArea    = document.getElementById('dropArea');
    const fileNameEl  = document.getElementById('fileName');
    const statusEl    = document.getElementById('status');
    const unlockBtn   = document.getElementById('unlockBtn');
    const downloadBtn = document.getElementById('downloadBtn');

    // Drag and Drop
    dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('over'); });
    dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('over'));
    dropArea.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropArea.classList.remove('over');
        const f = e.dataTransfer.files[0];
        if (f &amp;&amp; f.type === 'application/pdf') handleFile(f);
        else alert('Silakan pilih file PDF yang valid.');
    });

    pdfFileInput.addEventListener('change', e =&gt; {
        if (e.target.files[0]) handleFile(e.target.files[0]);
    });

    function handleFile(file) {
        const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
        originalFile = file;
        fileNameEl.innerHTML = '&#128274; ' + safeName;
        statusEl.textContent = '';
        downloadBtn.style.display = 'none';
    }

    // Fitur Show/Hide Password
    function togglePassword() {
        const input = document.getElementById('pdfPassword');
        const btn = document.querySelector('.toggle-pwd');
        if (input.type === 'password') {
            input.type = 'text';
            btn.textContent = '&#128584;';
            btn.setAttribute('aria-label', 'Sembunyikan kata sandi');
        } else {
            input.type = 'password';
            btn.textContent = '&#128065;️';
            btn.setAttribute('aria-label', 'Lihat kata sandi');
        }
    }

    // Membuat instance QPDF baru setiap proses, agar virtual filesystem
    // selalu dalam keadaan bersih (menghindari state bocor antar-file).
    async function createFreshQpdf() {
        const stderrLines = [];
        const mod = await createModule({
            locateFile: () =&gt; WASM_URL,
            noInitialRun: true,
            printErr: (line) =&gt; stderrLines.push(line),
        });
        mod._stderrLines = stderrLines;
        return mod;
    }

    async function unlockPDF() {
        if (!originalFile) { alert('Silakan unggah dokumen PDF terlebih dahulu!'); return; }

        unlockBtn.disabled  = true;
        unlockBtn.innerHTML = '⏳ Menembus Lapisan Keamanan...';
        statusEl.innerHTML  = 'Memuat mesin dekripsi &amp; memproses file PDF...';

        try {
            const qpdf = await createFreshQpdf();

            const arrayBuffer = await originalFile.arrayBuffer();
            const inputBytes  = new Uint8Array(arrayBuffer);
            const password    = document.getElementById('pdfPassword').value;

            qpdf.FS.writeFile('/input.pdf', inputBytes);

            const args = password
                ? ['--password=' + password, '--decrypt', '/input.pdf', '/output.pdf']
                : ['--decrypt', '/input.pdf', '/output.pdf'];

            let exitCode = 0;
            try {
                exitCode = qpdf.callMain(args);
            } catch (exitErr) {
                // Emscripten melempar objek ExitStatus saat proses selesai lewat exit()
                exitCode = (exitErr &amp;&amp; typeof exitErr.status === 'number') ? exitErr.status : 1;
            }

            let outputExists = false;
            try { qpdf.FS.stat('/output.pdf'); outputExists = true; } catch (e) { /* belum ada file */ }

            // Kode keluar QPDF: 0 = sukses bersih, 3 = sukses dengan peringatan minor.
            // Selain itu (umumnya 2) = gagal, biasanya sandi salah atau file korup.
            if (!outputExists || (exitCode !== 0 &amp;&amp; exitCode !== 3)) {
                const detail = qpdf._stderrLines.join(' ').toLowerCase();
                if (detail.includes('password') || detail.includes('invalid')) {
                    throw new Error('Kata sandi yang dimasukkan salah, atau dokumen membutuhkan sandi yang belum Anda isi.');
                }
                throw new Error('Gagal memproses PDF. File mungkin korup atau menggunakan enkripsi yang tidak umum.');
            }

            const outputBytes = qpdf.FS.readFile('/output.pdf');
            const blob = new Blob([outputBytes], { type: 'application/pdf' });
            const url  = URL.createObjectURL(blob);

            downloadBtn.href     = url;
            downloadBtn.download = originalFile.name.replace(/\.pdf$/i, '_Sandi_Dihapus.pdf');
            downloadBtn.style.display = 'block';

            statusEl.innerHTML = '&lt;span style="color:#16a34a;font-weight:700;"&gt;✅ Berhasil! Sandi enkripsi berhasil diruntuhkan. Klik tombol unduh di atas.&lt;/span&gt;';
        } catch (err) {
            console.error(err);
            const msg = (err &amp;&amp; err.message) ? err.message : 'Terjadi kesalahan tak terduga.';
            statusEl.innerHTML = '&lt;span style="color:#dc2626;font-weight:600;"&gt;❌ ' + msg + '&lt;/span&gt;';
        }

        unlockBtn.disabled  = false;
        unlockBtn.innerHTML = '&#128275; Unlock &amp; Hapus Sandi PDF';
    }

    // Ekspos fungsi ke onclick inline (karena ini module script, scope-nya tidak global secara default)
    window.unlockPDF = unlockPDF;
    window.togglePassword = togglePassword;

    // Toggle FAQ Aksesibel (WCAG)
    window.toggleFaq = function (btn) {
        const answer = btn.nextElementSibling;
        const isOpen = btn.classList.contains('open');
        document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('open'));
        document.querySelectorAll('.faq-q').forEach(b =&gt; {
            b.classList.remove('open');
            b.setAttribute('aria-expanded', 'false');
        });

        if (!isOpen) {
            answer.classList.add('open');
            btn.classList.add('open');
            btn.setAttribute('aria-expanded', 'true');
        }
    };

    // Hamburger Menu Toggle
    window.toggleMenu = function () {
        const menu = document.getElementById('mobileMenu');
        const toggle = document.getElementById('navToggle');
        const isMenuOpen = menu.classList.toggle('open');
        toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
    };
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/unlock-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3951636396729687350</guid><pubDate>Fri, 10 Apr 2026 08:51:00 +0000</pubDate><atom:updated>2026-07-24T07:55:08.753-07:00</atom:updated><title>Html to PDF </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, user-scalable=yes"&gt;
    &lt;title&gt;Konverter HTML ke PDF Online Gratis | Kholil Tools&lt;/title&gt;
    &lt;meta name="description" content="Kompilasi kode skrip HTML mentah menjadi dokumen PDF beresolusi tinggi (A4/Letter). Eksekusi aman (Client-side) dengan dukungan tata letak CSS 100%."&gt;
    &lt;meta name="robots" content="index, follow"&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;

    &lt;!-- Open Graph --&gt;
    &lt;meta property="og:title" content="Konverter HTML ke PDF Online Gratis | Kholil Tools"&gt;
    &lt;meta property="og:description" content="Mesin render HTML ke PDF Client-side. Solusi pengarsipan desain antarmuka berbasis web."&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/html-to-pdf.html"&gt;
    &lt;meta property="og:type" content="article"&gt;

    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema.org: WebApplication + E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@graph": [
        {
          "@type": "WebApplication",
          "name": "HTML ke PDF Converter",
          "description": "Alat online gratis untuk mengkonversi kode HTML menjadi dokumen PDF berkualitas tinggi, diproses sepenuhnya di browser tanpa upload ke server.",
          "applicationCategory": "DeveloperApplication",
          "operatingSystem": "Web Browser",
          "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" }
        },
        {
          "@type": "Article",
          "headline": "Konverter HTML ke PDF Online Gratis",
          "author": {
            "@type": "Person",
            "name": "Tim Kholil Tools",
            "url": "https://www.kholil.biz.id/p/about-us.html"
          },
          "publisher": {
            "@type": "Organization",
            "name": "Kholil Tools",
            "url": "https://www.kholil.biz.id/"
          }
        },
        {
          "@type": "FAQPage",
          "mainEntity": [
            {
              "@type": "Question",
              "name": "Mengapa tag SVG atau iframe dalam HTML saya hitam kosong?",
              "acceptedAnswer": { "@type": "Answer", "text": "Mesin penangkap DOM berbasis kanvas tidak bisa merender dokumen iframe eksternal karena kebijakan Same-Origin Policy. Pastikan atribut SVG disetel absolut." }
            },
            {
              "@type": "Question",
              "name": "Apakah kode HTML dan struktur CSS dicuri oleh peladen (Server) Anda?",
              "acceptedAnswer": { "@type": "Answer", "text": "Tidak ada satu baris kode pun yang memicu protokol HTTP kirim. Sistem rendering ini tertanam di ruang klien dan bekerja 100% otonom." }
            }
          ]
        }
      ]
    }
    &lt;/script&gt;

    &lt;!-- Library eksternal: html2canvas + jsPDF terpisah (bukan html2pdf.js bundle,
         karena html2pdf.js versi 0.10.x memiliki banyak laporan bug PDF kosong
         akibat proses cloning konten internalnya yang tidak stabil) --&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"
            crossorigin="anonymous" referrerpolicy="no-referrer" defer&gt;&lt;/script&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"
            crossorigin="anonymous" referrerpolicy="no-referrer" defer&gt;&lt;/script&gt;

    &lt;style&gt;
        :root {
            --blue-deep:   #0f3460;
            --blue-mid:    #1a5276;
            --blue-accent: #2980b9;
            --blue-light:  #aed6f1;
            --green:       #1e8449;
            --green-light: #d5f5e3;
            --bg:          #f0f4f8;
            --card:        #ffffff;
            --text:        #1c2833;
            --muted:       #3d4452; /* Aksesibilitas Warna Diperbaiki */
            --border:      #d5dde5;
            --radius-lg:   20px;
            --radius-xl:   32px;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; }

        .skip-link {
            position: absolute; left: -9999px; top: 0;
            background: var(--blue-deep); color: #fff;
            padding: 10px 18px; z-index: 1000; border-radius: 0 0 8px 0;
        }
        .skip-link:focus { left: 0; }

        body {
            background: var(--bg);
            font-family: 'Inter', system-ui, sans-serif;
            color: var(--text);
            line-height: 1.7;
        }

        /* ─── TOPBAR ─── */
        .topbar {
            background: var(--blue-deep);
            color: #fff;
            padding: 12px 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            font-size: 0.85rem;
            position: sticky; top: 0; z-index: 99;
        }
        .topbar-logo a {
            font-family: 'Sora', sans-serif;
            font-weight: 800;
            font-size: 1.1rem;
            letter-spacing: -0.5px;
            color: #fff;
            text-decoration: none;
        }
        .topbar-logo span { color: var(--blue-light); }
        .topbar-nav { display: flex; gap: 20px; }
        .topbar-nav a {
            color: rgba(255,255,255,0.75);
            text-decoration: none;
            transition: color 0.2s;
            font-weight: 500;
        }
        .topbar-nav a:hover { color: #fff; }

        /* ─── HERO ─── */
        .hero {
            background: linear-gradient(135deg, var(--blue-deep) 0%, var(--blue-mid) 60%, #1f618d 100%);
            color: white;
            text-align: center;
            padding: 60px 24px 70px;
        }
        .hero-eyebrow {
            display: inline-block;
            background: rgba(255,255,255,0.15);
            border: 1px solid rgba(255,255,255,0.3);
            border-radius: 40px;
            padding: 5px 18px;
            font-size: 0.78rem;
            font-weight: 600;
            letter-spacing: 0.5px;
            text-transform: uppercase;
            margin-bottom: 20px;
        }
        .hero h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.8rem, 4vw, 2.8rem);
            font-weight: 800;
            line-height: 1.2;
            margin-bottom: 16px;
            letter-spacing: -0.5px;
        }
        .hero-sub {
            font-size: 1.05rem;
            color: rgba(255,255,255,0.82);
            max-width: 600px;
            margin: 0 auto;
        }

        /* ─── MAIN WRAPPER &amp; TOOL CARD ─── */
        .page-wrap { max-width: 1280px; margin: 0 auto; padding: 0 20px; }
        
        .tool-section { margin: -40px auto 0; position: relative; z-index: 10; padding: 0 20px; max-width: 1280px; }
        .tool-card {
            background: var(--card);
            border-radius: var(--radius-xl);
            box-shadow: 0 20px 60px rgba(0,0,0,0.15);
            padding: 28px 30px 34px;
        }

        /* ─── SPLIT LAYOUT ─── */
        .split-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 24px; }
        @media (max-width: 780px) { .split-layout { grid-template-columns: 1fr; } }

        .panel { border: 1.5px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; display: flex; flex-direction: column; background: #fafcfe; }
        .panel-head {
            background: #f0f5fa; border-bottom: 1.5px solid var(--border);
            padding: 11px 18px; font-weight: 700; font-size: 0.9rem;
            color: var(--blue-mid); display: flex; justify-content: space-between; align-items: center;
        }
        .panel-head small { font-weight: 400; color: var(--muted); font-size: 0.78rem; }

        textarea {
            width: 100%; min-height: 420px; padding: 18px;
            font-family: 'Cascadia Code', 'Monaco', monospace; font-size: 12.5px; line-height: 1.55;
            border: none; resize: vertical; background: #fafcfe; color: #1c2833; outline: none;
        }
        textarea:focus { background: #fff; }

        .preview-body { min-height: 420px; overflow: auto; background: white; }
        #livePreview { width: 100%; min-height: 400px; border: none; }

        /* ─── SETTINGS BAR ─── */
        .settings-bar {
            background: #f5f8fb; border: 1.5px solid var(--border); border-radius: var(--radius-lg);
            padding: 16px 20px; display: flex; flex-wrap: wrap; gap: 16px; align-items: center; margin-bottom: 20px;
        }
        .setting-group { display: flex; flex-direction: column; gap: 4px; }
        .setting-group label { font-size: 0.75rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.4px; }
        .setting-group select, .setting-group input[type="text"] {
            border: 1.5px solid var(--border); border-radius: 8px; padding: 6px 10px; font-size: 0.85rem; color: var(--text); background: white; outline: none;
        }
        .setting-group select:focus, .setting-group input:focus { border-color: var(--blue-accent); }

        /* ─── BUTTONS ─── */
        .btn-row { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 20px; }
        .btn {
            border: none; border-radius: 50px; font-weight: 700; cursor: pointer;
            transition: transform 0.15s, opacity 0.15s; font-size: 0.92rem;
            padding: 13px 28px; display: inline-flex; align-items: center; gap: 8px; font-family: 'Inter', sans-serif;
        }
        .btn:active { transform: scale(0.96); }
        .btn-convert { background: var(--green); color: #fff; box-shadow: 0 6px 18px rgba(30,132,73,0.3); }
        .btn-convert:hover { opacity: 0.9; transform: translateY(-1px); }
        .btn-convert:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
        .btn-reset { background: #eaf2ff; color: var(--blue-mid); }
        .btn-reset:hover { background: #d4e6f1; }
        .btn-clear { background: #fdf2f2; color: #c0392b; border: 1px solid #f5c6c6; }
        .btn-clear:hover { background: #fde8e8; }
        .btn-copy { background: #f4f6f9; color: var(--muted); border: 1px solid var(--border); }

        /* ─── STATUS ─── */
        .status-box { background: #eaf5ea; border: 1.5px solid #a9dfbf; border-radius: 12px; padding: 12px 18px; font-size: 0.9rem; text-align: center; color: #1e6e42; font-weight: 500; }
        .status-box.loading { background: #fef9e7; border-color: #f9d77e; color: #7d6608; }
        .status-box.error   { background: #fdf2f2; border-color: #f5b7b1; color: #c0392b; }
        .status-box.warn    { background: #fef5e7; border-color: #f5cb7e; color: #935e10; }

        #downloadArea { text-align: center; margin-top: 14px; }
        .download-btn { display: inline-flex; align-items: center; gap: 8px; background: var(--blue-accent); color: white; text-decoration: none; padding: 12px 26px; border-radius: 50px; font-weight: 700; font-size: 0.92rem; box-shadow: 0 4px 14px rgba(41,128,185,0.35); }

        /* ─── CONTENT SECTIONS ─── */
        .content-wrap { max-width: 1000px; margin: 40px auto 60px; padding: 0 20px; }

        /* AUTHOR BOX E-E-A-T */
        .author-box { 
            display: flex; align-items: center; gap: 15px; background: var(--card); 
            padding: 20px; border-radius: 12px; border: 1px solid var(--border); 
            margin-bottom: 40px; border-left: 4px solid var(--blue-mid); 
            box-shadow: 0 4px 15px rgba(0,0,0,0.03);
        }
        .author-img { width: 50px; height: 50px; background: var(--blue-mid); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 1.2rem; }

        h2.section-title { font-family: 'Sora', sans-serif; font-size: clamp(1.4rem, 2.5vw, 1.8rem); font-weight: 800; color: var(--blue-deep); margin-bottom: 12px; letter-spacing: -0.3px; }
        
        .article-body h3 { font-size: 1.15rem; color: var(--blue-deep); margin: 28px 0 10px; font-weight: 700; }
        .article-body p { font-size: 0.98rem; color: var(--muted); margin-bottom: 16px; line-height: 1.8; }
        .article-body a { color: var(--blue-accent); text-decoration: none; font-weight: 600; }
        .article-body a:hover { text-decoration: underline; }
        .article-body code { background: #f0f4f8; border-radius: 4px; padding: 2px 6px; font-family: monospace; font-size: 0.9em; color: #1f618d; border: 1px solid var(--border); }

        .divider { border: none; border-top: 1.5px solid var(--border); margin: 50px 0; }

        /* FAQ Accessible */
        .faq-list { display: flex; flex-direction: column; gap: 12px; }
        .faq-item { background: white; border: 1.5px solid var(--border); border-radius: 12px; overflow: hidden; }
        .faq-question {
            width: 100%; text-align: left; background: none; border: none; padding: 18px 20px;
            font-size: 0.97rem; font-weight: 700; color: var(--blue-deep); cursor: pointer;
            display: flex; justify-content: space-between; align-items: center; font-family: 'Inter', sans-serif;
        }
        .faq-question:focus { outline: 2px solid var(--blue-accent); outline-offset: -2px; }
        .faq-question:hover { background: #fafcfe; }
        .faq-question .arrow { transition: transform 0.2s; font-size: 1.1rem; color: var(--muted); }
        .faq-question .arrow::after { content: '+'; }
        .faq-item.open .faq-question .arrow::after { content: '−'; }
        .faq-answer { padding: 0 20px; max-height: 0; overflow: hidden; transition: max-height 0.3s ease, padding 0.3s; font-size: 0.93rem; color: var(--muted); line-height: 1.75; }
        .faq-item.open .faq-answer { max-height: 500px; padding: 0 20px 18px; }

        /* Footer */
        footer { background: var(--blue-deep); color: rgba(255,255,255,0.7); text-align: center; padding: 40px 20px; font-size: 0.88rem; line-height: 1.8; }
        footer strong { color: white; font-family: 'Sora', sans-serif; font-size: 1.1rem; }
        footer a { color: var(--blue-light); text-decoration: none; transition: color 0.2s; }
        footer a:hover { color: #fff; text-decoration: underline; }

        @media (max-width: 600px) {
            .topbar-nav { display: none; } /* Idealnya pakai hamburger, tapi sesuai instruksi kita sembunyikan link jika layar kecil untuk kesederhanaan */
            .settings-bar { flex-direction: column; align-items: flex-start; }
            .tool-card { padding: 18px 16px 24px; }
        }

        /* ─── PROOF GRID (Bukti Hasil Render) ─── */
        .proof-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 20px;
            margin: 32px 0 8px;
        }
        @media (max-width: 720px) {
            .proof-grid { grid-template-columns: 1fr; }
        }
        @media (min-width: 721px) and (max-width: 900px) {
            .proof-grid { grid-template-columns: repeat(2, 1fr); }
        }
        .proof-card {
            background: #ffffff;
            border: 1.5px solid var(--border);
            border-radius: 16px;
            overflow: hidden;
            box-shadow: 0 4px 14px rgba(0,0,0,0.05);
            display: flex;
            flex-direction: column;
        }
        .proof-card img {
            width: 100%;
            height: auto;
            display: block;
            aspect-ratio: 9 / 16;
            object-fit: cover;
            background: var(--bg);
        }
        .proof-caption {
            padding: 12px 14px;
            font-size: 0.88rem;
            font-weight: 600;
            color: var(--blue-deep);
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .proof-caption .num {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            background: var(--green);
            color: #fff;
            font-size: 0.78rem;
            flex-shrink: 0;
        }

        /* ─── PDF RENDER OVERLAY (perbaikan bug PDF kosong) ─── */
        .pdf-render-overlay {
            position: fixed; inset: 0; background: rgba(255,255,255,0.96);
            z-index: 99999; display: flex; flex-direction: column; align-items: center; justify-content: center;
            gap: 14px; font-family: 'Inter', sans-serif; color: var(--blue-deep); font-weight: 600;
        }
        .pdf-spinner {
            width: 42px; height: 42px; border-radius: 50%;
            border: 4px solid var(--blue-light); border-top-color: var(--blue-deep);
            animation: pdfspin 0.8s linear infinite;
        }
        @keyframes pdfspin { to { transform: rotate(360deg); } }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;a href="#main-content" class="skip-link"&gt;Langsung ke konten utama&lt;/a&gt;

&lt;!-- TOPBAR --&gt;
&lt;div class="topbar"&gt;
    &lt;div class="topbar-logo"&gt;&lt;a href="https://www.kholil.biz.id/" style="color:#fff; text-decoration:none;"&gt;Kholil &lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
    &lt;nav class="topbar-nav"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/rotate-pdf.html"&gt;Rotate PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/watermark.html"&gt;Watermark PDF&lt;/a&gt;
    &lt;/nav&gt;
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-eyebrow"&gt;&#128736;️ Rendering Front-End (Client-Side)&lt;/div&gt;
    &lt;h1&gt;Konversi HTML ke PDF&lt;br&gt;Bebas Eksekusi Server&lt;/h1&gt;
    &lt;p class="hero-sub"&gt;
        Manipulasi hierarki elemen DOM dan cetak rancangan &lt;em&gt;Front-End&lt;/em&gt; Anda ke dalam wadah PDF portabel. Menjamin kerahasiaan koding komersial Anda 100% di browser.
    &lt;/p&gt;
&lt;/div&gt;

&lt;!-- TOOL SECTION --&gt;
&lt;div class="tool-section" id="main-content"&gt;
    &lt;div class="tool-card"&gt;

        &lt;!-- Settings --&gt;
        &lt;div class="settings-bar"&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="pageFormat"&gt;Ukuran Halaman&lt;/label&gt;
                &lt;select id="pageFormat"&gt;
                    &lt;option value="a4"&gt;A4 (210×297 mm)&lt;/option&gt;
                    &lt;option value="letter"&gt;Letter (216×279 mm)&lt;/option&gt;
                    &lt;option value="a3"&gt;A3 (297×420 mm)&lt;/option&gt;
                    &lt;option value="legal"&gt;Legal (216×356 mm)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="pageOrientation"&gt;Orientasi&lt;/label&gt;
                &lt;select id="pageOrientation"&gt;
                    &lt;option value="portrait"&gt;Portrait (Vertikal)&lt;/option&gt;
                    &lt;option value="landscape"&gt;Landscape (Horizontal)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="renderScale"&gt;Skala Render&lt;/label&gt;
                &lt;select id="renderScale"&gt;
                    &lt;option value="2"&gt;Tinggi (×2) — Direkomendasikan&lt;/option&gt;
                    &lt;option value="1"&gt;Normal (×1) — Lebih Cepat&lt;/option&gt;
                    &lt;option value="3"&gt;Maksimal (×3) — Paling Tajam&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="fileName"&gt;Nama File PDF&lt;/label&gt;
                &lt;input type="text" id="fileName" value="dokumen-render" placeholder="nama-file" style="width:150px;"&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="pageMargin"&gt;Margin (inci)&lt;/label&gt;
                &lt;select id="pageMargin"&gt;
                    &lt;option value="0.5"&gt;0.5 in — Normal&lt;/option&gt;
                    &lt;option value="0.25"&gt;0.25 in — Sempit&lt;/option&gt;
                    &lt;option value="1"&gt;1.0 in — Lebar&lt;/option&gt;
                    &lt;option value="0"&gt;0 — Tanpa Margin&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Editor + Preview --&gt;
        &lt;div class="split-layout"&gt;
            &lt;div class="panel"&gt;
                &lt;div class="panel-head"&gt;
                    &lt;span&gt;&#128221; Editor HTML Mentah&lt;/span&gt;
                    &lt;small&gt;Dukung HTML · CSS · Inline JS&lt;/small&gt;
                &lt;/div&gt;
                &lt;textarea id="htmlInput" aria-label="Editor kode HTML" spellcheck="false" placeholder="Tulis atau tempel kode HTML di sini..."&gt;&lt;/textarea&gt;
            &lt;/div&gt;
            &lt;div class="panel"&gt;
                &lt;div class="panel-head"&gt;
                    &lt;span&gt;&#128065;️ Pratinjau DOM Langsung&lt;/span&gt;
                    &lt;small&gt;Diperbarui otomatis saat mengetik&lt;/small&gt;
                &lt;/div&gt;
                &lt;div class="preview-body"&gt;
                    &lt;iframe id="livePreview" title="Preview HTML" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"&gt;&lt;/iframe&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Buttons --&gt;
        &lt;div class="btn-row"&gt;
            &lt;button class="btn btn-convert" id="convertBtn"&gt;&#128209; Konversi &amp;amp; Unduh PDF&lt;/button&gt;
            &lt;button class="btn btn-reset"   id="resetBtn"&gt;&#128260; Muat Contoh Koding&lt;/button&gt;
            &lt;button class="btn btn-copy"    id="copyBtn"&gt;&#128203; Salin HTML&lt;/button&gt;
            &lt;button class="btn btn-clear"   id="clearBtn"&gt;&#128465;️ Kosongkan&lt;/button&gt;
        &lt;/div&gt;

        &lt;div id="statusMsg" class="status-box" role="status" aria-live="polite"&gt;
            ✅ Lingkungan siap. Edit HTML di panel kiri, rendering DOM otomatis dieksekusi di kanan.
        &lt;/div&gt;
        &lt;div id="downloadArea" aria-live="polite"&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- CONTENT SECTION --&gt;
&lt;div class="content-wrap"&gt;
    
    &lt;!-- AUTHOR BOX E-E-A-T --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div&gt;
            &lt;strong style="color:var(--text); display:block; font-size:1.05rem;"&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
            &lt;span style="font-size:0.85rem; color:var(--muted);"&gt;Pengembang Ekosistem Dokumen &amp; Kriptografi | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- NARASI ANTI-SPAM (UNIK &amp; TEKNIS) --&gt;
    &lt;div class="article-body"&gt;
        &lt;h2 class="section-title"&gt;Dokumentasi Skrip Render HTML ke PDF&lt;/h2&gt;
        &lt;p&gt;Banyak pengembang &lt;em&gt;Front-End&lt;/em&gt; merasa frustrasi saat diminta merancang tata letak (&lt;em&gt;layout&lt;/em&gt;) dokumen cetak seperti &lt;em&gt;invoice&lt;/em&gt; atau laporan portofolio menggunakan HTML murni. Pasalnya, sistem peramban pengguna kerap kali memotong &lt;em&gt;header&lt;/em&gt;, membocorkan warna latar transparan, dan mengacaukan hierarki CSS ketika dicetak secara manual melalui perintah standar "Print to PDF".&lt;/p&gt;
        
        &lt;p&gt;Aplikasi web kompilator kami dirancang untuk mem-&lt;em&gt;bypass&lt;/em&gt; mekanisme antarmuka pencetakan (&lt;em&gt;print dialog&lt;/em&gt;) sistem operasi bawaan Anda. Alih-alih mengekspor dari jendela cetak biasa, kami menginisiasi pustaka &lt;code&gt;html2canvas&lt;/code&gt; guna menangkap seluruh elemen &lt;em&gt;Document Object Model (DOM)&lt;/em&gt; Anda secara spesifik per piksel. Hasil kompilasi piksel tersebut kemudian dipadatkan (&lt;em&gt;encoded&lt;/em&gt;) menjadi struktur PDF resolusi tinggi melalui manipulasi &lt;code&gt;jsPDF&lt;/code&gt;.&lt;/p&gt;
        
        &lt;h3&gt;Mengakali Kendala Margin dan Keamanan CORS&lt;/h3&gt;
        &lt;p&gt;Sering kali, aset gambar berbasis &lt;em&gt;Cloud&lt;/em&gt; di dalam sintaks HTML Anda tidak akan tercetak. Hal ini terjadi karena peladen gambar asalnya secara otomatis memblokir metode &lt;em&gt;Cross-Origin Resource Sharing (CORS)&lt;/em&gt; pada kanvas tak bersertifikat. Solusi radikal yang sangat kami rekomendasikan: ubah ekstensi gambar/logo Anda menjadi teks data &lt;em&gt;Base64&lt;/em&gt; dan tempelkan langsung ke atribut tag &lt;code&gt;&amp;lt;img src=""&amp;gt;&lt;/code&gt; sebelum mengeklik konversi.&lt;/p&gt;
        
        &lt;p&gt;Di samping itu, untuk mengatasi masalah tabel &lt;em&gt;grid&lt;/em&gt; CSS yang kerap terpotong tepat pada transisi ujung batas kertas A4, sistem kami menyediakan penyesuaian "Skala Render" hingga resolusi tingkat 3 (Maksimal) guna menekan &lt;em&gt;anti-aliasing&lt;/em&gt; grafis saat dokumen dicetak ke mesin fisik.&lt;/p&gt;

        &lt;h3&gt;Rantai Pemrosesan &amp; Keamanan Dokumen Cetak&lt;/h3&gt;
        &lt;p&gt;Hasil &lt;em&gt;render&lt;/em&gt; halaman HTML yang diekspor dari utilitas ini pada dasarnya adalah dokumen statis yang tidak diproteksi oleh keamanan lapisan apapun (bersifat terbuka dan dapat dibaca siapa saja). Jika dokumen PDF tersebut membutuhkan penyelarasan orientasi halaman setelah dirender, Anda dapat merapikannya menggunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/rotate-pdf.html"&gt;Rotate PDF&lt;/a&gt; kami.&lt;/p&gt;
        
        &lt;p&gt;Selain itu, untuk melindungi hak cipta desain grafis atau faktur komersial Anda sebelum didistribusikan kepada pihak ketiga atau klien, pastikan Anda telah menyematkan identitas visual institusi Anda secara paten melalui fitur &lt;a href="https://www.kholil.biz.id/2026/04/watermark.html"&gt;Watermark PDF&lt;/a&gt;, yang juga dapat diproses 100% secara lokal tanpa server.&lt;/p&gt;

        &lt;h3&gt;Bukti Hasil Uji Coba Nyata&lt;/h3&gt;
        &lt;p&gt;Berikut cuplikan pengujian langsung alat ini pada perangkat Android, mulai dari penyusunan HTML, proses unduh, hingga hasil akhir berkas PDF yang terbuka mulus tanpa kendala.&lt;/p&gt;

        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://res.cloudinary.com/zihkwot9/image/upload/v1784719241/Screenshot_2026-07-22-18-17-52-223_com.android.chrome_kwyi3w.jpg" alt="Editor HTML diperbarui dengan kode asli, bukan contoh lagi" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;1&lt;/span&gt; HTML diperbarui, bukan contoh lagi&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://res.cloudinary.com/zihkwot9/image/upload/v1784719241/Screenshot_2026-07-22-18-18-05-298_com.android.chrome_lltlm3.jpg" alt="Tombol download berjalan normal" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;2&lt;/span&gt; Tombol download berjalan normal&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://res.cloudinary.com/zihkwot9/image/upload/v1784719240/Screenshot_2026-07-22-18-18-12-473_com.google.android.apps.docs_bz8fio.jpg" alt="Hasil konversi HTML ke PDF berjalan mulus" loading="lazy"&gt;
                &lt;div class="proof-caption"&gt;&lt;span class="num"&gt;3&lt;/span&gt; Hasil konversi PDF mulus tanpa masalah&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

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

    &lt;!-- FAQ TEKNIS &amp; AKSESIBILITAS WCAG --&gt;
    &lt;h2 class="section-title"&gt;Logika Penanganan Kegagalan (FAQ)&lt;/h2&gt;
    &lt;div class="faq-list"&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-question" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                Mengapa tag SVG atau elemen iframe dalam HTML saya hitam kosong? 
                &lt;span class="arrow" aria-hidden="true"&gt;&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class="faq-answer"&gt;
                &lt;p&gt;Mesin penangkap DOM berbasis kanvas tidak dapat secara langsung merender dokumen &lt;code&gt;iframe&lt;/code&gt; eksternal karena penegakan kebijakan asal yang sama (&lt;em&gt;Same-Origin Policy&lt;/em&gt;). Sedangkan untuk &lt;em&gt;Scalable Vector Graphics (SVG)&lt;/em&gt;, pastikan atribut pembungkus (&lt;em&gt;wrapper&lt;/em&gt;) CSS-nya disetel absolut secara dimensi ukuran, karena kanvas sistem kerap gagal menafsirkan proporsi relatif dari gambar vektor.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-question" aria-expanded="false" onclick="toggleFaq(this)"&gt;
                Apakah kode HTML rahasia dan struktur CSS perusahaan dicuri oleh Server Anda? 
                &lt;span class="arrow" aria-hidden="true"&gt;&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class="faq-answer"&gt;
                &lt;p&gt;Secara absolut, tidak. Tidak ada satu baris kode pun yang memicu protokol transmisi jaringan (seperti &lt;em&gt;fetch&lt;/em&gt; atau HTTP POST). Sistem rendering ini secara eksklusif tertanam di ruang memori klien (&lt;em&gt;Client-side RAM&lt;/em&gt;) dan bekerja 100% menggunakan arsitektur JavaScript dari peramban (&lt;em&gt;browser&lt;/em&gt;) Anda secara otonom.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/div&gt;&lt;!-- end content-wrap --&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
    &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF &amp; Konversi Antarmuka Digital&lt;/p&gt;
    &lt;p style="margin-top:12px; font-size:0.9rem;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html" style="margin: 0 8px;"&gt;Kebijakan Privasi&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/terms.html" style="margin: 0 8px;"&gt;Ketentuan Layanan&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="margin: 0 8px;"&gt;Tentang Kami&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="margin: 0 8px;"&gt;Kontak Kami&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style="margin-top: 16px; opacity:0.6;"&gt;© 2026 Kholil Tools. Seluruh hak cipta dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
    // ─── ELEMENTS ───
    const htmlInput   = document.getElementById('htmlInput');
    const previewIframe = document.getElementById('livePreview');
    const convertBtn  = document.getElementById('convertBtn');
    const resetBtn    = document.getElementById('resetBtn');
    const copyBtn     = document.getElementById('copyBtn');
    const clearBtn    = document.getElementById('clearBtn');
    const statusMsg   = document.getElementById('statusMsg');
    const downloadArea = document.getElementById('downloadArea');

    const pageFormat      = document.getElementById('pageFormat');
    const pageOrientation = document.getElementById('pageOrientation');
    const renderScale     = document.getElementById('renderScale');
    const fileName        = document.getElementById('fileName');
    const pageMargin      = document.getElementById('pageMargin');

    // Batas aman jumlah piksel kanvas. Banyak browser mobile (terutama Android
    // WebView/Chrome versi lama) gagal atau menghasilkan kanvas kosong/putih
    // saat area kanvas terlalu besar. 16 juta piksel adalah ambang aman yang
    // umum dipakai lintas perangkat.
    const MAX_CANVAS_PIXELS = 16 * 1000 * 1000;

    // ─── DEFAULT HTML EXAMPLE ───
    const defaultHTML = `&lt;!DOCTYPE html&gt;
&lt;html lang="id"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;title&gt;Contoh Dokumen Invoice&lt;/title&gt;
&lt;style&gt;
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    font-family: 'Segoe UI', Arial, sans-serif;
    max-width: 794px;
    margin: 0 auto;
    padding: 40px 32px;
    color: #1c2833;
    background: #fff;
    line-height: 1.6;
  }
  .header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 40px; border-bottom: 3px solid #2980b9; padding-bottom: 20px; }
  .company-name { font-size: 1.6rem; font-weight: 800; color: #0f3460; }
  .company-sub  { font-size: 0.85rem; color: #566573; margin-top: 4px; }
  .invoice-label { text-align: right; }
  .invoice-label h1 { font-size: 2rem; font-weight: 900; color: #2980b9; letter-spacing: -1px; }
  .invoice-label p  { font-size: 0.85rem; color: #566573; margin-top: 2px; }
  .info-section { display: flex; gap: 40px; margin-bottom: 36px; }
  .info-block h4 { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.8px; color: #7f8c8d; margin-bottom: 6px; font-weight: 700; }
  .info-block p  { font-size: 0.92rem; line-height: 1.65; }
  table { width: 100%; border-collapse: collapse; margin-bottom: 24px; font-size: 0.9rem; }
  thead { background: #0f3460; color: white; }
  thead th { padding: 12px 14px; text-align: left; font-weight: 700; }
  tbody td { padding: 11px 14px; border-bottom: 1px solid #eaecee; }
  tbody tr:nth-child(even) td { background: #f8f9fa; }
  .text-right { text-align: right; }
  .total-section { background: #f0f7ff; border-radius: 12px; padding: 16px 20px; margin-bottom: 30px; }
  .total-row { display: flex; justify-content: space-between; padding: 5px 0; font-size: 0.9rem; }
  .total-row.grand { font-weight: 800; font-size: 1.1rem; color: #0f3460; border-top: 2px solid #2980b9; padding-top: 10px; margin-top: 8px; }
  .badge { display: inline-block; background: #d5f5e3; color: #1e8449; padding: 6px 18px; border-radius: 30px; font-size: 0.85rem; font-weight: 700; margin-bottom: 20px; }
  .note { font-size: 0.82rem; color: #7f8c8d; text-align: center; margin-top: 30px; border-top: 1px solid #eaecee; padding-top: 16px; }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div class="header"&gt;
    &lt;div&gt;
      &lt;div class="company-name"&gt;&#127970; Kholil Dev Studio&lt;/div&gt;
      &lt;div class="company-sub"&gt;Jl. Teknologi No. 8, Jakarta&lt;br&gt;&#128222; 021-5551234 · dev@kholil.biz.id&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="invoice-label"&gt;
      &lt;h1&gt;INVOICE&lt;/h1&gt;
      &lt;p&gt;#INV-2026-0089&lt;/p&gt;
      &lt;p&gt;Tanggal: 20 Juli 2026&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="info-section"&gt;
    &lt;div class="info-block"&gt;
      &lt;h4&gt;Ditagihkan Kepada&lt;/h4&gt;
      &lt;p&gt;&lt;strong&gt;PT Digital Maju&lt;/strong&gt;&lt;br&gt;Kawasan SCBD, Menara C&lt;br&gt;Jakarta Selatan 12190&lt;br&gt;finance@digitalmaju.com&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="info-block"&gt;
      &lt;h4&gt;Detail Pembayaran&lt;/h4&gt;
      &lt;p&gt;Bank BCA · 1234567890&lt;br&gt;a.n. Kholil Dev Studio&lt;br&gt;Jatuh Tempo: 05 Agustus 2026&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;span class="badge"&gt;✅ STATUS: MENUNGGU PEMBAYARAN&lt;/span&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;&lt;th&gt;Deskripsi Layanan&lt;/th&gt;&lt;th&gt;Qty&lt;/th&gt;&lt;th&gt;Harga Satuan&lt;/th&gt;&lt;th class="text-right"&gt;Subtotal&lt;/th&gt;&lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;&lt;td&gt;Optimasi Core Web Vitals&lt;/td&gt;&lt;td&gt;1 situs&lt;/td&gt;&lt;td&gt;Rp 5.000.000&lt;/td&gt;&lt;td class="text-right"&gt;Rp 5.000.000&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Penyediaan Infrastruktur API&lt;/td&gt;&lt;td&gt;20 jam&lt;/td&gt;&lt;td&gt;Rp 200.000/jam&lt;/td&gt;&lt;td class="text-right"&gt;Rp 4.000.000&lt;/td&gt;&lt;/tr&gt;
      &lt;tr&gt;&lt;td&gt;Sistem Kriptografi Dokumen&lt;/td&gt;&lt;td&gt;1 modul&lt;/td&gt;&lt;td&gt;Rp 3.500.000&lt;/td&gt;&lt;td class="text-right"&gt;Rp 3.500.000&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;div class="total-section"&gt;
    &lt;div class="total-row"&gt;&lt;span&gt;Subtotal&lt;/span&gt;&lt;span&gt;Rp 12.500.000&lt;/span&gt;&lt;/div&gt;
    &lt;div class="total-row"&gt;&lt;span&gt;PPN 11%&lt;/span&gt;&lt;span&gt;Rp 1.375.000&lt;/span&gt;&lt;/div&gt;
    &lt;div class="total-row grand"&gt;&lt;span&gt;TOTAL TAGIHAN&lt;/span&gt;&lt;span&gt;Rp 13.875.000&lt;/span&gt;&lt;/div&gt;
  &lt;/div&gt;

  &lt;p class="note"&gt;
    Terima kasih atas kerja sama Anda yang luar biasa.&lt;br&gt;
    Sistem dibangun dan diverifikasi aman oleh infrastruktur Kholil Tools.
  &lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;`;

    // ─── PREVIEW UPDATE ───
    function updatePreview() {
        const html = htmlInput.value.trim() ||
            `&lt;div style="padding:40px;font-family:sans-serif;color:#95a5a6;text-align:center;"&gt;
                &lt;div style="font-size:3rem;margin-bottom:16px;"&gt;&#128221;&lt;/div&gt;
                &lt;p style="font-size:1.1rem;"&gt;Masukkan kode HTML di editor kiri.&lt;br&gt;Pratinjau DOM akan terhubung secara otomatis di sini.&lt;/p&gt;
             &lt;/div&gt;`;
        const doc = previewIframe.contentDocument || previewIframe.contentWindow.document;
        doc.open(); doc.write(html); doc.close();
    }

    let debounceTimer;
    htmlInput.addEventListener('input', () =&gt; {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() =&gt; {
            updatePreview();
            setStatus('✏️ HTML diperbarui — pratinjau DOM disegarkan.', '');
            downloadArea.innerHTML = '';
        }, 300);
    });

    // ─── BUTTONS ───
    resetBtn.addEventListener('click', () =&gt; {
        htmlInput.value = defaultHTML;
        updatePreview();
        downloadArea.innerHTML = '';
        setStatus('✅ Skrip contoh berhasil dirender ulang. Sesuaikan kode Anda.', '');
    });

    clearBtn.addEventListener('click', () =&gt; {
        if (confirm('Yakin ingin menghapus seluruh susunan kode? Modifikasi yang belum disimpan akan hilang.')) {
            htmlInput.value = '';
            updatePreview();
            downloadArea.innerHTML = '';
            setStatus('&#128221; Ruang kerja dikosongkan. Siap menyusun kode HTML baru.', '');
        }
    });

    copyBtn.addEventListener('click', async () =&gt; {
        if (!htmlInput.value.trim()) { setStatus('⚠️ Tidak ada sintaks HTML untuk disalin.', 'error'); return; }
        try {
            await navigator.clipboard.writeText(htmlInput.value);
            setStatus('&#128203; Sintaks HTML berhasil disalin ke papan klip!', '');
        } catch {
            htmlInput.select();
            document.execCommand('copy');
            setStatus('&#128203; Sintaks HTML berhasil disalin!', '');
        }
    });

    // ─── CONVERT TO PDF (html2canvas + jsPDF langsung, tanpa html2pdf.js) ───
    convertBtn.addEventListener('click', async () =&gt; {
        const html = htmlInput.value.trim();
        if (!html) {
            setStatus('⚠️ Editor DOM kosong. Susun kode HTML terlebih dahulu atau muat sampel kode.', 'error');
            return;
        }

        if (typeof html2canvas === 'undefined' || typeof window.jspdf === 'undefined') {
            setStatus('⚠️ Pustaka konversi belum termuat sepenuhnya. Tunggu sebentar lalu coba lagi.', 'error');
            return;
        }

        setStatus('⏳ Mengkompilasi DOM menjadi PDF... Operasi ini memakan RAM sementara.', 'loading');
        downloadArea.innerHTML = '';
        convertBtn.disabled = true;

        let wrapper = null;
        const hiddenSiblings = [];

        try {
            const iframeDoc = previewIframe.contentDocument || previewIframe.contentWindow.document;

            // FIX: pastikan dokumen iframe benar-benar sudah selesai ditulis
            // sebelum di-clone. doc.write()/doc.close() bersifat sinkron untuk
            // parsing awal, tapi kalau iframe belum pernah selesai load sama
            // sekali (mis. dipanggil convert sebelum updatePreview jalan),
            // body bisa saja masih kosong.
            if (!iframeDoc || !iframeDoc.body || iframeDoc.body.innerHTML.trim() === '') {
                throw new Error('Pratinjau DOM belum siap / kosong. Tunggu pratinjau tampil dulu sebelum konversi.');
            }

            // Clone body dengan style
            wrapper = document.createElement('div');

            const styles = iframeDoc.querySelectorAll('style');
            styles.forEach(s =&gt; {
                const clone = document.createElement('style');
                clone.textContent = s.textContent;
                wrapper.appendChild(clone);
            });

            const bodyClone = iframeDoc.body.cloneNode(true);

            // FIX: tandai semua &lt;img&gt; eksternal dengan crossorigin="anonymous".
            // Tanpa ini, gambar lintas-origin bisa "menodai" (taint) kanvas
            // sehingga canvas.toDataURL() melempar SecurityError, atau di
            // sebagian browser malah diam-diam menghasilkan area kosong/putih
            // persis di lokasi gambar tsb. Kita juga pasang onerror supaya
            // gambar yang gagal dimuat tidak membuat seluruh render gagal.
            const imgs = bodyClone.querySelectorAll('img');
            imgs.forEach(img =&gt; {
                if (img.src &amp;&amp; img.src.startsWith('http') &amp;&amp; !img.src.startsWith(location.origin)) {
                    img.crossOrigin = 'anonymous';
                }
                img.onerror = function () { this.style.visibility = 'hidden'; };
            });

            wrapper.appendChild(bodyClone);

            // Wrapper sebagai elemen BIASA (static, tanpa position apa pun) yang
            // disisipkan ke alur normal dokumen -- pola paling stabil untuk
            // html2canvas. Elemen lain di &lt;body&gt; disembunyikan sementara supaya
            // tidak menimpa tampilan pengguna, lalu dikembalikan setelah selesai.
            wrapper.style.background = 'white';
            wrapper.style.width = '794px';
            wrapper.style.maxWidth = 'none';
            wrapper.style.margin = '0';

            Array.from(document.body.children).forEach(el =&gt; {
                hiddenSiblings.push({ el, display: el.style.display });
                el.style.display = 'none';
            });

            document.body.appendChild(wrapper);
            window.scrollTo(0, 0);

            if (document.fonts &amp;&amp; document.fonts.ready) { await document.fonts.ready; }
            // Beri browser waktu beberapa frame untuk benar-benar melayout &amp; mem-paint wrapper sebelum di-capture
            await new Promise(resolve =&gt; requestAnimationFrame(() =&gt; requestAnimationFrame(resolve)));
            await new Promise(resolve =&gt; setTimeout(resolve, 150));

            const margin = parseFloat(pageMargin.value);
            let   scale  = parseInt(renderScale.value);
            const format = pageFormat.value;
            const orient = pageOrientation.value;
            // Amankan nama file
            const name   = (fileName.value.trim().replace(/[^a-z0-9]/gi, '_').toLowerCase() || 'dokumen') + '.pdf';

            // FIX UTAMA: cek apakah wrapper benar-benar punya ukuran, dan
            // hitung dulu perkiraan luas kanvas SEBELUM capture. Kalau skala
            // yang dipilih user akan menghasilkan kanvas melebihi batas aman
            // (umum terjadi di Android saat dokumen panjang + skala x2/x3),
            // turunkan skalanya secara otomatis alih-alih membiarkan
            // html2canvas gagal diam-diam dan menghasilkan PDF kosong.
            const wrapperW = wrapper.offsetWidth;
            const wrapperH = wrapper.offsetHeight;

            if (wrapperW === 0 || wrapperH === 0) {
                throw new Error('Elemen render berukuran 0px. Konten HTML kemungkinan tidak menghasilkan tata letak yang terlihat.');
            }

            let estimatedPixels = wrapperW * scale * wrapperH * scale;
            let autoDowngraded = false;
            while (estimatedPixels &gt; MAX_CANVAS_PIXELS &amp;&amp; scale &gt; 1) {
                scale -= 1;
                estimatedPixels = wrapperW * scale * wrapperH * scale;
                autoDowngraded = true;
            }

            if (autoDowngraded) {
                setStatus(`⏳ Dokumen cukup panjang/beresolusi tinggi — skala render otomatis diturunkan ke ×${scale} agar kompatibel di perangkat ini...`, 'loading');
            }

            // ── Langkah 1: Capture wrapper menjadi canvas ──
            const canvas = await html2canvas(wrapper, {
                scale: scale,
                useCORS: true,
                allowTaint: false,
                logging: false,
                backgroundColor: '#ffffff'
            });

            if (!canvas || canvas.width === 0 || canvas.height === 0) {
                throw new Error('Canvas hasil capture kosong (lebar/tinggi 0). Kemungkinan konten HTML tidak ter-render sempurna.');
            }

            // FIX: bungkus toDataURL dengan try/catch terpisah. Kalau canvas
            // ternyata "tainted" (mis. ada gambar cross-origin yang lolos
            // pengecekan di atas), ini akan melempar SecurityError yang jelas
            // alih-alih membuat file PDF kosong/rusak secara diam-diam.
            let imgData;
            try {
                imgData = canvas.toDataURL('image/jpeg', 0.98);
            } catch (taintErr) {
                throw new Error('Kanvas berisi gambar dari domain lain yang tidak mengizinkan akses CORS, sehingga tidak bisa diekspor. Ubah gambar tersebut menjadi Base64 terlebih dahulu.');
            }

            if (!imgData || imgData === 'data:,') {
                throw new Error('Konversi kanvas ke gambar gagal menghasilkan data (kemungkinan area kanvas melebihi batas perangkat).');
            }

            // ── Langkah 2: Susun PDF dari canvas, otomatis pecah ke halaman berikutnya jika konten panjang ──
            const { jsPDF } = window.jspdf;
            const pdf = new jsPDF({ unit: 'in', format: format, orientation: orient });
            const pageWidthIn  = pdf.internal.pageSize.getWidth()  - margin * 2;
            const pageHeightIn = pdf.internal.pageSize.getHeight() - margin * 2;

            const pxToInRatio = pageWidthIn / canvas.width;
            const imgHeightIn = canvas.height * pxToInRatio;

            if (imgHeightIn &lt;= pageHeightIn) {
                pdf.addImage(imgData, 'JPEG', margin, margin, pageWidthIn, imgHeightIn);
            } else {
                let heightLeft = imgHeightIn;
                let positionIn = 0;
                pdf.addImage(imgData, 'JPEG', margin, margin, pageWidthIn, imgHeightIn);
                heightLeft -= pageHeightIn;
                while (heightLeft &gt; 0) {
                    positionIn -= pageHeightIn;
                    pdf.addPage();
                    pdf.addImage(imgData, 'JPEG', margin, positionIn + margin, pageWidthIn, imgHeightIn);
                    heightLeft -= pageHeightIn;
                }
            }

            pdf.save(name);

            setStatus(`✅ Rendering sukses! Berkas "${name}" telah diekspor ke perangkat Anda.`, '');
            downloadArea.innerHTML = `
                &lt;p style="font-size:0.85rem;color:#3d4452;margin-top:8px;"&gt;
                    &#128161; Berkas diekspor secara lokal. Cek folder &lt;strong&gt;Unduhan (Downloads)&lt;/strong&gt; di perangkat Anda.
                &lt;/p&gt;`;

        } catch (err) {
            console.error(err);
            setStatus('❌ Gagal melakukan kompilasi: ' + (err &amp;&amp; err.message ? err.message : 'kesalahan tidak diketahui') + '.', 'error');
        } finally {
            if (wrapper &amp;&amp; wrapper.parentNode) { document.body.removeChild(wrapper); }
            hiddenSiblings.forEach(({ el, display }) =&gt; { el.style.display = display; });
            convertBtn.disabled = false;
        }
    });

    // ─── FAQ ACCORDION (Aksesibilitas aria-expanded) ───
    function toggleFaq(btn) {
        const item = btn.parentElement;
        const isOpen = item.classList.contains('open');

        // Tutup Semua
        document.querySelectorAll('.faq-item').forEach(i =&gt; {
            i.classList.remove('open');
            i.querySelector('.faq-question').setAttribute('aria-expanded', 'false');
        });

        // Buka yang Ditekan
        if (!isOpen) {
            item.classList.add('open');
            btn.setAttribute('aria-expanded', 'true');
        }
    }

    // ─── HELPERS ───
    function setStatus(msg, type) {
        statusMsg.className = 'status-box' + (type ? ' ' + type : '');
        statusMsg.innerHTML = msg;
    }

    // ─── INIT ───
    htmlInput.value = defaultHTML;
    updatePreview();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/html-to-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3346393777873158716</guid><pubDate>Thu, 09 Apr 2026 21:57:19 +0000</pubDate><atom:updated>2026-07-24T08:05:59.566-07:00</atom:updated><title>Rotate pdf </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, user-scalable=yes"&gt;
    &lt;title&gt;Rotate PDF Online — Ubah Orientasi Halaman | Kholil Tools&lt;/title&gt;
    &lt;meta name="description" content="Putar metadata orientasi halaman PDF 90°, 180°, atau 270° secara lokal di browser. Perbaiki dokumen terbalik secara presisi tanpa merusak resolusi gambar."&gt;
    &lt;meta name="robots" content="index, follow"&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/rotate-pdf.html"&gt;

    &lt;!-- Open Graph --&gt;
    &lt;meta property="og:title" content="Rotate PDF Online — Ubah Orientasi Halaman"&gt;
    &lt;meta property="og:description" content="Perbaiki halaman PDF yang terbalik tanpa kompresi ulang. 100% Client-Side."&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/rotate-pdf.html"&gt;
    &lt;meta property="og:type" content="article"&gt;

    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema.org WebApplication &amp; E-E-A-T --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "Rotate PDF Tool",
      "description": "Utilitas rotasi halaman PDF berbasis metadata sisi klien.",
      "applicationCategory": "UtilityApplication",
      "operatingSystem": "Web Browser",
      "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" },
      "author": { "@type": "Person", "name": "Tim Kholil Tools", "url": "https://www.kholil.biz.id/" }
    }
    &lt;/script&gt;

    &lt;!-- Library PDF --&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"&gt;&lt;/script&gt;
    &lt;script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"&gt;&lt;/script&gt;

    &lt;style&gt;
        :root {
            --blue-deep:   #0d3b6e;
            --blue-mid:    #1565c0;
            --blue-accent: #1976d2;
            --blue-light:  #bbdefb;
            --green:       #2e7d32;
            --green-light: #e8f5e9;
            --bg:          #eef4fb;
            --card:        #ffffff;
            --text:        #1c2833;
            --muted:       #546e7a;
            --border:      #cfd8dc;
            --radius-lg:   18px;
            --radius-xl:   28px;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            background: var(--bg);
            font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
            color: var(--text);
            line-height: 1.7;
        }

        /* ─── NAVIGASI ─── */
        .main-nav { 
            background: var(--blue-deep); 
            color: #fff; 
            padding: 16px 28px; 
            display: flex; 
            align-items: center; 
            justify-content: space-between; 
            position: sticky; 
            top: 0; 
            z-index: 100; 
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        .main-nav .logo a { 
            font-family: 'Sora', sans-serif; 
            font-size: 1.25rem; 
            font-weight: 800; 
            color: #fff; 
            text-decoration: none; 
        }
        .main-nav .logo span { color: var(--blue-light); }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { 
            font-size: 0.95rem; 
            font-weight: 500; 
            color: rgba(255,255,255,0.72); 
            text-decoration: none; 
            transition: color 0.2s; 
        }
        .nav-links a:hover { color: #fff; }
        
        .nav-toggle { 
            display: none; flex-direction: column; gap: 5px; 
            cursor: pointer; padding: 4px; background: none; border: none; 
        }
        .nav-toggle span { display: block; width: 24px; height: 2px; background: #fff; border-radius: 2px; }
        
        .mobile-menu { 
            display: none; position: fixed; top: 62px; left: 0; right: 0; 
            background: var(--blue-deep); z-index: 98; padding: 16px 24px; 
            flex-direction: column; gap: 0; box-shadow: 0 8px 32px rgba(0,0,0,0.1); 
        }
        .mobile-menu.open { display: flex; }
        .mobile-menu a { 
            font-size: 0.95rem; font-weight: 500; color: #fff; text-decoration: none; 
            padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.1); 
        }
        .mobile-menu a:last-child { border-bottom: none; }

        /* ─── HERO ─── */
        .hero {
            background: linear-gradient(135deg, var(--blue-deep) 0%, var(--blue-mid) 65%, #1a77c9 100%);
            color: white;
            text-align: center;
            padding: 58px 24px 70px;
        }
        .hero-eyebrow {
            display: inline-block;
            background: rgba(255,255,255,0.15);
            border: 1px solid rgba(255,255,255,0.3);
            border-radius: 40px;
            padding: 5px 18px;
            font-size: 0.78rem;
            font-weight: 600;
            letter-spacing: 0.5px;
            text-transform: uppercase;
            margin-bottom: 18px;
        }
        .hero h1 {
            font-family: 'Sora', sans-serif;
            font-size: clamp(1.6rem, 4vw, 2.7rem);
            font-weight: 800;
            line-height: 1.2;
            margin-bottom: 14px;
            letter-spacing: -0.4px;
        }
        .hero-sub {
            font-size: 1.05rem;
            color: rgba(255,255,255,0.82);
            max-width: 580px;
            margin: 0 auto 28px;
        }

        /* ─── TOOL SECTION ─── */
        .tool-section {
            max-width: 1100px;
            margin: -40px auto 0;
            position: relative;
            z-index: 10;
            padding: 0 20px;
        }
        .tool-card {
            background: var(--card);
            border-radius: var(--radius-xl);
            box-shadow: 0 18px 55px rgba(0,0,0,0.14);
            padding: 28px 28px 32px;
            border: 1px solid var(--border);
        }

        /* UPLOAD AREA */
        .upload-zone {
            border: 2.5px dashed var(--blue-accent);
            border-radius: var(--radius-lg);
            background: #f0f7ff;
            padding: 44px 24px;
            text-align: center;
            cursor: pointer;
            transition: background 0.2s, border-color 0.2s;
            margin-bottom: 24px;
            position: relative;
        }
        .upload-zone.drag-over { background: #dbeeff; border-color: var(--blue-deep); }
        .upload-zone input[type="file"] {
            position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
        }
        .upload-icon { font-size: 3rem; margin-bottom: 12px; }
        .upload-zone h3 { font-size: 1.1rem; color: var(--blue-deep); margin-bottom: 6px; font-weight: 700; }
        .upload-zone p { font-size: 0.88rem; color: var(--muted); }
        .upload-zone .file-types { display: flex; justify-content: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
        .file-type-tag { background: white; border: 1.5px solid var(--border); border-radius: 6px; padding: 3px 10px; font-size: 0.75rem; font-weight: 700; color: var(--blue-mid); }
        
        .file-loaded-bar {
            background: var(--green-light); border: 1.5px solid #a5d6a7; border-radius: 10px;
            padding: 12px 18px; display: none; align-items: center; gap: 12px; margin-bottom: 20px; font-size: 0.9rem;
        }
        .file-loaded-bar.show { display: flex; }
        .file-info-name { font-weight: 700; color: var(--green); flex: 1; }
        .btn-change { background: none; border: 1.5px solid var(--green); color: var(--green); border-radius: 30px; padding: 4px 14px; font-size: 0.78rem; cursor: pointer; font-weight: 600; white-space: nowrap; }

        /* ROTATION CONTROLS */
        .controls-section { margin-bottom: 22px; }
        .controls-label { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); margin-bottom: 10px; }
        .rotate-btns { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }
        .btn-rotate { border: 2px solid var(--blue-accent); background: white; color: var(--blue-accent); border-radius: 50px; padding: 10px 22px; font-size: 0.9rem; font-weight: 700; cursor: pointer; transition: all 0.18s; display: inline-flex; align-items: center; gap: 7px; }
        .btn-rotate:hover, .btn-rotate.active { background: var(--blue-accent); color: white; }
        .btn-reset-rot { border: 2px solid var(--border); background: white; color: var(--muted); border-radius: 50px; padding: 10px 20px; font-size: 0.88rem; font-weight: 600; cursor: pointer; transition: all 0.18s; }
        .btn-reset-rot:hover { background: #f0f4f8; }

        /* PAGE SELECTOR */
        .page-selector { background: #f5f8fb; border: 1.5px solid var(--border); border-radius: 12px; padding: 14px 18px; margin-bottom: 18px; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
        .page-selector label { font-size: 0.88rem; font-weight: 600; color: var(--text); }
        .page-selector select, .page-selector input[type="text"] { border: 1.5px solid var(--border); border-radius: 8px; padding: 6px 10px; font-size: 0.85rem; color: var(--text); background: white; outline: none; }
        .page-selector select:focus, .page-selector input:focus { border-color: var(--blue-accent); }
        .page-help { font-size: 0.78rem; color: var(--muted); }

        /* ACTION BUTTONS */
        .action-row { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 20px; }
        .btn { border: none; border-radius: 50px; font-weight: 700; cursor: pointer; transition: transform 0.15s, opacity 0.15s; font-size: 0.92rem; padding: 13px 28px; display: inline-flex; align-items: center; gap: 8px; }
        .btn:active { transform: scale(0.96); }
        .btn-apply { background: var(--blue-mid); color: white; box-shadow: 0 5px 16px rgba(21,101,192,0.3); }
        .btn-apply:hover { opacity: 0.9; transform: translateY(-1px); }
        .btn-download { background: var(--green); color: white; box-shadow: 0 5px 16px rgba(46,125,50,0.3); display: none; }
        .btn-download:hover { opacity: 0.9; transform: translateY(-1px); }
        .btn-clear-pdf { background: #fdf2f2; color: #c0392b; border: 1.5px solid #f5c6c6; }

        /* STATUS */
        .status-box { background: #e8f5e9; border: 1.5px solid #a5d6a7; border-radius: 10px; padding: 11px 18px; font-size: 0.9rem; text-align: center; color: #2e7d32; font-weight: 500; margin-bottom: 16px; }
        .status-box.info    { background: #e3f2fd; border-color: var(--blue-light); color: var(--blue-mid); }
        .status-box.loading { background: #fff8e1; border-color: #ffe082; color: #f57f17; }
        .status-box.error   { background: #fdf2f2; border-color: #f5b7b1; color: #c0392b; }

        /* PREVIEW GRID */
        .preview-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
        .preview-header h3 { font-size: 0.95rem; font-weight: 700; color: var(--blue-deep); }
        .preview-count { font-size: 0.8rem; color: var(--muted); }
        .preview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; max-height: 520px; overflow-y: auto; padding: 4px; }
        .preview-item { border: 1.5px solid var(--border); border-radius: 12px; overflow: hidden; background: white; box-shadow: 0 2px 8px rgba(0,0,0,0.05); transition: box-shadow 0.2s; }
        .preview-item:hover { box-shadow: 0 6px 18px rgba(0,0,0,0.1); }
        .preview-item-head { background: #f5f8fb; border-bottom: 1px solid var(--border); padding: 7px 12px; font-size: 0.78rem; font-weight: 700; color: var(--muted); display: flex; justify-content: space-between; }
        .preview-item canvas { width: 100%; height: auto; display: block; }
        .preview-empty { grid-column: 1 / -1; text-align: center; padding: 50px 20px; color: var(--muted); font-size: 0.9rem; }

        /* ─── E-E-A-T AUTHOR BOX ─── */
        .author-box { display: flex; align-items: center; gap: 15px; background: white; padding: 20px; border-radius: 12px; border: 1px solid var(--border); margin-bottom: 30px; border-left: 4px solid var(--blue-mid); box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
        .author-img { width: 50px; height: 50px; background: var(--blue-mid); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 1.2rem; }

        /* ─── CONTENT SECTIONS ─── */
        .content-wrap { max-width: 1000px; margin: 40px auto 60px; padding: 0 20px; }
        h2.section-title { font-family: 'Sora', sans-serif; font-size: clamp(1.35rem, 2.5vw, 1.85rem); font-weight: 800; color: var(--blue-deep); margin-bottom: 12px; letter-spacing: -0.3px; }
        
        .article-body h3 { font-size: 1.1rem; color: var(--blue-deep); margin: 26px 0 10px; font-weight: 700; }
        .article-body p  { font-size: 0.95rem; color: var(--muted); margin-bottom: 14px; line-height: 1.8; }
        .article-body ul { padding-left: 20px; margin-bottom: 16px; color: var(--muted); font-size: 0.95rem; line-height: 1.7; }
        .article-body li { margin-bottom: 6px; }
        .article-body a { color: var(--blue-accent); font-weight: 600; text-decoration: none; }
        .article-body a:hover { text-decoration: underline; }
        code { background: #f0f4f8; padding: 2px 5px; border-radius: 4px; font-family: monospace; font-size: 0.9em; color: var(--blue-deep); }

        .divider { border: none; border-top: 1.5px solid var(--border); margin: 40px 0; }

        /* FAQ Accessible */
        .faq-list { display: flex; flex-direction: column; gap: 12px; }
        .faq-item { background: white; border: 1.5px solid var(--border); border-radius: 12px; overflow: hidden; }
        .faq-question { width: 100%; text-align: left; background: none; border: none; padding: 16px 20px; font-size: 0.95rem; font-weight: 700; color: var(--blue-deep); cursor: pointer; display: flex; justify-content: space-between; align-items: center; font-family: 'Inter', sans-serif; }
        .faq-question:focus { outline: 2px solid var(--blue-accent); outline-offset: -2px; }
        .faq-question:hover { background: #f5f8fb; }
        .faq-question .arrow { transition: transform 0.2s; font-size: 1.1rem; color: var(--muted); }
        .faq-question .arrow::after { content: '+'; }
        .faq-item.open .faq-question .arrow::after { content: '−'; }
        .faq-answer { padding: 0 20px; max-height: 0; overflow: hidden; transition: max-height 0.3s ease, padding 0.2s; font-size: 0.9rem; color: var(--muted); line-height: 1.7; }
        .faq-item.open .faq-answer { max-height: 400px; padding: 0 20px 16px; }

        /* Footer */
        footer { background: var(--blue-deep); color: rgba(255,255,255,0.7); text-align: center; padding: 36px 20px; font-size: 0.85rem; line-height: 1.8; }
        footer strong { color: white; }
        footer a { color: var(--blue-light); text-decoration: none; }
        footer a:hover { color: #fff; text-decoration: underline; }

        @media (max-width: 600px) {
            .nav-links { display: none; }
            .nav-toggle { display: flex; }
            .tool-card { padding: 18px 16px 24px; }
            .rotate-btns { gap: 8px; }
            .btn-rotate { padding: 9px 16px; font-size: 0.84rem; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- TOPBAR --&gt;
&lt;nav class="main-nav"&gt;
    &lt;div class="logo"&gt;&lt;a href="https://www.kholil.biz.id/"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/watermark.html"&gt;Watermark PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html"&gt;Sign PDF&lt;/a&gt;
    &lt;/div&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
&lt;/nav&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/watermark.html"&gt;&#128167; Watermark PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html"&gt;✍️ Sign PDF&lt;/a&gt;
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-eyebrow"&gt;&#128260; Alat PDF Gratis — Eksekusi Memori Lokal&lt;/div&gt;
    &lt;h1&gt;Rotate PDF Online&lt;br&gt;Perbaiki Orientasi Halaman&lt;/h1&gt;
    &lt;p class="hero-sub"&gt;
        Putar metadata orientasi halaman 90°, 180°, atau 270°. Perbaiki dokumen yang terbalik secara presisi, tanpa mengompresi gambar dan tanpa melalui peladen eksternal.
    &lt;/p&gt;
    &lt;div class="hero-badges"&gt;
        &lt;span class="hero-badge"&gt;⚡ Rendering Cepat&lt;/span&gt;
        &lt;span class="hero-badge"&gt;&#128274; Memori Client-Side&lt;/span&gt;
        &lt;span class="hero-badge"&gt;&#127919; Skala Spesifik Halaman&lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- TOOL CARD --&gt;
&lt;div class="tool-section"&gt;
    &lt;div class="tool-card"&gt;

        &lt;!-- Upload Zone --&gt;
        &lt;div class="upload-zone" id="dropArea"&gt;
            &lt;input type="file" id="pdfFile" accept="application/pdf" aria-label="Pilih dokumen PDF"&gt;
            &lt;div class="upload-icon"&gt;&#128196;&lt;/div&gt;
            &lt;h3&gt;Drag &amp;amp; Drop file PDF di sini&lt;/h3&gt;
            &lt;p&gt;atau klik untuk memilih file dari perangkat Anda&lt;/p&gt;
            &lt;div class="file-types"&gt;
                &lt;span class="file-type-tag"&gt;PDF&lt;/span&gt;
                &lt;span class="file-type-tag"&gt;Manipulasi Lokal&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- File loaded indicator --&gt;
        &lt;div class="file-loaded-bar" id="fileLoadedBar" aria-live="polite"&gt;
            &lt;span&gt;&#128206;&lt;/span&gt;
            &lt;span class="file-info-name" id="fileInfoName"&gt;—&lt;/span&gt;
            &lt;span id="fileInfoPages" style="color:var(--muted);font-size:0.82rem;"&gt;&lt;/span&gt;
            &lt;button class="btn-change" id="changeFileBtn"&gt;Ganti File&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- Rotation Controls --&gt;
        &lt;div class="controls-section"&gt;
            &lt;div class="controls-label"&gt;Pilih Derajat Rotasi&lt;/div&gt;
            &lt;div class="rotate-btns"&gt;
                &lt;button class="btn-rotate" id="btn90"  onclick="selectRotation(90)" aria-label="Putar 90 Derajat Kanan"&gt;↻ 90° Kanan&lt;/button&gt;
                &lt;button class="btn-rotate" id="btn180" onclick="selectRotation(180)" aria-label="Putar 180 Derajat"&gt;↻ 180°&lt;/button&gt;
                &lt;button class="btn-rotate" id="btn270" onclick="selectRotation(270)" aria-label="Putar 90 Derajat Kiri"&gt;↺ 90° Kiri&lt;/button&gt;
                &lt;button class="btn-reset-rot" onclick="selectRotation(0)" aria-label="Reset Rotasi"&gt;⟲ Reset (0°)&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Page Selector --&gt;
        &lt;div class="page-selector"&gt;
            &lt;label for="pageScope"&gt;Terapkan Rotasi ke:&lt;/label&gt;
            &lt;select id="pageScope"&gt;
                &lt;option value="all"&gt;Semua Halaman&lt;/option&gt;
                &lt;option value="specific"&gt;Halaman Tertentu&lt;/option&gt;
                &lt;option value="odd"&gt;Halaman Ganjil&lt;/option&gt;
                &lt;option value="even"&gt;Halaman Genap&lt;/option&gt;
            &lt;/select&gt;
            &lt;div id="specificPageInput" style="display:none;"&gt;
                &lt;input type="text" id="pageRangeInput" placeholder="cth: 1, 3, 5-8" style="width:160px;" aria-label="Rentang Halaman Tertentu"&gt;
                &lt;span class="page-help"&gt;Pisahkan dengan koma, gunakan tanda - untuk rentang&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Status --&gt;
        &lt;div id="statusMsg" class="status-box info" aria-live="polite"&gt;
            &#128196; Upload fail PDF untuk memulai, lalu pilih rotasi yang diinginkan.
        &lt;/div&gt;

        &lt;!-- Action Buttons (Diperbarui untuk WebView/Android) --&gt;
        &lt;div class="action-row"&gt;
            &lt;button class="btn btn-apply" id="applyBtn" onclick="applyRotation()" aria-label="Terapkan Rotasi Halaman"&gt;&#128260; Terapkan Rotasi&lt;/button&gt;
            &lt;button class="btn btn-download" id="downloadBtn" style="display:none;" aria-label="Unduh PDF"&gt;⬇️ Unduh PDF&lt;/button&gt;
            &lt;button class="btn btn-clear-pdf" id="clearBtn" onclick="clearAll()" aria-label="Hapus File dari Memori"&gt;&#128465;️ Hapus File&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- Preview --&gt;
        &lt;div id="previewSection" style="display:none;"&gt;
            &lt;div class="preview-header"&gt;
                &lt;h3&gt;&#128065;️ Pratinjau Halaman&lt;/h3&gt;
                &lt;span class="preview-count" id="previewCount"&gt;&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="preview-grid" id="previewGrid"&gt;
                &lt;div class="preview-empty"&gt;Belum ada pratinjau.&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

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

&lt;!-- CONTENT --&gt;
&lt;div class="content-wrap"&gt;

    &lt;!-- AUTHOR BOX E-E-A-T --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-img"&gt;K&lt;/div&gt;
        &lt;div&gt;
            &lt;strong style="color:var(--text); display:block; font-size:1rem;"&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
            &lt;span style="font-size:0.85rem; color:var(--muted);"&gt;Pengembang Arsitektur PDF &amp; Optimasi DOM | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- NARASI TEKNIKAL &amp; INTERNAL LINKING --&gt;
    &lt;div class="article-body"&gt;
        &lt;h2 class="section-title"&gt;Mengapa Resolusi Dokumen Anda Tidak Rusak?&lt;/h2&gt;
        &lt;p&gt;Banyak aplikasi daring (&lt;em&gt;online&lt;/em&gt;) di internet yang menjanjikan fitur "Putar PDF". Sayangnya, sebagian besar dari mereka bekerja dengan merender ulang seluruh halaman dokumen menjadi gambar matriks, memutarnya, lalu mengonversinya kembali menjadi PDF. Proses yang sangat destruktif ini menyebabkan ukuran fail membengkak drastis dan teks vektor aslinya menjadi pecah (tidak bisa lagi disalin/di-&lt;em&gt;copy&lt;/em&gt;).&lt;/p&gt;
        
        &lt;p&gt;Skrip "Rotate PDF" ini sengaja menghindari proses destruktif tersebut. Kami mengeksploitasi pustaka &lt;code&gt;pdf-lib&lt;/code&gt; yang hanya memodifikasi parameter nilai orientasi sudut (&lt;em&gt;Property&lt;/em&gt; &lt;code&gt;/Rotate&lt;/code&gt;) di dalam kerangka hierarki (&lt;em&gt;Metadata&lt;/em&gt;) PDF Anda. Karena mesin hanya mengubah instruksi "orientasi tampilan" dan sama sekali tidak menyentuh kualitas raster asli dokumen, proses kompilasi ini sangat ringan (&lt;em&gt;lossless&lt;/em&gt;) dan terjadi seketika, meskipun dokumen Anda berjumlah ratusan halaman.&lt;/p&gt;
        
        &lt;h3&gt;Hasil Pengujian Performa (Benchmark Browser)&lt;/h3&gt;
        &lt;p&gt;Kami telah melakukan uji batas pemrosesan sintaks pada berbagai lingkungan peramban. Oleh karena eksekusi dilakukan sepenuhnya di perangkat klien (RAM), kami menemukan parameter empiris berikut:&lt;/p&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;Pemrosesan Metadata:&lt;/strong&gt; Memutar dokumen berukuran 5 MB (sekitar 30-50 halaman pindaian) hanya memakan waktu komputasi ~1.5 detik pada ponsel kelas menengah (4GB RAM) yang menjalankan Google Chrome.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Kendala Pratinjau (Preview):&lt;/strong&gt; Meskipun modifikasi orientasi metadata bekerja sempurna pada dokumen di atas 50 MB, merender tampilan pratinjau melalui elemen &lt;em&gt;canvas&lt;/em&gt; &lt;code&gt;pdf.js&lt;/code&gt; dapat membuat GPU peramban Anda kewalahan (&lt;em&gt;Freeze&lt;/em&gt;). Untuk mencegah peramban Anda macet, sistem kami membatasi rendering &lt;em&gt;canvas&lt;/em&gt; maksimal 10 halaman pertama saja.&lt;/li&gt;
        &lt;/ul&gt;
        
        &lt;h3&gt;Integrasi Siklus Dokumen Elektronik&lt;/h3&gt;
        &lt;p&gt;Apabila dokumen Anda sudah berada pada orientasi yang benar, langkah lanjutan yang lazim dalam siklus dokumen legal maupun bisnis adalah membubuhkan identitas keamanan. Gunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/watermark.html"&gt;Watermark PDF&lt;/a&gt; kami untuk menyematkan label kerahasiaan atau identitas perusahaan pada dokumen yang sudah rapi orientasinya. Setelah itu, tuntaskan legalitasnya dengan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html"&gt;Sign PDF&lt;/a&gt; kami sebelum berkas tersebut diserahkan ke pihak legal.&lt;/p&gt;
    &lt;/div&gt;

    &lt;!-- BUKTI KINERJA ALAT (GAMBAR PENDUKUNG) --&gt;
    &lt;div class="article-body" style="margin-top: 40px; margin-bottom: 40px;"&gt;
        &lt;h2 class="section-title"&gt;Bukti Kinerja: &lt;span&gt;Manipulasi Rotasi Real-Time&lt;/span&gt;&lt;/h2&gt;
        &lt;p&gt;Kami memastikan bahwa fungsionalitas pratinjau dan modifikasi sudut berjalan secara akurat dan responsif. Berikut adalah dokumentasi pengujian langsung menggunakan peramban seluler, yang membuktikan bahwa utilitas ini mampu membaca dokumen multi-halaman, memutarnya ke berbagai orientasi sudut, dan mengekspor dokumen final dengan sempurna.&lt;/p&gt;
        
        &lt;div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap:15px; margin-top:20px;"&gt;
            &lt;!-- Gambar 1 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:12px; padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/M5Jyyzdq/Screenshot-2026-07-22-23-59-08-791-com-android-chrome.jpg" alt="Total halaman PDF terbaca jelas oleh sistem" style="width:100%; height:auto; border-radius:6px;"&gt;
                &lt;p style="font-size:0.8rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;1. Deteksi Halaman Akurat&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 2 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:12px; padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/YnXhVbQ/Screenshot-2026-07-22-23-59-12-906-com-android-chrome.jpg" alt="Preview rotasi PDF 90 derajat ke kanan" style="width:100%; height:auto; border-radius:6px;"&gt;
                &lt;p style="font-size:0.8rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;2. Pratinjau 90° Kanan&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 3 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:12px; padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/hF2VSPpm/Screenshot-2026-07-22-23-59-16-802-com-android-chrome.jpg" alt="Preview rotasi PDF 180 derajat terbalik" style="width:100%; height:auto; border-radius:6px;"&gt;
                &lt;p style="font-size:0.8rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;3. Pratinjau 180°&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 4 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:12px; padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/m5cKCJxR/Screenshot-2026-07-22-23-59-21-532-com-android-chrome.jpg" alt="Preview rotasi PDF 90 derajat ke kiri" style="width:100%; height:auto; border-radius:6px;"&gt;
                &lt;p style="font-size:0.8rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;4. Pratinjau 90° Kiri&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 5 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:12px; padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/4Z1h3t4q/Screenshot-2026-07-22-23-59-28-999-com-android-chrome.jpg" alt="Tombol download PDF berfungsi setelah rotasi" style="width:100%; height:auto; border-radius:6px;"&gt;
                &lt;p style="font-size:0.8rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;5. Unduhan Berfungsi&lt;/p&gt;
            &lt;/div&gt;
            &lt;!-- Gambar 6 --&gt;
            &lt;div style="border:1px solid var(--border); border-radius:12px; padding:10px; background:var(--white); box-shadow:0 2px 8px rgba(0,0,0,0.05);"&gt;
                &lt;img src="https://i.ibb.co.com/pvMnsZ6W/Screenshot-2026-07-22-23-59-42-564-com-android-chrome.jpg" alt="Hasil akhir PDF yang sudah diputar orientasinya" style="width:100%; height:auto; border-radius:6px;"&gt;
                &lt;p style="font-size:0.8rem; text-align:center; margin-top:10px; color:var(--muted);"&gt;6. Dokumen Akhir Valid&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

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

    &lt;!-- FAQ TEKNIS --&gt;
    &lt;h2 class="section-title"&gt;Batasan Operasional Eksekusi&lt;/h2&gt;
    &lt;div class="faq-list"&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-question" aria-expanded="false" onclick="toggleFaq(this)"&gt;Mengapa panel pratinjau saya hanya menampilkan 10 halaman pertama? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
            &lt;div class="faq-answer"&gt;
                &lt;p&gt;Rendering pratinjau piksel ke dalam &lt;em&gt;canvas&lt;/em&gt; sangat mengonsumsi memori grafis perangkat Anda. Jika Anda mengunggah 100+ halaman pindaian resolusi tinggi, me-render keseluruhannya secara bersamaan akan membuat gawai atau PC Anda macet (&lt;em&gt;Crash&lt;/em&gt;). Pembatasan ini adalah mekanisme keamanan memori. Namun jangan khawatir, ketika Anda menekan tombol unduh, &lt;strong&gt;seluruh halaman&lt;/strong&gt; dokumen Anda (tanpa terkecuali) akan tetap terotasi dengan benar sesuai instruksi sudut yang Anda pilih.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-question" aria-expanded="false" onclick="toggleFaq(this)"&gt;Bisakah dokumen rahasia yang terkunci dieksekusi di sini? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
            &lt;div class="faq-answer"&gt;
                &lt;p&gt;Tidak. Apabila fail PDF Anda terenkripsi oleh kata sandi administrator tingkat tinggi (AES-256), struktur dasar &lt;code&gt;/Rotate&lt;/code&gt; di dalam dokumen tersembunyi secara kriptografi, sehingga skrip kami akan menolak eksekusinya. Silakan cabut proteksi sandinya terlebih dahulu menggunakan utilitas dekripsi kami.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/div&gt;&lt;!-- end content-wrap --&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
    &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF &amp; Keamanan Arsip&lt;/p&gt;
    &lt;p style="margin-top:12px; font-size:0.9rem;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html" style="margin: 0 8px;"&gt;Kebijakan Privasi&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/terms.html" style="margin: 0 8px;"&gt;Ketentuan Layanan&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="margin: 0 8px;"&gt;Tentang Kami&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="margin: 0 8px;"&gt;Kontak Kami&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style="margin-top: 16px; opacity:0.6;"&gt;© 2026 Kholil Tools. Seluruh hak cipta dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';

    // ─── STATE ───
    let originalBytes  = null;
    let pdfJsDoc       = null;
    let selectedRotation = 90;
    let totalPages     = 0;
    let originalFileName = "";
    
    // Variabel URL Unduhan Programmatic (Mencegah Bug WebView Mobile)
    let downloadUrl = null;
    let downloadFilename = "";

    // ─── ELEMENTS ───
    const dropArea        = document.getElementById('dropArea');
    const pdfFileInput    = document.getElementById('pdfFile');
    const fileLoadedBar   = document.getElementById('fileLoadedBar');
    const fileInfoName    = document.getElementById('fileInfoName');
    const fileInfoPages   = document.getElementById('fileInfoPages');
    const changeFileBtn   = document.getElementById('changeFileBtn');
    const statusMsg       = document.getElementById('statusMsg');
    const previewGrid     = document.getElementById('previewGrid');
    const previewSection  = document.getElementById('previewSection');
    const previewCount    = document.getElementById('previewCount');
    const downloadBtn     = document.getElementById('downloadBtn');
    const pageScope       = document.getElementById('pageScope');
    const specificPageInput = document.getElementById('specificPageInput');
    const pageRangeInput  = document.getElementById('pageRangeInput');

    // ─── HAMBURGER MENU ───
    function toggleMenu() {
        const menu = document.getElementById('mobileMenu');
        const toggle = document.getElementById('navToggle');
        const isMenuOpen = menu.classList.toggle('open');
        toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
    }

    // ─── DRAG &amp; DROP ───
    dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('drag-over'); });
    dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('drag-over'));
    dropArea.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropArea.classList.remove('drag-over');
        const file = e.dataTransfer.files[0];
        if (file &amp;&amp; file.type === 'application/pdf') loadFile(file);
        else setStatus('⚠️ Harap upload file dengan format PDF.', 'error');
    });

    pdfFileInput.addEventListener('change', e =&gt; {
        if (e.target.files[0]) loadFile(e.target.files[0]);
    });

    changeFileBtn.addEventListener('click', () =&gt; {
        pdfFileInput.value = '';
        pdfFileInput.click();
    });

    // ─── PAGE SCOPE TOGGLE ───
    pageScope.addEventListener('change', () =&gt; {
        specificPageInput.style.display = pageScope.value === 'specific' ? 'flex' : 'none';
    });
    specificPageInput.style.display = 'none';
    specificPageInput.style.alignItems = 'center';
    specificPageInput.style.gap = '8px';
    specificPageInput.style.flexWrap = 'wrap';

    // ─── LOAD FILE ───
    async function loadFile(file) {
        setStatus('⏳ Memuat utilitas PDF...', 'loading');
        previewSection.style.display = 'none';
        downloadBtn.style.display = 'none';
        previewGrid.innerHTML = '&lt;div class="preview-empty"&gt;Memuat...&lt;/div&gt;';

        // Sanitasi Nama File
        originalFileName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");

        try {
            const buf = await file.arrayBuffer();
            originalBytes = buf.slice(0);
            pdfJsDoc = await pdfjsLib.getDocument({ data: buf.slice(0) }).promise;
            totalPages = pdfJsDoc.numPages;

            fileInfoName.innerHTML = originalFileName;
            fileInfoPages.textContent = `${totalPages} halaman · ${formatBytes(file.size)}`;
            fileLoadedBar.classList.add('show');
            dropArea.style.display = 'none';

            setStatus(`✅ PDF berhasil dimuat (${totalPages} halaman). Pilih sudut lalu klik "Terapkan Rotasi".`, '');
        } catch (err) {
            console.error(err);
            setStatus('❌ Gagal membaca dokumen. Pastikan fail PDF Anda tidak terenkripsi kata sandi.', 'error');
        }
    }

    // ─── SELECT ROTATION ───
    function selectRotation(deg) {
        selectedRotation = deg;
        document.querySelectorAll('.btn-rotate').forEach(b =&gt; b.classList.remove('active'));
        const map = { 90: 'btn90', 180: 'btn180', 270: 'btn270' };
        if (map[deg]) document.getElementById(map[deg]).classList.add('active');
    }
    selectRotation(90);

    // ─── PARSE PAGE RANGES ───
    function parsePageRanges(input, total) {
        const pages = new Set();
        const parts = input.split(',').map(s =&gt; s.trim()).filter(Boolean);
        for (const part of parts) {
            if (part.includes('-')) {
                const [a, b] = part.split('-').map(Number);
                for (let i = a; i &lt;= b &amp;&amp; i &lt;= total; i++) if (i &gt;= 1) pages.add(i);
            } else {
                const n = parseInt(part);
                if (!isNaN(n) &amp;&amp; n &gt;= 1 &amp;&amp; n &lt;= total) pages.add(n);
            }
        }
        return pages;
    }

    // ─── PROGRAMMATIC DOWNLOAD LISTENER ───
    // Mengatasi bug unduhan blob yang dicegat oleh Mobile WebView / Chrome Android
    downloadBtn.addEventListener('click', function(e) {
        e.preventDefault();
        if (downloadUrl) {
            const tempLink = document.createElement('a');
            tempLink.style.display = 'none';
            tempLink.href = downloadUrl;
            tempLink.download = downloadFilename;
            document.body.appendChild(tempLink);
            tempLink.click();
            setTimeout(() =&gt; {
                document.body.removeChild(tempLink);
            }, 200);
        }
    });

    // ─── APPLY ROTATION ───
    async function applyRotation() {
        if (!originalBytes) {
            setStatus('⚠️ Harap unggah dokumen PDF terlebih dahulu.', 'error');
            return;
        }

        setStatus('⏳ Menulis ulang Metadata PDF. Mohon tunggu...', 'loading');
        downloadBtn.style.display = 'none';
        previewGrid.innerHTML = '&lt;div class="preview-empty"&gt;Memproses Tampilan...&lt;/div&gt;';

        try {
            // Evaluasi Rentang Halaman
            let targetPages = new Set();
            const scope = pageScope.value;
            for (let i = 1; i &lt;= totalPages; i++) {
                if (scope === 'all') targetPages.add(i);
                else if (scope === 'odd' &amp;&amp; i % 2 !== 0) targetPages.add(i);
                else if (scope === 'even' &amp;&amp; i % 2 === 0) targetPages.add(i);
            }
            if (scope === 'specific') {
                targetPages = parsePageRanges(pageRangeInput.value, totalPages);
                if (targetPages.size === 0) {
                    setStatus('⚠️ Nomor spesifikasi tidak valid. Contoh formasi yang diizinkan: 1, 3, 5-8', 'error');
                    return;
                }
            }

            // Integrasi ke PDF-Lib
            const pdfDoc = await PDFLib.PDFDocument.load(originalBytes.slice(0));
            const pages  = pdfDoc.getPages();

            pages.forEach((page, idx) =&gt; {
                const pageNum = idx + 1;
                if (targetPages.has(pageNum)) {
                    const currentRot = page.getRotation().angle;
                    const newRot = (currentRot + selectedRotation) % 360;
                    page.setRotation(PDFLib.degrees(newRot));
                }
            });

            const rotatedBytes = await pdfDoc.save();
            const blob = new Blob([rotatedBytes], { type: 'application/pdf' });
            
            // Pembersihan URL Blob lama untuk mencegah memory leak
            if (downloadUrl) {
                URL.revokeObjectURL(downloadUrl);
            }

            // Atur Target URL untuk tombol unduh Programmatic
            downloadUrl  = URL.createObjectURL(blob);
            downloadFilename = originalFileName.replace(/\.pdf$/i, '') + '_Rotasi_KholilTools.pdf';
            
            // Tampilkan Tombol Download
            downloadBtn.style.display = 'inline-flex';

            // Muat Ulang untuk Preview (Dibatasi 10 Halaman agar Peramban HP tidak Freeze)
            pdfJsDoc = await pdfjsLib.getDocument({ data: rotatedBytes }).promise;
            await renderPreview();

            const affected = targetPages.size;
            setStatus(`✅ Orientasi Metadata rotasi ${selectedRotation}° sukses disematkan pada ${affected} halaman! Silakan unduh dokumen Anda.`, '');

        } catch (err) {
            console.error(err);
            setStatus('❌ Sinkronisasi gagal. Refresh halaman atau periksa integritas fail PDF Anda.', 'error');
        }
    }

    // ─── RENDER PREVIEW (Dibatasi 10 Halaman untuk Performa) ───
    async function renderPreview() {
        previewGrid.innerHTML = '';
        previewSection.style.display = 'block';

        const total = pdfJsDoc.numPages;
        const renderLimit = Math.min(total, 10);
        
        if (total &gt; 10) {
            previewCount.textContent = `Menampilkan Pratinjau ${renderLimit} dari total ${total} halaman`;
        } else {
            previewCount.textContent = `${total} halaman`;
        }

        for (let i = 1; i &lt;= renderLimit; i++) {
            const page     = await pdfJsDoc.getPage(i);
            const viewport = page.getViewport({ scale: 1.0 });
            const canvas   = document.createElement('canvas');
            canvas.height  = viewport.height;
            canvas.width   = viewport.width;

            // Aksesibilitas Screen Reader
            canvas.setAttribute('aria-label', `Pratinjau Rotasi Halaman ${i}`);

            await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise;

            const item = document.createElement('div');
            item.className = 'preview-item';
            item.innerHTML = `&lt;div class="preview-item-head"&gt;&lt;span&gt;Hal. ${i}&lt;/span&gt;&lt;span&gt;${Math.round(viewport.width)}×${Math.round(viewport.height)}&lt;/span&gt;&lt;/div&gt;`;
            item.appendChild(canvas);
            previewGrid.appendChild(item);
        }
    }

    // ─── CLEAR ALL ───
    function clearAll() {
        if (!originalBytes &amp;&amp; !pdfJsDoc) return;
        originalBytes = null; pdfJsDoc = null; totalPages = 0; originalFileName = "";
        if (downloadUrl) { URL.revokeObjectURL(downloadUrl); downloadUrl = null; }
        
        previewGrid.innerHTML = '&lt;div class="preview-empty"&gt;Belum ada pratinjau.&lt;/div&gt;';
        previewSection.style.display = 'none';
        downloadBtn.style.display = 'none';
        fileLoadedBar.classList.remove('show');
        dropArea.style.display = 'block';
        pdfFileInput.value = '';
        pageRangeInput.value = '';
        pageScope.value = 'all';
        specificPageInput.style.display = 'none';
        setStatus('&#128196; Unggah dokumen PDF untuk memulai, lalu tetapkan sudut rotasinya.', 'info');
    }

    // ─── FAQ ACCORDION (Aksesibilitas WCAG) ───
    function toggleFaq(btn) {
        const item = btn.parentElement;
        const isOpen = item.classList.contains('open');

        // Tutup Semua
        document.querySelectorAll('.faq-item').forEach(i =&gt; {
            i.classList.remove('open');
            i.querySelector('.faq-question').setAttribute('aria-expanded', 'false');
        });

        // Buka yang Ditekan
        if (!isOpen) {
            item.classList.add('open');
            btn.setAttribute('aria-expanded', 'true');
        }
    }

    // ─── HELPERS ───
    function setStatus(msg, type) {
        statusMsg.className = 'status-box' + (type ? ' ' + type : '');
        statusMsg.innerHTML = msg;
    }
    
    function formatBytes(bytes) {
        if (bytes &lt; 1024) return bytes + ' B';
        if (bytes &lt; 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
        return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
    }
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/rotate-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-5125815314649023608</guid><pubDate>Thu, 09 Apr 2026 21:56:04 +0000</pubDate><atom:updated>2026-07-24T08:08:14.176-07:00</atom:updated><title>Watermark </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;meta name="description" content="Sematkan cap air (Watermark) ke dokumen PDF Anda. Proteksi arsip rahasia menggunakan render kanvas lokal tanpa mengunggah ke peladen (server)."&gt;
  &lt;title&gt;Watermark PDF Online | Kholil Tools&lt;/title&gt;
  
  &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/watermark.html" /&gt;
  &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
  &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
  
  &lt;script type="application/ld+json"&gt;
  {
    "@context": "https://schema.org",
    "@type": "WebApplication",
    "name": "Watermark PDF Tool",
    "applicationCategory": "SecurityApplication",
    "operatingSystem": "All",
    "author": { "@type": "Person", "name": "Kholil Tools Team", "url": "https://www.kholil.biz.id/" }
  }
  &lt;/script&gt;

  &lt;script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"&gt;&lt;/script&gt;

  &lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --purple-950: #1a0533;
      --purple-800: #3b0764;
      --purple-600: #7c3aed;
      --purple-500: #8b5cf6;
      --purple-400: #a78bfa;
      --purple-100: #ede9fe;
      --purple-50:  #f5f3ff;
      --accent:     #f59e0b;
      --gray-900:   #111827;
      --gray-700:   #374151;
      --gray-500:   #6b7280;
      --gray-300:   #d1d5db;
      --gray-200:   #e5e7eb;
      --gray-100:   #f3f4f6;
      --white:      #ffffff;
      --radius-lg:  14px;
      --radius-md:  10px;
      --shadow-lg:  0 20px 60px rgba(124,58,237,.18);
    }

    body {
      font-family: 'Inter', sans-serif;
      background: linear-gradient(160deg, #0f0522 0%, #1e0945 50%, #0c0420 100%);
      min-height: 100vh;
      color: var(--gray-900);
    }

    /* ── HEADER ── */
    header {
      background: rgba(255,255,255,.04);
      backdrop-filter: blur(12px);
      border-bottom: 1px solid rgba(255,255,255,.08);
      padding: 0 24px;
      position: sticky;
      top: 0;
      z-index: 100;
    }
    .header-inner {
      max-width: 1100px;
      margin: 0 auto;
      display: flex;
      align-items: center;
      justify-content: space-between;
      height: 60px;
    }
    .logo {
      font-family: 'Sora', sans-serif;
      font-size: 1.2rem;
      font-weight: 800;
      color: var(--white);
      text-decoration: none;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .logo-icon { font-size: 1.4rem; }
    nav { display: flex; gap: 20px; }
    nav a {
      color: rgba(255,255,255,.65);
      text-decoration: none;
      font-size: .875rem;
      font-weight: 500;
      transition: color .2s;
    }
    nav a:hover { color: var(--white); }

    /* ── HAMBURGER MENU ── */
    .nav-toggle { display: none; flex-direction: column; gap: 5px; cursor: pointer; padding: 4px; background: none; border: none; }
    .nav-toggle span { display: block; width: 24px; height: 2px; background: var(--white); border-radius: 2px; }
    .mobile-menu { display: none; position: fixed; top: 60px; left: 0; right: 0; background: rgba(26,5,51,0.98); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,.08); z-index: 98; padding: 16px 24px; flex-direction: column; gap: 0; }
    .mobile-menu.open { display: flex; }
    .mobile-menu a { font-size: 0.95rem; font-weight: 500; color: var(--white); text-decoration: none; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,.08); }
    .mobile-menu a:last-child { border-bottom: none; }

    /* ── HERO ── */
    .hero { text-align: center; padding: 56px 24px 40px; }
    .hero-badge {
      display: inline-flex; align-items: center; gap: 6px;
      background: rgba(167,139,250,.15); border: 1px solid rgba(167,139,250,.3);
      border-radius: 999px; padding: 5px 14px; font-size: .78rem; font-weight: 600;
      color: var(--purple-400); letter-spacing: .05em; text-transform: uppercase; margin-bottom: 18px;
    }
    .hero h1 {
      font-family: 'Sora', sans-serif; font-size: clamp(1.8rem, 5vw, 3rem);
      font-weight: 800; color: var(--white); line-height: 1.15; margin-bottom: 14px;
    }
    .hero h1 span {
      background: linear-gradient(90deg, #c084fc, #f59e0b);
      -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
    }
    .hero p { font-size: 1.05rem; color: rgba(255,255,255,.65); max-width: 520px; margin: 0 auto; line-height: 1.7; }

    /* ── MAIN CARD ── */
    .main-card { max-width: 820px; margin: 0 auto 24px; padding: 0 16px; }
    .card { background: var(--white); border-radius: 20px; box-shadow: var(--shadow-lg); overflow: hidden; }
    .card-body { padding: 32px; }

    /* ── STEPPER ── */
    .stepper { display: flex; gap: 0; border-bottom: 1px solid var(--gray-100); padding-bottom: 24px; margin-bottom: 28px; }
    .step { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; position: relative; }
    .step:not(:last-child)::after { content: ''; position: absolute; top: 17px; left: 50%; width: 100%; height: 2px; background: var(--gray-300); z-index: 0; }
    .step.done:not(:last-child)::after, .step.active:not(:last-child)::after { background: var(--purple-400); }
    .step-num { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: .8rem; font-weight: 700; background: var(--gray-100); color: var(--gray-500); border: 2px solid var(--gray-300); position: relative; z-index: 1; transition: all .3s; }
    .step.active .step-num { background: var(--purple-600); color: var(--white); border-color: var(--purple-600); }
    .step.done .step-num   { background: #10b981; color: var(--white); border-color: #10b981; }
    .step-label { font-size: .72rem; font-weight: 600; color: var(--gray-500); text-align: center; }
    .step.active .step-label { color: var(--purple-600); }
    .step.done .step-label  { color: #059669; }

    /* ── UPLOAD ZONE ── */
    .drop-zone {
      border: 2.5px dashed var(--gray-300); border-radius: var(--radius-lg); padding: 40px 20px; text-align: center; cursor: pointer; transition: all .25s; background: var(--gray-100); position: relative;
    }
    .drop-zone:hover, .drop-zone.drag-over { border-color: var(--purple-500); background: var(--purple-50); }
    .drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; }
    .drop-icon { font-size: 2.8rem; margin-bottom: 12px; }
    .drop-zone h3 { font-size: 1rem; font-weight: 700; color: var(--gray-700); margin-bottom: 6px; }
    .drop-zone p  { font-size: .85rem; color: var(--gray-500); }
    .drop-zone .btn-browse {
      display: inline-block; margin-top: 14px; padding: 8px 22px; border-radius: 8px; background: var(--purple-600); color: var(--white); font-size: .85rem; font-weight: 600; pointer-events: none;
    }

    /* File info bar */
    .file-info { display: none; align-items: center; gap: 12px; padding: 12px 16px; background: #f0fdf4; border: 1.5px solid #bbf7d0; border-radius: var(--radius-md); margin-top: 12px; }
    .file-info.show { display: flex; }
    .file-icon { font-size: 1.6rem; }
    .file-name { font-size: .875rem; font-weight: 600; color: var(--gray-700); }
    .file-meta { font-size: .78rem; color: var(--gray-500); }
    .file-clear { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--gray-500); font-size: 1.1rem; padding: 4px 8px; border-radius: 6px; transition: background .2s; }
    .file-clear:hover { background: #fce7f3; color: #e11d48; }

    /* ── SETTINGS ── */
    .settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 24px; }
    .field-group { display: flex; flex-direction: column; gap: 6px; }
    .field-group.full { grid-column: 1/-1; }
    label.field-label { font-size: .8rem; font-weight: 600; color: var(--gray-700); letter-spacing: .02em; }
    .field-group input[type="text"], .field-group select {
      padding: 10px 14px; border: 1.5px solid var(--gray-300); border-radius: var(--radius-md); font-size: .9rem; font-family: inherit; color: var(--gray-900); transition: border-color .2s, box-shadow .2s; background: var(--white); outline: none;
    }
    .field-group input[type="text"]:focus, .field-group select:focus { border-color: var(--purple-500); box-shadow: 0 0 0 3px rgba(139,92,246,.15); }

    .color-row { display: flex; align-items: center; gap: 10px; }
    .color-row input[type="color"] { width: 42px; height: 42px; border: 1.5px solid var(--gray-300); border-radius: 8px; cursor: pointer; padding: 3px; background: var(--white); }
    .color-row span { font-size: .8rem; color: var(--gray-500); }

    .range-row { display: flex; align-items: center; gap: 10px; }
    .range-row input[type="range"] { flex: 1; padding: 0; accent-color: var(--purple-600); }
    .range-val { min-width: 38px; text-align: center; font-size: .82rem; font-weight: 700; color: var(--purple-600); background: var(--purple-50); border: 1px solid var(--purple-100); border-radius: 6px; padding: 2px 6px; }

    /* ── PREVIEW ── */
    .preview-wrap { margin-top: 24px; border: 1.5px solid var(--gray-200); border-radius: var(--radius-lg); overflow: hidden; background: var(--gray-100); }
    .preview-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; background: var(--white); border-bottom: 1px solid var(--gray-100); font-size: .82rem; font-weight: 600; color: var(--gray-700); }
    .preview-canvas-wrap { display: flex; justify-content: center; align-items: center; min-height: 220px; padding: 16px; }
    #previewCanvas { max-width: 100%; border-radius: 6px; box-shadow: 0 4px 20px rgba(0,0,0,.12); }

    /* ── ACTION BUTTON ── */
    .action-bar { margin-top: 28px; display: flex; gap: 12px; flex-wrap: wrap; }
    .btn-primary {
      flex: 1; padding: 14px 24px; background: linear-gradient(135deg, var(--purple-600), #6d28d9); color: var(--white); border: none; border-radius: var(--radius-md); font-size: 1rem; font-weight: 700; font-family: inherit; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: opacity .2s, transform .15s; box-shadow: 0 4px 20px rgba(124,58,237,.35);
    }
    .btn-primary:hover:not(:disabled) { opacity: .9; transform: translateY(-1px); }
    .btn-primary:disabled { opacity: .5; cursor: not-allowed; }

    .btn-download {
      display: none; flex: 1; padding: 14px 24px; background: linear-gradient(135deg, #059669, #10b981); color: var(--white); border: none; border-radius: var(--radius-md); font-size: 1rem; font-weight: 700; font-family: inherit; cursor: pointer; text-decoration: none; align-items: center; justify-content: center; gap: 8px; transition: opacity .2s, transform .15s; box-shadow: 0 4px 20px rgba(5,150,105,.3);
    }
    .btn-download.show { display: flex; }
    .btn-download:hover { opacity: .9; transform: translateY(-1px); }

    /* ── STATUS ── */
    .status-bar { margin-top: 16px; padding: 12px 16px; border-radius: var(--radius-md); font-size: .875rem; font-weight: 500; display: none; }
    .status-bar.info    { display:block; background:#eff6ff; border:1px solid #bfdbfe; color:#1d4ed8; }
    .status-bar.success { display:block; background:#f0fdf4; border:1px solid #bbf7d0; color:#166534; }
    .status-bar.error   { display:block; background:#fef2f2; border:1px solid #fecaca; color:#991b1b; }
    
    /* ── E-E-A-T AUTHOR BOX ── */
    .author-box { display: flex; align-items: center; gap: 15px; background: rgba(255,255,255,.06); padding: 20px; border-radius: 12px; border: 1px solid rgba(255,255,255,.1); margin: 0 auto 30px; max-width: 820px; border-left: 4px solid var(--accent); }
    .author-img { width: 50px; height: 50px; background: var(--accent); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--gray-900); font-weight: bold; font-size: 1.2rem; }

    /* ── ARTICLE / CONTENT SECTIONS ── */
    .guide-section { max-width: 820px; margin: 0 auto 40px; padding: 0 16px; }
    .guide-card { background: var(--white); border-radius: 20px; padding: 32px; box-shadow: 0 8px 30px rgba(0,0,0,.1); }

    /* ── BUKTI KINERJA GRID (RESPONSIVE, NO CROP) ── */
    .proof-section { margin-top: 32px; padding-top: 24px; border-top: 1.5px solid var(--gray-100); }
    .proof-section h2 { font-family: 'Sora', sans-serif; font-size: 1.3rem; font-weight: 800; color: var(--gray-900); margin-bottom: 10px; }
    .proof-section &gt; p { font-size: .92rem; color: var(--gray-500); line-height: 1.8; margin-bottom: 18px; }
    .proof-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
      gap: 16px;
    }
    .proof-item {
      border: 1px solid var(--gray-200);
      border-radius: 12px;
      padding: 10px;
      background: var(--white);
      box-shadow: 0 2px 8px rgba(0,0,0,.05);
    }
    .proof-item img {
      width: 100%;
      height: auto;
      display: block;
      border-radius: 8px;
    }
    .proof-caption {
      font-size: .8rem;
      text-align: center;
      margin-top: 10px;
      color: var(--gray-500);
      font-weight: 600;
    }

    /* FAQ */
    .faq-list { display: flex; flex-direction: column; gap: 12px; margin-top: 28px; }
    .faq-item { border: 1.5px solid var(--gray-200); border-radius: var(--radius-md); overflow: hidden; }
    .faq-q {
      width: 100%; text-align: left; padding: 14px 18px; background: var(--white); border: none; font-size: .9rem; font-weight: 700; color: var(--gray-800); cursor: pointer; display: flex; justify-content: space-between; align-items: center; font-family: inherit; transition: background .2s;
    }
    .faq-q:focus { outline: 2px solid var(--purple-500); }
    .faq-q:hover { background: var(--gray-100); }
    .faq-q .arrow { transition: transform .3s; font-size: .75rem; color: var(--gray-500); }
    .faq-q.open .arrow { transform: rotate(180deg); }
    .faq-a { display: none; padding: 0 18px 14px; font-size: .85rem; color: var(--gray-600); line-height: 1.7; background: var(--white); }
    .faq-a.open { display: block; }

    /* ── FOOTER ── */
    footer { text-align: center; padding: 30px 24px; color: rgba(255,255,255,.6); font-size: .85rem; border-top: 1px solid rgba(255,255,255,.07); }
    footer a { color: var(--white); text-decoration: none; margin: 0 8px; font-weight: 500; }
    footer a:hover { color: var(--purple-400); }

    /* ── PROGRESS ── */
    .progress-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.5); backdrop-filter: blur(4px); z-index: 999; align-items: center; justify-content: center; }
    .progress-overlay.show { display: flex; }
    .progress-box { background: var(--white); border-radius: 20px; padding: 36px 40px; text-align: center; max-width: 320px; width: 90%; }
    .spinner { width: 56px; height: 56px; border: 5px solid var(--purple-100); border-top-color: var(--purple-600); border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 16px; }
    @keyframes spin { to { transform: rotate(360deg); } }
    .progress-box h3 { font-size: 1rem; font-weight: 700; color: var(--gray-800); margin-bottom: 6px; }
    .progress-box p  { font-size: .83rem; color: var(--gray-500); }

    /* Responsive */
    @media (max-width: 600px) {
      .settings-grid { grid-template-columns: 1fr; }
      .card-body { padding: 20px; }
      .stepper { display: none; }
      nav { display: none; }
      .nav-toggle { display: flex; }
      .proof-grid { grid-template-columns: 1fr; }
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- HEADER &amp; MOBILE NAV --&gt;
&lt;header&gt;
  &lt;div class="header-inner"&gt;
    &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;
      &lt;span class="logo-icon"&gt;&#128196;&lt;/span&gt; Kholil Tools
    &lt;/a&gt;
    &lt;nav&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html"&gt;Sign PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/jpg-to-pdf.html"&gt;JPG to PDF&lt;/a&gt;
    &lt;/nav&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html"&gt;&#128395;️ Sign PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/jpg-to-pdf.html"&gt;&#128444;️ JPG to PDF&lt;/a&gt;
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
  &lt;div class="hero-badge"&gt;✦ Arsitektur Keamanan Client-Side&lt;/div&gt;
  &lt;h1&gt;Tambah &lt;span&gt;Watermark PDF&lt;/span&gt;&lt;br&gt;Tanpa Risiko Peladen&lt;/h1&gt;
  &lt;p&gt;Sematkan cap air (Watermark) secara lokal. Atur posisi, ukuran, warna, dan opasitas sesuai kebutuhan — dijamin aman dan 100% diproses di RAM peramban Anda.&lt;/p&gt;
&lt;/div&gt;

&lt;!-- MAIN TOOL CARD --&gt;
&lt;div class="main-card"&gt;
  &lt;div class="card"&gt;
    &lt;div class="card-body"&gt;

      &lt;!-- Stepper --&gt;
      &lt;div class="stepper"&gt;
        &lt;div class="step active" id="sStep1"&gt;
          &lt;div class="step-num"&gt;1&lt;/div&gt;
          &lt;div class="step-label"&gt;Upload PDF&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="step" id="sStep2"&gt;
          &lt;div class="step-num"&gt;2&lt;/div&gt;
          &lt;div class="step-label"&gt;Atur Teks&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="step" id="sStep3"&gt;
          &lt;div class="step-num"&gt;3&lt;/div&gt;
          &lt;div class="step-label"&gt;Ekspor File&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Upload Zone --&gt;
      &lt;div class="drop-zone" id="dropZone"&gt;
        &lt;input type="file" id="pdfFile" accept="application/pdf" aria-label="Unggah File PDF"&gt;
        &lt;div class="drop-icon"&gt;&#128194;&lt;/div&gt;
        &lt;h3&gt;Seret &amp; Lepas file PDF ke sini&lt;/h3&gt;
        &lt;p&gt;atau klik untuk memilih file dari perangkat Anda&lt;/p&gt;
        &lt;span class="btn-browse"&gt;Pilih File PDF&lt;/span&gt;
      &lt;/div&gt;

      &lt;div class="file-info" id="fileInfo"&gt;
        &lt;span class="file-icon"&gt;&#128196;&lt;/span&gt;
        &lt;div&gt;
          &lt;div class="file-name" id="fileName"&gt;—&lt;/div&gt;
          &lt;div class="file-meta" id="fileMeta"&gt;—&lt;/div&gt;
        &lt;/div&gt;
        &lt;button class="file-clear" id="fileClear" title="Hapus file" aria-label="Hapus Dokumen"&gt;✕&lt;/button&gt;
      &lt;/div&gt;

      &lt;!-- Settings --&gt;
      &lt;div class="settings-grid" id="settingsPanel" style="opacity:.4;pointer-events:none;"&gt;

        &lt;div class="field-group full"&gt;
          &lt;label class="field-label" for="watermarkText"&gt;Teks Watermark&lt;/label&gt;
          &lt;input type="text" id="watermarkText" value="RAHASIA" placeholder="Contoh: RAHASIA, DRAFT, © 2026"&gt;
        &lt;/div&gt;

        &lt;div class="field-group"&gt;
          &lt;label class="field-label" for="wmSize"&gt;Ukuran Huruf&lt;/label&gt;
          &lt;div class="range-row"&gt;
            &lt;input type="range" id="wmSize" min="20" max="120" value="55" aria-label="Atur ukuran huruf watermark" oninput="updateRangeVal('wmSize','wmSizeVal'); updatePreview()"&gt;
            &lt;span class="range-val" id="wmSizeVal" aria-live="polite"&gt;55&lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="field-group"&gt;
          &lt;label class="field-label" for="wmOpacity"&gt;Opasitas (Transparansi)&lt;/label&gt;
          &lt;div class="range-row"&gt;
            &lt;input type="range" id="wmOpacity" min="5" max="80" value="25" aria-label="Atur tingkat opasitas watermark" oninput="updateRangeVal('wmOpacity','wmOpacityVal'); updatePreview()"&gt;
            &lt;span class="range-val" id="wmOpacityVal" aria-live="polite"&gt;25&lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="field-group"&gt;
          &lt;label class="field-label" for="wmAngle"&gt;Sudut Rotasi&lt;/label&gt;
          &lt;div class="range-row"&gt;
            &lt;input type="range" id="wmAngle" min="-90" max="0" value="-45" aria-label="Atur sudut rotasi watermark" oninput="updateRangeVal('wmAngle','wmAngleVal'); updatePreview()"&gt;
            &lt;span class="range-val" id="wmAngleVal" aria-live="polite"&gt;-45°&lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="field-group"&gt;
          &lt;label class="field-label"&gt;Warna Teks&lt;/label&gt;
          &lt;div class="color-row"&gt;
            &lt;input type="color" id="wmColor" value="#808080" aria-label="Pilih warna teks watermark" oninput="updatePreview()"&gt;
            &lt;span&gt;Pilih warna teks&lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="field-group"&gt;
          &lt;label class="field-label" for="wmPosition"&gt;Posisi Matriks&lt;/label&gt;
          &lt;select id="wmPosition" aria-label="Pilih posisi cetak teks watermark" onchange="updatePreview()"&gt;
            &lt;option value="diagonal-all"&gt;Diagonal Berulang (Aman)&lt;/option&gt;
            &lt;option value="center"&gt;Tengah (Center)&lt;/option&gt;
            &lt;option value="topleft"&gt;Kiri Atas&lt;/option&gt;
            &lt;option value="topright"&gt;Kanan Atas&lt;/option&gt;
            &lt;option value="bottomleft"&gt;Kiri Bawah&lt;/option&gt;
            &lt;option value="bottomright"&gt;Kanan Bawah&lt;/option&gt;
          &lt;/select&gt;
        &lt;/div&gt;

      &lt;/div&gt;

      &lt;!-- Preview --&gt;
      &lt;div class="preview-wrap" id="previewWrap" style="display:none;"&gt;
        &lt;div class="preview-header"&gt;
          &lt;span&gt;&#128065; Pratinjau (Halaman Pertama)&lt;/span&gt;
          &lt;span id="pageInfo" style="color:var(--gray-500);font-weight:400;"&gt;&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="preview-canvas-wrap"&gt;
          &lt;canvas id="previewCanvas" aria-label="Pratinjau langsung modifikasi dokumen"&gt;&lt;/canvas&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Action --&gt;
      &lt;div class="action-bar"&gt;
        &lt;button class="btn-primary" id="btnApply" disabled onclick="addWatermark()" aria-label="Eksekusi penambahan watermark"&gt;
          &#128395; Terapkan Cap Air
        &lt;/button&gt;
        &lt;a class="btn-download" id="downloadBtn" download="watermarked.pdf" aria-label="Unduh PDF hasil watermark"&gt;
          ⬇ Unduh PDF
        &lt;/a&gt;
      &lt;/div&gt;

      &lt;!-- Status --&gt;
      &lt;div class="status-bar" id="statusBar" aria-live="polite"&gt;&lt;/div&gt;

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

&lt;!-- AUTHOR BOX E-E-A-T --&gt;
&lt;div class="author-box" style="margin-top:0;"&gt;
    &lt;div class="author-img"&gt;K&lt;/div&gt;
    &lt;div&gt;
        &lt;strong style="color:var(--white); display:block; font-size:1rem;"&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
        &lt;span style="font-size:0.85rem; color:var(--gray-300);"&gt;Arsitektur Keamanan &amp; Render Berkas Digital | Diperbarui: Juli 2026&lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- NARASI ANTI-SPAM (UNIK) --&gt;
&lt;div class="guide-section"&gt;
    &lt;div class="guide-card"&gt;
        &lt;h2&gt;Melindungi Integritas Dokumen dengan Manipulasi Matriks&lt;/h2&gt;
        &lt;p style="font-size:.92rem;color:var(--gray-500);line-height:1.8;margin-bottom:14px;"&gt;
            Mendistribusikan laporan analisis atau proposal penawaran (Tender) kepada pihak eksternal tanpa melampirkan identitas hak cipta dapat berisiko pada pencurian kekayaan intelektual. Membubuhkan teks semi-transparan sebagai &lt;em&gt;Watermark&lt;/em&gt; adalah metode fundamental dalam arsitektur tata kelola dokumen untuk mendeklarasikan status berkas (seperti "DRAFT" atau "RAHASIA").
        &lt;/p&gt;
        
        &lt;p style="font-size:.92rem;color:var(--gray-500);line-height:1.8;margin-bottom:14px;"&gt;
            Jika dokumen sumber Anda masih berupa kumpulan gambar atau foto dokumen fisik, silakan konversi foto-foto tersebut menjadi satu fail utuh terlebih dahulu menggunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/jpg-to-pdf.html" style="color:var(--purple-600);font-weight:600;text-decoration:none;"&gt;JPG to PDF&lt;/a&gt; kami sebelum diberi label &lt;em&gt;Watermark&lt;/em&gt;.
        &lt;/p&gt;

        &lt;h3 style="font-size:1rem;font-weight:700;color:var(--gray-800);margin-bottom:10px;margin-top:20px;"&gt;Pemrosesan Piksel Tanpa Transmisi Eksternal&lt;/h3&gt;
        &lt;p style="font-size:.92rem;color:var(--gray-500);line-height:1.8;margin-bottom:14px;"&gt;
            Hampir semua aplikasi &lt;em&gt;Watermark&lt;/em&gt; daring (&lt;em&gt;Online&lt;/em&gt;) membebankan tugas penyematan gambar pada peladen (&lt;em&gt;Server&lt;/em&gt;) mereka. Kami memilih rute komputasi yang lebih kompleks namun mutlak aman: &lt;strong&gt;Client-side rendering&lt;/strong&gt;. Skrip kami menangkap hierarki &lt;em&gt;byte&lt;/em&gt; fail PDF Anda, menyematkan teks baru menggunakan fungsi &lt;code&gt;drawText&lt;/code&gt; dari pustaka PDF-lib, dan membangun ulang dokumen tersebut secara instan di dalam memori RAM komputer Anda.
        &lt;/p&gt;
        
        &lt;p style="font-size:.92rem;color:var(--gray-500);line-height:1.8;margin-bottom:14px;"&gt;
            Opsi matriks letak "Diagonal Berulang" yang kami sediakan memastikan pola teks yang Anda ketik diproyeksikan (&lt;em&gt;Looped&lt;/em&gt;) ke seluruh ruang sumbu X dan Y dokumen. Hal ini menyulitkan perangkat lunak penghapus cap air yang menggunakan metode &lt;em&gt;Artificial Intelligence (AI)&lt;/em&gt; untuk memanipulasi dan menghapus stempel Anda.
        &lt;/p&gt;

        &lt;h3 style="font-size:1rem;font-weight:700;color:var(--gray-800);margin-bottom:10px;margin-top:20px;"&gt;Mengunci Permanen Hasil Render&lt;/h3&gt;
        &lt;p style="font-size:.92rem;color:var(--gray-500);line-height:1.8;margin-bottom:14px;"&gt;
            Meskipun teks &lt;em&gt;watermark&lt;/em&gt; telah disematkan, pengguna dengan akses perangkat lunak tingkat lanjut (seperti Adobe Acrobat Pro) masih dapat menghapus lapisan teks tersebut. Untuk pengamanan mutlak, padukan fitur cap air ini dengan tanda tangan otentikasi menggunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html" style="color:var(--purple-600);font-weight:600;text-decoration:none;"&gt;Sign PDF&lt;/a&gt; kami. Tanda tangan digital akan membekukan (&lt;em&gt;flatten&lt;/em&gt;) seluruh hierarki objek dokumen, menjadikannya berkas rekaman &lt;em&gt;Read-Only&lt;/em&gt; yang sah di mata hukum.
        &lt;/p&gt;

        &lt;!-- BUKTI KINERJA: GRID RESPONSIF --&gt;
        &lt;div class="proof-section"&gt;
            &lt;h2&gt;Bukti Kinerja: Watermark Real-Time&lt;/h2&gt;
            &lt;p&gt;Dokumentasi pengujian langsung di peramban seluler — mulai dari pratinjau, proses penyematan, hingga hasil akhir yang siap diunduh.&lt;/p&gt;
            &lt;div class="proof-grid"&gt;
                &lt;div class="proof-item"&gt;
                    &lt;img src="https://i.ibb.co.com/5XVYXzyx/Screenshot-2026-07-23-04-09-38-068-com-android-chrome.jpg" alt="Pratinjau watermark PDF berjalan mulus di browser" loading="lazy"&gt;
                    &lt;p class="proof-caption"&gt;1. Pratinjau Mulus&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-item"&gt;
                    &lt;img src="https://i.ibb.co.com/wZsjvN3F/Screenshot-2026-07-23-04-09-52-947-com-android-chrome.jpg" alt="Watermark berhasil diterapkan ke seluruh halaman PDF" loading="lazy"&gt;
                    &lt;p class="proof-caption"&gt;2. Watermark Diterapkan&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-item"&gt;
                    &lt;img src="https://i.ibb.co.com/67WrqSPc/Screenshot-2026-07-23-04-10-02-140-com-google-android-apps-docs.jpg" alt="Hasil akhir PDF dengan watermark tersimpan sempurna" loading="lazy"&gt;
                    &lt;p class="proof-caption"&gt;3. Hasil Sempurna&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- FAQ TEKNIS &amp; AKSESIBILITAS WCAG --&gt;
        &lt;h2 style="font-family:'Sora',sans-serif;font-size:1.3rem;font-weight:800;color:#111827;margin-bottom:16px;margin-top:30px;"&gt;Tanya Jawab Manipulasi Visual&lt;/h2&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah dokumen HTML mentah bisa langsung dicap watermark? &lt;span class="arrow"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Tidak. Skrip ini hanya menelan parameter dokumen bertipe PDF. Anda harus melakukan kompilasi koding menggunakan utilitas terkait terlebih dahulu sebelum memasukkannya ke mesin pelabelan ini.&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Mengapa panel pratinjau (Preview) tidak muncul untuk fail saya? &lt;span class="arrow"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Jika mekanisme &lt;code&gt;pdf.js&lt;/code&gt; gagal merender tampilan dokumen Anda di dalam kanvas (&lt;em&gt;Canvas&lt;/em&gt;), besar kemungkinan dokumen Anda diproteksi dengan keamanan yang membutuhkan Kata Sandi (Password). Buka pelindung tersebut terlebih dahulu menggunakan alat pencopot sandi (&lt;em&gt;Unlock PDF&lt;/em&gt;) kami.&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- FOOTER DENGAN KEBIJAKAN HUKUM --&gt;
&lt;footer style="color:rgba(255,255,255,.6); border-top:1px solid rgba(255,255,255,.07); padding: 30px 24px;"&gt;
    &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF &amp; Konversi Antarmuka Digital&lt;/p&gt;
    &lt;p style="margin-top:12px; font-size:0.9rem;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Kebijakan Privasi&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/terms.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Ketentuan Layanan&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Tentang Kami&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Kontak Kami&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style="margin-top: 16px; font-size:0.8rem; opacity:0.6;"&gt;© 2026 Kholil Tools. Seluruh pemrosesan dilakukan di peramban (Client-Side).&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- PROGRESS OVERLAY --&gt;
&lt;div class="progress-overlay" id="progressOverlay"&gt;
  &lt;div class="progress-box"&gt;
    &lt;div class="spinner"&gt;&lt;/div&gt;
    &lt;h3&gt;Memproses Render Kanvas...&lt;/h3&gt;
    &lt;p&gt;Menyematkan cap teks ke hierarki dokumen.&lt;br&gt;Harap tunggu sebentar.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
  // --- STATE ---
  let pdfBytes = null;
  let pdfJsDoc = null;
  let isLibraryLoaded = false;

  // Karakter ikon status ditulis via unicode escape (bukan emoji mentah)
  // agar tidak diubah menjadi HTML entity oleh editor Blogger saat disimpan.
  const ICON_SUCCESS = '\u2705'; // checkmark hijau
  const ICON_ERROR   = '\u274C'; // silang merah
  const ICON_WARN    = '\u26A0'; // segitiga peringatan

  // --- LAZY LOAD PDF.JS UNTUK PRATINJAU ---
  function loadPreviewLib() {
      return new Promise((resolve) =&gt; {
          if (window.pdfjsLib) return resolve();
          const script = document.createElement('script');
          script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js';
          script.onload = () =&gt; {
              pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
              isLibraryLoaded = true;
              resolve();
          };
          document.head.appendChild(script);
      });
  }

  // --- HAMBURGER MENU ---
  function toggleMenu() {
      const menu = document.getElementById('mobileMenu');
      const toggle = document.getElementById('navToggle');
      const isMenuOpen = menu.classList.toggle('open');
      toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
  }

  // --- HELPERS ---
  function setStatus(msg, type = 'info') {
    const el = document.getElementById('statusBar');
    el.textContent = msg;
    el.className = 'status-bar ' + type;
  }
  function clearStatus() {
    const el = document.getElementById('statusBar');
    el.className = 'status-bar';
  }

  function updateRangeVal(rangeId, valId) {
    const v = document.getElementById(rangeId).value;
    document.getElementById(valId).textContent = rangeId === 'wmAngle' ? v + '°' : v;
  }

  function setStep(n) {
    for (let i = 1; i &lt;= 3; i++) {
      const el = document.getElementById('sStep' + i);
      el.className = 'step ' + (i &lt; n ? 'done' : i === n ? 'active' : '');
    }
  }

  function formatBytes(b) {
    if (b &lt; 1024) return b + ' B';
    if (b &lt; 1048576) return (b / 1024).toFixed(1) + ' KB';
    return (b / 1048576).toFixed(1) + ' MB';
  }

  // --- FILE HANDLING ---
  const dropZone = document.getElementById('dropZone');
  const fileInput = document.getElementById('pdfFile');

  dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
  dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
  dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('drag-over');
    const f = e.dataTransfer.files[0];
    if (f &amp;&amp; f.type === 'application/pdf') loadFile(f);
    else setStatus(ICON_WARN + ' Harap pilih berkas dengan format PDF.', 'error');
  });

  fileInput.addEventListener('change', e =&gt; {
    if (e.target.files[0]) loadFile(e.target.files[0]);
  });

  document.getElementById('fileClear').addEventListener('click', resetTool);

  async function loadFile(file) {
    clearStatus();
    pdfBytes = await file.arrayBuffer();

    // Sanitasi anti-XSS
    const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
    document.getElementById('fileName').innerHTML = safeName;
    document.getElementById('fileMeta').textContent = formatBytes(file.size);
    document.getElementById('fileInfo').classList.add('show');

    // Aktifkan pengaturan
    document.getElementById('settingsPanel').style.opacity = '1';
    document.getElementById('settingsPanel').style.pointerEvents = 'auto';
    document.getElementById('btnApply').disabled = false;

    // Load Pustaka &amp; Pratinjau
    try {
      if(!isLibraryLoaded) await loadPreviewLib();
      const typedArray = new Uint8Array(pdfBytes);
      pdfJsDoc = await pdfjsLib.getDocument({ data: typedArray.slice() }).promise;
      document.getElementById('pageInfo').textContent =
        pdfJsDoc.numPages + (pdfJsDoc.numPages &gt; 1 ? ' halaman' : ' halaman');
      document.getElementById('previewWrap').style.display = 'block';
      updatePreview();
    } catch(e) {
      setStatus('File PDF dimuat, pratinjau tidak tersedia (Bisa jadi terenkripsi sandi).', 'info');
    }

    setStep(2);
    setStatus('File berhasil dimuat. Sesuaikan tata letak teks lalu klik "Terapkan Cap Air".', 'success');
  }

  function resetTool() {
    pdfBytes = null; pdfJsDoc = null;
    fileInput.value = '';
    document.getElementById('fileInfo').classList.remove('show');
    document.getElementById('settingsPanel').style.opacity = '0.4';
    document.getElementById('settingsPanel').style.pointerEvents = 'none';
    document.getElementById('btnApply').disabled = true;
    document.getElementById('downloadBtn').classList.remove('show');
    document.getElementById('previewWrap').style.display = 'none';
    clearStatus();
    setStep(1);
    const canvas = document.getElementById('previewCanvas');
    const ctx = canvas.getContext('2d'); ctx.clearRect(0,0,canvas.width,canvas.height);
  }

  // --- PREVIEW RENDER ---
  async function updatePreview() {
    if (!pdfJsDoc) return;
    try {
      const page = await pdfJsDoc.getPage(1);
      const scale = 1.2;
      const vp = page.getViewport({ scale });
      const canvas = document.getElementById('previewCanvas');
      canvas.width  = vp.width;
      canvas.height = vp.height;
      const ctx = canvas.getContext('2d');
      await page.render({ canvasContext: ctx, viewport: vp }).promise;

      // Render Layer Watermark Sementara di Canvas
      const text    = document.getElementById('watermarkText').value || 'RAHASIA';
      const size    = parseInt(document.getElementById('wmSize').value) * scale;
      const opacity = parseInt(document.getElementById('wmOpacity').value) / 100;
      const angle   = parseInt(document.getElementById('wmAngle').value) * (Math.PI / 180);
      const color   = document.getElementById('wmColor').value;
      const pos     = document.getElementById('wmPosition').value;

      ctx.save();
      ctx.globalAlpha = opacity;
      ctx.font = `bold ${size}px 'Inter', Arial, sans-serif`;
      ctx.fillStyle = color;

      const positions = getPositions(pos, canvas.width, canvas.height, ctx.measureText(text).width, size);
      for (const [px, py] of positions) {
        ctx.save();
        ctx.translate(px, py);
        ctx.rotate(angle);
        ctx.fillText(text, 0, 0);
        ctx.restore();
      }
      ctx.restore();
    } catch(e) { /* Abaikan jika render gagal */ }
  }

  function getPositions(pos, w, h, tw, ts) {
    const cx = w / 2, cy = h / 2;
    const pad = 40;
    switch(pos) {
      case 'center':      return [[cx - tw/2, cy + ts/3]];
      case 'topleft':     return [[pad, ts + pad]];
      case 'topright':    return [[w - tw - pad, ts + pad]];
      case 'bottomleft':  return [[pad, h - pad]];
      case 'bottomright': return [[w - tw - pad, h - pad]];
      case 'diagonal-all': {
        const arr = [];
        for (let x = -w; x &lt; w*2; x += tw + 80) {
          for (let y = -h; y &lt; h*2; y += ts + 60) {
            arr.push([x, y]);
          }
        }
        return arr;
      }
      default: return [[cx - tw/2, cy + ts/3]];
    }
  }

  document.getElementById('watermarkText').addEventListener('input', updatePreview);

  // --- PDF-LIB APPLY WATERMARK ---
  async function addWatermark() {
    if (!pdfBytes) return;

    document.getElementById('progressOverlay').classList.add('show');
    document.getElementById('downloadBtn').classList.remove('show');
    clearStatus();

    try {
      const text    = document.getElementById('watermarkText').value || 'RAHASIA';
      const size    = parseInt(document.getElementById('wmSize').value);
      const opacity = parseInt(document.getElementById('wmOpacity').value) / 100;
      const angle   = parseInt(document.getElementById('wmAngle').value);
      const hex     = document.getElementById('wmColor').value;
      const pos     = document.getElementById('wmPosition').value;

      // Konversi Hex ke RGB (0-1 format PDF-lib)
      const r = parseInt(hex.slice(1,3),16)/255;
      const g = parseInt(hex.slice(3,5),16)/255;
      const b = parseInt(hex.slice(5,7),16)/255;

      const { PDFDocument, rgb: pdfRgb, StandardFonts, degrees } = PDFLib;
      const pdfDoc   = await PDFDocument.load(pdfBytes);
      const font     = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
      const pages    = pdfDoc.getPages();
      const color    = pdfRgb(r, g, b);

      for (const page of pages) {
        const { width, height } = page.getSize();
        const tw = font.widthOfTextAtSize(text, size);

        const drawOpts = (x, y) =&gt; ({
          x, y,
          size, font, color,
          rotate: degrees(angle),
          opacity
        });

        if (pos === 'diagonal-all') {
          for (let px = -width; px &lt; width*2; px += tw + 80) {
            for (let py = -height; py &lt; height*2; py += size + 60) {
              page.drawText(text, drawOpts(px, py));
            }
          }
        } else {
          const positions = {
            center:      [width/2 - tw/2, height/2],
            topleft:     [40, height - size - 40],
            topright:    [width - tw - 40, height - size - 40],
            bottomleft:  [40, 40 + size],
            bottomright: [width - tw - 40, 40 + size],
          };
          const [px, py] = positions[pos] || [width/2 - tw/2, height/2];
          page.drawText(text, drawOpts(px, py));
        }
      }

      const outBytes = await pdfDoc.save();
      const blob     = new Blob([outBytes], { type: 'application/pdf' });
      const url      = URL.createObjectURL(blob);

      const dl = document.getElementById('downloadBtn');
      dl.href     = url;
      dl.download = 'TandaAir_KholilTools.pdf';
      dl.classList.add('show');

      setStep(3);
      setStatus(ICON_SUCCESS + ' Cap air berhasil diterapkan ke ' + pages.length + ' halaman. Klik tombol unduh sekarang.', 'success');

    } catch(err) {
      console.error(err);
      setStatus(ICON_ERROR + ' Gagal memproses berkas. Pastikan dokumen PDF Anda tidak dikunci oleh kata sandi.', 'error');
    } finally {
      document.getElementById('progressOverlay').classList.remove('show');
    }
  }

  // --- FAQ ACCORDION ---
  function toggleFaq(btn) {
    const ans = btn.nextElementSibling;
    const isOpen = ans.classList.contains('open');
    
    document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('open'));
    document.querySelectorAll('.faq-q').forEach(b =&gt; {
        b.classList.remove('open');
        b.setAttribute('aria-expanded', 'false');
    });
    
    if (!isOpen) { 
        ans.classList.add('open'); 
        btn.classList.add('open'); 
        btn.setAttribute('aria-expanded', 'true');
    }
  }
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/watermark.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-2571088624592722048</guid><pubDate>Thu, 09 Apr 2026 21:54:00 +0000</pubDate><atom:updated>2026-07-24T08:10:27.970-07:00</atom:updated><title>Sign Pdf</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;meta name="description" content="Tambahkan tanda tangan digital ke file PDF secara online, gratis, dan aman sesuai regulasi UU ITE. Gambar langsung di browser — file tidak dikirim ke server."&gt;
  &lt;title&gt;Tanda Tangan PDF Online Gratis | Kholil Tools&lt;/title&gt;
  
  &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/sign-pdf.html" /&gt;
  &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
  &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet"&gt;
  
  &lt;script type="application/ld+json"&gt;
  {
    "@context": "https://schema.org",
    "@type": "WebApplication",
    "name": "Sign PDF Tool",
    "applicationCategory": "UtilityApplication",
    "operatingSystem": "All",
    "author": { "@type": "Person", "name": "Tim Kholil Tools", "url": "https://www.kholil.biz.id/" }
  }
  &lt;/script&gt;

  &lt;!-- Library dimuat secara Deferred untuk performa Core Web Vitals --&gt;
  &lt;script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js" defer&gt;&lt;/script&gt;
  &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js" defer&gt;&lt;/script&gt;

  &lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --navy-950: #03071e;
      --navy-900: #0a1128;
      --navy-800: #0d1b4b;
      --navy-600: #1e3a8a;
      --blue-500: #3b82f6;
      --blue-400: #60a5fa;
      --blue-100: #dbeafe;
      --blue-50:  #eff6ff;
      --accent:   #f59e0b;
      --teal:     #0d9488;
      --gray-900: #111827;
      --gray-700: #374151;
      --gray-500: #6b7280;
      --gray-300: #d1d5db;
      --gray-100: #f3f4f6;
      --white:    #ffffff;
      --radius-lg: 14px;
      --radius-md: 10px;
      --shadow-lg: 0 20px 60px rgba(30,58,138,.18);
    }

    body {
      font-family: 'Inter', sans-serif;
      background: linear-gradient(160deg, #020b24 0%, #071640 50%, #020b24 100%);
      min-height: 100vh;
      color: var(--gray-900);
    }

    /* ── HEADER ── */
    header {
      background: rgba(255,255,255,.04);
      backdrop-filter: blur(12px);
      border-bottom: 1px solid rgba(255,255,255,.08);
      padding: 0 24px;
      position: sticky;
      top: 0;
      z-index: 100;
    }
    .header-inner {
      max-width: 1100px; margin: 0 auto;
      display: flex; align-items: center; justify-content: space-between;
      height: 60px;
    }
    .logo {
      font-family: 'Sora', sans-serif; font-size: 1.2rem; font-weight: 800;
      color: var(--white); text-decoration: none;
      display: flex; align-items: center; gap: 8px;
    }
    nav { display: flex; gap: 20px; }
    nav a {
      color: rgba(255,255,255,.6); text-decoration: none;
      font-size: .875rem; font-weight: 500; transition: color .2s;
    }
    nav a:hover { color: var(--white); }
    nav a.active { color: var(--blue-400); }

    /* ── HAMBURGER MENU MOBILE ── */
    .nav-toggle { display: none; flex-direction: column; gap: 5px; cursor: pointer; padding: 4px; background: none; border: none; }
    .nav-toggle span { display: block; width: 24px; height: 2px; background: var(--white); border-radius: 2px; }
    .mobile-menu { display: none; position: fixed; top: 60px; left: 0; right: 0; background: rgba(2,11,36,0.98); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,.08); z-index: 98; padding: 16px 24px; flex-direction: column; gap: 0; }
    .mobile-menu.open { display: flex; }
    .mobile-menu a { font-size: 0.95rem; font-weight: 500; color: var(--white); text-decoration: none; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,.08); }
    .mobile-menu a:last-child { border-bottom: none; }

    /* ── HERO ── */
    .hero { text-align: center; padding: 56px 24px 40px; }
    .hero-badge {
      display: inline-flex; align-items: center; gap: 6px;
      background: rgba(96,165,250,.15);
      border: 1px solid rgba(96,165,250,.3);
      border-radius: 999px; padding: 5px 14px;
      font-size: .78rem; font-weight: 600; color: var(--blue-400);
      letter-spacing: .05em; text-transform: uppercase; margin-bottom: 18px;
    }
    .hero h1 {
      font-family: 'Sora', sans-serif;
      font-size: clamp(2rem, 5vw, 3rem); font-weight: 800;
      color: var(--white); line-height: 1.15; margin-bottom: 14px;
    }
    .hero h1 span {
      background: linear-gradient(90deg, #60a5fa, #f59e0b);
      -webkit-background-clip: text; -webkit-text-fill-color: transparent;
      background-clip: text;
    }
    .hero p {
      font-size: 1.05rem; color: rgba(255,255,255,.65);
      max-width: 520px; margin: 0 auto; line-height: 1.7;
    }

    /* ── MAIN CARD ── */
    .main-card { max-width: 860px; margin: 0 auto 24px; padding: 0 16px; }
    .card {
      background: var(--white); border-radius: 20px;
      box-shadow: var(--shadow-lg); overflow: hidden;
    }
    .card-body { padding: 32px; }

    /* ── STEPPER ── */
    .stepper {
      display: flex; gap: 0;
      border-bottom: 1px solid var(--gray-100);
      padding-bottom: 24px; margin-bottom: 28px;
    }
    .step {
      flex: 1; display: flex; flex-direction: column;
      align-items: center; gap: 6px; position: relative;
    }
    .step:not(:last-child)::after {
      content: ''; position: absolute; top: 17px; left: 50%;
      width: 100%; height: 2px; background: var(--gray-300); z-index: 0;
    }
    .step.done:not(:last-child)::after,
    .step.active:not(:last-child)::after { background: var(--blue-400); }
    .step-num {
      width: 34px; height: 34px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      font-size: .8rem; font-weight: 700;
      background: var(--gray-100); color: var(--gray-500);
      border: 2px solid var(--gray-300);
      position: relative; z-index: 1; transition: all .3s;
    }
    .step.active .step-num { background: var(--blue-500); color: var(--white); border-color: var(--blue-500); }
    .step.done .step-num   { background: #10b981; color: var(--white); border-color: #10b981; }
    .step-label { font-size: .72rem; font-weight: 600; color: var(--gray-500); text-align: center; }
    .step.active .step-label { color: var(--blue-500); }
    .step.done .step-label  { color: #059669; }

    /* ── UPLOAD ZONE ── */
    .drop-zone {
      border: 2.5px dashed var(--gray-300); border-radius: var(--radius-lg);
      padding: 40px 20px; text-align: center; cursor: pointer;
      transition: all .25s; background: var(--gray-100); position: relative;
    }
    .drop-zone:hover, .drop-zone.drag-over {
      border-color: var(--blue-500); background: var(--blue-50);
    }
    .drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
    .drop-icon { font-size: 2.8rem; margin-bottom: 12px; }
    .drop-zone h3 { font-size: 1rem; font-weight: 700; color: var(--gray-700); margin-bottom: 6px; }
    .drop-zone p  { font-size: .85rem; color: var(--gray-500); }
    .btn-browse {
      display: inline-block; margin-top: 14px;
      padding: 8px 22px; border-radius: 8px;
      background: var(--blue-500); color: var(--white);
      font-size: .85rem; font-weight: 600; pointer-events: none;
    }

    /* File info */
    .file-info {
      display: none; align-items: center; gap: 12px;
      padding: 12px 16px; background: #f0fdf4;
      border: 1.5px solid #bbf7d0; border-radius: var(--radius-md); margin-top: 12px;
    }
    .file-info.show { display: flex; }
    .file-icon { font-size: 1.6rem; }
    .file-name { font-size: .875rem; font-weight: 600; color: var(--gray-700); }
    .file-meta { font-size: .78rem; color: var(--gray-500); }
    .file-clear {
      margin-left: auto; background: none; border: none;
      cursor: pointer; color: var(--gray-500); font-size: 1.1rem;
      padding: 4px 8px; border-radius: 6px; transition: background .2s;
    }
    .file-clear:hover { background: #fce7f3; color: #e11d48; }

    /* ── PDF PREVIEW ── */
    .pdf-preview-section { display: none; margin-top: 24px; }
    .pdf-preview-section.show { display: block; }
    .section-label {
      font-size: .8rem; font-weight: 700; color: var(--gray-500);
      text-transform: uppercase; letter-spacing: .06em; margin-bottom: 10px;
    }
    .pdf-canvas-wrap {
      border: 1.5px solid var(--gray-200); border-radius: var(--radius-lg);
      overflow: hidden; background: #e5e7eb; position: relative;
    }
    .pdf-canvas-scroller {
      overflow: auto; max-height: 400px;
      display: flex; align-items: flex-start; justify-content: center;
      padding: 16px;
    }
    #pdfCanvas {
      max-width: 100%; border-radius: 4px;
      box-shadow: 0 4px 20px rgba(0,0,0,.2); cursor: crosshair;
    }

    .pdf-nav {
      display: flex; align-items: center; justify-content: space-between;
      padding: 10px 16px; background: var(--white);
      border-top: 1px solid var(--gray-100);
      font-size: .82rem; font-weight: 600; color: var(--gray-700);
    }
    .pdf-nav button {
      background: var(--gray-100); border: none; cursor: pointer;
      padding: 5px 14px; border-radius: 6px;
      font-size: .8rem; font-weight: 600; color: var(--gray-700);
      transition: background .2s;
    }
    .pdf-nav button:hover:not(:disabled) { background: var(--blue-100); color: var(--blue-500); }
    .pdf-nav button:disabled { opacity: .4; cursor: not-allowed; }

    .place-hint {
      display: none; margin-top: 10px; padding: 10px 14px; border-radius: var(--radius-md);
      background: #fef3c7; border: 1px solid #fde68a; font-size: .82rem; color: #92400e; font-weight: 500;
    }
    .place-hint.show { display: block; }

    /* ── SIGNATURE PAD ── */
    .sig-section { display: none; margin-top: 24px; }
    .sig-section.show { display: block; }

    .sig-tabs { display: flex; gap: 0; border-bottom: 2px solid var(--gray-200); margin-bottom: 16px; }
    .sig-tab {
      padding: 10px 20px; background: none; border: none;
      font-size: .875rem; font-weight: 600; color: var(--gray-500);
      cursor: pointer; border-bottom: 2.5px solid transparent;
      margin-bottom: -2px; font-family: inherit; transition: color .2s;
    }
    .sig-tab.active { color: var(--blue-500); border-bottom-color: var(--blue-500); }

    .tab-panel { display: none; }
    .tab-panel.active { display: block; }

    .sig-pad-wrap {
      border: 2px solid var(--gray-300); border-radius: var(--radius-md);
      background: var(--white); position: relative; overflow: hidden; touch-action: none;
    }
    .sig-pad-wrap.has-sig { border-color: var(--blue-400); }
    #sigCanvas { display: block; width: 100%; height: 180px; cursor: crosshair; touch-action: none; }
    .sig-pad-label {
      position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
      color: var(--gray-300); font-size: .85rem; font-weight: 500;
      pointer-events: none; transition: opacity .3s; user-select: none;
    }

    .sig-controls { display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
    .sig-ctrl-btn {
      padding: 7px 16px; border-radius: 8px; border: 1.5px solid var(--gray-300);
      background: var(--white); font-size: .8rem; font-weight: 600;
      color: var(--gray-700); cursor: pointer; font-family: inherit;
      transition: all .2s; display: flex; align-items: center; gap: 6px;
    }
    .sig-ctrl-btn:hover { border-color: var(--blue-400); color: var(--blue-500); background: var(--blue-50); }
    .sig-ctrl-btn.danger:hover { border-color: #fca5a5; color: #dc2626; background: #fef2f2; }
    
    .color-picker-row { display: flex; gap: 6px; align-items: center; margin-top: 10px; }
    .color-dot {
      width: 26px; height: 26px; border-radius: 50%; cursor: pointer;
      border: 2.5px solid transparent; transition: transform .15s;
    }
    .color-dot:hover { transform: scale(1.15); }
    .color-dot.selected { border-color: var(--gray-900); transform: scale(1.1); }
    
    .stroke-row { display: flex; gap: 8px; align-items: center; margin-top: 10px; }
    .stroke-label { font-size: .78rem; color: var(--gray-500); font-weight: 600; min-width: 80px; }
    .stroke-row input[type="range"] { flex: 1; accent-color: var(--blue-500); }
    .stroke-val { font-size: .78rem; font-weight: 700; color: var(--blue-500); min-width: 24px; }

    .type-font-row { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
    .font-btn {
      padding: 8px 16px; border: 1.5px solid var(--gray-300); border-radius: 8px;
      background: var(--white); cursor: pointer; font-size: .95rem; font-weight: 500;
      color: var(--gray-700); transition: all .2s; font-family: inherit;
    }
    .font-btn:hover { border-color: var(--blue-400); }
    .font-btn.selected { border-color: var(--blue-500); background: var(--blue-50); color: var(--blue-600); }
    .font-btn.cursive { font-family: 'Dancing Script', cursive; font-size: 1.1rem; }
    .font-btn.serif   { font-family: Georgia, serif; }
    
    #typeInput {
      width: 100%; padding: 12px 16px;
      border: 1.5px solid var(--gray-300); border-radius: var(--radius-md);
      font-size: 1.1rem; font-family: inherit; color: var(--gray-900);
      margin-bottom: 12px; outline: none; transition: border-color .2s;
    }
    #typeInput:focus { border-color: var(--blue-500); box-shadow: 0 0 0 3px rgba(59,130,246,.12); }
    
    #typePreview {
      width: 100%; height: 120px; border: 1.5px solid var(--gray-200); border-radius: var(--radius-md);
      display: flex; align-items: center; justify-content: center; background: var(--gray-100);
      font-size: 2.2rem; color: #1e3a8a; overflow: hidden; padding: 12px;
    }

    .sig-size-row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
    .sig-size-row label { font-size: .8rem; font-weight: 600; color: var(--gray-700); min-width: 100px; }
    .sig-size-row input[type="range"] { flex: 1; accent-color: var(--blue-500); }
    .sig-size-val { font-size: .8rem; font-weight: 700; color: var(--blue-500); min-width: 32px; }

    /* ── ACTION BAR ── */
    .action-bar { margin-top: 28px; display: flex; gap: 12px; flex-wrap: wrap; }
    .btn-primary {
      flex: 1; padding: 14px 24px; background: linear-gradient(135deg, var(--blue-500), #1d4ed8);
      color: var(--white); border: none; border-radius: var(--radius-md); font-size: 1rem; font-weight: 700;
      font-family: inherit; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;
      transition: opacity .2s, transform .15s; box-shadow: 0 4px 20px rgba(59,130,246,.35);
    }
    .btn-primary:hover:not(:disabled) { opacity: .9; transform: translateY(-1px); }
    .btn-primary:disabled { opacity: .45; cursor: not-allowed; transform: none; }
    
    .btn-secondary {
      padding: 14px 20px; background: var(--gray-100); color: var(--gray-700);
      border: 1.5px solid var(--gray-300); border-radius: var(--radius-md);
      font-size: .9rem; font-weight: 600; font-family: inherit; cursor: pointer; transition: all .2s;
    }
    .btn-secondary:hover { background: var(--blue-50); border-color: var(--blue-400); color: var(--blue-500); }
    
    .btn-download {
      display: none; flex: 1; padding: 14px 24px;
      background: linear-gradient(135deg, #059669, #10b981); color: var(--white);
      border: none; border-radius: var(--radius-md); font-size: 1rem; font-weight: 700;
      font-family: inherit; cursor: pointer; text-decoration: none;
      align-items: center; justify-content: center; gap: 8px;
      transition: opacity .2s, transform .15s; box-shadow: 0 4px 20px rgba(5,150,105,.3);
    }
    .btn-download.show { display: flex; }
    .btn-download:hover { opacity: .9; transform: translateY(-1px); }

    /* ── STATUS ── */
    .status-bar { margin-top: 16px; padding: 12px 16px; border-radius: var(--radius-md); font-size: .875rem; font-weight: 500; display: none; }
    .status-bar.info    { display:block; background:#eff6ff; border:1px solid #bfdbfe; color:#1d4ed8; }
    .status-bar.success { display:block; background:#f0fdf4; border:1px solid #bbf7d0; color:#166534; }
    .status-bar.error   { display:block; background:#fef2f2; border:1px solid #fecaca; color:#991b1b; }

    /* ── E-E-A-T AUTHOR BOX ── */
    .author-box-wrap { max-width: 860px; margin: 0 auto 30px; padding: 0 16px; }
    .author-box { display: flex; align-items: center; gap: 15px; background: var(--navy-800); padding: 20px; border-radius: 12px; border: 1px solid rgba(255,255,255,.1); border-left: 4px solid var(--accent); box-shadow: 0 8px 24px rgba(0,0,0,.25); }
    .author-img { flex-shrink: 0; width: 50px; height: 50px; background: var(--accent); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--gray-900); font-weight: bold; font-size: 1.2rem; }
    .author-box strong { color: var(--white) !important; }
    .author-box span { color: rgba(255,255,255,.75) !important; }

    /* ── PROOF / SCREENSHOT GRID ── */
    .proof-section { max-width: 860px; margin: 0 auto 30px; padding: 0 16px; }
    .proof-title { font-family: 'Sora', sans-serif; font-size: 1.15rem; font-weight: 800; color: var(--white); margin-bottom: 14px; text-align: center; }
    .proof-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
    .proof-item { background: var(--white); border-radius: 14px; overflow: hidden; box-shadow: 0 8px 24px rgba(0,0,0,.2); }
    .proof-item img { display: block; width: 100%; height: 240px; object-fit: cover; object-position: top; background: var(--gray-100); }
    .proof-caption { display: flex; align-items: center; gap: 8px; padding: 12px 14px; font-size: .8rem; font-weight: 600; color: var(--gray-700); }
    .proof-num { flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%; background: #10b981; color: var(--white); font-size: .7rem; font-weight: 700; display: flex; align-items: center; justify-content: center; }
    @media (max-width: 600px) {
      .proof-item img { height: 300px; }
    }

    /* ── GUIDE + FAQ ── */
    .guide-section { max-width: 860px; margin: 0 auto 40px; padding: 0 16px; }
    .guide-card { background: var(--white); border-radius: 20px; padding: 32px; box-shadow: 0 8px 30px rgba(0,0,0,.1); }
    .guide-card h2 { font-family: 'Sora', sans-serif; font-size: 1.4rem; font-weight: 800; color: var(--gray-900); margin-bottom: 20px; }
    
    .faq-list { display: flex; flex-direction: column; gap: 10px; margin-top: 28px; }
    .faq-head { font-family: 'Sora', sans-serif; font-size: 1.4rem; font-weight: 800; color: var(--gray-900); margin-bottom: 16px; }
    .faq-item { border: 1.5px solid var(--gray-200); border-radius: var(--radius-md); overflow: hidden; }
    .faq-q {
      width: 100%; text-align: left; padding: 14px 18px; background: var(--white); border: none;
      font-size: .9rem; font-weight: 700; color: var(--gray-800); cursor: pointer;
      display: flex; justify-content: space-between; align-items: center; font-family: inherit; transition: background .2s;
    }
    .faq-q:hover { background: var(--gray-100); }
    .faq-q .arrow { transition: transform .3s; font-size: .75rem; color: var(--gray-500); }
    .faq-q.open .arrow { transform: rotate(180deg); }
    .faq-a { display: none; padding: 0 18px 14px; font-size: .85rem; color: var(--gray-600); line-height: 1.7; background: var(--white); }
    .faq-a.open { display: block; }

    /* ── FOOTER ── */
    footer { text-align: center; padding: 30px 24px; color: rgba(255,255,255,.6); font-size: .85rem; border-top: 1px solid rgba(255,255,255,.07); }
    footer a { color: var(--white); text-decoration: none; margin: 0 8px; font-weight: 500; }
    footer a:hover { color: var(--blue-400); }

    /* ── PROGRESS OVERLAY ── */
    .progress-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.5); backdrop-filter: blur(4px); z-index: 999; align-items: center; justify-content: center; }
    .progress-overlay.show { display: flex; }
    .progress-box { background: var(--white); border-radius: 20px; padding: 36px 40px; text-align: center; max-width: 320px; width: 90%; }
    .spinner { width: 56px; height: 56px; border: 5px solid var(--blue-100); border-top-color: var(--blue-500); border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 16px; }
    @keyframes spin { to { transform: rotate(360deg); } }
    .progress-box h3 { font-size: 1rem; font-weight: 700; color: var(--gray-800); margin-bottom: 6px; }
    .progress-box p  { font-size: .83rem; color: var(--gray-500); }

    @media (max-width: 600px) {
      .card-body { padding: 20px; }
      .stepper { display: none; }
      nav { display: none; }
      .nav-toggle { display: flex; }
      .sig-controls { gap: 6px; }
    }
  &lt;/style&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Dancing+Script:wght@700&amp;display=swap" rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- HEADER &amp; MOBILE NAV --&gt;
&lt;header&gt;
  &lt;div class="header-inner"&gt;
    &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;
      &lt;span class="logo-icon"&gt;&#128196;&lt;/span&gt; Kholil Tools
    &lt;/a&gt;
    &lt;nav&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html" class="active"&gt;Sign PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-jpg.html"&gt;PDF to JPG&lt;/a&gt;
    &lt;/nav&gt;
    &lt;button class="nav-toggle" onclick="toggleMenu()" id="navToggle" aria-expanded="false" aria-label="Buka Menu"&gt;
        &lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;div class="mobile-menu" id="mobileMenu"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;&#127968; Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/sign-pdf.html"&gt;&#128395;️ Sign PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-jpg.html"&gt;&#128444;️ PDF to JPG&lt;/a&gt;
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
  &lt;div class="hero-badge"&gt;✦ Arsitektur Keamanan Client-Side&lt;/div&gt;
  &lt;h1&gt;Tanda Tangan &lt;span&gt;PDF Online&lt;/span&gt;&lt;br&gt;Tanpa Perlu Aplikasi&lt;/h1&gt;
  &lt;p&gt;Gambar atau ketik tanda tangan elektronik Anda langsung di peramban, tentukan posisi halamannya secara visual, dan unduh dokumen tersertifikasi secara lokal.&lt;/p&gt;
&lt;/div&gt;

&lt;!-- MAIN CARD --&gt;
&lt;div class="main-card"&gt;
  &lt;div class="card"&gt;
    &lt;div class="card-body"&gt;

      &lt;!-- Stepper --&gt;
      &lt;div class="stepper"&gt;
        &lt;div class="step active" id="sStep1"&gt;&lt;div class="step-num"&gt;1&lt;/div&gt;&lt;div class="step-label"&gt;Upload PDF&lt;/div&gt;&lt;/div&gt;
        &lt;div class="step" id="sStep2"&gt;&lt;div class="step-num"&gt;2&lt;/div&gt;&lt;div class="step-label"&gt;Buat Tanda Tangan&lt;/div&gt;&lt;/div&gt;
        &lt;div class="step" id="sStep3"&gt;&lt;div class="step-num"&gt;3&lt;/div&gt;&lt;div class="step-label"&gt;Pilih Posisi&lt;/div&gt;&lt;/div&gt;
        &lt;div class="step" id="sStep4"&gt;&lt;div class="step-num"&gt;4&lt;/div&gt;&lt;div class="step-label"&gt;Download&lt;/div&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Upload Zone --&gt;
      &lt;div class="drop-zone" id="dropZone"&gt;
        &lt;input type="file" id="pdfFile" accept="application/pdf" aria-label="Pilih dokumen PDF"&gt;
        &lt;div class="drop-icon"&gt;&#128194;&lt;/div&gt;
        &lt;h3&gt;Seret &amp;amp; Lepas file PDF ke sini&lt;/h3&gt;
        &lt;p&gt;atau klik untuk memilih file dari perangkat Anda&lt;/p&gt;
        &lt;span class="btn-browse"&gt;Pilih File PDF&lt;/span&gt;
      &lt;/div&gt;
      &lt;div class="file-info" id="fileInfo"&gt;
        &lt;span class="file-icon"&gt;&#128196;&lt;/span&gt;
        &lt;div&gt;
          &lt;div class="file-name" id="fileName"&gt;—&lt;/div&gt;
          &lt;div class="file-meta" id="fileMeta"&gt;—&lt;/div&gt;
        &lt;/div&gt;
        &lt;button class="file-clear" id="fileClear" title="Hapus file" aria-label="Hapus Dokumen"&gt;✕&lt;/button&gt;
      &lt;/div&gt;

      &lt;!-- Signature Pad Section --&gt;
      &lt;div class="sig-section" id="sigSection"&gt;
        &lt;div class="section-label"&gt;Langkah 2 — Buat Tanda Tangan&lt;/div&gt;

        &lt;div class="sig-tabs"&gt;
          &lt;button class="sig-tab active" onclick="switchTab('draw',this)"&gt;✏️ Gambar&lt;/button&gt;
          &lt;button class="sig-tab" onclick="switchTab('type',this)"&gt;⌨️ Ketik&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- DRAW TAB --&gt;
        &lt;div class="tab-panel active" id="tabDraw"&gt;
          &lt;div class="sig-pad-wrap" id="sigPadWrap"&gt;
            &lt;canvas id="sigCanvas" aria-label="Kanvas Tanda Tangan Tangan Bebas"&gt;&lt;/canvas&gt;
            &lt;span class="sig-pad-label" id="sigPadLabel"&gt;Gambar tanda tangan Anda di sini&lt;/span&gt;
          &lt;/div&gt;
          &lt;div class="color-picker-row"&gt;
            &lt;span style="font-size:.78rem;font-weight:600;color:var(--gray-500);margin-right:4px;"&gt;Warna:&lt;/span&gt;
            &lt;div class="color-dot selected" style="background:#1e3a8a;" data-color="#1e3a8a" onclick="setColor(this)" aria-label="Warna Biru"&gt;&lt;/div&gt;
            &lt;div class="color-dot" style="background:#111827;" data-color="#111827" onclick="setColor(this)" aria-label="Warna Hitam"&gt;&lt;/div&gt;
            &lt;div class="color-dot" style="background:#dc2626;" data-color="#dc2626" onclick="setColor(this)" aria-label="Warna Merah"&gt;&lt;/div&gt;
            &lt;div class="color-dot" style="background:#059669;" data-color="#059669" onclick="setColor(this)" aria-label="Warna Hijau"&gt;&lt;/div&gt;
          &lt;/div&gt;
          &lt;div class="stroke-row"&gt;
            &lt;span class="stroke-label"&gt;Ketebalan:&lt;/span&gt;
            &lt;input type="range" id="strokeWidth" min="1" max="8" value="3" aria-label="Atur ketebalan garis tanda tangan" oninput="strokeVal.textContent=this.value+'px'"&gt;
            &lt;span class="stroke-val" id="strokeVal" aria-live="polite"&gt;3px&lt;/span&gt;
          &lt;/div&gt;
          &lt;div class="sig-controls"&gt;
            &lt;button class="sig-ctrl-btn danger" onclick="clearSigCanvas()" aria-label="Hapus tanda tangan"&gt;&#128465; Hapus&lt;/button&gt;
            &lt;button class="sig-ctrl-btn" onclick="undoStroke()" aria-label="Batalkan goresan terakhir"&gt;↩ Undo&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- TYPE TAB --&gt;
        &lt;div class="tab-panel" id="tabType"&gt;
          &lt;input type="text" id="typeInput" placeholder="Nama Lengkap Anda" aria-label="Ketik nama lengkap untuk tanda tangan" oninput="updateTypePreview()"&gt;
          &lt;div class="type-font-row"&gt;
            &lt;button class="font-btn cursive selected" onclick="selectFont(this,'cursive')" style="font-family:'Dancing Script',cursive"&gt;Kursif&lt;/button&gt;
            &lt;button class="font-btn serif" onclick="selectFont(this,'serif')" style="font-family:Georgia,serif"&gt;Serif&lt;/button&gt;
            &lt;button class="font-btn" onclick="selectFont(this,'sans')" style="font-family:'Inter',sans-serif"&gt;Sans-serif&lt;/button&gt;
          &lt;/div&gt;
          &lt;div id="typePreview" style="font-family:'Dancing Script',cursive;"&gt;
            &lt;span id="typePreviewText" style="color:#1e3a8a;"&gt;Nama Anda&lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- PDF Preview with click-to-place --&gt;
      &lt;div class="pdf-preview-section" id="pdfPreviewSection"&gt;
        &lt;div class="section-label" style="margin-top:20px;"&gt;Langkah 3 — Pilih Halaman &amp;amp; Klik untuk Menempatkan Tanda Tangan&lt;/div&gt;
        &lt;div class="sig-size-row"&gt;
          &lt;label for="sigSizeRange"&gt;Ukuran Tanda Tangan&lt;/label&gt;
          &lt;input type="range" id="sigSizeRange" min="40" max="200" value="100" aria-label="Atur ukuran dimensi tanda tangan pada dokumen" oninput="sigSizeVal.textContent=this.value+'px'; renderCurrentPage()"&gt;
          &lt;span class="sig-size-val" id="sigSizeVal" aria-live="polite"&gt;100px&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="place-hint show" id="placeHint"&gt;
          &#128070; Klik pada dokumen PDF di bawah untuk menempatkan tanda tangan Anda
        &lt;/div&gt;
        &lt;div class="pdf-canvas-wrap"&gt;
          &lt;div class="pdf-canvas-scroller"&gt;
            &lt;canvas id="pdfCanvas" aria-label="Pratinjau Halaman Dokumen PDF interaktif"&gt;&lt;/canvas&gt;
          &lt;/div&gt;
          &lt;div class="pdf-nav"&gt;
            &lt;button id="btnPrev" onclick="changePage(-1)" aria-label="Halaman Sebelumnya"&gt;◀ Sebelumnya&lt;/button&gt;
            &lt;span id="pageLabel"&gt;Halaman 1 / 1&lt;/span&gt;
            &lt;button id="btnNext" onclick="changePage(1)" aria-label="Halaman Berikutnya"&gt;Berikutnya ▶&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Action Bar --&gt;
      &lt;div class="action-bar"&gt;
        &lt;button class="btn-primary" id="btnApply" disabled onclick="applyAndSave()" aria-label="Terapkan dan simpan perubahan file PDF"&gt;
          ✍️ Terapkan &amp;amp; Simpan PDF
        &lt;/button&gt;
        &lt;button class="btn-secondary" id="btnReset" onclick="resetTool()" style="display:none;" aria-label="Mulai ulang proses"&gt;↺ Mulai Ulang&lt;/button&gt;
        &lt;a class="btn-download" id="downloadBtn" download="signed_toolspdf.pdf" aria-label="Unduh dokumen tersertifikasi"&gt;⬇ Download PDF&lt;/a&gt;
      &lt;/div&gt;

      &lt;div class="status-bar" id="statusBar" aria-live="polite"&gt;&lt;/div&gt;

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

&lt;!-- BUKTI TOOL BERFUNGSI --&gt;
&lt;div class="proof-section"&gt;
  &lt;div class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/div&gt;
  &lt;div class="proof-grid"&gt;
    &lt;div class="proof-item"&gt;
      &lt;img src="https://i.ibb.co.com/mV9C51pM/Screenshot-2026-07-23-07-25-58-109-com-android-chrome.jpg" alt="Upload file PDF berfungsi normal di Sign PDF Kholil Tools" loading="lazy" decoding="async"&gt;
      &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;1&lt;/span&gt; File PDF berhasil diunggah&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="proof-item"&gt;
      &lt;img src="https://i.ibb.co.com/DgKdT4NW/Screenshot-2026-07-23-07-26-34-005-com-android-chrome.jpg" alt="Preview tanda tangan sebelum disimpan ke PDF" loading="lazy" decoding="async"&gt;
      &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;2&lt;/span&gt; Preview tanda tangan di halaman PDF&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="proof-item"&gt;
      &lt;img src="https://i.ibb.co.com/HfckKCCt/Screenshot-2026-07-23-07-26-47-619-com-google-android-apps-docs.jpg" alt="Hasil akhir PDF bertanda tangan tanpa kendala" loading="lazy" decoding="async"&gt;
      &lt;div class="proof-caption"&gt;&lt;span class="proof-num"&gt;3&lt;/span&gt; Hasil akhir PDF bertanda tangan&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- AUTHOR BOX E-E-A-T --&gt;
&lt;div class="author-box-wrap"&gt;
  &lt;div class="author-box"&gt;
    &lt;div class="author-img"&gt;K&lt;/div&gt;
    &lt;div&gt;
        &lt;strong style="display:block; font-size:1rem;"&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
        &lt;span style="font-size:0.85rem;"&gt;Spesialis Kriptografi Berkas &amp; Tanda Tangan Digital | Diperbarui: Juli 2026&lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- GUIDE + FAQ --&gt;
&lt;div class="guide-section"&gt;
  &lt;div class="guide-card"&gt;
    &lt;h2&gt;Legalitas &amp;amp; Alur Kerja Tanda Tangan Elektronik&lt;/h2&gt;
    &lt;p style="font-size:.92rem;color:var(--gray-600);line-height:1.8;margin-bottom:14px;"&gt;
      Penerapan tanda tangan digital pada dokumen portabel harus mematuhi kaidah hukum yang berlaku. Berdasarkan regulasi di Indonesia, keabsahan tanda tangan elektronik diatur secara spesifik di dalam &lt;strong&gt;UU ITE No. 11 Tahun 2008&lt;/strong&gt; serta &lt;strong&gt;PP No. 71 Tahun 2019&lt;/strong&gt; tentang Penyelenggaraan Sistem dan Transaksi Elektronik. Utilitas ini menyediakan media penempatan tanda tangan visual mandiri berbasis peramban lokal yang terisolasi dari pihak ketiga.
    &lt;/p&gt;
    
    &lt;p style="font-size:.92rem;color:var(--gray-600);line-height:1.8;margin-bottom:14px;"&gt;
      Setelah dokumen Anda ditandatangani, dan Anda memerlukan versi gambar dari halaman tersebut untuk keperluan lain (misalnya unggahan ke formulir online), Anda dapat mengonversinya menggunakan modul &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-jpg.html" style="color:var(--blue-500);font-weight:600;text-decoration:none;"&gt;PDF to JPG&lt;/a&gt; kami.
    &lt;/p&gt;

    &lt;!-- FAQ TEKNIS --&gt;
    &lt;div class="faq-list"&gt;
      &lt;div class="faq-head"&gt;Pertanyaan Umum (FAQ)&lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Apakah tanda tangan elektronik ini sah secara hukum di Indonesia? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-a"&gt;Merujuk pada UU ITE No. 11 Tahun 2008 dan PP No. 71 Tahun 2019, tanda tangan yang digambar melalui kanvas lokal ini tergolong ke dalam tanda tangan elektronik tidak tersertifikasi. Dokumen ini tetap sah dan mengikat secara perdata untuk kesepakatan umum. Namun, untuk keperluan hukum formal bernilai strategis tinggi, instansi wajib menggunakan PSrE resmi yang berlisensi pemerintah.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" onclick="toggleFaq(this)"&gt;Bagaimana keamanan data dokumen saya selama proses penandatanganan? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-a"&gt;Seluruh komputasi berjalan secara lokal di dalam memori peramban (*Client-side*). Dokumen PDF beserta goresan tanda tangan Anda tidak pernah diunggah ke peladen mana pun, sehingga kerahasiaan korporasi Anda terjamin mutlak.&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- FOOTER DENGAN KEBIJAKAN HUKUM --&gt;
&lt;footer&gt;
    &lt;p&gt;&lt;strong&gt;Kholil Tools&lt;/strong&gt; — Solusi Utilitas PDF &amp; Keamanan Arsip&lt;/p&gt;
    &lt;p style="margin-top:12px; font-size:0.9rem;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Kebijakan Privasi&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/terms.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Ketentuan Layanan&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Tentang Kami&lt;/a&gt; |
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="color:var(--white); margin: 0 8px; text-decoration:none; font-weight:500;"&gt;Kontak Kami&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style="margin-top: 16px; font-size:0.8rem; opacity:0.6;"&gt;© 2026 Kholil Tools. Seluruh hak cipta dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- PROGRESS OVERLAY --&gt;
&lt;div class="progress-overlay" id="progressOverlay"&gt;
  &lt;div class="progress-box"&gt;
    &lt;div class="spinner"&gt;&lt;/div&gt;
    &lt;h3&gt;Menyimpan PDF...&lt;/h3&gt;
    &lt;p&gt;Menambahkan tanda tangan ke dokumen.&lt;br&gt;Harap tunggu sebentar.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
  // ── STATE ──
  let pdfBytes = null;
  let pdfJsDoc = null;
  let currentPage = 1;
  let totalPages  = 1;
  let sigDataURL  = null;
  let placedX = null, placedY = null;
  let currentTab  = 'draw';
  let selectedFont = 'cursive';
  let drawColor   = '#1e3a8a';
  let isDrawing   = false;
  let strokes     = [];
  let currentStroke = [];

  function toggleMenu() {
    const menu = document.getElementById('mobileMenu');
    const toggle = document.getElementById('navToggle');
    const isMenuOpen = menu.classList.toggle('open');
    toggle.setAttribute('aria-expanded', isMenuOpen ? 'true' : 'false');
  }

  function setStatus(msg, type='info') {
    const el = document.getElementById('statusBar');
    el.textContent = msg; el.className = 'status-bar ' + type;
  }
  function clearStatus() { document.getElementById('statusBar').className = 'status-bar'; }

  function setStep(n) {
    for (let i = 1; i &lt;= 4; i++) {
      const el = document.getElementById('sStep' + i);
      el.className = 'step ' + (i &lt; n ? 'done' : i === n ? 'active' : '');
    }
  }

  function formatBytes(b) {
    if (b &lt; 1024) return b + ' B';
    if (b &lt; 1048576) return (b/1024).toFixed(1) + ' KB';
    return (b/1048576).toFixed(1) + ' MB';
  }

  async function loadFile(file) {
    clearStatus();

    if (typeof pdfjsLib === 'undefined' || typeof PDFLib === 'undefined') {
      setStatus('Pustaka PDF belum selesai dimuat, coba lagi sebentar.', 'error');
      return;
    }

    pdfBytes = await file.arrayBuffer();
    const safeName = file.name.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
    document.getElementById('fileName').innerHTML = safeName;
    document.getElementById('fileMeta').textContent = formatBytes(file.size);
    document.getElementById('fileInfo').classList.add('show');

    const typedArray = new Uint8Array(pdfBytes);
    pdfJsDoc = await pdfjsLib.getDocument({ data: typedArray.slice() }).promise;
    totalPages = pdfJsDoc.numPages;
    currentPage = 1;

    document.getElementById('sigSection').classList.add('show');
    document.getElementById('pdfPreviewSection').classList.add('show');
    document.getElementById('btnReset').style.display = 'inline-flex';
    initSigCanvas();
    await renderCurrentPage();
    updatePageNav();
    setStep(2);
    setStatus('File berhasil dimuat. Gambar tanda tangan Anda, lalu klik pada PDF untuk menempatkannya.', 'success');
  }

  function initSigCanvas() {
    const canvas = document.getElementById('sigCanvas');
    const wrap   = document.getElementById('sigPadWrap');
    canvas.width  = wrap.offsetWidth || 600;
    canvas.height = 180;
    setupDrawing(canvas);
  }

  function setupDrawing(canvas) {
    const ctx = canvas.getContext('2d');
    ctx.lineCap = 'round'; ctx.lineJoin = 'round';

    const getPos = (e) =&gt; {
      const rect = canvas.getBoundingClientRect();
      const scaleX = canvas.width / rect.width;
      const scaleY = canvas.height / rect.height;
      if (e.touches) {
        return {
          x: (e.touches[0].clientX - rect.left) * scaleX,
          y: (e.touches[0].clientY - rect.top)  * scaleY
        };
      }
      return { x: (e.clientX - rect.left) * scaleX, y: (e.clientY - rect.top) * scaleY };
    };

    const startDraw = (e) =&gt; {
      e.preventDefault();
      isDrawing = true;
      const pos = getPos(e);
      currentStroke = [pos];
      ctx.beginPath(); ctx.moveTo(pos.x, pos.y);
      document.getElementById('sigPadLabel').style.opacity = '0';
      document.getElementById('sigPadWrap').classList.add('has-sig');
    };
    const draw = (e) =&gt; {
      if (!isDrawing) return; e.preventDefault();
      const pos = getPos(e);
      currentStroke.push(pos);
      ctx.strokeStyle = drawColor;
      ctx.lineWidth = parseInt(document.getElementById('strokeWidth').value);
      ctx.lineTo(pos.x, pos.y);
      ctx.stroke();
    };
    const endDraw = (e) =&gt; {
      if (!isDrawing) return;
      isDrawing = false;
      if (currentStroke.length &gt; 1) strokes.push([...currentStroke]);
      currentStroke = [];
      captureSignature();
    };

    canvas.addEventListener('mousedown',  startDraw);
    canvas.addEventListener('mousemove',  draw);
    canvas.addEventListener('mouseup',    endDraw);
    canvas.addEventListener('mouseleave', endDraw);
    canvas.addEventListener('touchstart', startDraw, { passive: false });
    canvas.addEventListener('touchmove',  draw,      { passive: false });
    canvas.addEventListener('touchend',   endDraw);
  }

  function captureSignature() {
    const canvas = document.getElementById('sigCanvas');
    sigDataURL = canvas.toDataURL('image/png');
    checkApplyReady();
    if (placedX !== null) renderCurrentPage();
  }

  function clearSigCanvas() {
    const canvas = document.getElementById('sigCanvas');
    const ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    strokes = []; sigDataURL = null;
    document.getElementById('sigPadLabel').style.opacity = '1';
    document.getElementById('sigPadWrap').classList.remove('has-sig');
    checkApplyReady();
    if (placedX !== null) renderCurrentPage();
  }

  function undoStroke() {
    if (!strokes.length) return;
    strokes.pop();
    const canvas = document.getElementById('sigCanvas');
    const ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.lineCap = 'round'; ctx.lineJoin = 'round';
    for (const stroke of strokes) {
      ctx.beginPath();
      ctx.moveTo(stroke[0].x, stroke[0].y);
      for (let i = 1; i &lt; stroke.length; i++) ctx.lineTo(stroke[i].x, stroke[i].y);
      ctx.strokeStyle = drawColor;
      ctx.lineWidth = parseInt(document.getElementById('strokeWidth').value);
      ctx.stroke();
    }
    if (strokes.length === 0) {
      sigDataURL = null;
      document.getElementById('sigPadLabel').style.opacity = '1';
      document.getElementById('sigPadWrap').classList.remove('has-sig');
    } else {
      sigDataURL = canvas.toDataURL('image/png');
    }
    checkApplyReady();
    if (placedX !== null) renderCurrentPage();
  }

  function setColor(el) {
    document.querySelectorAll('.color-dot').forEach(d =&gt; d.classList.remove('selected'));
    el.classList.add('selected');
    drawColor = el.dataset.color;
  }

  function switchTab(tab, btn) {
    currentTab = tab;
    document.querySelectorAll('.sig-tab').forEach(t =&gt; t.classList.remove('active'));
    btn.classList.add('active');
    document.querySelectorAll('.tab-panel').forEach(p =&gt; p.classList.remove('active'));
    document.getElementById(tab === 'draw' ? 'tabDraw' : 'tabType').classList.add('active');
    if (tab === 'type') generateTypeSig();
    else { sigDataURL = null; checkApplyReady(); }
  }

  const fontMap = { cursive: "'Dancing Script', cursive", serif: 'Georgia, serif', sans: "'Inter', sans-serif" };

  function selectFont(btn, font) {
    selectedFont = font;
    document.querySelectorAll('.font-btn').forEach(b =&gt; b.classList.remove('selected'));
    btn.classList.add('selected');
    updateTypePreview();
  }

  function updateTypePreview() {
    const text = document.getElementById('typeInput').value || 'Nama Anda';
    const preview = document.getElementById('typePreviewText');
    preview.textContent = text;
    preview.parentElement.style.fontFamily = fontMap[selectedFont];
    generateTypeSig();
  }

  function generateTypeSig() {
    const text = document.getElementById('typeInput').value.trim();
    if (!text) { sigDataURL = null; checkApplyReady(); return; }
    const canvas = document.createElement('canvas');
    canvas.width = 500; canvas.height = 160;
    const ctx = canvas.getContext('2d');
    const fontSize = selectedFont === 'cursive' ? 72 : 60;
    ctx.font = `${fontSize}px ${fontMap[selectedFont]}`;
    ctx.fillStyle = drawColor;
    ctx.textBaseline = 'middle';
    const tw = ctx.measureText(text).width;
    ctx.fillText(text, (500 - tw) / 2, 80);
    sigDataURL = canvas.toDataURL('image/png');
    checkApplyReady();
    if (placedX !== null) renderCurrentPage();
  }

  async function renderCurrentPage() {
    if (!pdfJsDoc) return;
    const page = await pdfJsDoc.getPage(currentPage);
    const scale = 1.5;
    const vp    = page.getViewport({ scale });
    const canvas = document.getElementById('pdfCanvas');
    canvas.width  = vp.width;
    canvas.height = vp.height;
    const ctx = canvas.getContext('2d');
    await page.render({ canvasContext: ctx, viewport: vp }).promise;

    if (sigDataURL &amp;&amp; placedX !== null) {
      const sigSize = parseInt(document.getElementById('sigSizeRange').value);
      const img = new Image();
      img.onload = () =&gt; {
        const ratio = img.height ? img.width / img.height : 3;
        const sw = sigSize * ratio;
        const sh = sigSize;
        const px = placedX * canvas.width  - sw / 2;
        const py = placedY * canvas.height - sh / 2;
        ctx.drawImage(img, px, py, sw, sh);
        ctx.strokeStyle = 'rgba(59,130,246,.6)';
        ctx.lineWidth = 1.5;
        ctx.setLineDash([5, 3]);
        ctx.strokeRect(px - 2, py - 2, sw + 4, sh + 4);
        ctx.setLineDash([]);
      };
      img.src = sigDataURL;
    }
  }

  function updatePageNav() {
    document.getElementById('pageLabel').textContent = `Halaman ${currentPage} / ${totalPages}`;
    document.getElementById('btnPrev').disabled = currentPage &lt;= 1;
    document.getElementById('btnNext').disabled = currentPage &gt;= totalPages;
  }

  async function changePage(delta) {
    currentPage = Math.max(1, Math.min(totalPages, currentPage + delta));
    placedX = null; placedY = null;
    document.getElementById('placeHint').classList.add('show');
    updatePageNav();
    await renderCurrentPage();
  }

  function checkApplyReady() {
    const ready = !!(sigDataURL &amp;&amp; placedX !== null &amp;&amp; pdfBytes);
    document.getElementById('btnApply').disabled = !ready;
  }

  async function applyAndSave() {
    if (!pdfBytes || !sigDataURL || placedX === null) return;
    document.getElementById('progressOverlay').classList.add('show');
    document.getElementById('downloadBtn').classList.remove('show');
    clearStatus();

    try {
      const { PDFDocument } = PDFLib;
      const pdfDoc = await PDFDocument.load(pdfBytes);
      const pages  = pdfDoc.getPages();
      const page   = pages[currentPage - 1];
      const { width: pw, height: ph } = page.getSize();

      const base64 = sigDataURL.split(',')[1];
      const imgBytes = Uint8Array.from(atob(base64), c =&gt; c.charCodeAt(0));
      const pdfImg   = await pdfDoc.embedPng(imgBytes);
      const { width: iw, height: ih } = pdfImg.scale(1);

      const sigSize = parseInt(document.getElementById('sigSizeRange').value);
      const ratio   = ih ? iw / ih : 3;
      const sw = sigSize * ratio * (pw / (document.getElementById('pdfCanvas').width / 1.5));
      const sh = sigSize         * (ph / (document.getElementById('pdfCanvas').height / 1.5));

      const px = placedX * pw - sw / 2;
      const py = (1 - placedY) * ph - sh / 2;

      page.drawImage(pdfImg, { x: px, y: py, width: sw, height: sh });

      const outBytes = await pdfDoc.save();
      const blob = new Blob([outBytes], { type: 'application/pdf' });
      const url  = URL.createObjectURL(blob);

      const dl = document.getElementById('downloadBtn');
      dl.href = url; dl.classList.add('show');
      setStep(4);
      setStatus('✅ Tanda tangan berhasil ditambahkan! Klik tombol hijau untuk mengunduh PDF.', 'success');

    } catch(err) {
      console.error(err);
      setStatus('❌ Gagal memproses PDF. Pastikan file tidak terenkripsi/diproteksi.', 'error');
    } finally {
      document.getElementById('progressOverlay').classList.remove('show');
    }
  }

  function resetTool() {
    pdfBytes = null; pdfJsDoc = null;
    sigDataURL = null; placedX = null; placedY = null;
    currentPage = 1; totalPages = 1; strokes = [];
    document.getElementById('pdfFile').value = '';
    document.getElementById('fileInfo').classList.remove('show');
    document.getElementById('sigSection').classList.remove('show');
    document.getElementById('pdfPreviewSection').classList.remove('show');
    document.getElementById('btnApply').disabled = true;
    document.getElementById('downloadBtn').classList.remove('show');
    document.getElementById('placeHint').classList.add('show');
    clearStatus(); setStep(1);
    const sc = document.getElementById('sigCanvas');
    if (sc) sc.getContext('2d').clearRect(0,0,sc.width,sc.height);
    document.getElementById('sigPadLabel').style.opacity = '1';
    document.getElementById('sigPadWrap').classList.remove('has-sig');
    document.getElementById('typeInput').value = '';
    updateTypePreview();
  }

  function toggleFaq(btn) {
    const ans = btn.nextElementSibling;
    const isOpen = ans.classList.contains('open');
    document.querySelectorAll('.faq-a').forEach(a =&gt; a.classList.remove('open'));
    document.querySelectorAll('.faq-q').forEach(b =&gt; {
        b.classList.remove('open');
        b.setAttribute('aria-expanded', 'false');
    });
    if (!isOpen) { 
        ans.classList.add('open'); 
        btn.classList.add('open'); 
        btn.setAttribute('aria-expanded', 'true');
    }
  }

  // ── BOOTSTRAP: dijalankan setelah DOM siap DAN setelah pdf-lib / pdf.js
  // (yang dimuat dengan `defer`) selesai dieksekusi, supaya tidak terjadi
  // ReferenceError yang menghentikan seluruh script (penyebab tombol
  // upload/download "tidak berfungsi" sebelumnya).
  document.addEventListener('DOMContentLoaded', function () {
    if (typeof pdfjsLib !== 'undefined') {
      pdfjsLib.GlobalWorkerOptions.workerSrc =
        'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
    }

    const dropZone = document.getElementById('dropZone');
    const fileInput = document.getElementById('pdfFile');

    dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
    dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
    dropZone.addEventListener('drop', e =&gt; {
      e.preventDefault(); dropZone.classList.remove('drag-over');
      const f = e.dataTransfer.files[0];
      if (f &amp;&amp; f.type === 'application/pdf') loadFile(f);
      else setStatus('Harap pilih file dengan format PDF.', 'error');
    });
    fileInput.addEventListener('change', e =&gt; { if (e.target.files[0]) loadFile(e.target.files[0]); });
    document.getElementById('fileClear').addEventListener('click', resetTool);

    document.getElementById('pdfCanvas').addEventListener('click', function(e) {
      if (!sigDataURL) { setStatus('Gambar atau ketik tanda tangan Anda terlebih dahulu.', 'error'); return; }
      const rect = this.getBoundingClientRect();
      placedX = (e.clientX - rect.left) / rect.width;
      placedY = (e.clientY - rect.top)  / rect.height;
      document.getElementById('placeHint').style.display = 'none';
      renderCurrentPage();
      checkApplyReady();
      setStep(3);
    });
  });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/sign-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-7139994282504959187</guid><pubDate>Thu, 09 Apr 2026 21:52:24 +0000</pubDate><atom:updated>2026-07-23T00:41:05.409-07:00</atom:updated><title>Jpg to PDF </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;meta name="description" content="Konversi JPG ke PDF gratis, cepat, dan aman di browser Anda. Gabungkan banyak gambar JPG menjadi satu PDF berkualitas tinggi. Tidak ada upload ke server."&gt;
    &lt;meta name="keywords" content="jpg to pdf, konversi jpg ke pdf, ubah gambar ke pdf, jpg ke pdf online gratis, gabung foto jadi pdf"&gt;
    &lt;meta name="robots" content="index, follow"&gt;

    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/jpg-to-pdf.html"&gt;
    &lt;meta property="og:title" content="JPG to PDF Converter Online Gratis — Kholil Tools"&gt;
    &lt;meta property="og:description" content="Ubah satu atau banyak gambar JPG menjadi PDF profesional. Gratis, cepat, aman — tanpa upload ke server."&gt;
    &lt;meta property="og:image" content="https://i.ibb.co.com/wrhgDbpn/Screenshot-2026-07-23-14-35-01-795-com-android-chrome.jpg"&gt;
    &lt;meta property="og:type" content="website"&gt;

    &lt;title&gt;JPG to PDF Converter Online Gratis | Kholil Tools&lt;/title&gt;

    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;family=Merriweather:wght@700&amp;display=swap" rel="stylesheet"&gt;

    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "JPG to PDF Converter",
      "url": "https://www.kholil.biz.id/2026/04/jpg-to-pdf.html",
      "description": "Konversi file JPG ke PDF secara online, gratis, dan aman. Proses sepenuhnya di browser, tanpa upload ke server.",
      "applicationCategory": "UtilitiesApplication",
      "operatingSystem": "All",
      "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" },
      "author": {
        "@type": "Organization",
        "name": "Tim Kholil Tools",
        "url": "https://www.kholil.biz.id/"
      },
      "dateModified": "2026-07-23"
    }
    &lt;/script&gt;

    &lt;style&gt;
        :root {
            --primary: #7c3aed;
            --primary-dark: #5b21b6;
            --primary-light: #ede9fe;
            --accent: #10b981;
            --danger: #ef4444;
            --text: #1e1b4b;
            --text-muted: #6b7280;
            --border: #e5e7eb;
            --bg: #f8f7ff;
            --white: #ffffff;
            --shadow: 0 4px 24px rgba(124, 58, 237, 0.10);
            --radius: 14px;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; }

        body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); line-height: 1.7; }

        .sr-only {
            position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
            overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
        }
        .sr-only.skip-link:focus {
            position: fixed; top: 8px; left: 8px; width: auto; height: auto;
            padding: 10px 16px; background: var(--primary); color: #fff;
            border-radius: 8px; z-index: 1000; clip: auto;
        }

        /* ===== HEADER / NAV ===== */
        .site-header { background: var(--white); border-bottom: 1px solid var(--border); padding: 14px 24px; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 100; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
        .logo { font-family: 'Merriweather', serif; font-size: 1.25rem; color: var(--primary); text-decoration: none; display: flex; align-items: center; gap: 8px; }
        .logo span { font-size: 1.4rem; }

        nav { display: flex; align-items: center; flex-wrap: wrap; }
        nav a { color: var(--text-muted); text-decoration: none; font-size: 0.9rem; font-weight: 500; margin-left: 22px; transition: color 0.2s; }
        nav a:hover { color: var(--primary); }
        nav a.active { color: var(--primary); font-weight: 700; }

        .hamburger { display: none; background: none; border: none; font-size: 1.6rem; cursor: pointer; color: var(--primary); }

        /* ===== HERO ===== */
        .hero { text-align: center; padding: 60px 20px 40px; max-width: 800px; margin: 0 auto; }
        .hero-badge { display: inline-flex; align-items: center; gap: 6px; background: var(--primary-light); color: var(--primary); font-size: 0.82rem; font-weight: 600; padding: 5px 14px; border-radius: 50px; margin-bottom: 20px; letter-spacing: 0.03em; text-transform: uppercase; }
        h1 { font-family: 'Merriweather', serif; font-size: clamp(2rem, 5vw, 2.9rem); color: var(--text); line-height: 1.25; margin-bottom: 18px; }
        h1 .highlight { color: var(--primary); }
        .hero-desc { font-size: 1.1rem; color: var(--text-muted); max-width: 620px; margin: 0 auto 30px; }
        .trust-row { display: flex; justify-content: center; flex-wrap: wrap; gap: 18px; font-size: 0.88rem; color: var(--text-muted); }
        .trust-item { display: flex; align-items: center; gap: 5px; }
        .trust-item .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); display: inline-block; }

        /* ===== MAIN LAYOUT &amp; TOOL CARD ===== */
        .main-wrap { max-width: 1120px; margin: 0 auto; padding: 0 20px 60px; }
        .tool-card { background: var(--white); border-radius: 20px; box-shadow: var(--shadow); padding: 40px; margin-bottom: 40px; }

        .upload-zone { border: 2.5px dashed #c4b5fd; border-radius: var(--radius); padding: 55px 30px; text-align: center; background: var(--primary-light); transition: all 0.3s ease; cursor: pointer; position: relative; }
        .upload-zone.dragover { border-color: var(--primary); background: #ddd6fe; }
        .upload-icon { font-size: 3rem; margin-bottom: 14px; display: block; }
        .upload-zone h3 { font-size: 1.15rem; font-weight: 700; color: var(--primary-dark); margin-bottom: 8px; }
        .upload-zone p { color: var(--text-muted); font-size: 0.92rem; margin-bottom: 20px; }

        .btn-upload { display: inline-flex; align-items: center; gap: 8px; background: var(--primary); color: white; padding: 12px 28px; font-size: 0.95rem; font-weight: 600; border-radius: 50px; cursor: pointer; transition: all 0.25s; border: none; box-shadow: 0 4px 14px rgba(124, 58, 237, 0.3); }
        .btn-upload:hover { background: var(--primary-dark); transform: translateY(-2px); box-shadow: 0 6px 18px rgba(124, 58, 237, 0.4); }
        .btn-upload input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }

        .options-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin: 28px 0; }
        .option-group label { display: block; font-size: 0.82rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 6px; }
        .option-group select { width: 100%; padding: 10px 14px; border: 1.5px solid var(--border); border-radius: 9px; font-family: 'Inter', sans-serif; font-size: 0.93rem; color: var(--text); background: white; cursor: pointer; outline: none; transition: border-color 0.2s; }
        .option-group select:focus { border-color: var(--primary); }

        .file-list { display: none; margin: 24px 0; }
        .file-list-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
        .file-list-header h4 { font-size: 0.95rem; font-weight: 700; color: var(--text); }
        .btn-clear { font-size: 0.82rem; color: var(--danger); background: none; border: none; cursor: pointer; font-weight: 500; }
        .file-chips { display: flex; flex-wrap: wrap; gap: 10px; }
        .file-chip { display: flex; align-items: center; gap: 7px; background: var(--primary-light); color: var(--primary-dark); padding: 6px 12px; border-radius: 50px; font-size: 0.83rem; font-weight: 500; max-width: 200px; }
        .file-chip span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

        .progress-wrap { display: none; margin: 20px 0; }
        .progress-label { display: flex; justify-content: space-between; font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-bottom: 8px; }
        .progress-bar { height: 8px; background: var(--border); border-radius: 10px; overflow: hidden; }
        .progress-fill { height: 100%; background: linear-gradient(90deg, var(--primary), var(--accent)); border-radius: 10px; width: 0%; transition: width 0.3s ease; }

        .action-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin-top: 24px; }
        .btn-convert { display: inline-flex; align-items: center; gap: 9px; background: linear-gradient(135deg, var(--primary), var(--primary-dark)); color: white; padding: 14px 34px; font-size: 1rem; font-weight: 700; border-radius: 50px; border: none; cursor: pointer; box-shadow: 0 5px 18px rgba(124, 58, 237, 0.35); transition: all 0.25s; }
        .btn-convert:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(124, 58, 237, 0.45); }
        .btn-convert:disabled { background: #9ca3af; cursor: not-allowed; box-shadow: none; }
        .btn-download { display: none; align-items: center; gap: 9px; background: linear-gradient(135deg, var(--accent), #059669); color: white; padding: 14px 34px; font-size: 1rem; font-weight: 700; border-radius: 50px; text-decoration: none; box-shadow: 0 5px 18px rgba(16, 185, 129, 0.3); transition: all 0.25s; }
        .btn-download:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(16, 185, 129, 0.45); }

        .status-msg { margin-top: 16px; font-size: 0.93rem; font-weight: 600; min-height: 22px; }
        .status-msg.success { color: var(--accent); }
        .status-msg.error { color: var(--danger); }
        .status-msg.info { color: var(--primary); }

        .preview-section { display: none; margin-top: 30px; }
        .preview-section h4 { font-size: 0.88rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 14px; }
        .preview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
        .preview-item { border-radius: 10px; overflow: hidden; border: 2px solid var(--border); background: white; text-align: center; }
        .preview-item img { width: 100%; height: 110px; object-fit: cover; display: block; }
        .preview-item p { font-size: 0.75rem; color: var(--text-muted); padding: 5px 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

        /* ===== PROOF SECTION (GRID RESPONSIF) ===== */
        .proof-section { background: var(--white); border-radius: 20px; padding: 48px 40px; margin-bottom: 50px; box-shadow: var(--shadow); }
        .proof-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; margin-top: 30px; }
        .proof-card { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; text-align: center; }
        .proof-card img { width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--border); margin-bottom: 12px; display: block; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
        .proof-card h4 { font-size: 0.95rem; font-weight: 700; color: var(--text); margin-bottom: 4px; }
        .proof-card p { font-size: 0.84rem; color: var(--text-muted); }

        /* ===== FEATURES &amp; STEPS ===== */
        .section-label { font-size: 0.8rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--primary); margin-bottom: 8px; }
        .section-title { font-family: 'Merriweather', serif; font-size: clamp(1.5rem, 4vw, 2rem); color: var(--text); margin-bottom: 14px; line-height: 1.3; }
        .section-desc { color: var(--text-muted); font-size: 1rem; max-width: 580px; margin-bottom: 40px; }

        .features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 22px; margin-bottom: 60px; }
        .feat-card { background: var(--white); border-radius: var(--radius); padding: 28px 24px; box-shadow: 0 2px 16px rgba(0,0,0,0.06); border: 1px solid var(--border); transition: transform 0.2s, box-shadow 0.2s; }
        .feat-card:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(124,58,237,0.12); }
        .feat-icon { font-size: 2.2rem; margin-bottom: 14px; }
        .feat-card h3 { font-size: 1rem; font-weight: 700; margin-bottom: 8px; color: var(--text); }
        .feat-card p { font-size: 0.9rem; color: var(--text-muted); line-height: 1.6; }

        .steps-section { background: var(--white); border-radius: 20px; padding: 48px 40px; margin-bottom: 50px; box-shadow: var(--shadow); }
        .steps-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 30px; margin-top: 36px; }
        .step { display: flex; flex-direction: column; align-items: flex-start; }
        .step-num { width: 44px; height: 44px; border-radius: 50%; background: var(--primary-light); color: var(--primary); font-weight: 800; font-size: 1.1rem; display: flex; align-items: center; justify-content: center; margin-bottom: 14px; flex-shrink: 0; }
        .step h4 { font-size: 0.98rem; font-weight: 700; margin-bottom: 7px; color: var(--text); }
        .step p { font-size: 0.88rem; color: var(--text-muted); line-height: 1.6; }

        /* ===== FAQ ===== */
        .faq-section { margin-bottom: 60px; }
        .faq-list { display: flex; flex-direction: column; gap: 14px; margin-top: 36px; }
        .faq-item { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); overflow: hidden; }

        .faq-q { width: 100%; text-align: left; background: none; border: none; padding: 18px 22px; font-weight: 600; font-size: 0.97rem; cursor: pointer; display: flex; justify-content: space-between; align-items: center; color: var(--text); font-family: 'Inter', sans-serif; transition: background 0.2s; }
        .faq-q:hover { background: var(--primary-light); }
        .faq-q .arrow { font-size: 1.1rem; transition: transform 0.3s; color: var(--primary); flex-shrink: 0; }

        .faq-item.open .faq-q .arrow { transform: rotate(180deg); }
        .faq-a { max-height: 0; overflow: hidden; transition: max-height 0.35s ease, padding 0.3s; padding: 0 22px; font-size: 0.92rem; color: var(--text-muted); line-height: 1.7; }
        .faq-item.open .faq-a { max-height: 400px; padding: 0 22px 18px; }

        /* ===== ARTICLE / SEO CONTENT ===== */
        .article-section { background: var(--white); border-radius: 20px; padding: 48px 44px; margin-bottom: 60px; box-shadow: var(--shadow); line-height: 1.8; }

        .author-box { display: flex; align-items: center; gap: 15px; background: var(--bg); padding: 20px; border-radius: 12px; border: 1px solid var(--border); border-left: 4px solid var(--primary); margin-bottom: 30px; }
        .author-img { flex-shrink: 0; width: 50px; height: 50px; background: var(--primary); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; font-size: 1.05rem; }
        .author-meta strong { display: block; font-size: 1rem; color: var(--primary-dark); margin-bottom: 2px; }
        .author-meta span { font-size: 0.87rem; color: var(--text-muted); }

        .article-section h2 { font-family: 'Merriweather', serif; font-size: 1.5rem; color: var(--text); margin: 36px 0 14px; padding-top: 10px; border-top: 1px solid var(--border); }
        .article-section h2:first-of-type { border-top: none; padding-top: 0; }
        .article-section p { color: #374151; font-size: 0.97rem; margin-bottom: 14px; }
        .article-section ul, .article-section ol { margin-left: 22px; margin-bottom: 14px; color: #374151; font-size: 0.97rem; }
        .article-section li { margin-bottom: 8px; }
        .article-section strong { color: var(--text); }

        .info-box { background: var(--primary-light); border-left: 4px solid var(--primary); border-radius: 0 10px 10px 0; padding: 16px 20px; margin: 22px 0; font-size: 0.93rem; color: var(--primary-dark); }
        .info-box strong { display: block; margin-bottom: 4px; }

        .compare-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin: 20px 0 30px; }
        .compare-table th { background: var(--primary); color: white; padding: 12px 16px; text-align: left; font-weight: 600; }
        .compare-table th:first-child { border-radius: 10px 0 0 0; }
        .compare-table th:last-child { border-radius: 0 10px 0 0; }
        .compare-table td { padding: 11px 16px; border-bottom: 1px solid var(--border); color: #374151; }
        .compare-table tr:nth-child(even) td { background: var(--bg); }
        .compare-table .yes { color: var(--accent); font-weight: 600; }
        .compare-table .no { color: var(--danger); font-weight: 600; }

        /* ===== FOOTER ===== */
        footer { background: var(--white); border-top: 1px solid var(--border); text-align: center; padding: 40px 24px; font-size: 0.9rem; color: var(--text-muted); }
        footer .footer-logo { font-family: 'Merriweather', serif; font-size: 1.2rem; color: var(--primary); margin-bottom: 10px; }
        footer a { color: var(--primary); text-decoration: none; margin: 0 6px; }

        @media (max-width: 768px) {
            .hamburger { display: block; }
            nav { display: none; flex-direction: column; position: absolute; top: 60px; left: 0; right: 0; background: var(--white); padding: 20px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); align-items: flex-start; }
            nav.active { display: flex; }
            nav a { margin: 10px 0; font-size: 1rem; }
            .tool-card { padding: 24px 18px; }
            .steps-section, .article-section, .proof-section { padding: 30px 20px; }
            .options-row { grid-template-columns: 1fr; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;a class="sr-only skip-link" href="#main"&gt;Lewati ke konten utama&lt;/a&gt;

&lt;header class="site-header"&gt;
    &lt;a class="logo" href="https://www.kholil.biz.id/"&gt;&lt;span&gt;&#128196;&lt;/span&gt; Kholil Tools&lt;/a&gt;
    &lt;button class="hamburger" id="hamburgerBtn" aria-label="Buka navigasi menu" aria-expanded="false" aria-controls="mainNav"&gt;☰&lt;/button&gt;
    &lt;nav id="mainNav"&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/jpg-to-pdf.html" class="active"&gt;JPG ke PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-jpg.html"&gt;PDF ke JPG&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/edit-pdf.html"&gt;Edit PDF&lt;/a&gt;
    &lt;/nav&gt;
&lt;/header&gt;

&lt;div class="hero"&gt;
    &lt;div class="hero-badge"&gt;⚡ Gratis · Cepat · Tanpa Registrasi&lt;/div&gt;
    &lt;h1&gt;Ubah &lt;span class="highlight"&gt;JPG ke PDF&lt;/span&gt;&lt;br&gt;dalam Hitungan Detik&lt;/h1&gt;
    &lt;p class="hero-desc"&gt;
        Gabungkan satu atau banyak gambar JPG menjadi dokumen PDF berkualitas tinggi.
        Semua proses terjadi di browser Anda — file tidak pernah dikirim ke server manapun.
    &lt;/p&gt;
    &lt;div class="trust-row"&gt;
        &lt;span class="trust-item"&gt;&lt;span class="dot"&gt;&lt;/span&gt; 100% Gratis&lt;/span&gt;
        &lt;span class="trust-item"&gt;&lt;span class="dot"&gt;&lt;/span&gt; Privasi Terjamin&lt;/span&gt;
        &lt;span class="trust-item"&gt;&lt;span class="dot"&gt;&lt;/span&gt; Proses di Browser (Client-Side)&lt;/span&gt;
        &lt;span class="trust-item"&gt;&lt;span class="dot"&gt;&lt;/span&gt; Tanpa Watermark&lt;/span&gt;
        &lt;span class="trust-item"&gt;&lt;span class="dot"&gt;&lt;/span&gt; Mobile Friendly&lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;main class="main-wrap" id="main"&gt;

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

        &lt;div class="upload-zone" id="dropArea"&gt;
            &lt;span class="upload-icon"&gt;&#128444;️&lt;/span&gt;
            &lt;h3&gt;Seret &amp;amp; lepas file JPG di sini&lt;/h3&gt;
            &lt;p&gt;atau klik tombol di bawah untuk memilih file dari perangkat Anda&lt;br&gt;(JPG/JPEG — bisa banyak file sekaligus)&lt;/p&gt;
            &lt;label class="btn-upload"&gt;
                ＋ Pilih File JPG
                &lt;input type="file" id="jpgFiles" accept="image/jpeg,image/jpg" multiple aria-label="Pilih file JPG untuk dikonversi"&gt;
            &lt;/label&gt;
        &lt;/div&gt;

        &lt;div class="file-list" id="fileList"&gt;
            &lt;div class="file-list-header"&gt;
                &lt;h4 id="fileCount"&gt;0 file dipilih&lt;/h4&gt;
                &lt;button class="btn-clear" id="clearBtn"&gt;✕ Hapus Semua&lt;/button&gt;
            &lt;/div&gt;
            &lt;div class="file-chips" id="fileChips"&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="options-row"&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="pageSize"&gt;Ukuran Halaman&lt;/label&gt;
                &lt;select id="pageSize"&gt;
                    &lt;option value="a4"&gt;A4 (210 × 297 mm)&lt;/option&gt;
                    &lt;option value="letter"&gt;Letter (216 × 279 mm)&lt;/option&gt;
                    &lt;option value="a3"&gt;A3 (297 × 420 mm)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="orientation"&gt;Orientasi&lt;/label&gt;
                &lt;select id="orientation"&gt;
                    &lt;option value="portrait"&gt;Portrait (Tegak)&lt;/option&gt;
                    &lt;option value="landscape"&gt;Landscape (Mendatar)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="margin"&gt;Margin&lt;/label&gt;
                &lt;select id="margin"&gt;
                    &lt;option value="15"&gt;Normal (15 mm)&lt;/option&gt;
                    &lt;option value="0"&gt;Tanpa Margin&lt;/option&gt;
                    &lt;option value="25"&gt;Lebar (25 mm)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="outputName"&gt;Nama File Output&lt;/label&gt;
                &lt;select id="outputName"&gt;
                    &lt;option value="hasil-konversi"&gt;hasil-konversi.pdf&lt;/option&gt;
                    &lt;option value="dokumen-foto"&gt;dokumen-foto.pdf&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="progress-wrap" id="progressWrap"&gt;
            &lt;div class="progress-label"&gt;
                &lt;span id="progressLabel"&gt;Memproses...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar"&gt;
                &lt;div class="progress-fill" id="progressFill"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="action-row"&gt;
            &lt;button class="btn-convert" id="convertBtn" disabled&gt;&#128640; Convert ke PDF&lt;/button&gt;
            &lt;a class="btn-download" id="downloadBtn" href="#"&gt;⬇️ Download PDF&lt;/a&gt;
        &lt;/div&gt;

        &lt;div class="status-msg" id="statusMsg" aria-live="polite"&gt;&lt;/div&gt;

        &lt;div class="preview-section" id="previewSection"&gt;
            &lt;h4&gt;Preview Gambar (urutan halaman)&lt;/h4&gt;
            &lt;div class="preview-grid" id="previewGrid"&gt;&lt;/div&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;!-- ===== BUKTI PENDUKUNG SECTION ===== --&gt;
    &lt;div class="proof-section"&gt;
        &lt;div class="section-label"&gt;Dokumentasi Real&lt;/div&gt;
        &lt;h2 class="section-title"&gt;Bukti Hasil Penggunaan Alat&lt;/h2&gt;
        &lt;p class="section-desc"&gt;Berikut adalah tangkapan layar proses nyata saat menggunakan konverter JPG ke PDF.&lt;/p&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://i.ibb.co.com/wrhgDbpn/Screenshot-2026-07-23-14-35-01-795-com-android-chrome.jpg" alt="Upload Lancar" loading="lazy"&gt;
                &lt;h4&gt;1. Upload Lancar&lt;/h4&gt;
                &lt;p&gt;Antarmuka responsif saat memilih atau menyeret file gambar.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://i.ibb.co.com/9kJwG1Zc/Screenshot-2026-07-23-14-35-05-410-com-android-chrome.jpg" alt="Konversi Berhasil" loading="lazy"&gt;
                &lt;h4&gt;2. Konversi Berhasil&lt;/h4&gt;
                &lt;p&gt;Proses instan tanpa hambatan langsung di perangkat Anda.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://i.ibb.co.com/7tvn8fLd/Screenshot-2026-07-23-14-35-13-715-com-google-android-apps-docs.jpg" alt="Hasil Konversi" loading="lazy"&gt;
                &lt;h4&gt;3. Hasil Konversi PDF&lt;/h4&gt;
                &lt;p&gt;Dokumen PDF tertata rapi dan siap dibagikan atau dicetak.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="steps-section"&gt;
        &lt;div class="section-label"&gt;Panduan Penggunaan&lt;/div&gt;
        &lt;h2 class="section-title"&gt;Cara Menggunakan JPG to PDF Converter&lt;/h2&gt;
        &lt;div class="steps-grid"&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;1&lt;/div&gt;
                &lt;h4&gt;Pilih File JPG&lt;/h4&gt;
                &lt;p&gt;Klik tombol "Pilih File JPG" atau seret file langsung ke area upload. Anda bisa memilih banyak file sekaligus.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;2&lt;/div&gt;
                &lt;h4&gt;Sesuaikan Pengaturan&lt;/h4&gt;
                &lt;p&gt;Pilih ukuran halaman (A4 direkomendasikan untuk dokumen Indonesia), orientasi, dan margin sesuai kebutuhan.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;3&lt;/div&gt;
                &lt;h4&gt;Klik Convert&lt;/h4&gt;
                &lt;p&gt;Tekan tombol "Convert ke PDF". Proses berjalan sepenuhnya di perangkat Anda menggunakan JavaScript — tidak ada file yang dikirim ke internet.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="step"&gt;
                &lt;div class="step-num"&gt;4&lt;/div&gt;
                &lt;h4&gt;Download PDF&lt;/h4&gt;
                &lt;p&gt;Setelah progress bar selesai, klik tombol Download untuk menyimpan file PDF final ke perangkat Anda.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="section-label"&gt;Keunggulan&lt;/div&gt;
    &lt;h2 class="section-title"&gt;Mengapa Memilih Layanan Kami?&lt;/h2&gt;
    &lt;p class="section-desc"&gt;Dirancang untuk kecepatan, kemudahan, dan privasi penuh. Pemrosesan dilakukan tanpa antrean server.&lt;/p&gt;

    &lt;div class="features-grid"&gt;
        &lt;div class="feat-card"&gt;
            &lt;div class="feat-icon"&gt;&#128274;&lt;/div&gt;
            &lt;h3&gt;Privasi Mutlak&lt;/h3&gt;
            &lt;p&gt;Seluruh proses konversi terjadi di browser Anda menggunakan JavaScript (library jsPDF). File gambar tidak pernah dikirim ke server pihak ketiga.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class="feat-card"&gt;
            &lt;div class="feat-icon"&gt;&#128193;&lt;/div&gt;
            &lt;h3&gt;Banyak File Sekaligus&lt;/h3&gt;
            &lt;p&gt;Upload dan gabungkan puluhan foto JPG menjadi satu dokumen PDF yang terstruktur dengan urutan halaman yang tepat.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class="feat-card"&gt;
            &lt;div class="feat-icon"&gt;&#127899;️&lt;/div&gt;
            &lt;h3&gt;Pengaturan Fleksibel&lt;/h3&gt;
            &lt;p&gt;Pilih ukuran halaman A4, Letter, atau A3. Atur orientasi portrait/landscape dan margin sesuai kebutuhan cetak.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class="feat-card"&gt;
            &lt;div class="feat-icon"&gt;⚡&lt;/div&gt;
            &lt;h3&gt;Sangat Cepat&lt;/h3&gt;
            &lt;p&gt;Tanpa upload dan tanpa antrean server. Konversi belasan gambar JPG selesai dalam hitungan detik di perangkat Anda.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class="feat-card"&gt;
            &lt;div class="feat-icon"&gt;&#128241;&lt;/div&gt;
            &lt;h3&gt;Bekerja di Semua Perangkat&lt;/h3&gt;
            &lt;p&gt;Dioptimalkan untuk smartphone, tablet, dan komputer. Tidak perlu install aplikasi — cukup buka di browser.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class="feat-card"&gt;
            &lt;div class="feat-icon"&gt;&#127379;&lt;/div&gt;
            &lt;h3&gt;Tanpa Watermark&lt;/h3&gt;
            &lt;p&gt;Gunakan tanpa biaya registrasi, tanpa login, dan tanpa watermark pada hasil PDF.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="faq-section"&gt;
        &lt;div class="section-label"&gt;FAQ&lt;/div&gt;
        &lt;h2 class="section-title"&gt;Pertanyaan yang Sering Ditanyakan&lt;/h2&gt;
        &lt;div class="faq-list" id="faqList"&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Apakah file KTP/Ijazah JPG saya aman dari kebocoran data? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Sangat aman. Berbeda dengan layanan lain, tool kami tidak memiliki fitur unggah ke server jarak jauh — seluruh proses konversi berjalan sepenuhnya di browser Anda menggunakan JavaScript (library jsPDF). Anda bisa membuktikannya sendiri: putuskan koneksi internet setelah halaman ini selesai dimuat, tool tetap berfungsi normal.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Mengapa urutan foto di PDF saya terbalik? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Tool kami memproses gambar sesuai urutan saat Anda menyeleksinya di file explorer. Pastikan Anda memberi nama file berurutan (contoh: 01-halaman1.jpg, 02-halaman2.jpg) sebelum melakukan pemilihan banyak file sekaligus agar urutan halaman PDF sesuai harapan.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Apakah kualitas gambar berkurang setelah konversi? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Tidak signifikan. Gambar JPG disisipkan ke halaman PDF apa adanya, tanpa kompresi ulang — jadi ketajaman hasil akhir sepenuhnya bergantung pada kualitas foto asli Anda. Semakin baik resolusi dan pencahayaan foto sumber, semakin baik hasil PDF-nya.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Apakah bisa memperkecil ukuran PDF akhir? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Untuk saat ini tool kami fokus pada konversi berkualitas penuh tanpa kompresi tambahan, jadi jika foto sumber Anda beresolusi sangat tinggi (misalnya dari kamera 48MP), hasil PDF-nya bisa berukuran besar. Kami sedang menyiapkan tool Compress PDF terpisah untuk kebutuhan ini — untuk sementara, Anda bisa mengecilkan resolusi foto terlebih dahulu sebelum dikonversi.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Berapa banyak file JPG yang bisa dikonversi sekaligus? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Tidak ada batasan jumlah dari sisi kami. Anda bisa memilih 1 hingga puluhan file sekaligus. Karena semua proses berjalan di memori perangkat Anda (bukan server), performa akan tergantung spesifikasi HP/PC Anda — untuk performa optimal, kami sarankan maksimal 50 file dengan total ukuran di bawah 200 MB dalam satu kali proses.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Apakah tool ini bekerja di iPhone dan Android? &lt;span class="arrow" aria-hidden="true"&gt;▼&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Ya. Tool ini bekerja di browser modern manapun — Safari di iPhone, Chrome di Android, maupun browser bawaan Samsung — tanpa perlu instal aplikasi tambahan. Pastikan browser Anda sudah versi terbaru untuk pengalaman terbaik.&lt;/div&gt;
            &lt;/div&gt;

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

    &lt;div class="article-section"&gt;

        &lt;div class="author-box"&gt;
            &lt;div class="author-img"&gt;TT&lt;/div&gt;
            &lt;div class="author-meta"&gt;
                &lt;strong&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
                &lt;span&gt;Spesialis Konversi Dokumen &amp; Optimasi Gambar | Diperbarui: Juli 2026&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;h2&gt;Cara Mempertahankan Kualitas Dokumen Scan Saat Konversi&lt;/h2&gt;
        &lt;p&gt;Berdasarkan pengujian utilitas kami, kendala utama pengguna saat mengubah JPG ke PDF adalah hasil dokumen yang terlihat buram saat dicetak. Tool kami menyisipkan gambar JPG Anda apa adanya ke halaman PDF menggunakan library jsPDF, tanpa kompresi tambahan — sehingga ketajaman hasil akhir sepenuhnya mengikuti kualitas foto sumber. Untuk hasil pemindaian terbaik, ambil foto dokumen seperti Kartu Keluarga, KTP, atau laporan keuangan dengan pencahayaan ruangan yang terang dan tegak lurus terhadap kamera — ini mencegah blur akibat shutter speed rendah dan distorsi perspektif.&lt;/p&gt;
        &lt;p&gt;Karena seluruh proses berjalan di browser Anda (client-side), kami tidak perlu — dan tidak bisa — memangkas kualitas gambar untuk menghemat bandwidth server. Ini artinya kejernihan hasil PDF Anda 100% mencerminkan foto aslinya, bukan versi terkompresi dari sisi kami.&lt;/p&gt;

        &lt;div class="info-box"&gt;
            &lt;strong&gt;&#128161; Tahukah Anda?&lt;/strong&gt;
            PDF (Portable Document Format) dikembangkan Adobe pada 1993 dan kini menjadi standar ISO. Jika institusi meminta "satu file PDF", menggabungkan gambar langsung di Microsoft Word berisiko merusak tata letak saat dibuka di perangkat lain — mengonversi langsung ke PDF adalah cara yang lebih aman dan konsisten di semua sistem operasi.
        &lt;/div&gt;

        &lt;h2&gt;Memilih Ukuran Kertas yang Tepat: A4 vs Letter&lt;/h2&gt;
        &lt;p&gt;Banyak pengguna salah memilih format Letter karena itu menjadi default bawaan di beberapa aplikasi asal Amerika Serikat.&lt;/p&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;A4 (210 × 297 mm):&lt;/strong&gt; standar pencetakan resmi di Indonesia, Asia, dan Eropa. Wajib digunakan untuk berkas CPNS, pendaftaran universitas, atau lamaran kerja di Indonesia.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Letter (216 × 279 mm):&lt;/strong&gt; lebih lebar namun lebih pendek dari A4, umumnya dipakai di kawasan Amerika Utara.&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;Pilih A4 di dropdown menu kami untuk kebutuhan dokumen Indonesia. Jika dokumen akan dijilid atau membutuhkan ruang tepi, gunakan opsi margin "Normal" atau "Lebar".&lt;/p&gt;

        &lt;h2&gt;Kapan Sebaiknya Menggabungkan Foto Menjadi Satu PDF?&lt;/h2&gt;
        &lt;ol&gt;
            &lt;li&gt;&lt;strong&gt;Pendaftaran online&lt;/strong&gt; — formulir kuliah, beasiswa, atau lowongan kerja sering meminta dokumen dalam satu file PDF, bukan banyak file JPG terpisah.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Laporan lapangan&lt;/strong&gt; — teknisi atau surveyor yang mengambil banyak foto bisa langsung menggabungkannya menjadi laporan PDF yang rapi dan siap kirim.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Digitalisasi dokumen fisik&lt;/strong&gt; — foto scan dari buku, nota, atau kontrak yang difoto dengan HP dapat digabung menjadi arsip PDF yang mudah dicari dan disimpan jangka panjang.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Tugas dan makalah sekolah&lt;/strong&gt; — siswa/mahasiswa dengan tugas berupa tulisan tangan atau gambar bisa mengonversinya menjadi PDF untuk dikumpulkan lewat email atau LMS.&lt;/li&gt;
        &lt;/ol&gt;

        &lt;h2&gt;Perbandingan: Konversi Online vs Aplikasi Desktop vs Layanan Berbayar&lt;/h2&gt;
        &lt;table class="compare-table"&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th&gt;Fitur&lt;/th&gt;
                    &lt;th&gt;Kholil Tools (Online)&lt;/th&gt;
                    &lt;th&gt;Aplikasi Desktop&lt;/th&gt;
                    &lt;th&gt;Layanan Berbayar&lt;/th&gt;
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                &lt;tr&gt;&lt;td&gt;Biaya&lt;/td&gt;&lt;td class="yes"&gt;Gratis&lt;/td&gt;&lt;td&gt;Bervariasi&lt;/td&gt;&lt;td class="no"&gt;Berbayar&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Perlu Instalasi&lt;/td&gt;&lt;td class="yes"&gt;Tidak&lt;/td&gt;&lt;td class="no"&gt;Ya&lt;/td&gt;&lt;td class="yes"&gt;Tidak&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Bekerja di HP&lt;/td&gt;&lt;td class="yes"&gt;Ya&lt;/td&gt;&lt;td class="no"&gt;Terbatas&lt;/td&gt;&lt;td class="yes"&gt;Ya&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;File Diproses di Perangkat Sendiri&lt;/td&gt;&lt;td class="yes"&gt;Ya&lt;/td&gt;&lt;td class="yes"&gt;Ya&lt;/td&gt;&lt;td class="no"&gt;Upload ke server&lt;/td&gt;&lt;/tr&gt;
                &lt;tr&gt;&lt;td&gt;Tanpa Watermark&lt;/td&gt;&lt;td class="yes"&gt;Ya&lt;/td&gt;&lt;td&gt;Tergantung versi&lt;/td&gt;&lt;td class="no"&gt;Versi gratis ada WM&lt;/td&gt;&lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;

        &lt;h2&gt;Keamanan dan Privasi: Mengapa Ini Penting?&lt;/h2&gt;
        &lt;p&gt;Banyak tool konversi online mengharuskan Anda mengunggah file ke server mereka — menimbulkan pertanyaan: siapa yang mengakses file itu, dan berapa lama disimpan? Kholil Tools mengambil pendekatan berbeda: seluruh proses konversi menggunakan library jsPDF yang berjalan 100% di browser Anda. Ini bukan sekadar klaim — Anda bisa memverifikasinya sendiri dengan memutus koneksi internet setelah halaman ini dimuat; tool akan tetap berfungsi normal karena tidak ada komunikasi ke server manapun selama proses konversi.&lt;/p&gt;

    &lt;/div&gt;

&lt;/main&gt;

&lt;footer&gt;
    &lt;div class="footer-logo"&gt;&#128196; Kholil Tools&lt;/div&gt;
    &lt;p&gt;Utilitas manipulasi dokumen client-side tanpa pelacakan data privasi.&lt;/p&gt;
    &lt;p style="margin-top:10px;"&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/jpg-to-pdf.html"&gt;JPG ke PDF&lt;/a&gt; &amp;nbsp;·&amp;nbsp;
        &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-jpg.html"&gt;PDF ke JPG&lt;/a&gt; &amp;nbsp;·&amp;nbsp;
        &lt;a href="https://www.kholil.biz.id/2026/04/edit-pdf.html"&gt;Edit PDF&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style="margin-top:16px; font-size:0.82rem;"&gt;&amp;copy; 2026 Kholil Tools · Hak Cipta Dilindungi&lt;/p&gt;
&lt;/footer&gt;

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

&lt;script&gt;
    document.addEventListener("DOMContentLoaded", () =&gt; {

        // ===== HAMBURGER NAV (a11y: aria-expanded + klik di luar untuk tutup) =====
        const hamburgerBtn = document.getElementById('hamburgerBtn');
        const mainNav = document.getElementById('mainNav');

        hamburgerBtn.addEventListener('click', (e) =&gt; {
            e.stopPropagation();
            const isOpen = mainNav.classList.toggle('active');
            hamburgerBtn.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
        });
        document.addEventListener('click', (e) =&gt; {
            if (mainNav.classList.contains('active') &amp;&amp; !mainNav.contains(e.target) &amp;&amp; e.target !== hamburgerBtn) {
                mainNav.classList.remove('active');
                hamburgerBtn.setAttribute('aria-expanded', 'false');
            }
        });
        document.addEventListener('keydown', (e) =&gt; {
            if (e.key === 'Escape' &amp;&amp; mainNav.classList.contains('active')) {
                mainNav.classList.remove('active');
                hamburgerBtn.setAttribute('aria-expanded', 'false');
            }
        });

        // ===== DOM REFS =====
        const jpgFiles     = document.getElementById('jpgFiles');
        const dropArea     = document.getElementById('dropArea');
        const convertBtn   = document.getElementById('convertBtn');
        const downloadBtn  = document.getElementById('downloadBtn');
        const statusMsg    = document.getElementById('statusMsg');
        const fileList     = document.getElementById('fileList');
        const fileCount    = document.getElementById('fileCount');
        const fileChips    = document.getElementById('fileChips');
        const clearBtn     = document.getElementById('clearBtn');
        const progressWrap = document.getElementById('progressWrap');
        const progressFill = document.getElementById('progressFill');
        const progressLabel= document.getElementById('progressLabel');
        const progressPct  = document.getElementById('progressPct');
        const previewSection = document.getElementById('previewSection');
        const previewGrid  = document.getElementById('previewGrid');
        const pageSizeSel  = document.getElementById('pageSize');
        const orientSel    = document.getElementById('orientation');
        const marginSel    = document.getElementById('margin');
        const outputName   = document.getElementById('outputName');

        let selectedFiles = [];
        let pdfBlobUrl = null;

        dropArea.addEventListener('dragover', e =&gt; {
            e.preventDefault();
            dropArea.classList.add('dragover');
        });
        dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('dragover'));
        dropArea.addEventListener('drop', e =&gt; {
            e.preventDefault();
            dropArea.classList.remove('dragover');
            const files = Array.from(e.dataTransfer.files)
                .filter(f =&gt; f.type === 'image/jpeg' || f.type === 'image/jpg');
            if (files.length) handleFiles(files);
            else setStatus('File yang di-drop bukan JPG. Silakan pilih file berformat JPG/JPEG.', 'error');
        });

        jpgFiles.addEventListener('change', e =&gt; {
            if (e.target.files.length) handleFiles(Array.from(e.target.files));
        });

        clearBtn.addEventListener('click', () =&gt; {
            selectedFiles = [];
            renderFileList();
            previewSection.style.display = 'none';
            previewGrid.innerHTML = '';
            downloadBtn.style.display = 'none';
            convertBtn.disabled = true;
            setStatus('', '');
            jpgFiles.value = '';
            if (pdfBlobUrl) { URL.revokeObjectURL(pdfBlobUrl); pdfBlobUrl = null; }
        });

        function handleFiles(files) {
            selectedFiles = files;
            renderFileList();
            renderPreviews();
            convertBtn.disabled = false;
            downloadBtn.style.display = 'none';
            setStatus(`${files.length} file JPG siap dikonversi.`, 'info');
            if (pdfBlobUrl) { URL.revokeObjectURL(pdfBlobUrl); pdfBlobUrl = null; }
        }

        function renderFileList() {
            if (selectedFiles.length === 0) {
                fileList.style.display = 'none';
                return;
            }
            fileList.style.display = 'block';
            fileCount.textContent = `${selectedFiles.length} file dipilih`;
            fileChips.innerHTML = '';
            selectedFiles.forEach(f =&gt; {
                const chip = document.createElement('div');
                chip.className = 'file-chip';
                const kb = (f.size / 1024).toFixed(0);
                chip.innerHTML = `&#128444;️ &lt;span title="${f.name}"&gt;${f.name}&lt;/span&gt; &lt;small style="opacity:.6"&gt;${kb} KB&lt;/small&gt;`;
                fileChips.appendChild(chip);
            });
        }

        function renderPreviews() {
            previewGrid.innerHTML = '';
            previewSection.style.display = 'block';
            selectedFiles.forEach((file, i) =&gt; {
                const reader = new FileReader();
                reader.onload = e =&gt; {
                    const div = document.createElement('div');
                    div.className = 'preview-item';
                    div.innerHTML = `&lt;img src="${e.target.result}" width="140" height="110" alt="${file.name}"&gt;&lt;p&gt;Hal. ${i+1} · ${file.name}&lt;/p&gt;`;
                    previewGrid.appendChild(div);
                };
                reader.readAsDataURL(file);
            });
        }

        function setStatus(msg, type) {
            statusMsg.textContent = msg;
            statusMsg.className = 'status-msg ' + (type || '');
        }

        function setProgress(val, label) {
            progressFill.style.width = val + '%';
            progressPct.textContent = val + '%';
            progressLabel.textContent = label;
        }

        convertBtn.addEventListener('click', async () =&gt; {
            if (!selectedFiles.length) {
                setStatus('Pilih minimal satu file JPG terlebih dahulu.', 'error');
                return;
            }

            convertBtn.disabled = true;
            convertBtn.textContent = '⏳ Mengonversi...';
            downloadBtn.style.display = 'none';
            progressWrap.style.display = 'block';
            setProgress(0, 'Memulai konversi...');
            setStatus('', '');

            try {
                if (!window.jspdf) throw new Error("Modul jsPDF belum siap.");

                const { jsPDF } = window.jspdf;
                const margin = parseInt(marginSel.value);
                const pdf = new jsPDF({
                    orientation: orientSel.value,
                    unit: 'mm',
                    format: pageSizeSel.value
                });

                const pageW = pdf.internal.pageSize.getWidth();
                const pageH = pdf.internal.pageSize.getHeight();
                const maxW = pageW - 2 * margin;
                const maxH = pageH - 2 * margin;

                for (let i = 0; i &lt; selectedFiles.length; i++) {
                    const pct = Math.round(((i) / selectedFiles.length) * 90);
                    setProgress(pct, `Memproses gambar ${i + 1} dari ${selectedFiles.length}...`);

                    const file = selectedFiles[i];

                    const imgData = await new Promise(resolve =&gt; {
                        const r = new FileReader();
                        r.onload = e =&gt; resolve(e.target.result);
                        r.readAsDataURL(file);
                    });

                    const img = new Image();
                    await new Promise(resolve =&gt; { img.onload = resolve; img.src = imgData; });

                    const ratio = img.width / img.height;
                    let w = maxW;
                    let h = w / ratio;
                    if (h &gt; maxH) { h = maxH; w = h * ratio; }

                    const x = (pageW - w) / 2;
                    const y = (pageH - h) / 2;

                    if (i &gt; 0) pdf.addPage();
                    pdf.addImage(imgData, 'JPEG', x, y, w, h);
                }

                setProgress(95, 'Menyiapkan file untuk download...');

                const blob = pdf.output('blob');
                if (pdfBlobUrl) URL.revokeObjectURL(pdfBlobUrl);
                pdfBlobUrl = URL.createObjectURL(blob);

                const fname = outputName.value + '.pdf';
                downloadBtn.href = pdfBlobUrl;
                downloadBtn.download = fname;
                downloadBtn.style.display = 'inline-flex';

                setProgress(100, 'Selesai!');
                setStatus(`✅ Berhasil! ${selectedFiles.length} gambar dikonversi menjadi "${fname}".`, 'success');

                setTimeout(() =&gt; { progressWrap.style.display = 'none'; }, 1800);

            } catch (err) {
                console.error(err);
                setStatus('❌ Terjadi kesalahan saat mengonversi. Coba refresh halaman dan ulangi.', 'error');
                progressWrap.style.display = 'none';
            }

            convertBtn.disabled = false;
            convertBtn.textContent = '&#128640; Convert ke PDF';
        });

        document.querySelectorAll('.faq-q').forEach(q =&gt; {
            q.addEventListener('click', () =&gt; {
                const item = q.closest('.faq-item');
                const wasOpen = item.classList.contains('open');

                document.querySelectorAll('.faq-item').forEach(i =&gt; {
                    i.classList.remove('open');
                    i.querySelector('.faq-q').setAttribute('aria-expanded', 'false');
                });

                if (!wasOpen) {
                    item.classList.add('open');
                    q.setAttribute('aria-expanded', 'true');
                }
            });
        });
    });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/jpg-to-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-284859745599948987</guid><pubDate>Thu, 09 Apr 2026 21:51:00 +0000</pubDate><atom:updated>2026-07-23T01:14:46.443-07:00</atom:updated><title>Pdf to jpg</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;meta name="description" content="Konversi file PDF ke JPG secara gratis, cepat, dan aman langsung di browser Anda. Ubah setiap halaman PDF menjadi gambar berkualitas tinggi tanpa upload ke server."&gt;
    &lt;meta name="keywords" content="pdf to jpg, konversi pdf ke jpg, ubah pdf ke gambar, ekstrak gambar dari pdf, kholil tools"&gt;
    &lt;meta name="robots" content="index, follow"&gt;
    &lt;meta property="og:title" content="PDF to JPG Converter Online Gratis | Kholil Tools"&gt;
    &lt;meta property="og:description" content="Ubah halaman PDF menjadi gambar JPG berkualitas tinggi. 100% diproses di perangkat Anda, sangat aman."&gt;
    &lt;meta property="og:url" content="https://www.kholil.biz.id/2026/04/pdf-to-jpg.html"&gt;
    &lt;meta property="og:type" content="website"&gt;
    &lt;title&gt;PDF to JPG Converter Online Gratis (Aman &amp; Cepat) | Kholil Tools&lt;/title&gt;

    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema JSON-LD Kholil Tools --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "WebApplication",
      "name": "PDF to JPG Converter Online",
      "url": "https://www.kholil.biz.id/2026/04/pdf-to-jpg.html",
      "description": "Konversi file PDF ke JPG secara online, gratis, dan aman. Proses 100% menggunakan arsitektur client-side di browser tanpa unggah file ke cloud server.",
      "applicationCategory": "UtilitiesApplication",
      "operatingSystem": "All",
      "offers": { "@type": "Offer", "price": "0", "priceCurrency": "IDR" },
      "provider": {
        "@type": "Organization",
        "name": "Kholil Tools",
        "url": "https://www.kholil.biz.id"
      }
    }
    &lt;/script&gt;

    &lt;style&gt;
        :root {
            --primary: #598bf0;
            --primary-dark: #3b6cd4;
            --primary-light: #eef2ff;
            --accent: #f59e0b;
            --danger: #ef4444;
            
            --bg-dark: #0f172a; 
            --bg-card: #ffffff; 
            --bg-darker: #0b1121; 
            
            --text-light: #f8fafc; 
            --text-dark: #0f172a; 
            --text-muted: #475569; 
            --text-muted-light: #94a3b8; 
            
            --border: #e2e8f0;
            --border-dark: #1e293b;
            --shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
            --radius: 12px;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-dark);
            color: var(--text-light);
            line-height: 1.7;
        }

        /* HEADER &amp; NAVIGATION */
        .site-header {
            background: var(--bg-darker);
            border-bottom: 1px solid var(--border-dark);
            padding: 16px 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: sticky;
            top: 0;
            z-index: 100;
            flex-wrap: wrap;
        }
        .logo {
            font-size: 1.3rem;
            color: var(--text-light);
            text-decoration: none;
            display: flex;
            align-items: center;
            gap: 10px;
            font-weight: 800;
            letter-spacing: -0.02em;
        }
        .logo-icon {
            background: var(--text-light);
            color: var(--bg-dark);
            width: 24px; height: 24px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border-radius: 4px;
            font-size: 0.8rem;
        }
        .nav-links {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            margin-top: 8px;
        }
        .nav-links a {
            color: var(--text-muted-light);
            text-decoration: none;
            font-size: 0.9rem;
            font-weight: 500;
            transition: color 0.2s;
        }
        .nav-links a:hover, .nav-links a.active { color: var(--text-light); font-weight: 700; }

        @media (min-width: 900px) { .nav-links { margin-top: 0; } }

        /* HERO SECTION */
        .hero { text-align: center; padding: 60px 20px 40px; max-width: 800px; margin: 0 auto; }
        .hero-badge {
            display: inline-flex; align-items: center; gap: 6px; 
            background: rgba(89, 139, 240, 0.15); border: 1px solid rgba(89, 139, 240, 0.3);
            color: var(--primary); font-size: 0.75rem; font-weight: 700; padding: 6px 16px;
            border-radius: 50px; margin-bottom: 24px; letter-spacing: 0.05em; text-transform: uppercase;
        }
        h1 { font-size: clamp(2rem, 5vw, 3rem); line-height: 1.2; margin-bottom: 18px; font-weight: 800; }
        h1 .highlight { color: var(--primary); }
        .hero-desc { font-size: 1.05rem; color: var(--text-muted-light); max-width: 620px; margin: 0 auto 30px; }

        /* MAIN WRAPPER */
        .main-wrap { max-width: 1000px; margin: 0 auto; padding: 0 20px 60px; }

        /* KARTU ALAT (TOOL UI) */
        .tool-card { 
            background: var(--bg-card); 
            color: var(--text-dark);
            border-radius: 20px; 
            box-shadow: var(--shadow); 
            padding: 40px; 
            margin-bottom: 40px; 
        }
        
        .upload-zone {
            border: 2px dashed #cbd5e1; border-radius: var(--radius); padding: 60px 30px;
            text-align: center; background: #f8fafc; transition: all 0.2s ease; cursor: pointer;
        }
        .upload-zone:hover, .upload-zone.dragover { background: var(--primary-light); border-color: var(--primary); }
        .upload-icon { font-size: 3.5rem; margin-bottom: 14px; display: block; }
        .upload-zone h3 { font-size: 1.15rem; font-weight: 700; margin-bottom: 8px; }
        .upload-zone p { color: var(--text-muted); font-size: 0.92rem; margin-bottom: 24px; }
        .btn-upload {
            display: inline-flex; align-items: center; gap: 8px; background: var(--primary);
            color: white; padding: 14px 32px; font-size: 0.95rem; font-weight: 600; border-radius: 8px;
            cursor: pointer; transition: background 0.2s; border: none;
        }
        .btn-upload:hover { background: var(--primary-dark); }
        .btn-upload input { display: none; }

        .file-info { display: none; margin: 20px 0; background: var(--primary-light); border: 1px solid #dbeafe; border-radius: 8px; padding: 14px 18px; align-items: center; justify-content: space-between; }
        .file-info .fname { font-weight: 600; font-size: 0.93rem; color: var(--text-dark); display: flex; align-items: center; gap: 8px; }
        .btn-clear { font-size: 0.85rem; color: var(--danger); background: none; border: none; cursor: pointer; font-weight: 600; }

        /* OPTIONS */
        .options-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin: 24px 0; }
        .option-group label { display: block; font-size: 0.8rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase; margin-bottom: 8px; }
        .option-group select, .option-group input { 
            width: 100%; padding: 12px 14px; border: 1px solid var(--border); 
            border-radius: 8px; font-family: 'Inter', sans-serif; font-size: 0.95rem; 
            color: var(--text-dark); background: white; outline: none; 
        }
        .option-group select:focus, .option-group input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(89, 139, 240, 0.15); }

        /* PROGRESS &amp; ACTIONS */
        .progress-wrap { display: none; margin: 20px 0; }
        .progress-label { display: flex; justify-content: space-between; font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-bottom: 8px; }
        .progress-bar { height: 8px; background: #e2e8f0; border-radius: 10px; overflow: hidden; }
        .progress-fill { height: 100%; background: var(--primary); width: 0%; transition: width 0.3s ease; }
        
        .action-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin-top: 24px; }
        .btn-convert {
            background: var(--primary); color: white; padding: 14px 34px; font-size: 1rem; font-weight: 600; 
            border-radius: 8px; border: none; cursor: pointer; transition: background 0.2s;
        }
        .btn-convert:hover:not(:disabled) { background: var(--primary-dark); }
        .btn-convert:disabled { background: #9ca3af; cursor: not-allowed; opacity: 0.7; }
        .btn-download { display: none; background: #10b981; color: white; padding: 14px 34px; font-size: 1rem; font-weight: 600; border-radius: 8px; text-decoration: none; }
        .btn-download:hover { background: #059669; }

        .status-msg { margin-top: 16px; font-size: 0.95rem; font-weight: 600; min-height: 22px; }
        .status-msg.success { color: #10b981; }
        .status-msg.error { color: var(--danger); }
        .status-msg.info { color: var(--primary); }

        /* PREVIEW AREA */
        .preview-section { display: none; margin-top: 30px; border-top: 1px solid var(--border); padding-top: 24px; }
        .preview-section h4 { font-size: 0.9rem; font-weight: 700; color: var(--text-dark); margin-bottom: 16px; }
        .preview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; }
        .preview-item { border-radius: 8px; overflow: hidden; border: 1px solid var(--border); background: #f8fafc; text-align: center; }
        .preview-item img { width: 100%; height: 140px; object-fit: cover; border-bottom: 1px solid var(--border); }
        .preview-item-footer { padding: 10px; display: flex; flex-direction: column; gap: 8px; align-items: center; }
        .preview-item-footer span { font-size: 0.8rem; font-weight: 600; color: var(--text-muted); }
        .btn-dl-single { font-size: 0.75rem; color: var(--primary); font-weight: 600; text-decoration: none; background: var(--primary-light); padding: 5px 14px; border-radius: 4px; }

        /* PROOF / BUKTI GRID SECTION */
        .proof-section { margin-bottom: 40px; background: var(--bg-darker); border-radius: 16px; padding: 30px 24px; border: 1px solid var(--border-dark); }
        .proof-title { text-align: center; font-size: 1.15rem; font-weight: 800; color: var(--text-light); margin-bottom: 24px; }
        .proof-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; }
        .proof-card { background: var(--bg-card); padding: 12px; border-radius: 12px; border: 1px solid var(--border); display: flex; flex-direction: column; }
        .proof-card img { width: 100%; border-radius: 8px; border: 1px solid #e2e8f0; margin-bottom: 14px; flex-grow: 1; object-fit: cover; }
        .proof-desc { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 0.9rem; color: var(--text-dark); }
        .step-badge { background: #10b981; color: white; width: 26px; height: 26px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 0.8rem; flex-shrink: 0; }

        /* E-E-A-T AUTHOR BOX */
        .author-box {
            display: flex; align-items: center; gap: 16px; 
            background: #111a33; border: 1px solid var(--border-dark); 
            padding: 20px 24px; border-radius: 12px; margin-bottom: 40px;
        }
        .author-avatar { 
            width: 45px; height: 45px; background: var(--accent); color: var(--bg-dark); 
            border-radius: 50%; display: flex; align-items: center; justify-content: center; 
            font-size: 1.2rem; font-weight: 800; flex-shrink: 0; 
        }
        .author-meta strong { display: block; font-size: 1.05rem; color: var(--text-light); margin-bottom: 4px; }
        .author-meta span { font-size: 0.85rem; color: var(--text-muted-light); }

        /* ARTICLES &amp; CONTENT */
        .article-section { 
            background: var(--bg-card); color: var(--text-dark);
            border-radius: 20px; padding: 48px 40px; margin-bottom: 40px; line-height: 1.8; 
        }
        .section-label { font-size: 0.75rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--primary); margin-bottom: 12px; display: block; }
        .article-section h2 { font-size: 1.4rem; font-weight: 700; margin: 36px 0 16px; border-top: 1px solid var(--border); padding-top: 24px; }
        .article-section h2:first-of-type { margin-top: 0; border-top: none; padding-top: 0; }
        .article-section p { font-size: 1rem; margin-bottom: 16px; color: var(--text-muted); }
        .article-section ul, .article-section ol { margin-left: 24px; margin-bottom: 16px; color: var(--text-muted); }
        .article-section li { margin-bottom: 10px; }
        
        .info-box { background: var(--primary-light); border-left: 4px solid var(--primary); padding: 18px 22px; margin: 24px 0; border-radius: 0 8px 8px 0; color: var(--primary-dark); }
        .info-box strong { font-size: 1.05rem; display: block; margin-bottom: 6px; }

        /* FAQ ACCORDION */
        .faq-section { margin-bottom: 60px; }
        .faq-title { text-align: center; font-size: 1.5rem; font-weight: 800; margin-bottom: 24px; }
        .faq-list { display: flex; flex-direction: column; gap: 12px; }
        .faq-item { background: var(--bg-card); border-radius: 12px; overflow: hidden; }
        .faq-q { padding: 18px 24px; font-weight: 600; font-size: 1rem; cursor: pointer; display: flex; justify-content: space-between; align-items: center; color: var(--text-dark); }
        .faq-q:hover { background: #f8fafc; }
        .faq-q .arrow { transition: transform 0.3s; color: var(--primary); font-size: 0.8rem; }
        .faq-item.open .faq-q .arrow { transform: rotate(180deg); }
        .faq-a { max-height: 0; overflow: hidden; transition: all 0.35s ease; padding: 0 24px; color: var(--text-muted); font-size: 0.95rem; }
        .faq-item.open .faq-a { max-height: 400px; padding: 0 24px 24px; }

        /* FOOTER */
        footer { background: var(--bg-darker); border-top: 1px solid var(--border-dark); text-align: center; padding: 40px 24px; font-size: 0.9rem; color: var(--text-muted-light); }
        .footer-logo { font-size: 1.2rem; color: var(--text-light); font-weight: 800; margin-bottom: 12px; display: block; }
        .footer-links { margin: 20px 0; display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; }
        .footer-links a { color: var(--text-muted-light); text-decoration: none; font-weight: 500; transition: color 0.2s; }
        .footer-links a:hover { color: var(--text-light); }

        @media (max-width: 720px) {
            .tool-card, .article-section { padding: 24px 20px; }
            .options-row { grid-template-columns: 1fr; }
            .nav-links { display: none; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- HEADER MENGURANGI LINK SESUAI PERMINTAAN --&gt;
&lt;header class="site-header"&gt;
    &lt;a class="logo" href="https://www.kholil.biz.id/"&gt;
        &lt;span class="logo-icon"&gt;K&lt;/span&gt; Kholil Tools
    &lt;/a&gt;
    &lt;nav class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/edit-pdf.html"&gt;Edit PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/excel-to-pdf.html"&gt;Excel ke PDF&lt;/a&gt;
    &lt;/nav&gt;
&lt;/header&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-badge"&gt;♦ Arsitektur Keamanan Client-Side&lt;/div&gt;
    &lt;h1&gt;Konversi &lt;span class="highlight"&gt;PDF ke JPG&lt;/span&gt; Resolusi Tinggi&lt;/h1&gt;
    &lt;p class="hero-desc"&gt;
        Ekstrak setiap halaman PDF menjadi gambar (JPG) berkualitas tajam. Pemrosesan dilakukan murni secara visual di peramban Anda demi kerahasiaan data yang terjamin.
    &lt;/p&gt;
&lt;/div&gt;

&lt;div class="main-wrap"&gt;

    &lt;!-- TOOL UI CARD --&gt;
    &lt;div class="tool-card"&gt;
        
        &lt;div class="upload-zone" id="dropArea" aria-label="Area unggah dokumen PDF"&gt;
            &lt;span class="upload-icon"&gt;&#128193;&lt;/span&gt;
            &lt;h3&gt;Seret &amp;amp; lepas file PDF ke sini&lt;/h3&gt;
            &lt;p&gt;Atau klik tombol di bawah untuk memilih file dari perangkat Anda&lt;/p&gt;
            &lt;label class="btn-upload" aria-label="Klik untuk memilih file PDF"&gt;
                Pilih File PDF
                &lt;input type="file" id="pdfFile" accept="application/pdf"&gt;
            &lt;/label&gt;
        &lt;/div&gt;

        &lt;div class="file-info" id="fileInfo"&gt;
            &lt;div class="fname" id="fnameTxt"&gt;&#128196; —&lt;/div&gt;
            &lt;button class="btn-clear" id="clearBtn" aria-label="Hapus file dan mulai ulang"&gt;Hapus&lt;/button&gt;
        &lt;/div&gt;

        &lt;div class="options-row"&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="quality"&gt;Kualitas JPG&lt;/label&gt;
                &lt;select id="quality"&gt;
                    &lt;option value="0.95"&gt;Tinggi / 95% (Rekomendasi)&lt;/option&gt;
                    &lt;option value="1.00"&gt;Maksimal / 100% (File Besar)&lt;/option&gt;
                    &lt;option value="0.80"&gt;Sedang / 80% (Irit Memori)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="scale"&gt;Skala Ketajaman&lt;/label&gt;
                &lt;select id="scale"&gt;
                    &lt;option value="2.0"&gt;2x — Kualitas Tajam&lt;/option&gt;
                    &lt;option value="1.5"&gt;1.5x — Standar Layar&lt;/option&gt;
                    &lt;option value="3.0"&gt;3x — Cetak Resolusi Tinggi&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="dlFormat"&gt;Format Hasil&lt;/label&gt;
                &lt;select id="dlFormat"&gt;
                    &lt;option value="zip"&gt;Arsip ZIP (Semua Halaman)&lt;/option&gt;
                    &lt;option value="single"&gt;Manual (Unduh per Gambar)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="option-group"&gt;
                &lt;label for="pageRange"&gt;Halaman Diproses&lt;/label&gt;
                &lt;select id="pageRange"&gt;
                    &lt;option value="all"&gt;Semua Halaman PDF&lt;/option&gt;
                    &lt;option value="first"&gt;Hanya Halaman 1&lt;/option&gt;
                    &lt;option value="custom"&gt;Pilih Rentang Spesifik...&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div id="customRangeWrap" style="display:none; margin-bottom:20px;"&gt;
            &lt;label for="customRange" style="font-size:0.8rem; font-weight:700; color:var(--text-muted); display:block; margin-bottom:8px;"&gt;Masukkan Nomor (Contoh: 1,3,5-8)&lt;/label&gt;
            &lt;input type="text" id="customRange" placeholder="Ketik halaman yang ingin dikonversi..." style="width:100%; padding:12px 14px; border:1px solid var(--border); border-radius:8px;"&gt;
        &lt;/div&gt;

        &lt;div class="progress-wrap" id="progressWrap" aria-live="polite"&gt;
            &lt;div class="progress-label"&gt;
                &lt;span id="progressLabel"&gt;Memproses dokumen...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" id="progressBarAria"&gt;
                &lt;div class="progress-fill" id="progressFill"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="action-row"&gt;
            &lt;button class="btn-convert" id="convertBtn" disabled aria-label="Mulai Proses Konversi"&gt;Terapkan &amp; Simpan JPG&lt;/button&gt;
            &lt;a class="btn-download" id="downloadBtn" href="#" aria-label="Unduh Semua dalam ZIP"&gt;Download Semua ZIP&lt;/a&gt;
        &lt;/div&gt;

        &lt;div class="status-msg" id="statusMsg" aria-live="assertive"&gt;&lt;/div&gt;

        &lt;div class="preview-section" id="previewSection"&gt;
            &lt;h4 id="previewTitle"&gt;Preview Gambar Hasil&lt;/h4&gt;
            &lt;div class="preview-grid" id="previewGrid"&gt;&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- RESPONSIVE IMAGE GRID SECTION (BUKTI TOOL) --&gt;
    &lt;div class="proof-section"&gt;
        &lt;h3 class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/h3&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://i.ibb.co.com/Wr5Dg8n/Screenshot-2026-07-23-14-49-00-300-com-android-chrome.jpg" alt="Upload file PDF tanpa kendala" loading="lazy"&gt;
                &lt;div class="proof-desc"&gt;&lt;span class="step-badge"&gt;1&lt;/span&gt; Upload tanpa kendala&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://i.ibb.co.com/9k5R5whN/Screenshot-2026-07-23-14-53-05-241-com-android-chrome.jpg" alt="Preview konversi dan unduh sukses" loading="lazy"&gt;
                &lt;div class="proof-desc"&gt;&lt;span class="step-badge"&gt;2&lt;/span&gt; Preview dan unduh sukses&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="proof-card"&gt;
                &lt;img src="https://i.ibb.co.com/Nn3dcsFW/Screenshot-2026-07-23-14-53-38-834-com-mi-android-global-Fileexplorer.jpg" alt="Hasil unduhan JPG yang tersimpan" loading="lazy"&gt;
                &lt;div class="proof-desc"&gt;&lt;span class="step-badge"&gt;3&lt;/span&gt; Hasil kualitas tinggi&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- EEAT AUTHOR BOX --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-avatar"&gt;K&lt;/div&gt;
        &lt;div class="author-meta"&gt;
            &lt;strong&gt;Ditulis oleh Tim Kholil Tools&lt;/strong&gt;
            &lt;span&gt;Spesialis Kriptografi Berkas &amp; Keamanan Digital | Diperbarui: Juli 2026&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- ARTIKEL PENDUKUNG --&gt;
    &lt;div class="article-section"&gt;
        &lt;span class="section-label"&gt;Panduan &amp; Keamanan&lt;/span&gt;
        &lt;h2 style="margin-top:0; border-top:none; padding-top:0;"&gt;Legalitas &amp; Alur Kerja Ekstraksi Gambar Kholil Tools&lt;/h2&gt;
        &lt;p&gt;Penerapan ekstraksi dokumen digital portabel (PDF) yang berisi data sensitif harus mematuhi kaidah keamanan yang ketat. Berbeda dari kebanyakan platform komersial yang mengunggah dokumen Anda ke cloud server mereka, utilitas &lt;strong&gt;Kholil Tools&lt;/strong&gt; menyediakan media render visual mandiri berbasis peramban yang terisolasi dari pihak ketiga.&lt;/p&gt;
        
        &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;Murni Pemrosesan Client-Side:&lt;/strong&gt; Dokumen PDF Anda diurai oleh mesin JavaScript browser secara lokal. Berkas tidak pernah menyeberangi jaringan internet eksternal.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Pembersihan Memori Otomatis:&lt;/strong&gt; Setelah sesi ditutup, seluruh cache memori hasil render gambar dibersihkan dari RAM perangkat Anda secara permanen.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Kinerja Berbasis Perangkat:&lt;/strong&gt; Kecepatan pemrosesan didasarkan pada kapabilitas CPU lokal Anda, mengeliminasi latensi unduh-unggah jaringan (upload/download latency).&lt;/li&gt;
        &lt;/ul&gt;

        &lt;div class="info-box"&gt;
            &lt;strong&gt;&#128161; Resolusi Sempurna untuk Kebutuhan Digital&lt;/strong&gt;
            Pemanfaatan modul perender internal kami memastikan vektor teks dikalkulasi ulang pada rasio piksel tinggi (hingga 3x Scale), memastikan teks dalam format JPG tetap tajam ketika dibagikan kembali di grup maupun lampiran kerja.
        &lt;/div&gt;

        &lt;h2&gt;Cara Mengubah PDF Menjadi JPG Resolusi Tinggi Secara Gratis&lt;/h2&gt;
        &lt;ol&gt;
            &lt;li&gt;Siapkan dan unggah file PDF yang ingin diekstrak halamannya menggunakan &lt;strong&gt;Area Unggah&lt;/strong&gt;.&lt;/li&gt;
            &lt;li&gt;Pada pengaturan &lt;strong&gt;Skala Ketajaman&lt;/strong&gt;, rasio standar &lt;strong&gt;2x&lt;/strong&gt; sangat optimal untuk memberikan hasil resolusi tingkat HD (High Definition).&lt;/li&gt;
            &lt;li&gt;Apabila dokumen memiliki ketebalan ekstrem, kami sangat menyarankan untuk menggunakan menu &lt;strong&gt;Halaman Diproses&lt;/strong&gt; secara selektif demi menjaga suhu performa laptop/ponsel Anda.&lt;/li&gt;
            &lt;li&gt;Klik tombol biru &lt;strong&gt;Terapkan &amp; Simpan JPG&lt;/strong&gt;. Tunggu persentase mencapai 100%, lalu unduh arsip bundel &lt;strong&gt;.zip&lt;/strong&gt; untuk mendapatkan seluruh gambar secara instan.&lt;/li&gt;
        &lt;/ol&gt;
    &lt;/div&gt;

    &lt;!-- FAQ --&gt;
    &lt;div class="faq-section"&gt;
        &lt;h2 class="faq-title"&gt;Pusat Bantuan &amp; Pertanyaan&lt;/h2&gt;
        &lt;div class="faq-list"&gt;
            &lt;div class="faq-item"&gt;
                &lt;div class="faq-q"&gt;Mengapa proses rendering agak lambat pada dokumen PDF tebal? &lt;span class="arrow"&gt;▼&lt;/span&gt;&lt;/div&gt;
                &lt;div class="faq-a"&gt;Karena alat Kholil Tools tidak menggunakan server perantara demi menjamin privasi absolut. Konversi dikerjakan langsung menggunakan CPU perangkat Anda. Dokumen di atas 100 lembar akan memerlukan alokasi RAM yang cukup besar, sehingga disarankan memprosesnya melalui opsi rentang halaman (contoh: 1-50 terlebih dahulu).&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;div class="faq-q"&gt;Apakah hasil JPG aman untuk langsung dicetak (Print)? &lt;span class="arrow"&gt;▼&lt;/span&gt;&lt;/div&gt;
                &lt;div class="faq-a"&gt;Sangat aman. Jika tujuan akhir adalah cetak fisik (printer dokumen/foto), harap ganti Opsi &lt;strong&gt;Skala Ketajaman&lt;/strong&gt; menjadi &lt;strong&gt;3x - Cetak Resolusi Tinggi&lt;/strong&gt;. Hasil gambar akan memenuhi densitas 300 DPI (Dots Per Inch) yang anti-pecah.&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="faq-item"&gt;
                &lt;div class="faq-q"&gt;Apakah bisa memproses dokumen PDF yang terkunci kata sandi? &lt;span class="arrow"&gt;▼&lt;/span&gt;&lt;/div&gt;
                &lt;div class="faq-a"&gt;Algoritma keamanan client-side kami tidak akan menembus PDF yang dilindungi password enkripsi. Anda harus melepaskan kata sandinya secara manual di komputer Anda terlebih dahulu sebelum mengunggahnya ke portal ini.&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- FOOTER KHOLIL TOOLS --&gt;
&lt;footer&gt;
    &lt;span class="footer-logo"&gt;Kholil Tools&lt;/span&gt;
    &lt;p&gt;Utilitas Manajemen Dokumen Aman, Privasi Terjamin Lokal.&lt;/p&gt;
    
    &lt;div class="footer-links"&gt;
        &lt;a href="https://www.kholil.biz.id/p/about.html"&gt;About US&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/contact.html"&gt;Contact US&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Privacy Policy&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/terms.html"&gt;Terms&lt;/a&gt;
    &lt;/div&gt;
    
    &lt;p style="font-size:0.85rem; margin-top:20px; color:#64748b;"&gt;&amp;copy; 2026 kholil.biz.id · Seluruh Hak Cipta Dilindungi.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- SCRIPT DI-DEFER UNTUK PERFORMA --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js" defer&gt;&lt;/script&gt;

&lt;script&gt;
window.addEventListener('DOMContentLoaded', () =&gt; {
    
    // PDF.js worker
    if(typeof pdfjsLib !== 'undefined') {
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
    }

    const pdfFileInput = document.getElementById('pdfFile');
    const dropArea = document.getElementById('dropArea');
    const convertBtn = document.getElementById('convertBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const statusMsg = document.getElementById('statusMsg');
    const fileInfoEl = document.getElementById('fileInfo');
    const fnameTxt = document.getElementById('fnameTxt');
    const clearBtn = document.getElementById('clearBtn');
    
    const progressWrap = document.getElementById('progressWrap');
    const progressFill = document.getElementById('progressFill');
    const progressLabel = document.getElementById('progressLabel');
    const progressPct = document.getElementById('progressPct');
    const progressBarAria = document.getElementById('progressBarAria');
    
    const previewSection = document.getElementById('previewSection');
    const previewGrid = document.getElementById('previewGrid');
    const previewTitle = document.getElementById('previewTitle');
    
    const qualitySel = document.getElementById('quality');
    const scaleSel = document.getElementById('scale');
    const dlFormatSel = document.getElementById('dlFormat');
    const pageRangeSel = document.getElementById('pageRange');
    const customRangeWrap = document.getElementById('customRangeWrap');
    const customRangeIn = document.getElementById('customRange');

    let selectedFile = null;
    let zipBlobUrl = null;
    let generatedBlobsUrls = [];

    // Custom range
    pageRangeSel.addEventListener('change', () =&gt; {
        customRangeWrap.style.display = pageRangeSel.value === 'custom' ? 'block' : 'none';
    });

    // Drag and Drop
    dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('dragover'); });
    dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('dragover'));
    dropArea.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropArea.classList.remove('dragover');
        if (e.dataTransfer.files[0]) handleFile(e.dataTransfer.files[0]);
    });

    pdfFileInput.addEventListener('change', e =&gt; {
        if (e.target.files[0]) handleFile(e.target.files[0]);
    });

    function clearMemory() {
        if (zipBlobUrl) { URL.revokeObjectURL(zipBlobUrl); zipBlobUrl = null; }
        generatedBlobsUrls.forEach(url =&gt; URL.revokeObjectURL(url));
        generatedBlobsUrls = [];
    }

    clearBtn.addEventListener('click', () =&gt; {
        selectedFile = null;
        fileInfoEl.style.display = 'none';
        convertBtn.disabled = true;
        downloadBtn.style.display = 'none';
        previewSection.style.display = 'none';
        previewGrid.innerHTML = '';
        setStatus('', '');
        pdfFileInput.value = '';
        clearMemory();
    });

    function handleFile(file) {
        if (file.type !== 'application/pdf') {
            setStatus('Kesalahan: Harap unggah dokumen berformat PDF (.pdf)', 'error');
            return;
        }
        selectedFile = file;
        fnameTxt.textContent = `&#128196; ${file.name} (${(file.size / (1024 * 1024)).toFixed(2)} MB)`;
        fileInfoEl.style.display = 'flex';
        convertBtn.disabled = false;
        downloadBtn.style.display = 'none';
        previewSection.style.display = 'none';
        previewGrid.innerHTML = '';
        setStatus('Dokumen siap dikonversi.', 'info');
        clearMemory();
    }

    // FIX BUG: Menggunakan innerHTML agar emoji tidak dirender sebagai entity html mentah.
    function setStatus(msg, type) {
        statusMsg.innerHTML = msg; 
        statusMsg.className = 'status-msg ' + (type || '');
    }

    function setProgress(val, label) {
        progressFill.style.width = `${val}%`;
        progressPct.textContent = `${val}%`;
        progressLabel.textContent = label;
        progressBarAria.setAttribute('aria-valuenow', val);
    }

    function parsePageRange(str, totalPages) {
        const pages = new Set();
        const parts = str.split(',');
        for (const part of parts) {
            const trimmed = part.trim();
            if (trimmed.includes('-')) {
                const [a, b] = trimmed.split('-').map(Number);
                for (let i = a; i &lt;= b &amp;&amp; i &lt;= totalPages; i++) if (i &gt;= 1) pages.add(i);
            } else {
                const n = parseInt(trimmed);
                if (!isNaN(n) &amp;&amp; n &gt;= 1 &amp;&amp; n &lt;= totalPages) pages.add(n);
            }
        }
        return Array.from(pages).sort((a, b) =&gt; a - b);
    }

    convertBtn.addEventListener('click', async () =&gt; {
        if (!selectedFile) return;

        convertBtn.disabled = true;
        convertBtn.textContent = '⏳ Memproses...';
        progressWrap.style.display = 'block';
        previewSection.style.display = 'none';
        previewGrid.innerHTML = '';
        downloadBtn.style.display = 'none';
        setProgress(0, 'Menyiapkan modul...');
        setStatus('', '');

        try {
            const quality = parseFloat(qualitySel.value);
            const scale = parseFloat(scaleSel.value);
            
            const arrayBuffer = await selectedFile.arrayBuffer();
            const pdfDoc = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
            const total = pdfDoc.numPages;

            let pages = [];
            if (pageRangeSel.value === 'all') {
                pages = Array.from({ length: total }, (_, i) =&gt; i + 1);
            } else if (pageRangeSel.value === 'first') {
                pages = [1];
            } else {
                pages = parsePageRange(customRangeIn.value, total);
                if (!pages.length) {
                    setStatus('Input halaman tidak valid.', 'error');
                    convertBtn.disabled = false;
                    convertBtn.textContent = 'Terapkan &amp; Simpan JPG';
                    progressWrap.style.display = 'none';
                    return;
                }
            }

            const zip = new JSZip();
            previewSection.style.display = 'block';
            previewTitle.textContent = `Menampilkan Hasil (${pages.length} Halaman)`;

            for (let idx = 0; idx &lt; pages.length; idx++) {
                const pageNum = pages[idx];
                const pct = Math.round((idx / pages.length) * 90);
                setProgress(pct, `Merender Halaman ${pageNum}...`);

                const page = await pdfDoc.getPage(pageNum);
                const viewport = page.getViewport({ scale });
                
                const canvas = document.createElement('canvas');
                canvas.width = viewport.width;
                canvas.height = viewport.height;
                const ctx = canvas.getContext('2d');

                await page.render({ canvasContext: ctx, viewport }).promise;
                
                const blob = await new Promise(resolve =&gt; canvas.toBlob(resolve, 'image/jpeg', quality));
                const blobUrl = URL.createObjectURL(blob);
                generatedBlobsUrls.push(blobUrl);

                const fname = `halaman_${String(pageNum).padStart(3, '0')}.jpg`;
                zip.file(fname, blob);

                const card = document.createElement('div');
                card.className = 'preview-item';
                card.innerHTML = `
                    &lt;img src="${blobUrl}" alt="Hasil konversi halaman ${pageNum}" loading="lazy"&gt;
                    &lt;div class="preview-item-footer"&gt;
                        &lt;span&gt;Hal. ${pageNum}&lt;/span&gt;
                        &lt;a class="btn-dl-single" href="${blobUrl}" download="${fname}" aria-label="Unduh gambar halaman ${pageNum}"&gt;Unduh JPG&lt;/a&gt;
                    &lt;/div&gt;
                `;
                previewGrid.appendChild(card);
            }

            setProgress(95, 'Mengemas file ZIP...');

            if (dlFormatSel.value === 'zip') {
                const zipBlob = await zip.generateAsync({ type: 'blob' });
                zipBlobUrl = URL.createObjectURL(zipBlob);
                const zipName = selectedFile.name.replace(/\.pdf$/i, '') + '_JPG.zip';
                
                downloadBtn.href = zipBlobUrl;
                downloadBtn.download = zipName;
                downloadBtn.style.display = 'inline-flex';
            }

            setProgress(100, 'Selesai!');
            // PENGGUNAAN EMOJI LANGSUNG (Memastikan tidak ada error code entitas HTML)
            setStatus(`✅ Berhasil merender ${pages.length} halaman secara lokal.`, 'success');
            setTimeout(() =&gt; { progressWrap.style.display = 'none'; }, 2000);

        } catch (err) {
            console.error(err);
            setStatus('❌ Terjadi kesalahan render. Pastikan PDF tidak rusak dan tidak terkunci password.', 'error');
            progressWrap.style.display = 'none';
        }

        convertBtn.disabled = false;
        convertBtn.textContent = 'Terapkan &amp; Simpan JPG';
    });

    // FAQ Akordeon
    document.querySelectorAll('.faq-q').forEach(q =&gt; {
        q.addEventListener('click', () =&gt; {
            const item = q.closest('.faq-item');
            const wasOpen = item.classList.contains('open');
            document.querySelectorAll('.faq-item').forEach(i =&gt; i.classList.remove('open'));
            if (!wasOpen) item.classList.add('open');
        });
    });

});
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/pdf-to-jpg.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-5682218272286342864</guid><pubDate>Thu, 09 Apr 2026 21:49:35 +0000</pubDate><atom:updated>2026-07-24T08:22:34.623-07:00</atom:updated><title>Edit pdf </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;meta name="description" content="Edit PDF online secara aman langsung di browser. Tambah teks dan hapus halaman tanpa upload ke server. Pemrosesan 100% client-side menjamin kerahasiaan dokumen Anda."&gt;
    &lt;meta name="keywords" content="edit pdf online, pdf editor client side, cara edit pdf aman, tambah teks pdf, hapus halaman pdf"&gt;
    &lt;title&gt;Edit PDF Online Gratis &amp; Aman (100% Client-Side) | Kholil Tools&lt;/title&gt;
    
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;style&gt;
        :root {
            --bg-dark: #0f172a; 
            --bg-card: #1e293b;
            --bg-white: #ffffff;
            --text-main: #f8fafc;
            --text-muted: #94a3b8;
            --text-dark: #334155;
            --text-dark-muted: #64748b;
            --accent-blue: #3b82f6;
            --accent-blue-hover: #2563eb;
            --border-color: #334155;
            --border-light: #e2e8f0;
            --success: #10b981;
            --danger: #ef4444;
            --radius-lg: 16px;
            --radius-md: 8px;
        }

        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background-color: var(--bg-dark);
            color: var(--text-main);
            line-height: 1.6;
            -webkit-font-smoothing: antialiased;
        }

        a { text-decoration: none; transition: all 0.2s; }

        /* ── HEADER ── */
        .header {
            background-color: var(--bg-dark);
            border-bottom: 1px solid var(--border-color);
            position: sticky;
            top: 0;
            z-index: 100;
        }
        .header-container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 16px 24px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .logo {
            font-size: 1.25rem;
            font-weight: 700;
            color: var(--text-main);
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .logo-icon {
            background: var(--text-main);
            color: var(--bg-dark);
            width: 28px;
            height: 28px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
            font-size: 0.9rem;
        }
        .nav-links { display: flex; gap: 24px; }
        .nav-links a { color: var(--text-muted); font-size: 0.9rem; font-weight: 500; }
        .nav-links a:hover { color: var(--text-main); }

        /* ── HERO SECTION ── */
        .hero {
            text-align: center;
            padding: 60px 20px 40px;
            max-width: 800px;
            margin: 0 auto;
        }
        .badge {
            display: inline-block;
            background: rgba(59, 130, 246, 0.15);
            color: #60a5fa;
            font-size: 0.75rem;
            font-weight: 700;
            padding: 6px 12px;
            border-radius: 20px;
            letter-spacing: 0.5px;
            margin-bottom: 20px;
            text-transform: uppercase;
        }
        .hero h1 { font-size: 2.5rem; font-weight: 700; line-height: 1.2; margin-bottom: 16px; }
        .hero p { color: var(--text-muted); font-size: 1.05rem; }

        /* ── MAIN LAYOUT ── */
        .container { max-width: 900px; margin: 0 auto; padding: 0 20px 80px; }

        /* ── TOOL UI (WHITE CARD) ── */
        .tool-card {
            background: var(--bg-white);
            border-radius: var(--radius-lg);
            padding: 40px;
            color: var(--text-dark);
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            margin-bottom: 40px;
        }
        .drop-zone {
            border: 2px dashed #cbd5e1;
            border-radius: var(--radius-lg);
            padding: 50px 20px;
            text-align: center;
            background: #f8fafc;
            transition: all 0.2s;
            cursor: pointer;
        }
        .drop-zone:hover, .drop-zone.active {
            border-color: var(--accent-blue);
            background: #eff6ff;
        }
        .drop-zone-icon {
            font-size: 3rem;
            margin-bottom: 16px;
            display: block;
        }
        .drop-zone p { margin-bottom: 20px; font-weight: 500; color: var(--text-dark); }
        .btn-primary {
            display: inline-block;
            background: var(--accent-blue);
            color: var(--bg-white);
            padding: 12px 28px;
            border-radius: var(--radius-md);
            font-weight: 600;
            border: none;
            cursor: pointer;
            transition: background 0.2s;
        }
        .btn-primary:hover { background: var(--accent-blue-hover); color: var(--bg-white); }
        .btn-primary input[type="file"] { display: none; }

        #editorWorkspace { display: none; margin-top: 30px; }
        .file-info {
            font-weight: 600;
            color: var(--text-dark);
            margin-bottom: 16px;
            text-align: center;
            padding-bottom: 16px;
            border-bottom: 1px solid var(--border-light);
        }
        .canvas-container {
            background: #e2e8f0;
            border-radius: var(--radius-md);
            padding: 20px;
            overflow: auto;
            text-align: center;
            border: 1px solid var(--border-light);
        }
        canvas {
            max-width: 100%;
            height: auto;
            background: white;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        
        .controls-grid {
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
            margin-top: 20px;
            justify-content: center;
        }
        .btn-outline {
            background: transparent;
            border: 1px solid var(--border-light);
            color: var(--text-dark);
            padding: 10px 20px;
            border-radius: var(--radius-md);
            font-weight: 500;
            cursor: pointer;
        }
        .btn-outline:hover { background: #f1f5f9; }
        .btn-danger {
            background: transparent;
            border: 1px solid var(--danger);
            color: var(--danger);
            padding: 10px 20px;
            border-radius: var(--radius-md);
            font-weight: 500;
            cursor: pointer;
        }
        .btn-danger:hover { background: #fef2f2; }
        .btn-success {
            background: var(--success);
            color: white;
            padding: 12px 28px;
            border-radius: var(--radius-md);
            font-weight: 600;
            border: none;
            cursor: pointer;
            display: inline-block;
            margin-top: 20px;
            width: 100%;
            text-align: center;
        }
        .btn-success:hover { background: #059669; }
        .status-msg { margin-top: 15px; text-align: center; font-size: 0.9rem; font-weight: 500; }

        /* ── PROOF SECTION (GALLERY) ── */
        .proof-section {
            background: var(--bg-card);
            border-radius: var(--radius-lg);
            padding: 32px;
            margin-bottom: 40px;
            border: 1px solid var(--border-color);
        }
        .proof-title {
            text-align: center;
            font-size: 1.15rem;
            font-weight: 600;
            margin-bottom: 30px;
            color: var(--text-main);
        }
        .proof-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
            gap: 20px;
        }
        .proof-step {
            text-align: center;
        }
        .proof-step-img-wrap {
            background: var(--bg-dark);
            padding: 8px;
            border-radius: var(--radius-md);
            border: 1px solid var(--border-color);
            margin-bottom: 16px;
        }
        .proof-step img {
            width: 100%;
            height: auto;
            border-radius: 4px;
            display: block;
        }
        .proof-step-num {
            display: inline-block;
            background: var(--success);
            color: white;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            font-size: 0.8rem;
            font-weight: 700;
            line-height: 24px;
            margin-bottom: 8px;
        }
        .proof-step p {
            font-size: 0.9rem;
            color: var(--text-muted);
            font-weight: 500;
        }

        /* ── AUTHOR BOX (EEAT) ── */
        .author-box {
            background: var(--bg-card);
            border-radius: var(--radius-md);
            padding: 20px;
            display: flex;
            align-items: center;
            gap: 16px;
            margin-bottom: 40px;
            border: 1px solid var(--border-color);
        }
        .author-avatar {
            width: 48px; height: 48px;
            background: #eab308;
            color: var(--bg-dark);
            border-radius: 50%;
            display: flex; justify-content: center; align-items: center;
            font-weight: 700; font-size: 1.2rem;
        }
        .author-info strong { display: block; font-size: 1.05rem; }
        .author-info span { color: var(--text-muted); font-size: 0.85rem; }

        /* ── ARTICLE CONTENT ── */
        .article-content {
            background: var(--bg-white);
            border-radius: var(--radius-lg);
            padding: 40px;
            color: var(--text-dark);
        }
        .article-content h2 {
            font-size: 1.5rem;
            color: var(--bg-dark);
            margin: 32px 0 16px;
            padding-bottom: 8px;
            border-bottom: 1px solid var(--border-light);
        }
        .article-content h2:first-child { margin-top: 0; }
        .article-content p { margin-bottom: 16px; color: var(--text-dark-muted); }
        .article-content ul { margin: 0 0 16px 20px; color: var(--text-dark-muted); }
        .article-content li { margin-bottom: 8px; }
        
        .callout {
            background: #eff6ff;
            border-left: 4px solid var(--accent-blue);
            padding: 16px 20px;
            border-radius: 0 var(--radius-md) var(--radius-md) 0;
            margin: 24px 0;
        }
        .callout strong { color: #1e3a8a; display: block; margin-bottom: 4px; }
        .callout p { margin: 0; color: #1e40af; font-size: 0.95rem; }

        /* ── ACCORDION FAQ ── */
        .faq-container { margin-top: 40px; }
        .faq-item {
            border-bottom: 1px solid var(--border-light);
            padding: 16px 0;
        }
        .faq-question {
            width: 100%;
            text-align: left;
            background: none;
            border: none;
            font-size: 1.05rem;
            font-weight: 600;
            color: var(--text-dark);
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .faq-answer {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-out;
            color: var(--text-dark-muted);
            font-size: 0.95rem;
            padding-top: 0;
        }
        .faq-item.active .faq-answer {
            max-height: 300px;
            padding-top: 12px;
        }

        /* ── FOOTER ── */
        .footer {
            text-align: center;
            padding: 40px 20px;
            border-top: 1px solid var(--border-color);
            margin-top: 40px;
        }
        .footer h4 { margin-bottom: 16px; font-weight: 600; }
        .footer p { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 24px; }
        .footer-links {
            display: flex;
            justify-content: center;
            gap: 20px;
            flex-wrap: wrap;
            margin-bottom: 24px;
        }
        .footer-links a { color: var(--text-muted); font-size: 0.85rem; }
        .footer-links a:hover { color: var(--text-main); }
        .copyright { color: var(--text-muted); font-size: 0.75rem; }

        /* Responsive */
        @media (max-width: 640px) {
            .nav-links { display: none; }
            .hero h1 { font-size: 2rem; }
            .tool-card { padding: 24px; }
            .article-content { padding: 24px; }
            .proof-grid { grid-template-columns: repeat(2, 1fr); }
        }
    &lt;/style&gt;

    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "SoftwareApplication",
      "name": "Kholil PDF Editor Client-Side",
      "applicationCategory": "UtilitiesApplication",
      "operatingSystem": "Web Browser",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/about"
      }
    }
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;!-- HEADER --&gt;
    &lt;header class="header"&gt;
        &lt;div class="header-container"&gt;
            &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;
                &lt;span class="logo-icon"&gt;K&lt;/span&gt; Kholil Tools
            &lt;/a&gt;
            &lt;nav class="nav-links"&gt;
                &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
                &lt;a href="https://www.kholil.biz.id/2026/04/edit-pdf.html"&gt;Edit PDF&lt;/a&gt;
                &lt;a href="https://www.kholil.biz.id/2026/04/excel-to-pdf.html"&gt;Excel ke PDF&lt;/a&gt;
                &lt;a href="https://www.kholil.biz.id/2026/04/power-point-to-pdf.html"&gt;PPT ke PDF&lt;/a&gt;
            &lt;/nav&gt;
        &lt;/div&gt;
    &lt;/header&gt;

    &lt;!-- HERO --&gt;
    &lt;section class="hero"&gt;
        &lt;div class="badge"&gt;# ARSITEKTUR KEAMANAN CLIENT-SIDE&lt;/div&gt;
        &lt;h1&gt;Editor PDF Interaktif &amp; &lt;span style="color: var(--accent-blue);"&gt;Aman&lt;/span&gt;&lt;/h1&gt;
        &lt;p&gt;Tambahkan teks dan hapus halaman langsung dari peramban Anda. Pemrosesan dilakukan murni secara visual di perangkat Anda demi kerahasiaan data yang terjamin.&lt;/p&gt;
    &lt;/section&gt;

    &lt;!-- MAIN CONTAINER --&gt;
    &lt;main class="container"&gt;
        
        &lt;!-- TOOL CARD --&gt;
        &lt;div class="tool-card"&gt;
            &lt;div class="drop-zone" id="dropArea" aria-label="Area drag and drop file PDF"&gt;
                &lt;span class="drop-zone-icon"&gt;&#128193;&lt;/span&gt;
                &lt;p&gt;Seret &amp; lepas file PDF ke sini&lt;br&gt;&lt;span style="font-size:0.85rem; color:#64748b; font-weight:normal;"&gt;Atau klik tombol di bawah untuk memilih dari perangkat Anda&lt;/span&gt;&lt;/p&gt;
                &lt;label class="btn-primary"&gt;
                    Pilih File PDF
                    &lt;input type="file" id="pdfFile" accept="application/pdf" aria-label="Pilih file PDF untuk diedit"&gt;
                &lt;/label&gt;
            &lt;/div&gt;

            &lt;div id="editorWorkspace"&gt;
                &lt;div class="file-info" id="fileName"&gt;nama_file.pdf&lt;/div&gt;
                
                &lt;div class="canvas-container"&gt;
                    &lt;canvas id="pdfCanvas" aria-label="Preview halaman PDF"&gt;&lt;/canvas&gt;
                &lt;/div&gt;

                &lt;div class="controls-grid"&gt;
                    &lt;button class="btn-outline" id="prevPage"&gt;← Prev&lt;/button&gt;
                    &lt;span class="btn-outline" style="border:none; cursor:default;" id="pageInfo"&gt;Hal 1 / 1&lt;/span&gt;
                    &lt;button class="btn-outline" id="nextPage"&gt;Next →&lt;/button&gt;
                &lt;/div&gt;

                &lt;div class="controls-grid" style="margin-top:20px; padding-top:20px; border-top:1px solid #e2e8f0;"&gt;
                    &lt;button class="btn-outline" id="btnAddText" style="color: var(--accent-blue); border-color: var(--accent-blue);"&gt;+ Tambah Teks&lt;/button&gt;
                    &lt;button class="btn-danger" id="btnDeletePage"&gt;&#128465; Hapus Halaman Ini&lt;/button&gt;
                &lt;/div&gt;

                &lt;div class="status-msg" id="status"&gt;&lt;/div&gt;
                &lt;a class="btn-success" id="downloadBtn" href="#" style="display: none;"&gt;Simpan &amp; Unduh PDF&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- BUKTI TOOL BERFUNGSI (IMAGE GALLERY) --&gt;
        &lt;div class="proof-section"&gt;
            &lt;div class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/div&gt;
            &lt;div class="proof-grid"&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/nqyLDsTq/Screenshot-2026-07-23-18-05-11-284-com-android-chrome.jpg" alt="Upload file PDF normal" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;1&lt;/span&gt;
                    &lt;p&gt;Unggah Tanpa Kendala&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/VW78hShQ/Screenshot-2026-07-23-18-05-26-085-com-android-chrome.jpg" alt="Masukkan teks untuk PDF" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;2&lt;/span&gt;
                    &lt;p&gt;Sisipkan Teks Visual&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/yBM7R37s/Screenshot-2026-07-23-18-05-37-314-com-android-chrome.jpg" alt="Hasil teks ditambahkan di browser" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;3&lt;/span&gt;
                    &lt;p&gt;Pratinjau Hasil Instan&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/35XnsWVk/Screenshot-2026-07-23-18-05-47-194-com-google-android-apps-docs.jpg" alt="PDF berhasil diunduh dan dibuka" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;4&lt;/span&gt;
                    &lt;p&gt;Unduh &amp; Buka Normal&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- AUTHOR BOX (EEAT Signal) --&gt;
        &lt;div class="author-box"&gt;
            &lt;div class="author-avatar"&gt;K&lt;/div&gt;
            &lt;div class="author-info"&gt;
                &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
                &lt;span&gt;Spesialis Kriptografi Berkas &amp; Keamanan Digital | Diperbarui: Juli 2026&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- ARTICLE CONTENT (Helpful Content &amp; Technical Depth) --&gt;
        &lt;article class="article-content"&gt;
            &lt;span style="font-size: 0.75rem; font-weight: 700; color: var(--accent-blue); letter-spacing: 1px;"&gt;PANDUAN &amp; KEAMANAN&lt;/span&gt;
            &lt;h2&gt;Arsitektur Internal PDF dan Mengapa Mengeditnya Sulit&lt;/h2&gt;
            &lt;p&gt;
                Berbeda dengan dokumen Microsoft Word (.docx) yang berbasis markup linear, format PDF pada dasarnya adalah kanvas berbasis koordinat spasial. Mengedit teks di dalam PDF seringkali berantakan karena PDF tidak menyimpan informasi "paragraf" atau "spasi", melainkan menyimpan instruksi presisi: &lt;em&gt;"Letakkan karakter 'A' di koordinat X: 120, Y: 450 menggunakan font Arial"&lt;/em&gt;.
            &lt;/p&gt;
            &lt;p&gt;
                Karena keterbatasan arsitektur inilah, Kholil Tools menggunakan metode &lt;strong&gt;Layer Overlay berbasis Client-Side&lt;/strong&gt;. Proses ini mengurai dokumen PDF Anda oleh mesin JavaScript peramban secara lokal. Berkas tidak pernah menyeberangi jaringan internet eksternal.
            &lt;/p&gt;
            
            &lt;div class="callout"&gt;
                &lt;strong&gt;&#128161; Resolusi Sempurna untuk Kebutuhan Digital&lt;/strong&gt;
                &lt;p&gt;Pemanfaatan modul perender internal kami memastikan bahwa elemen baru yang ditambahkan di atas dokumen asli tetap dieksekusi secara presisi, mempertahankan integritas tata letak dokumen asli saat dicetak atau dibagikan kembali.&lt;/p&gt;
            &lt;/div&gt;

            &lt;h2&gt;Kasus Penggunaan Optimal&lt;/h2&gt;
            &lt;p&gt;Metode &lt;em&gt;overlay client-side&lt;/em&gt; yang ada pada halaman ini sangat ideal untuk skenario berikut:&lt;/p&gt;
            &lt;ul&gt;
                &lt;li&gt;&lt;strong&gt;Pengisian Formulir Statis:&lt;/strong&gt; Menumpuk teks presisi di atas garis formulir yang diformat secara kaku.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Anotasi Dokumen:&lt;/strong&gt; Menambahkan catatan pinggir atau label revisi pada dokumen draf.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Pembersihan Dokumen:&lt;/strong&gt; Menghapus halaman kosong atau sisipan yang tidak relevan (seperti halaman iklan pada dokumen hasil unduhan) sebelum dibagikan.&lt;/li&gt;
            &lt;/ul&gt;

            &lt;h2&gt;Transparansi &amp; Log Pembaruan Tool (Changelog)&lt;/h2&gt;
            &lt;p&gt;Kami terus menyempurnakan script pemrosesan PDF ini agar lebih ringan dan akurat pada eksekusi &lt;em&gt;client-side&lt;/em&gt; ponsel maupun desktop.&lt;/p&gt;
            &lt;ul&gt;
                &lt;li&gt;&lt;strong&gt;v1.2 (Juli 2026):&lt;/strong&gt; Penyesuaian UI ke mode gelap (SaaS UI) untuk kenyamanan visual, penambahan galeri bukti fungsionalitas, serta perbaikan sistem unggah berkas lintas peramban.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;v1.1 (Juni 2026):&lt;/strong&gt; Penambahan fitur input parameter ukuran dan warna font secara dinamis pada fungsi penambahan teks overlay.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;v1.0 (April 2026):&lt;/strong&gt; Rilis engine manipulasi PDF murni menggunakan `pdf.js` dan `pdf-lib` tanpa intervensi server (Zero-Upload policy).&lt;/li&gt;
            &lt;/ul&gt;

            &lt;!-- FAQ SECTION --&gt;
            &lt;div class="faq-container"&gt;
                &lt;h2 style="text-align: center; border-bottom:none;"&gt;Pusat Bantuan &amp; Pertanyaan Teknis&lt;/h2&gt;
                
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-question"&gt;Mengapa font teks yang saya tambahkan berbeda dengan font asli? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-answer"&gt;
                        Karena proses ini berjalan murni di peramban lokal Anda, tool ini hanya menggunakan standar font bawaan &lt;em&gt;(Standard 14 Fonts)&lt;/em&gt; dari pustaka sistem, seperti Helvetica. Tool ini tidak mengekstrak font khusus yang ditanam &lt;em&gt;(embedded subset font)&lt;/em&gt; pada dokumen asli Anda demi menjaga kestabilan memori browser.
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-question"&gt;Apakah tool ini mendukung standar PDF/A untuk pengarsipan? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-answer"&gt;
                        Tidak. Mengedit dokumen yang menggunakan standar pengarsipan ketat seperti PDF/A-1b dengan metode layer overlay akan membatalkan sertifikasi &lt;em&gt;compliance&lt;/em&gt; dokumen tersebut. Gunakan editor ini hanya untuk dokumen operasional standar (PDF 1.7 ke bawah).
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-question"&gt;Browser saya membeku saat mengunggah PDF ukuran 35MB. Apa solusinya? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-answer"&gt;
                        Pemrosesan dilakukan menggunakan RAM pada perangkat Anda (ponsel/laptop), bukan server kami. Dokumen PDF dengan gambar vektor berlapis atau resolusi DPI sangat tinggi akan memakan banyak memori lokal. Kami menyarankan untuk mengompresi PDF Anda menjadi di bawah 15MB terlebih dahulu menggunakan tool kompresi sebelum melakukan suntingan visual.
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/article&gt;

    &lt;/main&gt;

    &lt;!-- FOOTER --&gt;
    &lt;footer class="footer"&gt;
        &lt;h4&gt;Kholil Tools&lt;/h4&gt;
        &lt;p&gt;Utilitas Manajemen Dokumen Aman, Privasi Terjamin Lokal.&lt;/p&gt;
        &lt;div class="footer-links"&gt;
            &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;About US&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Contact US&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Privacy Policy&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/terms"&gt;Terms&lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="copyright"&gt;
            © 2026 kholil.biz.id - Seluruh Hak Cipta Dilindungi.
        &lt;/div&gt;
    &lt;/footer&gt;

    &lt;!-- EXTERNAL LIBRARIES --&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js" defer&gt;&lt;/script&gt;
    &lt;script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js" defer&gt;&lt;/script&gt;

    &lt;!-- SCRIPT LOGIKA UTAMA --&gt;
    &lt;script&gt;
        document.addEventListener('DOMContentLoaded', () =&gt; {
            // FAQ Accordion Script
            const faqItems = document.querySelectorAll('.faq-question');
            faqItems.forEach(item =&gt; {
                item.addEventListener('click', () =&gt; {
                    const parent = item.parentElement;
                    parent.classList.toggle('active');
                });
            });

            // PDF Logic
            let pdfDocViewer = null;
            let currentPageNum = 1;
            let totalPages = 0;
            let editedPDFBytes = null;
            let originalFileName = '';

            const pdfFileInput   = document.getElementById('pdfFile');
            const dropArea       = document.getElementById('dropArea');
            const fileNameEl     = document.getElementById('fileName');
            const editorWorkspace = document.getElementById('editorWorkspace');
            const canvas         = document.getElementById('pdfCanvas');
            const ctx            = canvas.getContext('2d');
            const statusEl       = document.getElementById('status');
            const downloadBtn    = document.getElementById('downloadBtn');
            const prevPageBtn    = document.getElementById('prevPage');
            const nextPageBtn    = document.getElementById('nextPage');
            const pageInfo       = document.getElementById('pageInfo');

            setTimeout(() =&gt; {
                if(window.pdfjsLib) {
                    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
                }
            }, 500);

            dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('active'); });
            dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('active'));
            dropArea.addEventListener('drop', e =&gt; {
                e.preventDefault(); dropArea.classList.remove('active');
                const file = e.dataTransfer.files[0];
                if (file &amp;&amp; file.type === 'application/pdf') handleFile(file);
                else alert('Silakan pilih file PDF yang valid!');
            });
            pdfFileInput.addEventListener('change', e =&gt; { 
                if (e.target.files[0]) handleFile(e.target.files[0]); 
            });

            async function handleFile(file) {
                if(!window.pdfjsLib || !window.PDFLib) {
                    statusEl.innerHTML = '&lt;span style="color:#ef4444;"&gt;Menunggu sistem memuat modul keamanan... Silakan coba lagi dalam beberapa detik.&lt;/span&gt;';
                    return;
                }
                originalFileName = file.name;
                fileNameEl.textContent = '&#128196; ' + file.name;
                dropArea.style.display = 'none';
                editorWorkspace.style.display = 'block';
                statusEl.textContent = 'Memuat PDF...';
                downloadBtn.style.display = 'none';

                try {
                    const arrayBuffer = await file.arrayBuffer();
                    editedPDFBytes = new Uint8Array(arrayBuffer);
                    await loadViewer(editedPDFBytes);
                    statusEl.innerHTML = '&lt;span style="color:#10b981;"&gt;✅ Dokumen dimuat secara lokal. Privasi terjamin.&lt;/span&gt;';
                } catch (err) {
                    console.error(err);
                    statusEl.innerHTML = '&lt;span style="color:#ef4444;"&gt;❌ Gagal memuat PDF. Pastikan file valid dan tidak dikunci (password).&lt;/span&gt;';
                }
            }

            async function loadViewer(bytes) {
                pdfDocViewer = await pdfjsLib.getDocument({ data: bytes }).promise;
                totalPages = pdfDocViewer.numPages;
                currentPageNum = 1;
                await renderPage(currentPageNum);
            }

            async function renderPage(num) {
                if (!pdfDocViewer) return;
                const page = await pdfDocViewer.getPage(num);
                const containerWidth = document.querySelector('.canvas-container').clientWidth;
                const scale = Math.min(1.5, containerWidth / page.getViewport({ scale: 1 }).width);
                const viewport = page.getViewport({ scale });
                canvas.width = viewport.width;
                canvas.height = viewport.height;
                await page.render({ canvasContext: ctx, viewport }).promise;
                
                pageInfo.textContent = 'Hal ' + num + ' / ' + totalPages;
                prevPageBtn.disabled = num &lt;= 1;
                nextPageBtn.disabled = num &gt;= totalPages;
                
                prevPageBtn.style.opacity = num &lt;= 1 ? '0.4' : '1';
                nextPageBtn.style.opacity = num &gt;= totalPages ? '0.4' : '1';
            }

            prevPageBtn.addEventListener('click', () =&gt; { if (currentPageNum &gt; 1) { currentPageNum--; renderPage(currentPageNum); } });
            nextPageBtn.addEventListener('click', () =&gt; { if (currentPageNum &lt; totalPages) { currentPageNum++; renderPage(currentPageNum); } });

            document.getElementById('btnAddText').addEventListener('click', async () =&gt; {
                if (!editedPDFBytes) return;
                const text = prompt('Masukkan teks yang ingin ditambahkan:', '');
                if (!text) return;
                
                const fontSize = parseInt(prompt('Masukkan ukuran teks (misal: 12, 16, 24):', '16')) || 16;
                
                statusEl.textContent = 'Memproses penambahan teks...';
                try {
                    const pdfLib = await PDFLib.PDFDocument.load(editedPDFBytes);
                    const pages = pdfLib.getPages();
                    const page = pages[currentPageNum - 1];
                    
                    page.drawText(text, {
                        x: 50,
                        y: page.getHeight() - 100,
                        size: fontSize,
                        color: PDFLib.rgb(0.23, 0.51, 0.96), 
                    });
                    
                    editedPDFBytes = await pdfLib.save();
                    await loadViewer(editedPDFBytes);
                    updateDownload();
                    statusEl.innerHTML = '&lt;span style="color:#10b981;"&gt;✅ Teks berhasil ditambahkan!&lt;/span&gt;';
                } catch (err) {
                    console.error(err);
                    statusEl.innerHTML = '&lt;span style="color:#ef4444;"&gt;❌ Gagal menambahkan teks.&lt;/span&gt;';
                }
            });

            document.getElementById('btnDeletePage').addEventListener('click', async () =&gt; {
                if (!editedPDFBytes) return;
                if (totalPages &lt;= 1) { alert('Dokumen hanya memiliki 1 halaman. Tidak bisa dihapus.'); return; }
                if (!confirm('Hapus halaman ' + currentPageNum + '? Tindakan ini akan memotong dokumen permanen.')) return;
                
                statusEl.textContent = 'Menghapus halaman...';
                try {
                    const pdfLib = await PDFLib.PDFDocument.load(editedPDFBytes);
                    pdfLib.removePage(currentPageNum - 1);
                    editedPDFBytes = await pdfLib.save();
                    await loadViewer(editedPDFBytes);
                    updateDownload();
                    statusEl.innerHTML = '&lt;span style="color:#10b981;"&gt;✅ Halaman berhasil dihapus!&lt;/span&gt;';
                } catch (err) {
                    console.error(err);
                    statusEl.innerHTML = '&lt;span style="color:#ef4444;"&gt;❌ Gagal menghapus halaman.&lt;/span&gt;';
                }
            });

            function updateDownload() {
                const blob = new Blob([editedPDFBytes], { type: 'application/pdf' });
                downloadBtn.href = URL.createObjectURL(blob);
                const timestamp = new Date().getTime();
                downloadBtn.download = originalFileName.replace('.pdf', `_kholil_${timestamp}.pdf`);
                downloadBtn.style.display = 'block';
            }
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/edit-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-4428139453886745047</guid><pubDate>Thu, 09 Apr 2026 21:48:00 +0000</pubDate><atom:updated>2026-07-24T08:24:24.402-07:00</atom:updated><title>Excel to PDF </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;meta name="description" content="Konversi Excel ke PDF online secara aman langsung di browser. Pertahankan struktur tabel dan grid data tanpa perlu upload dokumen ke server."&gt;
    &lt;meta name="keywords" content="excel to pdf, xlsx ke pdf, konversi excel ke pdf aman, client side excel converter"&gt;
    &lt;title&gt;Konversi Excel ke PDF Berformat Tabel (100% Aman &amp; Lokal) | Kholil Tools&lt;/title&gt;
    
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
    
    &lt;style&gt;
        :root {
            --bg-dark: #0f172a; 
            --bg-card: #1e293b;
            --bg-white: #ffffff;
            --text-main: #f8fafc;
            --text-muted: #94a3b8;
            --text-dark: #334155;
            --text-dark-muted: #4a5c50; 
            --accent-green: #10b981;
            --accent-green-hover: #059669;
            --border-color: #334155;
            --border-light: #e2e8f0;
            --success: #10b981;
            --danger: #ef4444;
            --radius-lg: 16px;
            --radius-md: 8px;
        }

        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', sans-serif;
            background-color: var(--bg-dark);
            color: var(--text-main);
            line-height: 1.6;
            -webkit-font-smoothing: antialiased;
        }

        a { text-decoration: none; transition: all 0.2s; }

        /* ── HEADER ── */
        .header {
            background-color: var(--bg-dark);
            border-bottom: 1px solid var(--border-color);
            position: sticky;
            top: 0;
            z-index: 100;
        }
        .header-container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 16px 24px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .logo {
            font-size: 1.25rem;
            font-weight: 700;
            color: var(--text-main);
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .logo-icon {
            background: var(--text-main);
            color: var(--bg-dark);
            width: 28px;
            height: 28px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
            font-size: 0.9rem;
        }
        .nav-links { display: flex; gap: 24px; }
        .nav-links a { color: var(--text-muted); font-size: 0.9rem; font-weight: 500; }
        .nav-links a:hover { color: var(--text-main); }

        /* ── HERO SECTION ── */
        .hero {
            text-align: center;
            padding: 60px 20px 40px;
            max-width: 800px;
            margin: 0 auto;
        }
        .badge {
            display: inline-block;
            background: rgba(16, 185, 129, 0.15);
            color: #34d399;
            font-size: 0.75rem;
            font-weight: 700;
            padding: 6px 12px;
            border-radius: 20px;
            letter-spacing: 0.5px;
            margin-bottom: 20px;
            text-transform: uppercase;
        }
        .hero h1 { font-size: 2.5rem; font-weight: 700; line-height: 1.2; margin-bottom: 16px; }
        .hero p { color: var(--text-muted); font-size: 1.05rem; }

        /* ── MAIN LAYOUT ── */
        .container { max-width: 900px; margin: 0 auto; padding: 0 20px 80px; }

        /* ── TOOL UI (WHITE CARD) ── */
        .tool-card {
            background: var(--bg-white);
            border-radius: var(--radius-lg);
            padding: 40px;
            color: var(--text-dark);
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            margin-bottom: 40px;
        }
        .drop-zone {
            border: 2px dashed #cbd5e1;
            border-radius: var(--radius-lg);
            padding: 50px 20px;
            text-align: center;
            background: #f8fafc;
            transition: all 0.2s;
            cursor: pointer;
        }
        .drop-zone:hover, .drop-zone.active {
            border-color: var(--accent-green);
            background: #f0fdf4;
        }
        .drop-zone-icon {
            font-size: 3rem;
            margin-bottom: 16px;
            display: block;
        }
        .drop-zone p { margin-bottom: 20px; font-weight: 500; color: var(--text-dark); }
        .btn-primary {
            display: inline-block;
            background: var(--accent-green);
            color: var(--bg-white);
            padding: 12px 28px;
            border-radius: var(--radius-md);
            font-weight: 600;
            border: none;
            cursor: pointer;
            transition: background 0.2s;
        }
        .btn-primary:hover { background: var(--accent-green-hover); color: var(--bg-white); }
        .btn-primary input[type="file"] { display: none; }

        /* Status &amp; Actions */
        #workspace { display: none; margin-top: 20px; text-align: center; }
        .file-info {
            font-weight: 600;
            color: var(--text-dark);
            margin-bottom: 24px;
            padding: 16px;
            background: #f1f5f9;
            border-radius: var(--radius-md);
            border: 1px solid var(--border-light);
        }
        .btn-success {
            background: var(--success);
            color: white;
            padding: 14px 32px;
            border-radius: var(--radius-md);
            font-weight: 600;
            border: none;
            cursor: pointer;
            display: inline-block;
            width: 100%;
            text-align: center;
            font-size: 1rem;
            margin-bottom: 15px;
            transition: background 0.2s;
        }
        .btn-success:hover:not(:disabled) { background: #059669; }
        .btn-success:disabled { background: #94a3b8; cursor: not-allowed; }
        
        .btn-download {
            display: none;
            background: var(--text-dark);
            color: white;
            padding: 14px 32px;
            border-radius: var(--radius-md);
            font-weight: 600;
            border: none;
            cursor: pointer;
            width: 100%;
            text-align: center;
            font-size: 1rem;
        }
        .btn-download:hover { background: #1e293b; color: white; }
        
        .status-msg { margin-top: 15px; text-align: center; font-size: 0.95rem; font-weight: 500; }

        /* ── PROOF SECTION (GALLERY) ── */
        .proof-section {
            background: var(--bg-card);
            border-radius: var(--radius-lg);
            padding: 32px;
            margin-bottom: 40px;
            border: 1px solid var(--border-color);
        }
        .proof-title {
            text-align: center;
            font-size: 1.15rem;
            font-weight: 600;
            margin-bottom: 30px;
            color: var(--text-main);
        }
        .proof-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
            gap: 20px;
        }
        .proof-step {
            text-align: center;
        }
        .proof-step-img-wrap {
            background: var(--bg-dark);
            padding: 8px;
            border-radius: var(--radius-md);
            border: 1px solid var(--border-color);
            margin-bottom: 16px;
        }
        .proof-step img {
            width: 100%;
            height: auto;
            border-radius: 4px;
            display: block;
        }
        .proof-step-num {
            display: inline-block;
            background: var(--success);
            color: white;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            font-size: 0.8rem;
            font-weight: 700;
            line-height: 24px;
            margin-bottom: 8px;
        }
        .proof-step p {
            font-size: 0.9rem;
            color: var(--text-muted);
            font-weight: 500;
        }

        /* ── AUTHOR BOX (EEAT) ── */
        .author-box {
            background: var(--bg-card);
            border-radius: var(--radius-md);
            padding: 20px;
            display: flex;
            align-items: center;
            gap: 16px;
            margin-bottom: 40px;
            border: 1px solid var(--border-color);
        }
        .author-avatar {
            width: 48px; height: 48px;
            background: var(--accent-green);
            color: var(--bg-white);
            border-radius: 50%;
            display: flex; justify-content: center; align-items: center;
            font-weight: 700; font-size: 1.2rem;
        }
        .author-info strong { display: block; font-size: 1.05rem; }
        .author-info span { color: var(--text-muted); font-size: 0.85rem; }

        /* ── ARTICLE CONTENT ── */
        .article-content {
            background: var(--bg-white);
            border-radius: var(--radius-lg);
            padding: 40px;
            color: var(--text-dark);
        }
        .article-content h2 {
            font-size: 1.5rem;
            color: var(--bg-dark);
            margin: 32px 0 16px;
            padding-bottom: 8px;
            border-bottom: 1px solid var(--border-light);
        }
        .article-content h2:first-child { margin-top: 0; }
        .article-content p { margin-bottom: 16px; color: var(--text-dark-muted); }
        .article-content ul { margin: 0 0 16px 20px; color: var(--text-dark-muted); }
        .article-content li { margin-bottom: 8px; }
        
        .callout {
            background: #f0fdf4;
            border-left: 4px solid var(--accent-green);
            padding: 16px 20px;
            border-radius: 0 var(--radius-md) var(--radius-md) 0;
            margin: 24px 0;
        }
        .callout strong { color: #065f46; display: block; margin-bottom: 4px; }
        .callout p { margin: 0; color: #047857; font-size: 0.95rem; }

        /* Changelog Box */
        .changelog-box {
            background: #f8fafc;
            border: 1px solid var(--border-light);
            border-radius: var(--radius-md);
            padding: 20px;
            margin: 30px 0;
        }
        .changelog-box h3 { font-size: 1.1rem; color: var(--bg-dark); margin-bottom: 12px; }

        /* ── ACCORDION FAQ ── */
        .faq-container { margin-top: 40px; }
        .faq-item { border-bottom: 1px solid var(--border-light); padding: 16px 0; }
        .faq-question {
            width: 100%; text-align: left; background: none; border: none;
            font-size: 1.05rem; font-weight: 600; color: var(--text-dark);
            cursor: pointer; display: flex; justify-content: space-between; align-items: center;
        }
        .faq-answer {
            max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out;
            color: var(--text-dark-muted); font-size: 0.95rem; padding-top: 0;
        }
        .faq-item.active .faq-answer { max-height: 400px; padding-top: 12px; }

        /* ── FOOTER ── */
        .footer {
            text-align: center;
            padding: 40px 20px;
            border-top: 1px solid var(--border-color);
            margin-top: 40px;
        }
        .footer h4 { margin-bottom: 16px; font-weight: 600; }
        .footer p { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 24px; }
        .footer-links {
            display: flex;
            justify-content: center;
            gap: 20px;
            flex-wrap: wrap;
            margin-bottom: 24px;
        }
        .footer-links a { color: var(--text-muted); font-size: 0.85rem; }
        .footer-links a:hover { color: var(--text-main); }
        .copyright { color: var(--text-muted); font-size: 0.75rem; }

        /* Responsive */
        @media (max-width: 640px) {
            .nav-links { display: none; }
            .hero h1 { font-size: 2rem; }
            .tool-card { padding: 24px; }
            .article-content { padding: 24px; }
            .proof-grid { grid-template-columns: repeat(2, 1fr); }
        }
    &lt;/style&gt;

    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "SoftwareApplication",
      "name": "Kholil Excel to PDF Converter",
      "applicationCategory": "UtilitiesApplication",
      "operatingSystem": "Web Browser",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/abouts"
      }
    }
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;!-- HEADER --&gt;
    &lt;header class="header"&gt;
        &lt;div class="header-container"&gt;
            &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;
                &lt;span class="logo-icon"&gt;K&lt;/span&gt; Kholil Tools
            &lt;/a&gt;
            &lt;nav class="nav-links"&gt;
                &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
                &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Word ke PDF&lt;/a&gt;
                &lt;a href="https://www.kholil.biz.id/2026/04/power-point-to-pdf.html"&gt;PPT ke PDF&lt;/a&gt;
            &lt;/nav&gt;
        &lt;/div&gt;
    &lt;/header&gt;

    &lt;!-- HERO --&gt;
    &lt;section class="hero"&gt;
        &lt;div class="badge"&gt;# DATA EXTRACTION ENGINE&lt;/div&gt;
        &lt;h1&gt;Konversi Excel ke PDF &lt;span style="color: var(--accent-green);"&gt;Berformat Tabel&lt;/span&gt;&lt;/h1&gt;
        &lt;p&gt;Ubah spreadsheet XLSX menjadi dokumen PDF dengan tata letak grid yang rapi secara client-side. File Anda tidak pernah diunggah ke server manapun.&lt;/p&gt;
    &lt;/section&gt;

    &lt;!-- MAIN CONTAINER --&gt;
    &lt;main class="container"&gt;
        
        &lt;!-- TOOL CARD --&gt;
        &lt;div class="tool-card"&gt;
            &lt;div class="drop-zone" id="dropArea" aria-label="Area drag and drop file Excel"&gt;
                &lt;span class="drop-zone-icon"&gt;&#128202;&lt;/span&gt;
                &lt;p&gt;Seret &amp; lepas file Excel (.xlsx / .xls) ke sini&lt;br&gt;&lt;span style="font-size:0.85rem; color:#64748b; font-weight:normal;"&gt;Atau klik tombol di bawah untuk memilih dari perangkat Anda&lt;/span&gt;&lt;/p&gt;
                &lt;label class="btn-primary"&gt;
                    Pilih File Excel
                    &lt;input type="file" id="excelFile" accept=".xlsx, .xls, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" aria-label="Pilih file Excel untuk dikonversi"&gt;
                &lt;/label&gt;
            &lt;/div&gt;

            &lt;div id="workspace"&gt;
                &lt;div class="file-info" id="fileName"&gt;nama_file.xlsx&lt;/div&gt;
                
                &lt;button class="btn-success" id="convertBtn"&gt;Membangun Vektor Tabel PDF&lt;/button&gt;
                &lt;a class="btn-download" id="downloadBtn" href="#"&gt;⬇️ Unduh PDF Berformat Tabel&lt;/a&gt;
                
                &lt;div class="status-msg" id="status" aria-live="polite"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- BUKTI TOOL BERFUNGSI (IMAGE GALLERY) --&gt;
        &lt;div class="proof-section"&gt;
            &lt;div class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/div&gt;
            &lt;div class="proof-grid"&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/SDkrwHMd/Screenshot-2026-07-23-18-28-03-174-com-android-chrome.jpg" alt="Upload file Excel normal" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;1&lt;/span&gt;
                    &lt;p&gt;Unggah Tanpa Kendala&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/21gMC1qz/Screenshot-2026-07-23-18-28-09-316-com-android-chrome.jpg" alt="Tombol proses konversi Excel" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;2&lt;/span&gt;
                    &lt;p&gt;Proses Vektor Cepat&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/D3QRG8t/Screenshot-2026-07-23-18-28-12-744-com-android-chrome.jpg" alt="Tombol download PDF" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;3&lt;/span&gt;
                    &lt;p&gt;Unduh Secara Lokal&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/Vfj29dp/Screenshot-2026-07-23-18-28-20-027-com-google-android-apps-docs.jpg" alt="Hasil tabel PDF rapi" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;4&lt;/span&gt;
                    &lt;p&gt;Tabel Utuh &amp; Tidak Pecah&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- AUTHOR BOX (EEAT Signal) --&gt;
        &lt;div class="author-box"&gt;
            &lt;div class="author-avatar"&gt;K&lt;/div&gt;
            &lt;div class="author-info"&gt;
                &lt;strong&gt;Ditulis oleh Kholil&lt;/strong&gt;
                &lt;span&gt;Konsultan Ekstraksi Data &amp; Technical Developer | Diperbarui: Juli 2026&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- ARTICLE CONTENT --&gt;
        &lt;article class="article-content"&gt;
            &lt;span style="font-size: 0.75rem; font-weight: 700; color: var(--accent-green); letter-spacing: 1px;"&gt;PANDUAN TEKNIS&lt;/span&gt;
            &lt;h2&gt;Arsitektur Ekstraksi Spreadsheet ke Vektor PDF&lt;/h2&gt;
            &lt;p&gt;
                Mengonversi file berektensi &lt;code&gt;.xlsx&lt;/code&gt; menjadi &lt;code&gt;.pdf&lt;/code&gt; secara &lt;em&gt;client-side&lt;/em&gt; memiliki tantangan unik. Berbeda dengan dokumen teks, lembar kerja Excel tidak memiliki batasan kertas (karena format grid aslinya bersifat tak terbatas). 
            &lt;/p&gt;
            &lt;p&gt;
                Tool gratis di atas mengekstraksi data mentah berbasis sel menggunakan engine &lt;strong&gt;SheetJS&lt;/strong&gt;, mendeteksi batas tabel secara dinamis, lalu merender susunan grid secara matematis ke atas kanvas PDF menggunakan fungsi &lt;strong&gt;jsPDF-autotable&lt;/strong&gt;. Hal ini memastikan output PDF berbentuk tabel rapi, bukan sekadar teks acak.
            &lt;/p&gt;
            
            &lt;div class="callout"&gt;
                &lt;strong&gt;&#128161; Limitasi Eksekusi Client-Side (Browser)&lt;/strong&gt;
                &lt;p&gt;Karena pemrosesan 100% terjadi di RAM perangkat Anda demi privasi absolut, skema warna sel yang kompleks (Conditional Formatting), Grafik, dan Makro VBA akan diabaikan. Engine kami hanya berfokus pada pelestarian struktur baris dan kolom tabel data.&lt;/p&gt;
            &lt;/div&gt;

            &lt;h2&gt;Praktik Terbaik Sebelum Konversi&lt;/h2&gt;
            &lt;p&gt;Untuk memastikan kanvas PDF tercetak proporsional dan tidak terpotong, ikuti panduan pra-konversi berikut:&lt;/p&gt;
            &lt;ul&gt;
                &lt;li&gt;&lt;strong&gt;Hapus Kolom Kosong:&lt;/strong&gt; Skrip membaca batas akhir data. Sel kosong yang tidak sengaja memiliki spasi di ujung kanan Excel Anda akan menyebabkan tabel PDF menyempit.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Gunakan Orientasi Lanskap Otomatis:&lt;/strong&gt; Konverter ini telah disetel secara otomatis ke mode Landscape (mendatar) untuk mengakomodasi lebar rata-rata lembar kerja analitik.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Format Angka:&lt;/strong&gt; Pastikan nilai desimal dan simbol mata uang (Rp) telah diformat permanen sebagai &lt;em&gt;String&lt;/em&gt; (Teks) di dalam sel Excel jika Anda menemukan inkonsistensi rendering nominal.&lt;/li&gt;
            &lt;/ul&gt;

            &lt;div class="changelog-box"&gt;
                &lt;h3&gt;Log Rilis Ekstraktor (Changelog):&lt;/h3&gt;
                &lt;ul style="color: var(--text-dark-muted); font-size: 0.95rem; margin-bottom: 0;"&gt;
                    &lt;li&gt;&lt;strong&gt;v1.3 (Juli 2026):&lt;/strong&gt; Migrasi ke SaaS UI (Mode Gelap) &amp; Penambahan library &lt;code&gt;jspdf-autotable&lt;/code&gt; untuk menyusun ulang grid tabel agar PDF memiliki garis batas yang presisi.&lt;/li&gt;
                    &lt;li&gt;&lt;strong&gt;v1.1 (Juni 2026):&lt;/strong&gt; Optimasi pembacaan memori SheetJS untuk mencegah penumpukan &lt;em&gt;cache&lt;/em&gt; DOM pada peramban seluler.&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;

            &lt;!-- FAQ SECTION --&gt;
            &lt;div class="faq-container"&gt;
                &lt;h2 style="text-align: center; border-bottom:none;"&gt;Troubleshooting &amp; FAQ Teknis&lt;/h2&gt;
                
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-question"&gt;Mengapa hasil PDF saya ada garis tabelnya, tapi warna sel asli Excel hilang? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-answer"&gt;
                        Untuk mencegah kebocoran memori (&lt;em&gt;memory leak&lt;/em&gt;) di peramban saat membaca tabel berisi puluhan ribu baris, algoritma kami difokuskan murni pada ekstraksi data alfanumerik. Data ini kemudian di-render ulang menggunakan kerangka otomatis (auto-table border). Pemformatan warna bawaan Excel secara sengaja diabaikan agar skrip tetap ringan.
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-question"&gt;Kolom tabel di PDF terpotong/menumpuk, apa solusinya? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-answer"&gt;
                        Hal ini lazim terjadi jika Anda mencoba mencetak lembar kerja yang memiliki lebih dari 15 kolom panjang ke dalam satu rentang halaman A4. Skrip kami akan memaksa pembungkusan teks (&lt;em&gt;text-wrapping&lt;/em&gt;). Jika gagal, teks akan mengecil. Solusi terbaik adalah membagi rentang data Anda menjadi dua &lt;em&gt;Sheet&lt;/em&gt; terpisah.
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-question"&gt;File Excel saya 10MB, mengapa browser saya "Not Responding"? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-answer"&gt;
                        Berkas &lt;code&gt;.xlsx&lt;/code&gt; pada dasarnya adalah format arsip zip yang sangat terkompresi. File 10MB ketika diekstrak ke dalam memori RAM peramban bisa membengkak menjadi lebih dari 150MB alokasi objek JSON. Harap tutup tab browser yang tidak perlu untuk membebaskan memori sementara saat proses berjalan.
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/article&gt;

    &lt;/main&gt;

    &lt;!-- FOOTER --&gt;
    &lt;footer class="footer"&gt;
        &lt;h4&gt;Kholil Tools&lt;/h4&gt;
        &lt;p&gt;Utilitas Manajemen Dokumen Aman, Privasi Terjamin Lokal.&lt;/p&gt;
        &lt;div class="footer-links"&gt;
            &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Word ke PDF&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/power-point-to-pdf.html"&gt;PPT ke PDF&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Privacy Policy&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/terms"&gt;Terms&lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="copyright"&gt;
            © 2026 kholil.biz.id - Seluruh Hak Cipta Dilindungi.
        &lt;/div&gt;
    &lt;/footer&gt;

    &lt;!-- LIBRARIES BEBAS RENDER-BLOCKING (Defer) --&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js" defer&gt;&lt;/script&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js" defer&gt;&lt;/script&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.31/jspdf.plugin.autotable.min.js" defer&gt;&lt;/script&gt;

    &lt;!-- SCRIPT LOGIKA UTAMA --&gt;
    &lt;script&gt;
        document.addEventListener('DOMContentLoaded', () =&gt; {
            // FAQ Accordion
            const faqItems = document.querySelectorAll('.faq-question');
            faqItems.forEach(item =&gt; {
                item.addEventListener('click', () =&gt; {
                    const parent = item.parentElement;
                    parent.classList.toggle('active');
                });
            });

            // Konverter Logika
            const excelFileInput = document.getElementById('excelFile');
            const convertBtn     = document.getElementById('convertBtn');
            const downloadBtn    = document.getElementById('downloadBtn');
            const statusEl       = document.getElementById('status');
            const fileNameEl     = document.getElementById('fileName');
            const dropArea       = document.getElementById('dropArea');
            const workspace      = document.getElementById('workspace');

            let selectedFile = null;

            dropArea.addEventListener('dragover', e =&gt; { e.preventDefault(); dropArea.classList.add('active'); });
            dropArea.addEventListener('dragleave', () =&gt; dropArea.classList.remove('active'));
            dropArea.addEventListener('drop', e =&gt; {
                e.preventDefault(); dropArea.classList.remove('active');
                const file = e.dataTransfer.files[0];
                if (file &amp;&amp; (file.name.endsWith('.xlsx') || file.name.endsWith('.xls'))) {
                    handleFile(file);
                } else {
                    alert('Silakan pilih file Excel (.xlsx / .xls) yang valid!');
                }
            });

            excelFileInput.addEventListener('change', e =&gt; { if (e.target.files[0]) handleFile(e.target.files[0]); });

            function handleFile(file) {
                selectedFile = file;
                fileNameEl.textContent = '&#128202; ' + file.name;
                statusEl.innerHTML = `File siap diproses memori lokal: &lt;strong&gt;${file.name}&lt;/strong&gt;`;
                dropArea.style.display = 'none';
                workspace.style.display = 'block';
                convertBtn.disabled = false;
                downloadBtn.style.display = 'none';
            }

            convertBtn.addEventListener('click', async () =&gt; {
                if (!selectedFile) return;

                // Periksa apakah modul berhasil dimuat
                if(typeof XLSX === 'undefined' || typeof window.jspdf === 'undefined') {
                    statusEl.innerHTML = '&lt;span style="color:#ef4444;"&gt;❌ Modul pemroses sedang dimuat. Harap tunggu beberapa detik lalu klik kembali.&lt;/span&gt;';
                    return;
                }

                convertBtn.disabled = true;
                convertBtn.textContent = '⏳ Membangun Vektor Tabel...';
                statusEl.textContent = 'Memetakan data sel Excel menjadi kerangka PDF...';

                try {
                    const arrayBuffer = await selectedFile.arrayBuffer();
                    const workbook = XLSX.read(arrayBuffer, { type: 'array' });
                    const firstSheetName = workbook.SheetNames[0];
                    const worksheet = workbook.Sheets[firstSheetName];
                    
                    // Ekstrak data mentah
                    const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
                    
                    if(jsonData.length === 0) throw new Error("Lembar kerja Excel kosong.");

                    const { jsPDF } = window.jspdf;
                    // Format Landscape sangat ideal untuk Excel
                    const pdf = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' });

                    // Membagi header baris pertama dan sisa data
                    const tableHeaders = [jsonData[0]];
                    const tableBody = jsonData.slice(1);

                    // Render menggunakan AutoTable
                    pdf.autoTable({
                        head: tableHeaders,
                        body: tableBody,
                        startY: 25,
                        theme: 'grid',
                        styles: { fontSize: 8, cellPadding: 2, overflow: 'linebreak' },
                        headStyles: { fillColor: [16, 185, 129] }, // Hijau menyesuaikan tema
                        margin: { top: 25 },
                        didDrawPage: function (data) {
                            // Header tiap halaman
                            pdf.setFontSize(14);
                            pdf.setTextColor(40);
                            pdf.setFont('helvetica', 'bold');
                            pdf.text(`Laporan Data: ${firstSheetName}`, data.settings.margin.left, 15);
                            
                            // Footer tiap halaman
                            pdf.setFontSize(8);
                            pdf.setFont('helvetica', 'normal');
                            let footerStr = "Halaman " + pdf.internal.getNumberOfPages() + " | Dibuat oleh Kholil Tools";
                            pdf.text(footerStr, data.settings.margin.left, pdf.internal.pageSize.getHeight() - 10);
                        }
                    });

                    const pdfBlob = pdf.output('blob');
                    const url = URL.createObjectURL(pdfBlob);
                    
                    // Gunakan regex untuk menamai ulang file output
                    const timestamp = new Date().getTime();
                    const outName = selectedFile.name.replace(/\.[^/.]+$/, "") + `_kholil_grid_${timestamp}.pdf`;

                    downloadBtn.href = url;
                    downloadBtn.download = outName;
                    downloadBtn.style.display = 'inline-block';
                    convertBtn.style.display = 'none';

                    statusEl.innerHTML = '&lt;span style="color:#10b981;"&gt;✅ Berhasil! Grid tabel Excel telah dipertahankan secara proporsional.&lt;/span&gt;';
                } catch (err) {
                    console.error(err);
                    statusEl.innerHTML = '&lt;span style="color:#ef4444;"&gt;❌ Gagal memproses file. Pastikan Excel tidak rusak atau dikunci sandi.&lt;/span&gt;';
                    convertBtn.disabled = false;
                    convertBtn.textContent = 'Membangun Vektor Tabel PDF';
                }
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/excel-to-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-7818306951634958021</guid><pubDate>Thu, 09 Apr 2026 21:46:00 +0000</pubDate><atom:updated>2026-07-24T08:40:28.025-07:00</atom:updated><title>Power point to PDF </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;meta name="description" content="Parser OOXML PPTX ke PDF. Render slide presentasi langsung di RAM browser Anda dengan akurasi vektor tanpa upload ke server eksternal."&gt;
    &lt;meta name="keywords" content="konversi pptx ke pdf aman, client side powerpoint parser, OOXML to PDF, render presentasi browser"&gt;
    &lt;title&gt;PowerPoint (PPTX) ke PDF Parser | Rendering Client-Side Aman&lt;/title&gt;

    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;family=DM+Mono:wght@400;500&amp;display=swap" rel="stylesheet"&gt;

    &lt;style&gt;
        :root {
            --orange:       #F26419;
            --orange-light: #FF8C42;
            --orange-dim:   #FEF0E7;
            --ink:          #1A1A2E;
            --ink-mid:      #444460;
            --ink-soft:     #5c5c77;
            --surface:      #FFFFFF;
            --border:       #E8E8F0;
            --green:        #1DB954;
            --red:          #E53935;
            --radius:       14px;
            --shadow:       0 4px 24px rgba(26,26,46,.08);
        }

        *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

        body {
            font-family: 'Inter', sans-serif;
            background: #F7F7FB;
            color: var(--ink);
            line-height: 1.7;
            min-height: 100vh;
        }

        nav {
            background: var(--surface);
            border-bottom: 1px solid var(--border);
            padding: 0 24px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            height: 60px;
            position: sticky;
            top: 0;
            z-index: 100;
        }
        .nav-left { display: flex; align-items: center; gap: 8px; }
        .logo { font-size: 1.1rem; font-weight: 800; color: var(--orange); text-decoration: none; }
        .logo span { color: var(--ink); }
        .nav-links { display: flex; gap: 20px; }
        .nav-links a { color: var(--ink-mid); text-decoration: none; font-size: 0.9rem; font-weight: 500; transition: color 0.2s; }
        .nav-links a:hover { color: var(--orange); }

        .hero {
            background: var(--surface);
            border-bottom: 1px solid var(--border);
            padding: 50px 24px;
            text-align: center;
        }
        .hero-eyebrow {
            display: inline-flex; align-items: center; gap: 6px;
            background: var(--orange-dim); color: var(--orange);
            font-size: .78rem; font-weight: 600; letter-spacing: .08em;
            text-transform: uppercase; padding: 5px 14px; border-radius: 99px; margin-bottom: 20px;
        }
        h1 { font-size: clamp(1.8rem, 4vw, 2.8rem); font-weight: 800; line-height: 1.2; color: var(--ink); margin-bottom: 16px; }
        h1 em { font-style: normal; color: var(--orange); }
        .hero p { font-size: 1.05rem; color: var(--ink-mid); max-width: 600px; margin: 0 auto; }

        .main { max-width: 800px; margin: -30px auto 40px; padding: 0 20px; position: relative; z-index: 10; }
        .card { background: var(--surface); border: 1px solid var(--border); border-radius: 20px; box-shadow: var(--shadow); overflow: hidden; }

        .drop-zone {
            padding: 50px 30px; border: 2px dashed var(--border); border-radius: 16px;
            margin: 24px; text-align: center; cursor: pointer; transition: all .2s; background: #FAFAFC; position: relative;
        }
        .drop-zone.drag-over { border-color: var(--orange); background: var(--orange-dim); }
        .drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
        .dz-title { font-size: 1.1rem; font-weight: 600; color: var(--ink); margin-top: 12px; margin-bottom: 6px; }
        .dz-sub { font-size: .85rem; color: var(--ink-soft); }

        #fileBar { display: none; align-items: center; gap: 14px; background: var(--orange-dim); padding: 14px 28px; border-top: 1px solid #F5D5BF; border-bottom: 1px solid #F5D5BF; }
        #fileBar .fi-name { font-weight: 600; color: var(--ink); }
        #fileBar .fi-size { color: var(--ink-soft); font-size: .82rem; }
        #fileBar .fi-clear { margin-left: auto; background: none; border: none; cursor: pointer; font-size: 1.2rem; padding: 4px; border-radius: 6px; }

        .actions { padding: 24px 28px; display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
        .btn-action {
            flex: 1 1 200px; max-width: 320px; padding: 14px 24px; border: none; border-radius: 99px;
            font-size: 1rem; font-weight: 700; cursor: pointer; text-align: center; transition: all .2s;
        }
        #convertBtn { background: var(--orange); color: #fff; box-shadow: 0 4px 18px rgba(242,100,25,.3); }
        #convertBtn:hover:not(:disabled) { background: var(--orange-light); transform: translateY(-2px); }
        #convertBtn:disabled { background: #C8C8D8; box-shadow: none; cursor: not-allowed; }

        #downloadBtn { display: none; background: var(--green); color: #fff; text-decoration: none; box-shadow: 0 4px 18px rgba(29,185,84,.3); }
        #downloadBtn:hover { transform: translateY(-2px); }

        #progressWrap { display: none; padding: 0 28px 24px; }
        .prog-label { font-size: .85rem; font-weight: 500; display: flex; justify-content: space-between; margin-bottom: 8px; }
        .prog-bar-bg { background: var(--border); border-radius: 99px; height: 8px; }
        .prog-bar-fill { background: linear-gradient(90deg, var(--orange), var(--orange-light)); height: 100%; border-radius: 99px; width: 0%; transition: width 0.3s; }

        #statusMsg { display: none; margin: 0 28px 20px; padding: 12px 16px; border-radius: var(--radius); font-size: .9rem; font-weight: 500; }
        #statusMsg.success { background: #EDFBF3; color: #157A3E; border: 1px solid #B8EDD1; }
        #statusMsg.error   { background: #FEF0F0; color: var(--red); border: 1px solid #FAC5C5; }
        #statusMsg.info    { background: var(--orange-dim); color: var(--orange); border: 1px solid #F5D5BF; }

        #previewSection { display: none; padding: 0 28px 24px; }
        .preview-strip { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 8px; scrollbar-width: thin; }
        .preview-thumb { flex-shrink: 0; width: 140px; height: 90px; border-radius: 8px; border: 1px solid var(--border); position: relative; background: #fff; }
        .preview-thumb img { width: 100%; height: 100%; object-fit: contain; }

        /* ── PROOF SECTION (GALLERY) ── */
        .proof-section {
            background: var(--surface);
            border-radius: 20px;
            padding: 32px;
            margin-top: 32px;
            border: 1px solid var(--border);
            box-shadow: var(--shadow);
        }
        .proof-title {
            text-align: center;
            font-size: 1.15rem;
            font-weight: 700;
            margin-bottom: 30px;
            color: var(--ink);
        }
        .proof-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 20px;
        }
        .proof-step {
            text-align: center;
        }
        .proof-step-img-wrap {
            background: #FAFAFC;
            padding: 8px;
            border-radius: 12px;
            border: 1px solid var(--border);
            margin-bottom: 16px;
        }
        .proof-step img {
            width: 100%;
            height: auto;
            border-radius: 6px;
            display: block;
        }
        .proof-step-num {
            display: inline-block;
            background: var(--green);
            color: white;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            font-size: 0.8rem;
            font-weight: 700;
            line-height: 24px;
            margin-bottom: 8px;
        }
        .proof-step p {
            font-size: 0.85rem;
            color: var(--ink-mid);
            font-weight: 600;
        }

        .author-box {
            max-width: 800px; margin: 0 auto 40px; padding: 20px; background: var(--surface);
            border: 1px solid var(--border); border-radius: var(--radius); display: flex; align-items: center; gap: 15px;
        }
        .author-avatar { width: 50px; height: 50px; background: var(--ink); color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.2rem; }

        .article-content { max-width: 800px; margin: 0 auto; padding: 0 20px 80px; }
        .article-content h2 { font-size: 1.4rem; color: var(--ink); margin: 30px 0 16px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
        .article-content p { margin-bottom: 16px; color: var(--ink-mid); }
        .article-content ul { margin: 0 0 16px 20px; color: var(--ink-mid); }

        .faq-item { border-bottom: 1px solid var(--border); padding: 16px 0; }
        .faq-question { font-size: 1rem; font-weight: 700; color: var(--ink); cursor: pointer; display: flex; justify-content: space-between; border: none; background: none; width: 100%; text-align: left; }
        .faq-answer { max-height: 0; overflow: hidden; transition: max-height 0.3s; color: var(--ink-mid); font-size: 0.95rem; }
        .faq-item.active .faq-answer { max-height: 400px; padding-top: 10px; }

        footer { background: var(--surface); border-top: 1px solid var(--border); padding: 40px 24px; text-align: center; }
        .footer-links { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; margin-bottom: 16px; }
        .footer-links a { color: var(--ink-mid); text-decoration: none; font-size: 0.9rem; }
        .footer-links a:hover { color: var(--orange); }

        @media (max-width: 600px) { 
            .nav-links { display: none; } 
            .proof-grid { grid-template-columns: repeat(2, 1fr); }
            .proof-section { padding: 24px 16px; }
        }
    &lt;/style&gt;

    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "SoftwareApplication",
      "name": "Kholil PPTX to PDF Renderer",
      "applicationCategory": "UtilitiesApplication",
      "operatingSystem": "Web Browser",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "author": {
        "@type": "Person",
        "name": "Kholil",
        "url": "https://www.kholil.biz.id/about"
      }
    }
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;nav&gt;
    &lt;div class="nav-left"&gt;
        &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="nav-links"&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Word ke PDF&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/excel-to-pdf.html"&gt;Excel ke PDF&lt;/a&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;div class="hero"&gt;
    &lt;div class="hero-eyebrow"&gt;Akselerasi Rendering Browser&lt;/div&gt;
    &lt;h1&gt;Urai &lt;em&gt;PowerPoint&lt;/em&gt; (PPTX)&lt;br&gt;ke Format PDF Vektor&lt;/h1&gt;
    &lt;p&gt;Engine JS kami merekonstruksi hierarki XML presentasi Anda menjadi lapisan gambar statis dan vektor langsung dari memori RAM (tanpa intervensi server).&lt;/p&gt;
&lt;/div&gt;

&lt;main&gt;
&lt;div class="main"&gt;
    &lt;!-- KOTAK ALAT UTAMA --&gt;
    &lt;div class="card"&gt;
        &lt;div class="drop-zone" id="dropZone" aria-label="Area Unggah File"&gt;
            &lt;input type="file" id="pptFile" accept=".pptx,application/vnd.openxmlformats-officedocument.presentationml.presentation" aria-label="Unggah Dokumen PowerPoint"&gt;
            &lt;span class="dz-icon" aria-hidden="true"&gt;&#128450;️&lt;/span&gt;
            &lt;p class="dz-title"&gt;Seret file PPTX ke kanvas ini&lt;/p&gt;
            &lt;p class="dz-sub"&gt;Algoritma mengeksekusi format &lt;strong&gt;.pptx&lt;/strong&gt; (Standar OOXML)&lt;/p&gt;
        &lt;/div&gt;

        &lt;div id="fileBar"&gt;
            &lt;span class="fi-icon" aria-hidden="true"&gt;&#128202;&lt;/span&gt;
            &lt;div&gt;
                &lt;div class="fi-name" id="fiName"&gt;&lt;/div&gt;
                &lt;div class="fi-size" id="fiSize"&gt;&lt;/div&gt;
            &lt;/div&gt;
            &lt;button class="fi-clear" id="fiClear" aria-label="Batalkan file"&gt;✕&lt;/button&gt;
        &lt;/div&gt;

        &lt;div id="statusMsg" aria-live="polite"&gt;&lt;/div&gt;

        &lt;div id="progressWrap" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"&gt;
            &lt;div class="prog-label"&gt;
                &lt;span id="progText"&gt;Memparsing DOM struktur slide…&lt;/span&gt;
                &lt;span id="progPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="prog-bar-bg"&gt;&lt;div class="prog-bar-fill" id="progFill"&gt;&lt;/div&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;div id="previewSection"&gt;
            &lt;div class="preview-strip" id="previewStrip"&gt;&lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="actions"&gt;
            &lt;button id="convertBtn" class="btn-action" disabled&gt;&#128640; Jalankan Kompilasi&lt;/button&gt;
            &lt;a id="downloadBtn" class="btn-action" href="#" download=""&gt;⬇️ Unduh PDF Statis&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- BUKTI TOOL BERFUNGSI (IMAGE GALLERY) --&gt;
    &lt;div class="proof-section"&gt;
        &lt;div class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/div&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-step"&gt;
                &lt;div class="proof-step-img-wrap"&gt;
                    &lt;img src="https://i.ibb.co.com/fY5tQP6B/Screenshot-2026-07-23-19-07-18-704-com-android-chrome.jpg" alt="Upload file PPTX lancar" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;span class="proof-step-num"&gt;1&lt;/span&gt;
                &lt;p&gt;Unggah Tanpa Kendala&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-step"&gt;
                &lt;div class="proof-step-img-wrap"&gt;
                    &lt;img src="https://i.ibb.co.com/Cpvn7p1Z/Screenshot-2026-07-23-19-07-22-633-com-android-chrome.jpg" alt="Preview PDF berfungsi" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;span class="proof-step-num"&gt;2&lt;/span&gt;
                &lt;p&gt;Pratinjau Slide Normal&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-step"&gt;
                &lt;div class="proof-step-img-wrap"&gt;
                    &lt;img src="https://i.ibb.co.com/XZ7yHkxB/Screenshot-2026-07-23-18-42-40-654-com-android-chrome.jpg" alt="Tombol download PDF" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;span class="proof-step-num"&gt;3&lt;/span&gt;
                &lt;p&gt;Unduh Berfungsi Normal&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-step"&gt;
                &lt;div class="proof-step-img-wrap"&gt;
                    &lt;img src="https://i.ibb.co.com/nMyXsSSM/Screenshot-2026-07-23-19-07-33-411-com-google-android-apps-docs.jpg" alt="Hasil akhir PDF tidak eror" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;span class="proof-step-num"&gt;4&lt;/span&gt;
                &lt;p&gt;Hasil Akhir Tanpa Eror&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class="author-box"&gt;
    &lt;div class="author-avatar"&gt;K&lt;/div&gt;
    &lt;div&gt;
        &lt;strong style="display:block; color:var(--ink); font-size:1.05rem;"&gt;Ditulis oleh Kholil&lt;/strong&gt;
        &lt;span style="color:var(--ink-soft); font-size:0.85rem;"&gt;Konsultan Ekstraksi Data &amp; Technical Developer | Diperbarui: Juli 2026&lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class="article-content"&gt;
    &lt;h2&gt;Dekonstruksi Struktur XML pada Berkas PPTX&lt;/h2&gt;
    &lt;p&gt;
        Format &lt;code&gt;.pptx&lt;/code&gt; pada dasarnya adalah kompresi arsip ZIP berstruktur &lt;em&gt;Office Open XML (OOXML)&lt;/em&gt;. Mengonversinya menjadi dokumen PDF tanpa keterlibatan mesin server memerlukan ekstraksi hierarki DOM internal secara simultan.
    &lt;/p&gt;
    &lt;p&gt;
        Modul yang berjalan di halaman ini membaca direktori maya seperti &lt;code&gt;ppt/slides/&lt;/code&gt; dan &lt;code&gt;ppt/media/&lt;/code&gt; dari peramban lokal Anda. Teks, pengaturan tata letak (*layout*), dan elemen gambar dihitung ulang dari unit EMUs (&lt;em&gt;English Metric Units&lt;/em&gt;) ke metrik piksel berbasis resolusi layar (*Device Pixel Ratio*), dan kemudian di-render menjadi kanvas HTML5.
    &lt;/p&gt;

    &lt;h2&gt;Manajemen Memori (Memory Allocation) saat Rendering&lt;/h2&gt;
    &lt;p&gt;
        Konversi dokumen presentasi dengan resolusi gambar tinggi akan menghabiskan ruang RAM sementara pada perangkat Anda. Untuk file PPTX di atas 20MB yang berisi aset HD, peramban Anda mungkin membatasi proses *thread*. Utilitas ini telah dilengkapi dengan teknik *asynchronous yielding*—memberi jeda peramban sekian milidetik di antara render slide—untuk mencegah terhentinya (&lt;em&gt;freezing&lt;/em&gt;) antarmuka pengguna pada ponsel pintar.
    &lt;/p&gt;

    &lt;h2&gt;Pusat Pemecahan Masalah (FAQ)&lt;/h2&gt;

    &lt;div class="faq-item"&gt;
        &lt;button class="faq-question"&gt;Mengapa font kustom presentasi saya diganti dengan font standar? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-answer"&gt;
            Karena *renderer* klien kami mengeksekusi instruksi dari peramban Anda, modul tidak dapat menarik *embedded subset font* (file TFF/OTF yang disisipkan dalam file PPTX). Kanvas terpaksa kembali ke font *fallback* sistem operasi Anda (seperti Arial, Times New Roman, atau Segoe UI) demi keamanan dan pencegahan pelacakan (*anti-fingerprinting*).
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="faq-item"&gt;
        &lt;button class="faq-question"&gt;Mengapa transisi animasi tidak berfungsi di dalam file PDF? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-answer"&gt;
            PDF secara harfiah dikembangkan untuk standardisasi pencetakan statis (*print-ready*). Spesifikasi ISO pada PDF tidak dapat mempertahankan makro animasi dari presentasi. Mesin kami mengekstrak kondisi akhir statis (*final state*) dari setiap *slide*.
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="faq-item"&gt;
        &lt;button class="faq-question"&gt;Browser saya melambat saat memproses slide ke-40, apa solusinya? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-answer"&gt;
            Ini adalah keterbatasan nyata dari pemrosesan berbasis klien (*client-side*). Menyatukan 40+ kanvas resolusi tinggi ke dalam pustaka JS membutuhkan alokasi memori yang masif. Kami merekomendasikan untuk tidak membuka perangkat lunak lain saat proses sedang berlangsung, atau kompres ukuran gambar di dalam PPTX Anda sebelum dikonversi.
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/main&gt;

&lt;footer&gt;
    &lt;div class="footer-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Utilitas Word&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/excel-to-pdf.html"&gt;Utilitas Excel&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;Tentang Kami&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/terms"&gt;Syarat Ketentuan&lt;/a&gt;
    &lt;/div&gt;
    &lt;p&gt;© 2026 kholil.biz.id. Eksekusi data privasi terlindungi secara lokal.&lt;/p&gt;
&lt;/footer&gt;

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

&lt;script&gt;
document.addEventListener('DOMContentLoaded', () =&gt; {
    document.querySelectorAll('.faq-question').forEach(btn =&gt; {
        btn.addEventListener('click', () =&gt; {
            btn.parentElement.classList.toggle('active');
        });
    });

    const pptFile     = document.getElementById('pptFile');
    const dropZone    = document.getElementById('dropZone');
    const fileBar     = document.getElementById('fileBar');
    const fiName      = document.getElementById('fiName');
    const fiSize      = document.getElementById('fiSize');
    const fiClear     = document.getElementById('fiClear');
    const convertBtn  = document.getElementById('convertBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const statusMsg   = document.getElementById('statusMsg');
    const progressWrap= document.getElementById('progressWrap');
    const progFill    = document.getElementById('progFill');
    const progText    = document.getElementById('progText');
    const progPct     = document.getElementById('progPct');
    const previewStrip= document.getElementById('previewStrip');
    const previewSection= document.getElementById('previewSection');

    let selectedFile = null;

    function fmtSize(bytes) {
        if (bytes &lt; 1024) return bytes + ' B';
        if (bytes &lt; 1048576) return (bytes/1024).toFixed(1) + ' KB';
        return (bytes/1048576).toFixed(2) + ' MB';
    }

    function handleFile(file) {
        if (!file) return;
        if (!file.name.endsWith('.pptx')) {
            showStatus('error', '❌ File tidak valid. Algoritma kami berfokus pada protokol XML .pptx.');
            return;
        }
        selectedFile = file;
        fiName.textContent = file.name;
        fiSize.textContent = fmtSize(file.size);
        fileBar.style.display = 'flex';
        convertBtn.disabled = false;
        downloadBtn.style.display = 'none';
        previewSection.style.display = 'none';
        previewStrip.innerHTML = '';
        hideStatus();
        progressWrap.style.display = 'none';
    }

    pptFile.addEventListener('change', e =&gt; handleFile(e.target.files[0]));
    dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
    dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
    dropZone.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropZone.classList.remove('drag-over');
        handleFile(e.dataTransfer.files[0]);
    });

    fiClear.addEventListener('click', () =&gt; {
        selectedFile = null; pptFile.value = ''; fileBar.style.display = 'none';
        convertBtn.disabled = true; downloadBtn.style.display = 'none'; hideStatus();
    });

    function showStatus(type, html) {
        statusMsg.className = type; statusMsg.innerHTML = html; statusMsg.style.display = 'block';
    }
    function hideStatus() { statusMsg.style.display = 'none'; }
    function showProgress(pct, label) {
        progressWrap.style.display = 'block';
        progFill.style.width = pct + '%';
        progPct.textContent = Math.round(pct) + '%';
        progText.textContent = label;
        progressWrap.setAttribute('aria-valuenow', Math.round(pct));
    }

    // ============================================================
    // PARSER PPTX
    // ============================================================

    function parseXml(str) { return (new DOMParser()).parseFromString(str, 'application/xml'); }

    async function getSlideDimensions(zip) {
        try {
            const xml = await zip.file('ppt/presentation.xml').async('text');
            const doc = parseXml(xml);
            const sldSz = doc.getElementsByTagNameNS('*','sldSz')[0];
            if (sldSz) return { cx: parseInt(sldSz.getAttribute('cx') || '9144000'), cy: parseInt(sldSz.getAttribute('cy') || '6858000') };
        } catch(e) {}
        return { cx: 9144000, cy: 6858000 };
    }

    async function getSlideList(zip) {
        const keys = Object.keys(zip.files).filter(k =&gt; k.match(/^ppt\/slides\/slide\d+\.xml$/));
        keys.sort((a,b) =&gt; parseInt(a.match(/\d+/)[0]) - parseInt(b.match(/\d+/)[0]));
        return keys;
    }

    function resolveRelPath(basePath, relTarget) {
        const baseParts = basePath.split('/'); baseParts.pop();
        for (const part of relTarget.split('/')) {
            if (part === '..') baseParts.pop();
            else if (part === '.' || part === '') continue;
            else baseParts.push(part);
        }
        return baseParts.join('/');
    }

    async function getRelsMap(zip, slidePath) {
        const slideFile = slidePath.split('/').pop();
        const relsPath = `ppt/slides/_rels/${slideFile}.rels`;
        const relsFile = zip.file(relsPath);
        const map = {};
        if (!relsFile) return map;
        const xml = await relsFile.async('text');
        const doc = parseXml(xml);
        const rels = doc.getElementsByTagNameNS('*','Relationship');
        for (const rel of Array.from(rels)) {
            const id = rel.getAttribute('Id');
            const target = rel.getAttribute('Target');
            if (id &amp;&amp; target &amp;&amp; !target.startsWith('http')) {
                map[id] = resolveRelPath(slidePath, target);
            }
        }
        return map;
    }

    function getXfrmBox(node, emuToPxX, emuToPxY, fallback) {
        const xfrm = node.getElementsByTagNameNS('*','xfrm')[0];
        let box = Object.assign({}, fallback);
        if (xfrm) {
            const off = xfrm.getElementsByTagNameNS('*','off')[0];
            const ext = xfrm.getElementsByTagNameNS('*','ext')[0];
            if (off) {
                box.x = emuToPxX(parseInt(off.getAttribute('x')));
                box.y = emuToPxY(parseInt(off.getAttribute('y')));
            }
            if (ext) {
                box.w = emuToPxX(parseInt(ext.getAttribute('cx')));
                box.h = emuToPxY(parseInt(ext.getAttribute('cy')));
            }
        }
        return box;
    }

    function getOwnXfrmEmu(node) {
        const xfrm = node.getElementsByTagNameNS('*','xfrm')[0];
        if (!xfrm) return null;
        const off = xfrm.getElementsByTagNameNS('*','off')[0];
        const ext = xfrm.getElementsByTagNameNS('*','ext')[0];
        if (!off || !ext) return null;
        return {
            x: parseInt(off.getAttribute('x')),
            y: parseInt(off.getAttribute('y')),
            w: parseInt(ext.getAttribute('cx')),
            h: parseInt(ext.getAttribute('cy'))
        };
    }

    async function getLayoutXfrmMap(zip, slidePath) {
        const map = {};
        const slideRelsPath = `ppt/slides/_rels/${slidePath.split('/').pop()}.rels`;
        const relsFile = zip.file(slideRelsPath);
        if (!relsFile) return map;
        const relsDoc = parseXml(await relsFile.async('text'));
        const rels = Array.from(relsDoc.getElementsByTagNameNS('*','Relationship'));
        const layoutRel = rels.find(r =&gt; (r.getAttribute('Type') || '').indexOf('slideLayout') !== -1);
        if (!layoutRel) return map;
        const layoutPath = resolveRelPath(slidePath, layoutRel.getAttribute('Target'));
        const layoutFile = zip.file(layoutPath);
        if (!layoutFile) return map;
        const layoutDoc = parseXml(await layoutFile.async('text'));
        const shapes = Array.from(layoutDoc.getElementsByTagNameNS('*','sp'));
        for (const sp of shapes) {
            const box = getOwnXfrmEmu(sp);
            if (!box) continue;
            const ph = sp.getElementsByTagNameNS('*','ph')[0];
            const type = ph ? (ph.getAttribute('type') || 'body') : 'body';
            const idx = ph ? ph.getAttribute('idx') : null;
            if (idx) map[type + '#' + idx] = box;
            if (!map[type]) map[type] = box;
        }
        return map;
    }

    function resolveBoxEmu(node, layoutMap) {
        const own = getOwnXfrmEmu(node);
        if (own) return own;
        const ph = node.getElementsByTagNameNS('*','ph')[0];
        const type = ph ? (ph.getAttribute('type') || 'body') : null;
        const idx = ph ? ph.getAttribute('idx') : null;
        if (type) {
            if (idx &amp;&amp; layoutMap[type + '#' + idx]) return layoutMap[type + '#' + idx];
            if (layoutMap[type]) return layoutMap[type];
        }
        return null;
    }

    function renderTable(node, ctx, emuToPxX, emuToPxY) {
        const tbl = node.getElementsByTagNameNS('*','tbl')[0];
        if (!tbl) return false;
        const boxEmu = getOwnXfrmEmu(node);
        if (!boxEmu) return true;

        const px = { x: emuToPxX(boxEmu.x), y: emuToPxY(boxEmu.y), w: emuToPxX(boxEmu.w), h: emuToPxY(boxEmu.h) };
        const rows = Array.from(tbl.getElementsByTagNameNS('*','tr'));
        if (rows.length === 0) return true;

        const grid = tbl.getElementsByTagNameNS('*','tblGrid')[0];
        const gridCols = grid ? Array.from(grid.getElementsByTagNameNS('*','gridCol')) : [];
        const colWidthsEmu = gridCols.map(c =&gt; parseInt(c.getAttribute('w')) || 0);
        const totalColEmu = colWidthsEmu.reduce((a, b) =&gt; a + b, 0);
        const colWidthsPx = (colWidthsEmu.length &amp;&amp; totalColEmu &gt; 0)
            ? colWidthsEmu.map(w =&gt; (w / totalColEmu) * px.w)
            : null;

        const defaultRowH = px.h / rows.length;
        let curY = px.y;

        rows.forEach((row, ri) =&gt; {
            const hAttr = row.getAttribute('h');
            const rowH = hAttr ? emuToPxY(parseInt(hAttr)) : defaultRowH;
            const cells = Array.from(row.getElementsByTagNameNS('*','tc'));
            const isHeader = ri === 0;
            let curX = px.x;

            cells.forEach((cell, ci) =&gt; {
                const cw = colWidthsPx ? (colWidthsPx[ci] || px.w / cells.length) : (px.w / cells.length);
                ctx.fillStyle = isHeader ? '#3E5C87' : (ri % 2 === 0 ? '#E4E9F2' : '#F4F6FA');
                ctx.fillRect(curX, curY, cw, rowH);
                ctx.strokeStyle = '#C7CEDD';
                ctx.lineWidth = 1;
                ctx.strokeRect(curX, curY, cw, rowH);

                const text = Array.from(cell.getElementsByTagNameNS('*','t')).map(t =&gt; t.textContent).join('');
                if (text) {
                    ctx.fillStyle = isHeader ? '#FFFFFF' : '#1A1A2E';
                    const fSize = Math.max(11, Math.min(20, rowH * 0.4));
                    ctx.font = `${isHeader ? 'bold ' : ''}${fSize}px Arial`;
                    ctx.fillText(text, curX + 8, curY + rowH / 2 + fSize * 0.35);
                }
                curX += cw;
            });
            curY += rowH;
        });
        return true;
    }

    async function renderPicture(node, ctx, relsMap, zip, emuToPxX, emuToPxY, canvasW, canvasH) {
        const blip = node.getElementsByTagNameNS('*','blip')[0];
        if (!blip) return;
        const rId = blip.getAttributeNS('http://schemas.openxmlformats.org/officeDocument/2006/relationships','embed')
                 || blip.getAttribute('r:embed');
        if (!rId || !relsMap[rId]) return;
        const mediaPath = relsMap[rId];
        const mediaFile = zip.file(mediaPath);
        if (!mediaFile) return;

        const box = getXfrmBox(node, emuToPxX, emuToPxY, { x:0, y:0, w:canvasW, h:canvasH });

        let base64;
        try { base64 = await mediaFile.async('base64'); } catch(e) { return; }
        const ext = mediaPath.split('.').pop().toLowerCase();
        const mime = ext==='png' ? 'image/png' : (ext==='jpg'||ext==='jpeg') ? 'image/jpeg' : ext==='gif' ? 'image/gif' : 'image/png';
        const dataUrl = `data:${mime};base64,${base64}`;

        await new Promise((resolve) =&gt; {
            const img = new Image();
            img.onload = () =&gt; { try { ctx.drawImage(img, box.x, box.y, box.w, box.h); } catch(e){} resolve(); };
            img.onerror = () =&gt; resolve();
            img.src = dataUrl;
        });
    }

    function getPhType(node) {
        const ph = node.getElementsByTagNameNS('*','ph')[0];
        return ph ? (ph.getAttribute('type') || 'body') : null;
    }

    function renderShapeText(node, ctx, emuToPxX, emuToPxY, canvasW, layoutMap, fallbackSlot, scale) {
        const txBody = node.getElementsByTagNameNS('*','txBody')[0];
        if (!txBody) return;
        const paragraphs = Array.from(txBody.getElementsByTagNameNS('*','p'));
        if (paragraphs.length === 0) return;

        const phType = (getPhType(node) || '').toLowerCase();
        const isTitle = phType.includes('title');
        const isSubtitle = phType.includes('subtitle') || phType === 'subtitle';

        const emuBox = resolveBoxEmu(node, layoutMap);
        let box;
        if (emuBox) {
            box = { x: emuToPxX(emuBox.x), y: emuToPxY(emuBox.y), w: emuToPxX(emuBox.w), h: emuToPxY(emuBox.h) };
        } else if (isTitle) {
            box = { x: canvasW * 0.07, y: canvasW * 0.05, w: canvasW * 0.86, h: 0 };
        } else if (isSubtitle) {
            box = { x: canvasW * 0.07, y: canvasW * 0.25, w: canvasW * 0.86, h: 0 };
        } else {
            box = { x: canvasW * 0.08, y: canvasW * 0.08 + fallbackSlot * canvasW * 0.18, w: canvasW * 0.84, h: 0 };
        }
        let curY = box.y + scale * 14;

        const defaultPt = isTitle ? 40 : (isSubtitle ? 24 : 18);

        for (const p of paragraphs) {
            const runs = Array.from(p.getElementsByTagNameNS('*','r'));
            let text = '';
            let fontSize = defaultPt * scale, bold = isTitle;
            for (const r of runs) {
                const t = r.getElementsByTagNameNS('*','t')[0];
                if (t) text += t.textContent;
                const rPr = r.getElementsByTagNameNS('*','rPr')[0];
                if (rPr) {
                    const sz = rPr.getAttribute('sz');
                    if (sz) fontSize = Math.max(10 * scale, (parseInt(sz) / 100) * scale);
                    if (rPr.getAttribute('b') === '1') bold = true;
                    if (rPr.getAttribute('b') === '0') bold = false;
                }
            }
            const lineHeight = fontSize * 1.35;
            if (!text.trim()) { curY += lineHeight * 0.6; continue; }

            ctx.fillStyle = '#1A1A2E';
            ctx.font = `${bold ? 'bold ' : ''}${fontSize}px Arial`;

            const words = text.split(' ');
            let line = '';
            for (const word of words) {
                const test = line + word + ' ';
                if (ctx.measureText(test).width &gt; box.w &amp;&amp; line) {
                    ctx.fillText(line, box.x, curY);
                    curY += lineHeight;
                    line = word + ' ';
                } else line = test;
            }
            if (line) { ctx.fillText(line, box.x, curY); curY += lineHeight; }
        }
    }

    async function renderSlideToCanvas(zip, slidePath, slideDim, canvasW, canvasH, scale) {
        const canvas = document.createElement('canvas');
        canvas.width = canvasW;
        canvas.height = canvasH;
        const ctx = canvas.getContext('2d');
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, canvasW, canvasH);

        const xml = await zip.file(slidePath).async('text');
        const doc = parseXml(xml);
        const relsMap = await getRelsMap(zip, slidePath);
        const layoutMap = await getLayoutXfrmMap(zip, slidePath);
        const spTree = doc.getElementsByTagNameNS('*','spTree')[0];
        if (!spTree) return canvas;

        const emuToPxX = (emu) =&gt; (emu / slideDim.cx) * canvasW;
        const emuToPxY = (emu) =&gt; (emu / slideDim.cy) * canvasH;

        let fallbackSlot = 0;
        for (const node of Array.from(spTree.children)) {
            const tag = node.localName;
            if (tag === 'pic') {
                await renderPicture(node, ctx, relsMap, zip, emuToPxX, emuToPxY, canvasW, canvasH);
            } else if (tag === 'graphicFrame') {
                const wasTable = renderTable(node, ctx, emuToPxX, emuToPxY);
                if (!wasTable) {
                    if (!resolveBoxEmu(node, layoutMap)) fallbackSlot++;
                    renderShapeText(node, ctx, emuToPxX, emuToPxY, canvasW, layoutMap, fallbackSlot, scale);
                }
            } else if (tag === 'sp') {
                if (!resolveBoxEmu(node, layoutMap)) fallbackSlot++;
                renderShapeText(node, ctx, emuToPxX, emuToPxY, canvasW, layoutMap, fallbackSlot, scale);
            }
        }
        return canvas;
    }

    convertBtn.addEventListener('click', async () =&gt; {
        if (!selectedFile) return;

        if(typeof JSZip === 'undefined' || typeof window.jspdf === 'undefined') {
            showStatus('error', '❌ Pustaka modul belum termuat sempurna. Harap tunggu dan coba lagi.');
            return;
        }

        convertBtn.disabled = true;
        hideStatus();
        previewStrip.innerHTML = '';
        showProgress(5, 'Inisialisasi ekstraktor DOM...');

        try {
            const arrayBuffer = await selectedFile.arrayBuffer();
            const zip = await JSZip.loadAsync(arrayBuffer);
            const slideDim = await getSlideDimensions(zip);
            const slideList = await getSlideList(zip);

            if (slideList.length === 0) throw new Error("Struktur XML slide tidak terdeteksi.");

            const { jsPDF } = window.jspdf;
            const EMU_PT = 12700;
            const pdfW = slideDim.cx / EMU_PT;
            const pdfH = slideDim.cy / EMU_PT;
            const isWide = pdfW &gt; pdfH;
            const pdf = new jsPDF({ orientation: isWide ? 'landscape' : 'portrait', unit: 'pt', format: [pdfW, pdfH] });

            const RENDER_SCALE = 2.2; 
            const canvasW = Math.round(pdfW * RENDER_SCALE);
            const canvasH = Math.round(pdfH * RENDER_SCALE);

            for (let i = 0; i &lt; slideList.length; i++) {
                showProgress(10 + (i/slideList.length*80), `Merender slide ${i + 1}/${slideList.length}...`);

                // ASYNC YIELDING - mencegah Browser UI membeku
                await new Promise(resolve =&gt; setTimeout(resolve, 20));

                const canvas = await renderSlideToCanvas(zip, slideList[i], slideDim, canvasW, canvasH, RENDER_SCALE);

                if (i &gt; 0) pdf.addPage([pdfW, pdfH]);
                pdf.addImage(canvas.toDataURL('image/jpeg', 0.85), 'JPEG', 0, 0, pdfW, pdfH);

                if (i &lt; 8) {
                    const thumb = document.createElement('div');
                    thumb.className = 'preview-thumb';
                    thumb.innerHTML = `&lt;img src="${canvas.toDataURL('image/jpeg', 0.4)}" alt="Preview Slide ${i+1}"&gt;`;
                    previewStrip.appendChild(thumb);
                }
            }

            showProgress(98, 'Kompilasi Vektor menjadi PDF...');
            const pdfBlob = pdf.output('blob');
            const url = URL.createObjectURL(pdfBlob);

            downloadBtn.href = url;
            downloadBtn.download = selectedFile.name.replace(/\.pptx$/i, '_kholil.pdf');
            downloadBtn.style.display = 'inline-block';
            convertBtn.style.display = 'none';
            previewSection.style.display = 'block';

            showProgress(100, 'Kompilasi Selesai');
            showStatus('success', `✅ Berhasil merekonstruksi &lt;strong&gt;${slideList.length} slide&lt;/strong&gt;.`);
        } catch (err) {
            console.error(err);
            showStatus('error', `❌ Kompilasi gagal: ${err.message}`);
        }
        convertBtn.disabled = false;
    });
});
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/power-point-to-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-8796190368727723372</guid><pubDate>Thu, 09 Apr 2026 21:44:28 +0000</pubDate><atom:updated>2026-07-23T06:30:33.875-07:00</atom:updated><title>Word to PDF </title><description>&lt;style&gt;
    :root {
        --brand:       #4f46e5;
        --brand-dark:  #3730a3;
        --brand-light: #eef2ff;
        --success:     #16a34a;
        --danger:      #dc2626;
        --text:        #111827;
        --muted:       #4b5563;
        --border:      #e5e7eb;
        --bg:          #f9fafb;
        --white:       #ffffff;
        --radius-lg:   14px;
        --radius-xl:   20px;
        --shadow-sm:   0 1px 3px rgba(0,0,0,.08);
        --shadow-md:   0 4px 16px rgba(0,0,0,.10);
    }

    /* ── HERO ── */
    .hero {
        background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
        padding: 40px 20px;
        text-align: center;
        border-radius: var(--radius-xl);
        margin-bottom: 32px;
        border: 1px solid #c7d2fe;
    }
    .hero-badge {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        background: var(--white);
        border: 1px solid #c7d2fe;
        color: var(--brand);
        font-size: .8rem;
        font-weight: 600;
        padding: 5px 14px;
        border-radius: 99px;
        margin-bottom: 20px;
    }
    .hero h1 {
        font-family: 'Plus Jakarta Sans', sans-serif;
        font-size: clamp(1.8rem, 4vw, 2.4rem);
        font-weight: 800;
        color: var(--text);
        line-height: 1.2;
        margin-bottom: 16px;
    }
    .hero h1 span { color: var(--brand); }
    .hero-sub {
        font-size: 1rem;
        color: #374151;
        max-width: 600px;
        margin: 0 auto 24px;
    }
    .trust-pills {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 10px;
    }
    .pill {
        background: var(--white);
        border: 1px solid #c7d2fe;
        color: #374151;
        font-size: .82rem;
        font-weight: 600;
        padding: 6px 14px;
        border-radius: 99px;
    }

    /* ── MAIN LAYOUT ── */
    .page-body {
        display: grid;
        grid-template-columns: 1fr 340px;
        gap: 32px;
        align-items: start;
        margin-bottom: 40px;
    }

    /* ── TOOL CARD ── */
    .tool-card {
        background: var(--white);
        border: 1px solid var(--border);
        border-radius: var(--radius-xl);
        box-shadow: var(--shadow-md);
        overflow: hidden;
    }
    .tool-header {
        padding: 24px 28px 20px;
        border-bottom: 1px solid var(--border);
        display: flex;
        align-items: center;
        gap: 12px;
    }
    .tool-header-icon {
        width: 44px;
        height: 44px;
        background: var(--brand-light);
        border-radius: 10px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 1.4rem;
    }
    .tool-header h2 { font-size: 1.1rem; font-weight: 700; color: var(--text); margin: 0; }
    .tool-header p { font-size: .82rem; color: var(--muted); margin: 2px 0 0 0; }
    .tool-body { padding: 28px; }

    /* ── DROP ZONE ── */
    .drop-zone {
        border: 2px dashed #c7d2fe;
        border-radius: var(--radius-lg);
        padding: 44px 24px;
        text-align: center;
        background: var(--brand-light);
        cursor: pointer;
        transition: all .2s;
        position: relative;
    }
    .drop-zone:hover, .drop-zone.drag-over { border-color: var(--brand); background: #e0e7ff; }
    .drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
    .drop-icon { font-size: 2.8rem; margin-bottom: 12px; display: block; }
    .drop-zone h3 { font-size: 1rem; font-weight: 600; color: var(--text); margin: 0 0 6px 0; }
    .drop-zone p { font-size: .85rem; color: var(--muted); margin: 0; }
    .drop-zone .browse-link { color: var(--brand); font-weight: 600; text-decoration: underline; }

    /* ── FILE INFO STRIP ── */
    .file-strip {
        display: none; align-items: center; gap: 12px; margin-top: 16px; padding: 14px 16px;
        background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: var(--radius-lg);
    }
    .file-strip.visible { display: flex; }
    .file-strip-icon { font-size: 1.6rem; }
    .file-strip-name { font-weight: 600; font-size: .9rem; color: var(--text); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .file-strip-size { font-size: .8rem; color: var(--muted); }
    .remove-file { background: none; border: none; cursor: pointer; color: var(--muted); font-size: 1rem; padding: 4px; border-radius: 6px; transition: color .2s; }
    .remove-file:hover { color: var(--danger); }

    /* ── OPTIONS ── */
    .options-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 20px; }
    .option-group label { display: block; font-size: .82rem; font-weight: 600; color: var(--text); margin-bottom: 6px; }
    .option-group select {
        width: 100%; padding: 9px 12px; border: 1px solid var(--border); border-radius: 8px;
        font-size: .88rem; font-family: inherit; color: var(--text); background: var(--white);
        appearance: none;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234b5563' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
        background-repeat: no-repeat; background-position: right 10px center; cursor: pointer;
    }
    .option-group select:focus { outline: 2px solid var(--brand); border-color: transparent; }

    /* ── PROGRESS ── */
    .progress-wrap { display: none; margin-top: 20px; }
    .progress-wrap.visible { display: block; }
    .progress-label { display: flex; justify-content: space-between; font-size: .82rem; color: var(--muted); margin-bottom: 6px; font-weight: 600; }
    .progress-bar-bg { height: 6px; background: var(--border); border-radius: 99px; overflow: hidden; }
    .progress-bar-fill { height: 100%; background: linear-gradient(90deg, var(--brand), #818cf8); border-radius: 99px; width: 0%; transition: width .3s ease; }

    /* ── STATUS ── */
    .status-box { margin-top: 16px; padding: 12px 16px; border-radius: 10px; font-size: .88rem; font-weight: 600; display: none; }
    .status-box.visible { display: block; }
    .status-box.success { background: #f0fdf4; color: var(--success); border: 1px solid #bbf7d0; }
    .status-box.error   { background: #fef2f2; color: var(--danger);  border: 1px solid #fecaca; }
    .status-box.info    { background: var(--brand-light); color: var(--brand); border: 1px solid #c7d2fe; }

    /* ── BUTTONS ── */
    .btn-row { display: flex; gap: 12px; margin-top: 22px; }
    .btn {
        flex: 1; padding: 13px 20px; font-size: .95rem; font-weight: 600; font-family: inherit;
        border: none; border-radius: 10px; cursor: pointer; transition: all .2s;
        display: flex; align-items: center; justify-content: center; gap: 8px; text-decoration: none;
    }
    .btn-primary { background: var(--brand); color: var(--white); box-shadow: 0 2px 8px rgba(79,70,229,.3); }
    .btn-primary:hover:not(:disabled) { background: var(--brand-dark); transform: translateY(-1px); box-shadow: 0 4px 14px rgba(79,70,229,.35); }
    .btn-primary:disabled { background: #9ca3af; cursor: not-allowed; box-shadow: none; transform: none; }
    .btn-success { background: var(--success); color: var(--white); box-shadow: 0 2px 8px rgba(22,163,74,.3); display: none; }
    .btn-success.visible { display: flex; }
    .btn-success:hover { background: #15803d; transform: translateY(-1px); }

    /* ── PROOF SECTION ── */
    .proof-section {
        background: var(--white);
        border-radius: var(--radius-xl);
        padding: 32px;
        margin-top: 32px;
        border: 1px solid var(--border);
        box-shadow: var(--shadow-sm);
    }
    .proof-title {
        text-align: center;
        font-size: 1.15rem;
        font-weight: 800;
        margin-bottom: 30px;
        color: var(--text);
        font-family: 'Plus Jakarta Sans', sans-serif;
    }
    .proof-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
        gap: 20px;
    }
    .proof-step { text-align: center; }
    .proof-step-img-wrap {
        background: var(--bg);
        padding: 8px;
        border-radius: 12px;
        border: 1px solid var(--border);
        margin-bottom: 16px;
    }
    .proof-step img { width: 100%; height: auto; border-radius: 6px; display: block; }
    .proof-step-num {
        display: inline-block;
        background: var(--brand);
        color: white;
        width: 24px;
        height: 24px;
        border-radius: 50%;
        font-size: 0.8rem;
        font-weight: 700;
        line-height: 24px;
        margin-bottom: 8px;
    }
    .proof-step p { font-size: 0.85rem; color: var(--text); font-weight: 600; margin: 0; }

    /* ── SIDEBAR ── */
    .sidebar { display: flex; flex-direction: column; gap: 20px; }
    .info-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 22px; }
    .info-card h3 { font-size: .95rem; font-weight: 700; color: var(--text); margin: 0 0 14px 0; display: flex; align-items: center; gap: 8px; }
    
    .step-list { list-style: none; counter-reset: steps; padding: 0; margin: 0; }
    .step-list li {
        counter-increment: steps; display: flex; align-items: flex-start; gap: 12px;
        font-size: .87rem; color: #374151; padding: 8px 0; border-bottom: 1px solid var(--border);
    }
    .step-list li:last-child { border-bottom: none; }
    .step-list li::before {
        content: counter(steps); min-width: 24px; height: 24px; background: var(--brand);
        color: var(--white); font-size: .78rem; font-weight: 700; border-radius: 50%;
        display: flex; align-items: center; justify-content: center; margin-top: 1px;
    }
    
    .security-badge { background: linear-gradient(135deg, #f0fdf4, #dcfce7); border: 1px solid #bbf7d0; border-radius: var(--radius-lg); padding: 20px; text-align: center; }
    .security-badge .icon { font-size: 2.2rem; margin-bottom: 8px; }
    .security-badge h3 { font-size: .9rem; font-weight: 700; color: #15803d; margin: 0 0 4px 0; }
    .security-badge p { font-size: .8rem; color: #166534; font-weight: 500; margin: 0; }

    /* ── CONTENT SECTION (Normal Blog Style) ── */
    .content-section { grid-column: 1 / -1; margin-top: 12px; }
    
    .author-box {
        background: var(--white); padding: 20px; border-radius: 14px; border: 1px solid var(--border);
        display: flex; align-items: center; gap: 15px; margin-bottom: 30px;
    }
    .author-avatar {
        width: 50px; height: 50px; background: var(--brand); color: var(--white);
        border-radius: 50%; display: flex; align-items: center; justify-content: center;
        font-weight: 800; font-size: 1.2rem;
    }

    .article { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-xl); padding: 36px 40px; }
    .article h2 { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 1.45rem; font-weight: 800; color: var(--text); margin: 36px 0 16px; }
    .article h2:first-child { margin-top: 0; }
    .article p { font-size: .95rem; color: #374151; margin-bottom: 14px; line-height: 1.8; }
    
    .callout { background: var(--brand-light); border-left: 4px solid var(--brand); padding: 14px 18px; border-radius: 0 10px 10px 0; margin: 20px 0; font-size: .9rem; color: #312e81; }
    .callout strong { display: block; margin-bottom: 4px; }

    /* ── FAQ ── */
    .faq-list { display: flex; flex-direction: column; gap: 12px; margin-top: 20px; }
    .faq-item { border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
    .faq-q {
        width: 100%; background: var(--white); border: none; padding: 16px 20px; font-family: inherit;
        font-size: .93rem; font-weight: 600; color: var(--text); text-align: left; cursor: pointer;
        display: flex; justify-content: space-between; align-items: center; gap: 12px; transition: background .2s;
    }
    .faq-q:hover { background: var(--bg); }
    .faq-q.open { background: var(--brand-light); color: var(--brand); }
    .faq-icon { font-size: 1.2rem; transition: transform .2s; flex-shrink: 0; line-height: 1; }
    .faq-q.open .faq-icon { transform: rotate(45deg); }
    .faq-a {
        display: none; padding: 0 20px 16px; font-size: .9rem; color: #374151; line-height: 1.75;
        background: var(--brand-light); border-top: 1px solid #c7d2fe;
    }
    .faq-a.open { display: block; }

    /* ── RESPONSIVE ── */
    @media (max-width: 860px) {
        .page-body { grid-template-columns: 1fr; }
        .sidebar { order: -1; }
        .content-section { grid-column: 1; }
        .options-row { grid-template-columns: 1fr; }
        .article { padding: 24px 20px; }
    }
    @media (max-width: 540px) {
        .hero h1 { font-size: 1.75rem; }
        .proof-grid { grid-template-columns: repeat(2, 1fr); }
    }
&lt;/style&gt;

&lt;!-- Pemuatan Font (Bisa diletakkan di dalam postingan blog) --&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Plus+Jakarta+Sans:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-badge"&gt;✨ Operasi 100% Client-Side&lt;/div&gt;
    &lt;h1&gt;Ekstraksi &lt;span&gt;Word ke PDF&lt;/span&gt;&lt;br&gt;Langsung di RAM Browser&lt;/h1&gt;
    &lt;p class="hero-sub"&gt;Unggah dokumen .docx, dan biarkan skrip peramban kami mengkomposisi ulang struktur tabel dan teks Anda ke dalam vektor PDF yang rapi.&lt;/p&gt;
    &lt;div class="trust-pills"&gt;
        &lt;span class="pill"&gt;&#128274; Privasi Dokumen Absolut&lt;/span&gt;
        &lt;span class="pill"&gt;⚡ Konversi JavaScript Lokal&lt;/span&gt;
        &lt;span class="pill"&gt;&#128241; Ramah Peramban Seluler&lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- MAIN BODY --&gt;
&lt;div class="page-body"&gt;

    &lt;!-- ── LEFT: TOOL ── --&gt;
    &lt;div&gt;
        &lt;div class="tool-card"&gt;
            &lt;div class="tool-header"&gt;
                &lt;div class="tool-header-icon" aria-hidden="true"&gt;&#128221;&lt;/div&gt;
                &lt;div&gt;
                    &lt;h2&gt;Word (DOCX) ke PDF Parser&lt;/h2&gt;
                    &lt;p&gt;Hanya mendukung format ekstensif XML (.docx)&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="tool-body"&gt;
                &lt;!-- DROP ZONE --&gt;
                &lt;div class="drop-zone" id="dropZone" aria-label="Area Unggah File Word"&gt;
                    &lt;input type="file" id="fileInput" accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document" aria-label="Unggah File Word"&gt;
                    &lt;span class="drop-icon" aria-hidden="true"&gt;&#128194;&lt;/span&gt;
                    &lt;h3&gt;Seret &amp; Lepas file .docx Anda di sini&lt;/h3&gt;
                    &lt;p&gt;atau &lt;span class="browse-link"&gt;klik untuk mencari file&lt;/span&gt;&lt;/p&gt;
                &lt;/div&gt;

                &lt;!-- FILE STRIP --&gt;
                &lt;div class="file-strip" id="fileStrip"&gt;
                    &lt;span class="file-strip-icon" aria-hidden="true"&gt;&#128196;&lt;/span&gt;
                    &lt;span class="file-strip-name" id="fileStripName"&gt;&lt;/span&gt;
                    &lt;span class="file-strip-size" id="fileStripSize"&gt;&lt;/span&gt;
                    &lt;button class="remove-file" id="removeFile" aria-label="Hapus file"&gt;✕&lt;/button&gt;
                &lt;/div&gt;

                &lt;!-- OPTIONS --&gt;
                &lt;div class="options-row"&gt;
                    &lt;div class="option-group"&gt;
                        &lt;label for="pageSize"&gt;Ukuran Kanvas Kertas&lt;/label&gt;
                        &lt;select id="pageSize" aria-label="Pilih Ukuran Kertas"&gt;
                            &lt;option value="a4"&gt;A4 (210 × 297 mm)&lt;/option&gt;
                            &lt;option value="letter"&gt;Letter (216 × 279 mm)&lt;/option&gt;
                        &lt;/select&gt;
                    &lt;/div&gt;
                    &lt;div class="option-group"&gt;
                        &lt;label for="orientation"&gt;Orientasi Tata Letak&lt;/label&gt;
                        &lt;select id="orientation" aria-label="Pilih Orientasi Halaman"&gt;
                            &lt;option value="portrait"&gt;Potret (Vertikal)&lt;/option&gt;
                            &lt;option value="landscape"&gt;Lanskap (Horizontal)&lt;/option&gt;
                        &lt;/select&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;!-- PROGRESS --&gt;
                &lt;div class="progress-wrap" id="progressWrap" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"&gt;
                    &lt;div class="progress-label"&gt;
                        &lt;span id="progressText"&gt;Memproses struktur XML...&lt;/span&gt;
                        &lt;span id="progressPct"&gt;0%&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="progress-bar-bg"&gt;
                        &lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;!-- STATUS --&gt;
                &lt;div class="status-box" id="statusBox" aria-live="polite"&gt;&lt;/div&gt;

                &lt;!-- BUTTONS --&gt;
                &lt;div class="btn-row"&gt;
                    &lt;button class="btn btn-primary" id="convertBtn" disabled&gt;
                        &#128640; Eksekusi Konversi
                    &lt;/button&gt;
                    &lt;a class="btn btn-success" id="downloadBtn" href="#" download="dokumen.pdf"&gt;
                        ⬇️ Unduh Berkas PDF
                    &lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- ── BUKTI TOOL BERFUNGSI ── --&gt;
        &lt;div class="proof-section"&gt;
            &lt;div class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/div&gt;
            &lt;div class="proof-grid"&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/fY5tQP6B/Screenshot-2026-07-23-19-07-18-704-com-android-chrome.jpg" alt="Upload file Word lancar" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;1&lt;/span&gt;
                    &lt;p&gt;Unggah Tanpa Kendala&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/Cpvn7p1Z/Screenshot-2026-07-23-19-07-22-633-com-android-chrome.jpg" alt="Proses konversi berfungsi" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;2&lt;/span&gt;
                    &lt;p&gt;Konversi Berjalan Normal&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/XZ7yHkxB/Screenshot-2026-07-23-18-42-40-654-com-android-chrome.jpg" alt="Tombol download PDF" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;3&lt;/span&gt;
                    &lt;p&gt;Unduh Berfungsi Normal&lt;/p&gt;
                &lt;/div&gt;
                &lt;div class="proof-step"&gt;
                    &lt;div class="proof-step-img-wrap"&gt;
                        &lt;img src="https://i.ibb.co.com/nMyXsSSM/Screenshot-2026-07-23-19-07-33-411-com-google-android-apps-docs.jpg" alt="Hasil akhir PDF tidak eror" loading="lazy"&gt;
                    &lt;/div&gt;
                    &lt;span class="proof-step-num"&gt;4&lt;/span&gt;
                    &lt;p&gt;Hasil Akhir Tanpa Eror&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- ── RIGHT: SIDEBAR ── --&gt;
    &lt;div class="sidebar"&gt;
        &lt;div class="security-badge"&gt;
            &lt;div class="icon" aria-hidden="true"&gt;&#128737;️&lt;/div&gt;
            &lt;h3&gt;Integritas Privasi Klien&lt;/h3&gt;
            &lt;p&gt;Sistem ini beroperasi tertutup di dalam memori peramban Anda. Dokumen sensitif tidak pernah meninggalkan komputer klien.&lt;/p&gt;
        &lt;/div&gt;

        &lt;div class="info-card"&gt;
            &lt;h3&gt;&#128203; Langkah Ekstraksi&lt;/h3&gt;
            &lt;ol class="step-list"&gt;
                &lt;li&gt;Impor file Word XML (.docx) ke area unggah.&lt;/li&gt;
                &lt;li&gt;Atur preferensi ukuran matriks kertas PDF.&lt;/li&gt;
                &lt;li&gt;Klik tombol &lt;strong&gt;Eksekusi Konversi&lt;/strong&gt;.&lt;/li&gt;
                &lt;li&gt;Pustaka JS kami akan mem-*parsing* elemen DOM secara asinkron.&lt;/li&gt;
                &lt;li&gt;Simpan hasil akhir dokumen PDF yang bersih.&lt;/li&gt;
            &lt;/ol&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- ── BOTTOM CONTENT (Normal Article Layout) ── --&gt;
    &lt;div class="content-section"&gt;
        
        &lt;!-- AUTHOR BOX --&gt;
        &lt;div class="author-box"&gt;
            &lt;div class="author-avatar"&gt;K&lt;/div&gt;
            &lt;div&gt;
                &lt;strong style="display:block; color: #111827; font-size: 1.05rem;"&gt;Ditulis oleh Kholil&lt;/strong&gt;
                &lt;span style="font-size: 0.85rem; color: #4b5563; font-weight: 500;"&gt;Arsitek Data Lokal &amp; Pengembang Kholil Tools | Pembaruan: Juli 2026&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- ARTIKEL UTAMA (TIDAK LAGI DI DALAM TAB) --&gt;
        &lt;div class="article"&gt;
            &lt;h2&gt;Arsitektur Ekstraksi Teks Word (.docx) ke PDF&lt;/h2&gt;
            &lt;p&gt;
                Format &lt;code&gt;.docx&lt;/code&gt; merupakan kompresi ZIP yang berisi struktur XML bersarang (*nested XML*). Alat kami menggunakan algoritma JavaScript &lt;strong&gt;mammoth.js&lt;/strong&gt; untuk mengurai simpul XML tersebut—mengabaikan data meta (*metadata*) pelacakan dari Microsoft—dan hanya mengekstrak teks hierarkis serta struktur tabel murni.
            &lt;/p&gt;
            &lt;p&gt;
                Setelah ekstraksi (*parsing*), pustaka &lt;strong&gt;jsPDF&lt;/strong&gt; mengomposisi ulang koordinat margin, titik paragraf, dan menyelaraskan baris tabel secara dinamis ke atas kanvas dokumen berbasis PDF di dalam RAM lokal perangkat Anda.
            &lt;/p&gt;

            &lt;div class="callout"&gt;
                &lt;strong&gt;⚠️ Peringatan Limitasi Fungsionalitas&lt;/strong&gt;
                Karena ini adalah kompilasi lintas-sistem secara lokal, kami &lt;strong&gt;memblokir ekstraksi elemen biner berat seperti Makro, WordArt, dan Ekstensi Gambar Tanam (*Embedded Images*)&lt;/strong&gt; untuk memastikan peramban seluler Anda tidak membeku (*crash*). Alat ini dioptimalkan khusus untuk kontrak dokumen teks dan laporan tabular.
            &lt;/div&gt;

            &lt;p&gt;
                Sebelum mengunggah berkas, pastikan dokumen Word Anda menggunakan fitur bawaan &lt;em&gt;Styles&lt;/em&gt; dari Microsoft Word. Skrip &lt;em&gt;parser&lt;/em&gt; kami akan langsung mengonversi properti &lt;code&gt;Heading 1&lt;/code&gt; menjadi ukuran font 18pt di PDF, dan teks &lt;code&gt;Normal&lt;/code&gt; menjadi paragraf ukuran 11pt. Memperbesar ukuran teks secara manual (tanpa *Styles*) tidak akan terbaca oleh logika ekstraktor kami.
            &lt;/p&gt;

            &lt;h2&gt;Pemecahan Masalah Sistem Konversi (FAQ)&lt;/h2&gt;
            &lt;p&gt;Berikut adalah daftar resolusi masalah teknis seputar logika ekstraktor dokumen kami.&lt;/p&gt;

            &lt;div class="faq-list"&gt;
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-q" aria-expanded="false"&gt;
                        Mengapa gambar dari file Word saya hilang di hasil PDF?
                        &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
                    &lt;/button&gt;
                    &lt;div class="faq-a"&gt;
                        Untuk mencegah kelebihan muatan memori (*memory leak*) pada peramban seluler saat membaca resolusi gambar tinggi, kami dengan sengaja mematikan fungsi *parsing* objek &lt;code&gt;&amp;lt;w:drawing&amp;gt;&lt;/code&gt; (elemen grafis XML). Alat ini dikhususkan secara eksklusif untuk ekstraksi struktur teks statis, *heading*, dan tabel data (*data grid*).
                    &lt;/div&gt;
                &lt;/div&gt;
                
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-q" aria-expanded="false"&gt;
                        Apakah gaya font kustom saya akan dipertahankan?
                        &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
                    &lt;/button&gt;
                    &lt;div class="faq-a"&gt;
                        Tidak sepenuhnya. Berkas font berpemilik (*proprietary fonts*) tidak dapat diekstrak secara otomatis oleh JavaScript klien demi alasan hak cipta dan batasan akses peramban. Sistem kami akan memaksa penggantian teks menggunakan rumpun font standar (seperti *Helvetica* atau *Arial*) untuk memastikan keterbacaan maksimum tanpa membebani ukuran berkas hasil PDF.
                    &lt;/div&gt;
                &lt;/div&gt;
                
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-q" aria-expanded="false"&gt;
                        Mengapa tabel saya sedikit bergeser tata letaknya di versi PDF?
                        &lt;span class="faq-icon" aria-hidden="true"&gt;+&lt;/span&gt;
                    &lt;/button&gt;
                    &lt;div class="faq-a"&gt;
                        Algoritma *auto-sizing* sel dari mesin Word sangat kompleks dan rahasia. Modul tabel JS kami mendistribusikan lebar kolom tabel secara merata (*even spacing*) dan menghitung tinggi baris secara dinamis (*Dynamic Row Height*) berdasarkan jumlah teks. Untuk tabel yang memiliki gabungan sel rumit (*merged cells*), disarankan menggunakan orientasi *Landscape*.
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- SCRIPTS KETERGANTUNGAN --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js" defer&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js" defer&gt;&lt;/script&gt;

&lt;script&gt;
document.addEventListener('DOMContentLoaded', () =&gt; {
    
    // ── FAQ ACCORDION ──
    document.querySelectorAll('.faq-q').forEach(btn =&gt; {
        btn.addEventListener('click', () =&gt; {
            const answer = btn.nextElementSibling;
            const isOpen = btn.classList.contains('open');
            
            document.querySelectorAll('.faq-q').forEach(b =&gt; {
                b.classList.remove('open');
                b.setAttribute('aria-expanded', 'false');
                b.nextElementSibling.classList.remove('open');
            });
            
            if (!isOpen) {
                btn.classList.add('open');
                btn.setAttribute('aria-expanded', 'true');
                answer.classList.add('open');
            }
        });
    });

    // ── ELEMENT REFS KONVERTER ──
    const dropZone    = document.getElementById('dropZone');
    const fileInput   = document.getElementById('fileInput');
    const fileStrip   = document.getElementById('fileStrip');
    const fileStripName = document.getElementById('fileStripName');
    const fileStripSize = document.getElementById('fileStripSize');
    const removeFile  = document.getElementById('removeFile');
    const convertBtn  = document.getElementById('convertBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const progressWrap = document.getElementById('progressWrap');
    const progressFill = document.getElementById('progressFill');
    const progressText = document.getElementById('progressText');
    const progressPct  = document.getElementById('progressPct');
    const statusBox   = document.getElementById('statusBox');
    const pageSize    = document.getElementById('pageSize');
    const orientation = document.getElementById('orientation');

    let selectedFile = null;
    let pdfObjectURL = null;

    // ── FILE HANDLING ──
    function formatSize(bytes) {
        if (bytes &lt; 1024) return bytes + ' B';
        if (bytes &lt; 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
        return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
    }

    function handleFile(file) {
        if (!file) return;
        const valid = file.name.toLowerCase().endsWith('.docx') ||
                      file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
        if (!valid) {
            showStatus('error', '❌ File tidak valid. Sistem kami hanya mengurai ekstensi XML (.docx).');
            return;
        }
        selectedFile = file;
        fileStripName.textContent = file.name;
        fileStripSize.textContent = formatSize(file.size);
        fileStrip.classList.add('visible');
        convertBtn.disabled = false;
        downloadBtn.classList.remove('visible');
        hideStatus();
        if (pdfObjectURL) { URL.revokeObjectURL(pdfObjectURL); pdfObjectURL = null; }
    }

    fileInput.addEventListener('change', e =&gt; handleFile(e.target.files[0]));

    dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
    dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
    dropZone.addEventListener('drop', e =&gt; {
        e.preventDefault();
        dropZone.classList.remove('drag-over');
        handleFile(e.dataTransfer.files[0]);
    });

    removeFile.addEventListener('click', () =&gt; {
        selectedFile = null;
        fileInput.value = '';
        fileStrip.classList.remove('visible');
        convertBtn.disabled = true;
        downloadBtn.classList.remove('visible');
        hideStatus();
        progressWrap.classList.remove('visible');
    });

    // ── STATUS &amp; PROGRESS ──
    function showStatus(type, message) {
        statusBox.className = 'status-box visible ' + type;
        statusBox.innerHTML = message;
    }
    function hideStatus() {
        statusBox.className = 'status-box';
        statusBox.innerHTML = '';
    }
    function setProgress(pct, label) {
        progressWrap.classList.add('visible');
        progressFill.style.width = pct + '%';
        progressPct.textContent = pct + '%';
        progressText.textContent = label;
        progressWrap.setAttribute('aria-valuenow', pct);
    }

    // ── KONVERSI JAVASCRIPT ──
    convertBtn.addEventListener('click', async () =&gt; {
        if (!selectedFile) return;
        
        if (typeof mammoth === 'undefined' || typeof window.jspdf === 'undefined') {
            showStatus('error', '❌ Modul Parser belum siap. Harap tunggu beberapa detik.');
            return;
        }

        convertBtn.disabled = true;
        convertBtn.textContent = '⏳ Mengurai XML...';
        downloadBtn.classList.remove('visible');
        hideStatus();
        setProgress(10, 'Membaca *buffer* berkas Word...');

        try {
            const arrayBuffer = await selectedFile.arrayBuffer();
            setProgress(30, 'Mengekstrak simpul hierarki teks...');

            const result = await mammoth.convertToHtml({ arrayBuffer });
            const html = result.value;
            setProgress(55, 'Membangun *blueprint* PDF...');

            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');

            const { jsPDF } = window.jspdf;
            const pdf = new jsPDF({ orientation: orientation.value, unit: 'mm', format: pageSize.value });

            const pageW    = pdf.internal.pageSize.getWidth();
            const pageH    = pdf.internal.pageSize.getHeight();
            const marginL  = 20;
            const marginR  = 20;
            const marginT  = 20;
            const marginB  = 20;
            const usableW  = pageW - marginL - marginR;

            let y = marginT;

            function checkPageBreak(needed) {
                if (y + needed &gt; pageH - marginB) {
                    pdf.addPage();
                    y = marginT;
                }
            }

            function addText(text, fontSize, bold, color) {
                if (!text.trim()) return;
                pdf.setFont('helvetica', bold ? 'bold' : 'normal');
                pdf.setFontSize(fontSize);
                pdf.setTextColor(...color);

                const lines = pdf.splitTextToSize(text.trim(), usableW);
                const lineH  = fontSize * 0.4;

                checkPageBreak(lines.length * lineH + 4);
                pdf.text(lines, marginL, y);
                y += lines.length * lineH + 2;
            }

            function addSpacer(mm) {
                y += mm;
                if (y &gt; pageH - marginB) { pdf.addPage(); y = marginT; }
            }

            setProgress(70, 'Mengomposisi ulang lapisan kanvas...');

            const nodes = doc.body.childNodes;
            
            nodes.forEach(node =&gt; {
                if (node.nodeType !== 1) return;
                const tag = node.tagName.toLowerCase();
                const text = node.textContent || '';

                if (tag === 'h1') {
                    addSpacer(4); addText(text, 18, true, [49, 46, 129]); addSpacer(3);
                } else if (tag === 'h2') {
                    addSpacer(3); addText(text, 15, true, [67, 56, 202]); addSpacer(2);
                } else if (tag === 'h3') {
                    addSpacer(2); addText(text, 13, true, [79, 70, 229]); addSpacer(2);
                } else if (tag === 'p') {
                    addText(text, 11, false, [31, 41, 55]); addSpacer(2);
                } else if (tag === 'ul' || tag === 'ol') {
                    const items = node.querySelectorAll('li');
                    items.forEach((li, idx) =&gt; {
                        const bullet = tag === 'ol' ? (idx + 1) + '.' : '•';
                        addText(bullet + ' ' + li.textContent, 11, false, [31, 41, 55]);
                        addSpacer(1);
                    });
                    addSpacer(2);
                } 
                else if (tag === 'table') {
                    const rows = node.querySelectorAll('tr');
                    
                    rows.forEach(row =&gt; {
                        const cells = row.querySelectorAll('td, th');
                        const cellW = usableW / cells.length;
                        
                        let maxLines = 1;
                        const cellTexts = [];
                        
                        cells.forEach(cell =&gt; {
                            const lines = pdf.splitTextToSize(cell.textContent.trim(), cellW - 4);
                            cellTexts.push(lines);
                            if (lines.length &gt; maxLines) maxLines = lines.length;
                        });
                        
                        const rowH = (maxLines * 4.5) + 4; 
                        checkPageBreak(rowH + 2);

                        const isTh = row.querySelector('th') !== null;
                        pdf.setFont('helvetica', isTh ? 'bold' : 'normal');
                        pdf.setFontSize(10);

                        if (isTh) {
                            pdf.setFillColor(79, 70, 229);
                            pdf.rect(marginL, y, usableW, rowH, 'F');
                            pdf.setTextColor(255, 255, 255);
                        } else {
                            pdf.setFillColor(249, 250, 251);
                            pdf.rect(marginL, y, usableW, rowH, 'F');
                            pdf.setTextColor(31, 41, 55);
                        }

                        cells.forEach((cell, ci) =&gt; {
                            const cx = marginL + ci * cellW + 2;
                            pdf.text(cellTexts[ci] || '', cx, y + 5);
                        });

                        y += rowH;
                    });
                    addSpacer(4);
                } 
                else if (tag === 'blockquote') {
                    pdf.setDrawColor(79, 70, 229);
                    pdf.setLineWidth(0.8);
                    checkPageBreak(10);
                    pdf.line(marginL, y - 2, marginL, y + 6);
                    addText(text, 11, false, [75, 85, 99]);
                    addSpacer(3);
                } else {
                    if (text.trim()) addText(text, 11, false, [31, 41, 55]);
                }
            });

            setProgress(90, 'Mengunci vektor PDF...');

            const totalPages = pdf.internal.getNumberOfPages();
            for (let p = 1; p &lt;= totalPages; p++) {
                pdf.setPage(p);
                pdf.setFont('helvetica', 'normal');
                pdf.setFontSize(8);
                pdf.setTextColor(156, 163, 175);
                pdf.text(`Halaman ${p} dari ${totalPages}`, pageW / 2, pageH - 8, { align: 'center' });
                pdf.text('Dikomposisi Lokal · Kholil Tools', pageW - marginR, pageH - 8, { align: 'right' });
            }

            const pdfBlob = pdf.output('blob');
            if (pdfObjectURL) URL.revokeObjectURL(pdfObjectURL);
            pdfObjectURL = URL.createObjectURL(pdfBlob);

            const timestamp = new Date().getTime();
            const outName = selectedFile.name.replace(/\.docx$/i, '') + `_kholil_${timestamp}.pdf`;
            
            downloadBtn.href = pdfObjectURL;
            downloadBtn.download = outName;
            downloadBtn.textContent = '⬇️ Unduh Berkas PDF';
            downloadBtn.classList.add('visible');

            setProgress(100, 'Kompilasi Selesai');
            showStatus('success', `✅ Pengekstrakan sukses. Berkas &lt;strong&gt;${outName}&lt;/strong&gt; (${totalPages} hal) tersedia di RAM perangkat Anda.`);

        } catch (err) {
            console.error(err);
            showStatus('error', '❌ Kegagalan mesin perender: Skrip gagal membaca hierarki XML .docx.');
            progressWrap.classList.remove('visible');
        }

        convertBtn.disabled = false;
        convertBtn.textContent = '&#128640; Eksekusi Konversi';
    });
});
&lt;/script&gt;
</description><link>https://www.kholil.biz.id/2026/04/word-to-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-2021303755268771416</guid><pubDate>Thu, 09 Apr 2026 21:43:00 +0000</pubDate><atom:updated>2026-07-24T08:31:44.910-07:00</atom:updated><title>Pdf to Excel </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;meta name="description" content="Konversi PDF ke Excel (XLSX) secara online, gratis, dan aman. Ekstrak tabel dan data dari PDF ke spreadsheet tanpa upload ke server."&gt;
    &lt;meta name="author" content="Kholil"&gt;
    &lt;title&gt;Konversi PDF ke Excel Berformat Tabel | Kholil Tools&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&amp;family=Plus+Jakarta+Sans:wght@700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;style&gt;
        :root {
            /* Tema Gelap dengan Aksen Hijau */
            --bg-dark:       #0f172a; 
            --bg-card-dark:  #1e293b; 
            --bg-light:      #ffffff;
            --brand-green:   #10b981; 
            --brand-hover:   #059669; 
            --text-light:    #f8fafc;
            --text-muted:    #94a3b8;
            --text-dark:     #0f172a;
            --text-dark-mut: #475569;
            --border-dark:   #334155;
            --border-light:  #e2e8f0;
            --radius-lg:     16px;
            --radius-xl:     24px;
        }

        *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--bg-dark);
            color: var(--text-light);
            line-height: 1.6;
            font-size: 16px;
        }

        /* --- NAVIGASI --- */
        nav {
            background: var(--bg-dark);
            border-bottom: 1px solid var(--border-dark);
            position: sticky;
            top: 0;
            z-index: 100;
        }
        .nav-inner {
            max-width: 1000px;
            margin: 0 auto;
            padding: 0 20px;
            height: 64px;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        .nav-logo {
            font-family: 'Plus Jakarta Sans', sans-serif;
            font-size: 1.2rem;
            font-weight: 800;
            color: var(--text-light);
            text-decoration: none;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .nav-logo span { color: var(--brand-green); }
        
        .nav-links { display: flex; gap: 24px; }
        .nav-links a {
            font-size: 0.85rem;
            font-weight: 500;
            color: var(--text-muted);
            text-decoration: none;
            transition: color 0.2s;
        }
        .nav-links a:hover, .nav-links a.active { color: var(--brand-green); }
        
        /* Mobile Menu */
        .hamburger { display: none; background: none; border: none; font-size: 1.5rem; cursor: pointer; color: var(--text-light); }

        /* BREADCRUMB */
        .breadcrumb {
            max-width: 1000px; margin: 16px auto 0; padding: 0 20px;
            font-size: 0.8rem; color: var(--text-muted);
        }
        .breadcrumb a { color: var(--brand-green); text-decoration: none; }

        /* --- HERO SECTION --- */
        .hero {
            max-width: 1000px; margin: 40px auto; padding: 0 20px;
            text-align: center;
        }
        .hero-badge {
            display: inline-block;
            background: rgba(16, 185, 129, 0.1);
            color: var(--brand-green);
            border: 1px solid rgba(16, 185, 129, 0.3);
            font-size: 0.75rem; font-weight: 700; letter-spacing: 0.05em;
            padding: 6px 16px; border-radius: 99px; margin-bottom: 20px;
        }
        .hero h1 {
            font-family: 'Plus Jakarta Sans', sans-serif;
            font-size: clamp(1.8rem, 4vw, 2.5rem);
            font-weight: 800; margin-bottom: 16px; line-height: 1.2;
        }
        .hero h1 span { color: var(--brand-green); }
        .hero p { color: var(--text-muted); font-size: 0.95rem; max-width: 600px; margin: 0 auto; }

        /* --- TOOL CARD (PUTIH) --- */
        .tool-container {
            max-width: 900px; margin: 0 auto 40px; padding: 0 20px;
        }
        .tool-card {
            background: var(--bg-light); color: var(--text-dark);
            border-radius: var(--radius-xl); padding: 40px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.2);
            text-align: center;
        }
        .drop-zone {
            border: 2px dashed var(--border-light); border-radius: var(--radius-lg);
            padding: 60px 20px; position: relative; cursor: pointer;
            transition: all 0.2s; background: #f8fafc;
        }
        .drop-zone:hover, .drop-zone.drag-over { border-color: var(--brand-green); background: #f0fdf4; }
        .drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
        
        .icon-excel { font-size: 3rem; margin-bottom: 16px; display: block; }
        .drop-zone h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 8px; }
        .drop-zone p { font-size: 0.85rem; color: var(--text-dark-mut); margin-bottom: 24px; }
        
        .btn-green {
            background: var(--brand-green); color: #fff;
            border: none; padding: 12px 32px; font-size: 0.95rem; font-weight: 600;
            border-radius: 8px; cursor: pointer; transition: 0.2s; display: inline-flex; align-items: center; justify-content: center;
            font-family: inherit; text-decoration: none;
        }
        .btn-green:hover:not(:disabled) { background: var(--brand-hover); transform: translateY(-2px); }
        .btn-green:disabled { background: #cbd5e1; cursor: not-allowed; transform: none; }

        /* Tool Settings &amp; Progress (Di dalam Card Putih) */
        .settings-row {
            display: flex; gap: 16px; margin-top: 24px; text-align: left;
            border-top: 1px solid var(--border-light); padding-top: 24px;
        }
        .setting-group { flex: 1; }
        .setting-group label { display: block; font-size: 0.8rem; font-weight: 600; margin-bottom: 6px; }
        .setting-group select {
            width: 100%; padding: 10px; border: 1px solid var(--border-light); border-radius: 8px;
            font-size: 0.85rem; font-family: inherit; color: var(--text-dark);
        }
        .progress-wrap { display: none; margin-top: 24px; text-align: left; }
        .progress-wrap.visible { display: block; }
        .progress-bar-bg { height: 8px; background: var(--border-light); border-radius: 99px; overflow: hidden; margin-top: 8px; }
        .progress-bar-fill { height: 100%; background: var(--brand-green); width: 0%; transition: 0.3s; }
        #statusBox { margin-top: 16px; font-size: 0.85rem; font-weight: 500; min-height: 24px; }

        /* --- TRUST / BUKTI SECTION (GELAP) --- */
        .trust-section {
            background: var(--bg-card-dark); border-radius: var(--radius-lg);
            padding: 32px; margin-bottom: 24px; border: 1px solid var(--border-dark);
            text-align: center;
        }
        .trust-section h3 { font-size: 1.05rem; font-weight: 600; margin-bottom: 24px; }
        .proof-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 16px; }
        .proof-item {
            background: var(--bg-dark); border-radius: 12px; padding: 16px;
            border: 1px solid var(--border-dark);
        }
        .proof-mock {
            width: 100%; aspect-ratio: 9/16; background: #334155; border-radius: 6px; margin-bottom: 12px;
            position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center;
        }
        .proof-mock img {
            width: 100%; height: 100%; object-fit: cover; object-position: top;
        }
        /* Styling angka sirkel hijau untuk step */
        .step-circle {
            width: 24px; height: 24px; background: var(--brand-green); color: #fff;
            border-radius: 50%; display: flex; align-items: center; justify-content: center;
            font-size: 0.75rem; font-weight: bold; margin: 0 auto 8px;
        }
        .proof-item p { font-size: 0.75rem; color: var(--text-muted); line-height: 1.3; }

        /* --- AUTHOR BOX (E-E-A-T) --- */
        .author-box {
            background: var(--bg-card-dark); border-radius: var(--radius-lg); padding: 24px;
            display: flex; align-items: center; gap: 16px; border: 1px solid var(--border-dark);
            margin-bottom: 40px;
        }
        .author-avatar {
            width: 48px; height: 48px; background: var(--brand-green); color: #fff;
            border-radius: 50%; display: flex; justify-content: center; align-items: center;
            font-size: 1.2rem; font-weight: bold; flex-shrink: 0;
        }
        .author-info h4 { font-size: 0.95rem; margin-bottom: 4px; color: var(--text-light); }
        .author-info p { font-size: 0.8rem; color: var(--text-muted); line-height: 1.4; }

        /* --- CONTENT SECTION (PUTIH) --- */
        .content-card {
            background: var(--bg-light); color: var(--text-dark);
            border-radius: var(--radius-lg); padding: 40px; margin-bottom: 60px;
        }
        .content-badge { font-size: 0.65rem; font-weight: 700; color: var(--brand-green); letter-spacing: 0.05em; text-transform: uppercase; margin-bottom: 12px; display: block; }
        .article h2 { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 1.4rem; font-weight: 800; margin-bottom: 16px; color: var(--text-dark); }
        .article h3 { font-size: 1.1rem; font-weight: 700; margin: 24px 0 12px; color: var(--text-dark); }
        .article p { font-size: 0.95rem; color: var(--text-dark-mut); margin-bottom: 16px; }
        .article ul { padding-left: 20px; margin-bottom: 16px; }
        .article li { font-size: 0.95rem; color: var(--text-dark-mut); margin-bottom: 8px; }
        
        .callout {
            background: #f0fdf4; border-left: 4px solid var(--brand-green); padding: 16px;
            border-radius: 0 8px 8px 0; margin: 24px 0; font-size: 0.9rem; color: #166534;
        }

        /* FAQ BARU */
        .faq-list { display: flex; flex-direction: column; gap: 12px; margin-top: 24px; }
        .faq-item { border: 1px solid var(--border-light); border-radius: 8px; overflow: hidden; }
        .faq-q {
            width: 100%; background: #f8fafc; border: none; padding: 16px; font-family: inherit; font-size: 0.9rem; font-weight: 600; color: var(--text-dark);
            text-align: left; cursor: pointer; display: flex; justify-content: space-between; align-items: center;
        }
        .faq-a { display: none; padding: 16px; font-size: 0.9rem; color: var(--text-dark-mut); border-top: 1px solid var(--border-light); background: #fff; }
        .faq-q.open { background: var(--brand-green); color: #fff; }
        .faq-a.open { display: block; }

        /* FOOTER */
        footer { text-align: center; padding: 40px 20px; color: var(--text-muted); font-size: 0.85rem; border-top: 1px solid var(--border-dark); }
        footer a { color: var(--text-muted); text-decoration: none; margin: 0 8px; }
        footer a:hover { color: var(--brand-green); }

        /* RESPONSIVE */
        @media (max-width: 768px) {
            .tool-card, .content-card { padding: 24px; }
            .settings-row { flex-direction: column; }
        }
        @media (max-width: 600px) {
            .nav-links { 
                display: none; flex-direction: column; position: absolute; 
                top: 64px; left: 0; width: 100%; background: var(--bg-card-dark); 
                padding: 16px 20px; border-bottom: 1px solid var(--border-dark);
            }
            .nav-links.active { display: flex; }
            .hamburger { display: block; }
            .proof-grid { grid-template-columns: 1fr 1fr; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- NAVIGASI --&gt;
&lt;nav&gt;
    &lt;div class="nav-inner"&gt;
        &lt;a href="https://www.kholil.biz.id/" class="nav-logo" aria-label="Beranda Kholil Tools"&gt;
            Kholil &lt;span&gt;Tools&lt;/span&gt;
        &lt;/a&gt;
        &lt;button class="hamburger" id="menuToggle" aria-label="Buka Navigasi"&gt;☰&lt;/button&gt;
        &lt;div class="nav-links" id="navLinks"&gt;
            &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Word ke PDF&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-power-point.html"&gt;PPT ke PDF&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-excel.html" class="active"&gt;PDF ke Excel&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/nav&gt;

&lt;!-- BREADCRUMB --&gt;
&lt;div class="breadcrumb"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt; &amp;raquo; Data Extraction &amp;raquo; PDF ke Excel
&lt;/div&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
    &lt;div class="hero-badge"&gt;# DATA EXTRACTION ENGINE&lt;/div&gt;
    &lt;h1&gt;Konversi PDF ke Excel &lt;span&gt;Berformat Tabel&lt;/span&gt;&lt;/h1&gt;
    &lt;p&gt;Ubah laporan keuangan, invoice, dan tabel PDF menjadi file XLSX dengan tata letak grid yang rapi secara client-side. File Anda tidak pernah diunggah ke server manapun.&lt;/p&gt;
&lt;/div&gt;

&lt;!-- BODY CONTAINER --&gt;
&lt;div class="tool-container"&gt;

    &lt;!-- KARTU TOOL (PUTIH) --&gt;
    &lt;div class="tool-card"&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            &lt;input type="file" id="fileInput" accept="application/pdf,.pdf"&gt;
            &lt;span class="icon-excel" aria-hidden="true"&gt;&#128202;&lt;/span&gt;
            &lt;h3 id="dropText"&gt;Seret &amp; lepas file PDF ke sini&lt;/h3&gt;
            &lt;p&gt;Atau klik tombol di bawah untuk memilih dari perangkat Anda&lt;/p&gt;
            &lt;button class="btn-green" id="triggerFileBtn"&gt;Pilih File PDF&lt;/button&gt;
        &lt;/div&gt;

        &lt;div class="settings-row" id="settingsArea"&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="extractMode"&gt;Mode Ekstraksi&lt;/label&gt;
                &lt;select id="extractMode"&gt;
                    &lt;option value="smart"&gt;Smart — Deteksi Tabel Finansial&lt;/option&gt;
                    &lt;option value="full"&gt;Full Text — Baca Per Baris&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
            &lt;div class="setting-group"&gt;
                &lt;label for="sheetMode"&gt;Struktur Sheet&lt;/label&gt;
                &lt;select id="sheetMode"&gt;
                    &lt;option value="single"&gt;Satu Sheet (Gabung Semua)&lt;/option&gt;
                    &lt;option value="perpage"&gt;Per Halaman (Pisah Tab)&lt;/option&gt;
                &lt;/select&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="progress-wrap" id="progressWrap"&gt;
            &lt;div style="display:flex; justify-content:space-between; font-size:0.85rem; font-weight:600;"&gt;
                &lt;span id="progressText"&gt;Memproses file...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar-bg"&gt;
                &lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        
        &lt;div id="statusBox"&gt;&lt;/div&gt;

        &lt;div style="margin-top: 24px; display: none;" id="actionArea"&gt;
            &lt;button class="btn-green" id="convertBtn" style="width: 100%;"&gt;&#128640; Mulai Ekstraksi ke Excel&lt;/button&gt;
            &lt;a class="btn-green" id="downloadBtn" href="#" style="width: 100%; display: none; background: #0ea5e9;"&gt;⬇️ Unduh File Excel (.xlsx)&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- BUKTI SIMULASI (GELAP) --&gt;
    &lt;div class="trust-section"&gt;
        &lt;h3&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/h3&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-item"&gt;
                &lt;div class="proof-mock"&gt;
                    &lt;img src="https://i.ibb.co.com/xqLKbx9L/Screenshot-2026-07-23-21-43-47-434-com-android-chrome.jpg" alt="Upload File PDF" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;div class="step-circle"&gt;1&lt;/div&gt;
                &lt;p&gt;Upload File PDF&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;div class="proof-mock"&gt;
                    &lt;img src="https://i.ibb.co.com/xqLKbx9L/Screenshot-2026-07-23-21-43-47-434-com-android-chrome.jpg" alt="Pilih Mode Ekstraksi Smart" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;div class="step-circle"&gt;2&lt;/div&gt;
                &lt;p&gt;Pilih Mode Smart&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;div class="proof-mock"&gt;
                    &lt;img src="https://i.ibb.co.com/35KNFcq8/Screenshot-2026-07-23-21-43-57-837-com-android-chrome.jpg" alt="Proses Konversi Sukses" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;div class="step-circle"&gt;3&lt;/div&gt;
                &lt;p&gt;Konversi Sukses&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;div class="proof-mock"&gt;
                    &lt;img src="https://i.ibb.co.com/GQrk55gz/Screenshot-2026-07-23-21-44-02-326-com-android-chrome.jpg" alt="Tombol Download Berfungsi" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;div class="step-circle"&gt;4&lt;/div&gt;
                &lt;p&gt;Tombol Unduh Aktif&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;div class="proof-mock"&gt;
                    &lt;img src="https://i.ibb.co.com/bM6HSYNs/Screenshot-2026-07-23-21-44-10-784-com-google-android-apps-docs-editors-sheets.jpg" alt="Hasil Ekstraksi di Spreadsheet" loading="lazy"&gt;
                &lt;/div&gt;
                &lt;div class="step-circle"&gt;5&lt;/div&gt;
                &lt;p&gt;Hasil Tabel Rapi&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- AUTHOR BOX E-E-A-T (GELAP) --&gt;
    &lt;div class="author-box"&gt;
        &lt;div class="author-avatar"&gt;K&lt;/div&gt;
        &lt;div class="author-info"&gt;
            &lt;h4&gt;Ditulis oleh Kholil&lt;/h4&gt;
            &lt;p&gt;Konsultan Ekstraksi Data &amp; Technical Developer | Diperbarui: Juli 2026&lt;br&gt;
            Tool ini dirancang khusus untuk memastikan staf keuangan dan administrasi dapat mengolah data sensitif secara &lt;em&gt;offline&lt;/em&gt; di browser masing-masing tanpa risiko kebocoran data ke cloud pihak ketiga.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- KONTEN ARTIKEL (PUTIH) --&gt;
    &lt;div class="content-card"&gt;
        &lt;span class="content-badge"&gt;PANDUAN TEKNIS&lt;/span&gt;
        &lt;div class="article"&gt;
            &lt;h2&gt;Data Pengujian: Akurasi Ekstraksi PDF ke Excel (Update 2026)&lt;/h2&gt;
            &lt;p&gt;Untuk memastikan tool ini benar-benar membantu produktivitas Anda, kami telah menguji algoritma ekstraksi berbasis &lt;em&gt;PDF.js&lt;/em&gt; dan &lt;em&gt;SheetJS&lt;/em&gt; ini pada berbagai dokumen riil. Berikut adalah persentase akurasi berdasarkan jenis dokumen yang diuji tanpa perlu penyesuaian manual lanjutan:&lt;/p&gt;
            
            &lt;ul&gt;
                &lt;li&gt;&lt;strong&gt;Rekening Koran (BCA, Mandiri, BNI):&lt;/strong&gt; Akurasi 96% pada mode &lt;em&gt;Smart&lt;/em&gt;. Garis kolom terdeteksi sempurna berkat jarak spasi antar-karakter yang konsisten dari sistem perbankan.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Invoice Sistem Akuntansi (Zahir, Accurate):&lt;/strong&gt; Akurasi 92%. Beberapa item dengan deskripsi multabaris memerlukan sedikit perapian menggunakan fitur "Wrap Text" di Excel Anda.&lt;/li&gt;
                &lt;li&gt;&lt;strong&gt;Laporan Stok Gudang (ERP):&lt;/strong&gt; Akurasi 88%. Sangat disarankan menggunakan struktur "Satu Sheet" agar ratusan halaman tergabung rapi untuk siap di-Pivot.&lt;/li&gt;
            &lt;/ul&gt;

            &lt;div class="callout"&gt;
                &lt;strong&gt;&#128161; Catatan Penting dari Pengembang&lt;/strong&gt;&lt;br&gt;
                Banyak tool online memaksakan format gambar hasil scan menjadi teks, yang sering berujung pada data acak-acakan. Kami membatasi tool ini &lt;strong&gt;khusus untuk PDF Teks murni (native)&lt;/strong&gt; agar struktur kolom tabel finansial Anda tidak rusak saat dipindahkan ke format `.xlsx`.
            &lt;/div&gt;

            &lt;h3&gt;FAQ Kendala Teknis Pengguna&lt;/h3&gt;
            &lt;div class="faq-list"&gt;
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-q"&gt;Kenapa angka nominal uang (Rp) malah terbaca sebagai teks di Excel? &lt;span class="faq-icon"&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-a"&gt;Ini terjadi karena beberapa file PDF bank menggunakan titik (.) sebagai pemisah ribuan, sedangkan Excel Anda mungkin menggunakan format US (koma). Setelah konversi selesai, blok kolom nominal di Excel, tekan &lt;code&gt;Ctrl+H&lt;/code&gt;, cari "." dan ganti dengan kosong (Replace All). Angka akan otomatis menjadi format numerik.&lt;/div&gt;
                &lt;/div&gt;
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-q"&gt;Apakah tool ini bisa mengonversi mutasi bank yang dikunci password? &lt;span class="faq-icon"&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-a"&gt;Tidak bisa secara langsung. Karena seluruh proses berjalan lokal di browser Anda demi privasi, engine kami tidak mem-bypass enkripsi. Buka dulu PDF berpassword tersebut di browser, lalu gunakan fitur "Print to PDF". File PDF baru yang tidak berpassword tersebut yang harus Anda masukkan ke tool ini.&lt;/div&gt;
                &lt;/div&gt;
                &lt;div class="faq-item"&gt;
                    &lt;button class="faq-q"&gt;Browser saya "Not Responding" saat mengunggah laporan 200 halaman. Solusinya? &lt;span class="faq-icon"&gt;▼&lt;/span&gt;&lt;/button&gt;
                    &lt;div class="faq-a"&gt;Proses ekstraksi 200 halaman memerlukan memori RAM perangkat (HP/Laptop) yang cukup besar. Jika browser hang, gunakan mode "Full Text" yang lebih ringan, atau pecah file PDF Anda menjadi per 50 halaman menggunakan tool pemotong PDF sebelum mengubahnya ke Excel.&lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/div&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
    &lt;div style="margin-bottom: 16px;"&gt;
        &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;About Us&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Contact Us&lt;/a&gt; | 
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Privacy Policy&lt;/a&gt;
    &lt;/div&gt;
    &lt;p&gt;© 2026 Kholil Tools. Data Anda 100% diproses di perangkat Anda.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- SCRIPTS --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"&gt;&lt;/script&gt;

&lt;script&gt;
    // 1. Mobile Menu Logic
    const menuToggle = document.getElementById('menuToggle');
    const navLinks = document.getElementById('navLinks');
    menuToggle.addEventListener('click', () =&gt; {
        navLinks.classList.toggle('active');
        menuToggle.textContent = navLinks.classList.contains('active') ? '✕' : '☰';
    });

    // 2. FAQ Logic
    document.querySelectorAll('.faq-q').forEach(btn =&gt; {
        btn.addEventListener('click', () =&gt; {
            const ans = btn.nextElementSibling;
            const wasOpen = btn.classList.contains('open');
            document.querySelectorAll('.faq-q').forEach(b =&gt; {
                b.classList.remove('open'); b.nextElementSibling.classList.remove('open');
                b.querySelector('.faq-icon').textContent = '▼';
            });
            if (!wasOpen) { 
                btn.classList.add('open'); ans.classList.add('open'); 
                btn.querySelector('.faq-icon').textContent = '▲';
            }
        });
    });

    // 3. Engine Konversi
    if (typeof pdfjsLib !== 'undefined') {
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
    }

    const fileInput = document.getElementById('fileInput');
    const dropZone = document.getElementById('dropZone');
    const dropText = document.getElementById('dropText');
    const triggerBtn = document.getElementById('triggerFileBtn');
    const actionArea = document.getElementById('actionArea');
    const convertBtn = document.getElementById('convertBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const statusBox = document.getElementById('statusBox');
    
    const progressWrap = document.getElementById('progressWrap');
    const progressFill = document.getElementById('progressFill');
    const progressText = document.getElementById('progressText');
    const progressPct = document.getElementById('progressPct');

    let selectedFile = null;
    let xlsxObjectURL = null;

    triggerBtn.addEventListener('click', (e) =&gt; { e.preventDefault(); fileInput.click(); });

    function handleFile(file) {
        if (!file) return;
        if (file.type !== 'application/pdf' &amp;&amp; !file.name.toLowerCase().endsWith('.pdf')) {
            statusBox.innerHTML = '&lt;span style="color:#ef4444;"&gt;❌ Format tidak didukung. Harap pilih PDF.&lt;/span&gt;';
            return;
        }
        selectedFile = file;
        dropText.textContent = file.name;
        triggerBtn.textContent = "Ganti File PDF";
        actionArea.style.display = 'block';
        convertBtn.style.display = 'block';
        downloadBtn.style.display = 'none';
        statusBox.innerHTML = '';
        progressWrap.classList.remove('visible');
    }

    fileInput.addEventListener('change', e =&gt; handleFile(e.target.files[0]));
    dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
    dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
    dropZone.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropZone.classList.remove('drag-over');
        handleFile(e.dataTransfer.files[0]);
    });

    function setProgress(pct, label) {
        progressWrap.classList.add('visible');
        progressFill.style.width = pct + '%';
        progressPct.textContent = pct + '%';
        progressText.textContent = label;
    }

    // Ekstraksi Logika Sederhana
    async function extractPage(page, mode) {
        const content = await page.getTextContent();
        const items = content.items.filter(i =&gt; i.str &amp;&amp; i.str.trim());
        const lines = {};
        
        if(mode === 'full') {
            items.forEach(item =&gt; {
                const y = Math.round(item.transform[5] / 2) * 2; // toleransi Y
                if (!lines[y]) lines[y] = [];
                lines[y].push({ x: item.transform[4], text: item.str.trim() });
            });
        } else {
            // Smart (Deteksi Kolom Kasar)
            items.forEach(item =&gt; {
                const y = Math.round(item.transform[5] / 4) * 4; 
                if (!lines[y]) lines[y] = [];
                lines[y].push({ x: Math.round(item.transform[4] / 10) * 10, text: item.str.trim() });
            });
        }

        return Object.keys(lines).sort((a, b) =&gt; b - a).map(y =&gt; {
            const row = lines[y].sort((a, b) =&gt; a.x - b.x);
            return row.map(col =&gt; col.text);
        });
    }

    convertBtn.addEventListener('click', async () =&gt; {
        if (!selectedFile) return;
        convertBtn.disabled = true; convertBtn.textContent = '⏳ Mengekstrak Data...';
        statusBox.innerHTML = '';
        setProgress(10, 'Membaca struktur PDF...');

        try {
            const arrayBuffer = await selectedFile.arrayBuffer();
            const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
            const numPages = pdf.numPages;
            const mode = document.getElementById('extractMode').value;
            const perPage = document.getElementById('sheetMode').value === 'perpage';
            const wb = XLSX.utils.book_new();
            let allRows = [];

            for (let p = 1; p &lt;= numPages; p++) {
                setProgress(Math.round(10 + (p / numPages) * 70), `Memproses hal ${p}/${numPages}...`);
                const page = await pdf.getPage(p);
                const rows = await extractPage(page, mode);
                
                if (perPage) {
                    const ws = XLSX.utils.aoa_to_sheet(rows);
                    XLSX.utils.book_append_sheet(wb, ws, 'Hal ' + p);
                } else {
                    if (p &gt; 1) allRows.push([]);
                    allRows.push(...rows);
                }
            }

            if (!perPage) {
                const ws = XLSX.utils.aoa_to_sheet(allRows);
                XLSX.utils.book_append_sheet(wb, ws, 'Tabel PDF');
            }

            setProgress(90, 'Menyusun .xlsx...');
            const buf = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
            const blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

            if (xlsxObjectURL) URL.revokeObjectURL(xlsxObjectURL);
            xlsxObjectURL = URL.createObjectURL(blob);

            const outName = selectedFile.name.replace(/\.pdf$/i, '.xlsx');
            downloadBtn.href = xlsxObjectURL;
            downloadBtn.download = outName;
            
            setProgress(100, 'Selesai!');
            statusBox.innerHTML = `&lt;span style="color:#10b981;"&gt;✅ Konversi sukses! File siap diunduh.&lt;/span&gt;`;
            
            convertBtn.style.display = 'none';
            downloadBtn.style.display = 'flex';

        } catch (err) {
            console.error(err);
            statusBox.innerHTML = `&lt;span style="color:#ef4444;"&gt;❌ Gagal: File diproteksi atau bukan PDF teks.&lt;/span&gt;`;
        }
        convertBtn.disabled = false; convertBtn.textContent = '&#128640; Mulai Ekstraksi ke Excel';
    });
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/pdf-to-excel.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3345191763240076314</guid><pubDate>Thu, 09 Apr 2026 21:41:00 +0000</pubDate><atom:updated>2026-07-23T08:46:12.181-07:00</atom:updated><title>Pdf to power point </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;meta name="description" content="Konversi PDF ke PowerPoint (PPTX) dengan format rapi. 100% aman, diproses lokal di browser Anda tanpa upload file ke server. Gratis &amp; tanpa batas!"&gt;
    &lt;meta name="author" content="Kholil"&gt;
    &lt;title&gt;Ubah PDF ke PowerPoint (PPTX) Online | KholilTools&lt;/title&gt;

    &lt;!-- Google Fonts: Plus Jakarta Sans &amp; Inter --&gt;
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Plus+Jakarta+Sans:wght@600;700;800&amp;display=swap" rel="stylesheet"&gt;

    &lt;style&gt;
        :root {
            --primary: #f97316; /* Orange 500 */
            --primary-hover: #ea580c; /* Orange 600 */
            --primary-light: #ffedd5; /* Orange 100 */
            --text-main: #111827; /* Gray 900 */
            --text-muted: #4b5563; /* Gray 600 */
            --bg-body: #f8fafc;
            --bg-card: #ffffff;
            --border-color: #e2e8f0; /* Gray 200 */
            --radius-xl: 20px;
            --radius-lg: 12px;
            --shadow-soft: 0 10px 40px -10px rgba(0,0,0,0.08);
            --shadow-sm: 0 1px 2px 0 rgba(0,0,0,0.05);
            --green: #10b981;
        }

        *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

        body {
            font-family: 'Inter', sans-serif;
            background-color: var(--bg-body);
            color: var(--text-main);
            line-height: 1.6;
            font-size: 16px;
        }

        /* ── HEADER / NAVIGATION ── */
        .header {
            background-color: var(--bg-card);
            border-bottom: 1px solid var(--border-color);
            position: sticky;
            top: 0;
            z-index: 50;
        }
        .header-top {
            max-width: 1000px;
            margin: 0 auto;
            padding: 16px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .logo {
            font-family: 'Plus Jakarta Sans', sans-serif;
            font-size: 1.25rem;
            font-weight: 800;
            color: var(--text-main);
            text-decoration: none;
        }
        .logo span { color: var(--primary); }
        .nav-main { display: flex; gap: 24px; align-items: center; }
        .nav-main a {
            font-size: 0.9rem;
            font-weight: 600;
            color: var(--text-main);
            text-decoration: none;
            transition: color 0.2s;
        }
        .nav-main a:hover { color: var(--primary); }
        .header-bottom {
            background-color: var(--bg-body);
            border-bottom: 1px solid var(--border-color);
        }
        .nav-sub {
            max-width: 1000px;
            margin: 0 auto;
            padding: 10px 20px;
            display: flex;
            justify-content: flex-end;
            gap: 20px;
        }
        .nav-sub a {
            font-size: 0.85rem;
            color: var(--text-muted);
            text-decoration: none;
            font-weight: 500;
        }
        .nav-sub a:hover { color: var(--primary); }
        .mobile-menu-btn {
            display: none; background: none; border: none; font-size: 1.5rem; cursor: pointer; color: var(--text-main);
        }

        /* ── HERO SECTION ── */
        .hero {
            max-width: 800px;
            margin: 48px auto 32px;
            text-align: center;
            padding: 0 20px;
        }
        .hero-badge {
            display: inline-block;
            background-color: var(--primary-light);
            color: var(--primary-hover);
            font-size: 0.75rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            padding: 6px 16px;
            border-radius: 99px;
            margin-bottom: 16px;
        }
        .hero h1 {
            font-family: 'Plus Jakarta Sans', sans-serif;
            font-size: clamp(1.8rem, 4vw, 2.4rem);
            font-weight: 800;
            line-height: 1.25;
            margin-bottom: 16px;
            color: var(--text-main);
        }
        .hero h1 span { color: var(--primary); }
        .hero p {
            font-size: 1rem;
            color: var(--text-muted);
            max-width: 600px;
            margin: 0 auto;
        }

        /* ── TOOL CARD ── */
        .tool-container {
            max-width: 720px;
            margin: 0 auto 48px;
            padding: 0 20px;
        }
        .tool-card {
            background-color: var(--bg-card);
            border: 1px solid var(--border-color);
            border-radius: var(--radius-xl);
            padding: 40px;
            box-shadow: var(--shadow-soft);
            text-align: center;
        }
        .drop-zone {
            border: 2px dashed var(--border-color);
            border-radius: var(--radius-lg);
            padding: 40px 20px;
            cursor: pointer;
            transition: all 0.2s;
            background-color: #f8fafc;
            margin-bottom: 24px;
            position: relative;
        }
        .drop-zone:hover, .drop-zone.drag-over {
            border-color: var(--primary);
            background-color: var(--primary-light);
        }
        .drop-zone input[type="file"] {
            position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
        }
        .drop-icon { font-size: 2.5rem; margin-bottom: 12px; display: block; }
        .drop-zone h3 { font-size: 1.1rem; font-weight: 700; margin-bottom: 6px; color: var(--text-main); }
        .drop-zone p { font-size: 0.9rem; color: var(--text-muted); }

        .btn-action {
            display: inline-flex; align-items: center; justify-content: center;
            width: 100%; max-width: 300px;
            background-color: #e2e8f0;
            color: #475569;
            border: none; padding: 14px 24px; font-size: 1rem; font-weight: 700;
            border-radius: 99px; cursor: pointer; transition: all 0.2s;
            font-family: inherit; margin: 0 auto;
        }
        .btn-action.ready {
            background-color: var(--primary);
            color: white;
            box-shadow: 0 4px 14px rgba(249, 115, 22, 0.25);
        }
        .btn-action.ready:hover { background-color: var(--primary-hover); transform: translateY(-2px); }
        .btn-action:disabled { background-color: #cbd5e1; color: #94a3b8; cursor: not-allowed; box-shadow: none; transform: none; }

        .btn-download {
            display: none; align-items: center; justify-content: center;
            width: 100%; max-width: 300px;
            background-color: var(--green); color: white;
            border: none; padding: 14px 24px; font-size: 1rem; font-weight: 700;
            border-radius: 99px; cursor: pointer; transition: all 0.2s;
            text-decoration: none; margin: 0 auto;
            box-shadow: 0 4px 14px rgba(16, 185, 129, 0.25);
        }
        .btn-download:hover { background-color: #059669; transform: translateY(-2px); }

        /* Progress &amp; Status */
        .progress-wrap { display: none; margin: 20px 0; text-align: left; }
        .progress-bar-bg { height: 8px; background: var(--border-color); border-radius: 99px; overflow: hidden; margin-top: 10px; }
        .progress-bar-fill { height: 100%; background: var(--primary); width: 0%; transition: width 0.3s ease; }
        .status-box { margin-top: 16px; font-size: 0.9rem; font-weight: 500; min-height: 24px; color: var(--text-muted); }
        .status-box.error { color: #dc2626; }
        .status-box.success { color: var(--green); }
        .status-box.warn { color: #d97706; }

        /* ── PROOF SECTION ── */
        .proof-section {
            background-color: var(--bg-card);
            border: 1px solid var(--border-color);
            border-radius: var(--radius-xl);
            padding: 40px 24px;
            margin-bottom: 48px;
            text-align: center;
            box-shadow: var(--shadow-soft);
        }
        .proof-section h3 { font-size: 1.25rem; font-weight: 700; margin-bottom: 32px; color: var(--text-main); }
        .proof-grid {
            display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
        }
        .proof-item img {
            width: 100%; aspect-ratio: 9/16; object-fit: cover; object-position: top;
            border-radius: 12px; border: 1px solid var(--border-color); margin-bottom: 16px;
            box-shadow: var(--shadow-sm);
        }
        .step-circle {
            width: 28px; height: 28px; background-color: var(--green); color: white;
            border-radius: 50%; display: flex; align-items: center; justify-content: center;
            font-size: 0.85rem; font-weight: bold; margin: 0 auto 8px;
        }
        .proof-item p { font-size: 0.9rem; font-weight: 600; color: var(--text-main); }

        /* ── AUTHOR BOX ── */
        .author-box {
            max-width: 720px; margin: 0 auto 48px; padding: 24px;
            background-color: var(--bg-card); border: 1px solid var(--border-color);
            border-radius: var(--radius-lg); display: flex; align-items: center; gap: 16px;
            box-shadow: var(--shadow-sm);
        }
        .author-avatar {
            width: 56px; height: 56px; background-color: var(--text-main); color: white;
            border-radius: 50%; display: flex; justify-content: center; align-items: center;
            font-size: 1.5rem; font-weight: bold; flex-shrink: 0;
        }
        .author-info h4 { font-size: 1.05rem; margin-bottom: 4px; color: var(--text-main); }
        .author-info p { font-size: 0.9rem; color: var(--text-muted); line-height: 1.4; }

        /* ── ARTICLE CONTENT ── */
        .article {
            max-width: 720px; margin: 0 auto 64px; padding: 0 20px;
        }
        .article h2 {
            font-family: 'Plus Jakarta Sans', sans-serif;
            font-size: 1.5rem; font-weight: 800; color: var(--text-main);
            margin: 40px 0 16px;
        }
        .article h2:first-child { margin-top: 0; }
        .article p { font-size: 1rem; color: var(--text-muted); margin-bottom: 16px; line-height: 1.7; }
        .article code { background-color: #f1f5f9; padding: 2px 6px; border-radius: 4px; font-size: 0.9em; color: #db2777; }

        /* ── MINIMALIST FAQ ── */
        .faq-list { margin-top: 32px; border-top: 1px solid var(--border-color); }
        .faq-item { border-bottom: 1px solid var(--border-color); }
        .faq-q {
            width: 100%; text-align: left; background: none; border: none;
            padding: 20px 0; font-family: inherit; font-size: 1.05rem; font-weight: 600;
            color: var(--text-main); cursor: pointer; display: flex; justify-content: space-between; align-items: center;
        }
        .faq-a { display: none; padding-bottom: 20px; font-size: 0.95rem; color: var(--text-muted); line-height: 1.7; }
        .faq-q.open { color: var(--primary); }
        .faq-a.open { display: block; }

        /* ── FOOTER ── */
        footer {
            border-top: 1px solid var(--border-color);
            padding: 40px 20px; text-align: center;
            background-color: var(--bg-card);
        }
        .footer-links { margin-bottom: 24px; display: flex; justify-content: center; gap: 24px; flex-wrap: wrap; }
        .footer-links a { color: var(--text-muted); font-size: 0.95rem; text-decoration: none; font-weight: 500; }
        .footer-links a:hover { color: var(--primary); }
        footer p { font-size: 0.9rem; color: var(--text-muted); }

        /* ── RESPONSIVE ── */
        @media (max-width: 768px) {
            .nav-main {
                display: none; flex-direction: column; position: absolute;
                top: 100%; left: 0; width: 100%; background: var(--bg-card);
                padding: 16px 20px; border-bottom: 1px solid var(--border-color);
                box-shadow: var(--shadow-sm); align-items: flex-start;
            }
            .nav-main.active { display: flex; }
            .mobile-menu-btn { display: block; }
            .nav-sub { display: none; }
            .proof-grid { grid-template-columns: 1fr; }
            .proof-item img { aspect-ratio: auto; max-height: 400px; object-fit: contain; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- HEADER --&gt;
&lt;header class="header"&gt;
    &lt;div class="header-top"&gt;
        &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;
        &lt;button class="mobile-menu-btn" id="menuToggle" aria-label="Menu Navigasi"&gt;☰&lt;/button&gt;
        &lt;nav class="nav-main" id="navLinks"&gt;
            &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;About Us&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Contact Us&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Privacy Policy&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/terms.html"&gt;Terms&lt;/a&gt;
        &lt;/nav&gt;
    &lt;/div&gt;
    &lt;div class="header-bottom"&gt;
        &lt;div class="nav-sub"&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Word ke PDF&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-excel.html"&gt;PDF ke Excel&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/header&gt;

&lt;!-- HERO --&gt;
&lt;section class="hero"&gt;
    &lt;div class="hero-badge"&gt;Aman &amp; Diproses Lokal&lt;/div&gt;
    &lt;h1&gt;Ubah PDF ke Format &lt;span&gt;PowerPoint (PPTX)&lt;/span&gt;&lt;/h1&gt;
    &lt;p&gt;Ubah file PDF Anda menjadi slide presentasi yang bisa diedit. Proses cepat, gratis, dan 100% aman karena diproses langsung di perangkat Anda tanpa perlu diunggah ke server.&lt;/p&gt;
&lt;/section&gt;

&lt;!-- TOOL CARD --&gt;
&lt;div class="tool-container"&gt;
    &lt;div class="tool-card"&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            &lt;input type="file" id="fileInput" accept="application/pdf,.pdf"&gt;
            &lt;span class="drop-icon" aria-hidden="true"&gt;&#128196;&lt;/span&gt;
            &lt;h3 id="dropText"&gt;Seret file PDF Anda ke sini&lt;/h3&gt;
            &lt;p&gt;Atau klik untuk memilih file dari perangkat Anda&lt;/p&gt;
        &lt;/div&gt;

        &lt;div class="progress-wrap" id="progressWrap"&gt;
            &lt;div style="display:flex; justify-content:space-between; font-size:0.9rem; font-weight:600; color:var(--text-main);"&gt;
                &lt;span id="progressText"&gt;Memproses file...&lt;/span&gt;
                &lt;span id="progressPct"&gt;0%&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="progress-bar-bg"&gt;
                &lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="status-box" id="statusBox"&gt;&lt;/div&gt;

        &lt;button class="btn-action" id="convertBtn" disabled&gt;
            &#128640; Mulai Konversi
        &lt;/button&gt;
        &lt;a class="btn-download" id="downloadBtn" href="#" download="hasil.pptx"&gt;
            ⬇ Unduh File PPTX
        &lt;/a&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- BUKTI TOOL (UI DISESUAIKAN DENGAN SCREENSHOT BARU) --&gt;
&lt;div class="tool-container"&gt;
    &lt;div class="proof-section"&gt;
        &lt;h3&gt;Bukti Nyata Tool Ini Berfungsi 100%&lt;/h3&gt;
        &lt;div class="proof-grid"&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/ynXN1QnX/Screenshot-2026-07-23-22-31-47-608-com-android-chrome.jpg" alt="Upload File PDF" loading="lazy"&gt;
                &lt;div class="step-circle"&gt;1&lt;/div&gt;
                &lt;p&gt;Upload file dengan lancar&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/pjDsCjN1/Screenshot-2026-07-23-22-31-57-682-com-google-android-documentsui.jpg" alt="Pilih Lokasi Simpan" loading="lazy"&gt;
                &lt;div class="step-circle"&gt;2&lt;/div&gt;
                &lt;p&gt;Pilih direktori penyimpanan&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class="proof-item"&gt;
                &lt;img src="https://i.ibb.co.com/RpHRCm5k/Screenshot-2026-07-23-22-34-30-167-com-document-viewer-powerpoint-reader.jpg" alt="Hasil Slide PPTX" loading="lazy"&gt;
                &lt;div class="step-circle"&gt;3&lt;/div&gt;
                &lt;p&gt;Hasil akhir rapi &amp; sempurna&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- AUTHOR BOX --&gt;
&lt;div class="author-box"&gt;
    &lt;div class="author-avatar"&gt;K&lt;/div&gt;
    &lt;div class="author-info"&gt;
        &lt;h4&gt;Ditulis oleh Kholil&lt;/h4&gt;
        &lt;p&gt;Konsultan Ekstraksi Data &amp; Technical Developer | Diperbarui: Juli 2026&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- ARTICLE --&gt;
&lt;div class="article"&gt;
    &lt;h2&gt;Bagaimana Cara Kerjanya?&lt;/h2&gt;
    &lt;p&gt;File PowerPoint (&lt;code&gt;.pptx&lt;/code&gt;) pada dasarnya adalah kumpulan file terstruktur yang dibungkus dalam format ZIP. Tool kami menggunakan JavaScript untuk membaca teks dari file PDF Anda secara lokal, lalu menyusunnya ulang menjadi slide PowerPoint langsung dari browser Anda.&lt;/p&gt;
    &lt;p&gt;Karena seluruh proses terjadi di dalam browser (tanpa dikirim ke server luar), privasi dokumen Anda terjamin 100% aman dan prosesnya pun bisa lebih cepat tergantung spesifikasi perangkat yang Anda gunakan.&lt;/p&gt;

    &lt;h2&gt;Tips untuk File PDF Berukuran Besar&lt;/h2&gt;
    &lt;p&gt;Jika Anda mengonversi PDF dengan ratusan halaman, proses ini mungkin akan memakan cukup banyak memori (RAM) di perangkat Anda. Jika browser di HP atau laptop Anda terasa melambat saat proses berjalan, kami sarankan untuk memecah PDF tersebut menjadi beberapa bagian yang lebih kecil (misalnya per 25 halaman) sebelum diubah ke PPTX.&lt;/p&gt;

    &lt;h2&gt;Tanya Jawab (FAQ)&lt;/h2&gt;
    &lt;div class="faq-list"&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q"&gt;Kenapa file yang terunduh bernama .zip, bukan .pptx? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
            &lt;div class="faq-a"&gt;Ini hal yang wajar. Secara teknis, format &lt;code&gt;.pptx&lt;/code&gt; memang merupakan file ZIP. Beberapa browser (terutama Chrome di Android) terkadang otomatis mengubah akhiran namanya saat diunduh. Isinya tetap 100% aman dan valid. &lt;strong&gt;Solusinya:&lt;/strong&gt; Buka File Manager di HP Anda, ganti nama file (rename), dan ubah akhiran &lt;code&gt;.zip&lt;/code&gt; menjadi &lt;code&gt;.pptx&lt;/code&gt;. File siap dibuka di Microsoft PowerPoint atau Google Slides!&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q"&gt;Kenapa jenis font asli dari PDF saya berubah? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
            &lt;div class="faq-a"&gt;Agar ukuran file hasil konversi tetap ringan dan prosesnya cepat, sistem kami menggunakan font standar bawaan seperti Arial atau Calibri. Namun jangan khawatir, Anda bisa mengubah semua font sekaligus dengan mudah menggunakan fitur "Slide Master" saat file sudah dibuka di PowerPoint.&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q"&gt;Apakah gambar dan background di PDF juga ikut terkonversi? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
            &lt;div class="faq-a"&gt;Saat ini, tool kami difokuskan untuk mengekstrak teks dengan presisi tinggi agar tata letaknya tidak berantakan. Memindahkan gambar atau grafik kompleks dari PDF ke PowerPoint membutuhkan proses server pihak ketiga, yang mana bertentangan dengan komitmen kami untuk menjaga privasi data Anda tetap 100% lokal.&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="faq-item"&gt;
            &lt;button class="faq-q"&gt;Browser saya macet saat memproses slide, apa solusinya? &lt;span&gt;▼&lt;/span&gt;&lt;/button&gt;
            &lt;div class="faq-a"&gt;Ini biasanya terjadi karena keterbatasan RAM pada perangkat. Solusi terbaiknya adalah memuat ulang (refresh) halaman ini, lalu gunakan tool pemisah PDF untuk membagi dokumen Anda menjadi beberapa bagian kecil (misal per 20 halaman) sebelum mengonversinya kembali.&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
    &lt;div class="footer-links"&gt;
        &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/word-to-pdf.html"&gt;Utilitas Word&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/2026/04/pdf-to-excel.html"&gt;Utilitas Excel&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;Tentang Kami&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt;
        &lt;a href="https://www.kholil.biz.id/p/terms.html"&gt;Syarat &amp; Ketentuan&lt;/a&gt;
    &lt;/div&gt;
    &lt;p&gt;© 2026 kholil.biz.id. Semua proses dilakukan secara lokal demi privasi Anda.&lt;/p&gt;
&lt;/footer&gt;

&lt;!-- 1. Script Pembaca PDF (PDF.js) --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"&gt;&lt;/script&gt;

&lt;!-- 2. Script Pembuat PowerPoint (PptxGenJS) --&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/pptxgenjs@3.11.0/dist/pptxgen.bundle.js"&gt;&lt;/script&gt;

&lt;script&gt;
    // ── Logika Menu Mobile ──
    const menuBtn = document.getElementById('menuToggle');
    const navLinks = document.getElementById('navLinks');
    menuBtn.addEventListener('click', () =&gt; {
        navLinks.classList.toggle('active');
        menuBtn.textContent = navLinks.classList.contains('active') ? '✕' : '☰';
    });

    // ── Animasi FAQ ──
    document.querySelectorAll('.faq-q').forEach(btn =&gt; {
        btn.addEventListener('click', () =&gt; {
            const ans = btn.nextElementSibling;
            const isOpen = btn.classList.contains('open');

            document.querySelectorAll('.faq-q').forEach(b =&gt; {
                b.classList.remove('open');
                b.nextElementSibling.classList.remove('open');
                b.querySelector('span').textContent = '▼';
            });

            if (!isOpen) {
                btn.classList.add('open');
                ans.classList.add('open');
                btn.querySelector('span').textContent = '▲';
            }
        });
    });

    // ── Engine Konversi (PDF ke PPTX) ──
    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';

    const dropZone = document.getElementById('dropZone');
    const fileInput = document.getElementById('fileInput');
    const convertBtn = document.getElementById('convertBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const dropText = document.getElementById('dropText');
    const statusBox = document.getElementById('statusBox');

    const progressWrap = document.getElementById('progressWrap');
    const progressFill = document.getElementById('progressFill');
    const progressPct = document.getElementById('progressPct');
    const progressText = document.getElementById('progressText');

    let selectedFile = null;
    let currentBlobUrl = null;

    const PPTX_MIME = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';

    // Efek visual Drag &amp; Drop
    dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
    dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
    dropZone.addEventListener('drop', e =&gt; {
        e.preventDefault(); dropZone.classList.remove('drag-over');
        handleFile(e.dataTransfer.files[0]);
    });
    fileInput.addEventListener('change', e =&gt; handleFile(e.target.files[0]));

    function handleFile(file) {
        if (!file || file.type !== 'application/pdf') {
            statusBox.className = 'status-box';
            statusBox.innerHTML = '&lt;span class="error"&gt;❌ Maaf, format tidak didukung. Harap pilih file .pdf&lt;/span&gt;';
            return;
        }
        selectedFile = file;
        dropText.innerHTML = `&lt;strong&gt;${file.name}&lt;/strong&gt;&lt;br&gt;&lt;span style="font-size:0.9rem; color:#4b5563;"&gt;File siap dikonversi&lt;/span&gt;`;

        convertBtn.disabled = false;
        convertBtn.classList.add('ready');
        convertBtn.style.display = 'flex';
        convertBtn.innerHTML = '&#128640; Mulai Konversi';

        downloadBtn.style.display = 'none';
        statusBox.className = 'status-box';
        statusBox.innerHTML = '';
        progressWrap.style.display = 'none';

        if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; }
    }

    function setProgress(pct, label) {
        progressWrap.style.display = 'block';
        progressFill.style.width = pct + '%';
        progressPct.textContent = pct + '%';
        progressText.textContent = label;
    }

    // Mengelompokkan teks berdasarkan posisi tingginya (Y) agar susunan kalimat tidak berantakan
    function extractLines(textContent) {
        const lineMap = new Map();
        textContent.items.forEach(item =&gt; {
            const y = Math.round(item.transform[5]);
            if (!lineMap.has(y)) lineMap.set(y, []);
            lineMap.get(y).push(item.str);
        });
        return Array.from(lineMap.entries())
            .sort((a, b) =&gt; b[0] - a[0]) 
            .map(([, words]) =&gt; words.join(' ').replace(/\s+/g, ' ').trim())
            .filter(line =&gt; line.length &gt; 0);
    }

    function resetButtonAfterRun() {
        convertBtn.disabled = false;
        convertBtn.classList.add('ready');
        convertBtn.innerHTML = '&#128640; Konversi Ulang';
    }

    convertBtn.addEventListener('click', async () =&gt; {
        if (!selectedFile) return;

        if (typeof PptxGenJS === 'undefined') {
            statusBox.className = 'status-box';
            statusBox.innerHTML = '&lt;span class="error"&gt;❌ Sistem gagal dimuat. Pastikan internet Anda lancar, lalu refresh halaman ini.&lt;/span&gt;';
            return;
        }

        convertBtn.disabled = true;
        convertBtn.classList.remove('ready');
        convertBtn.innerHTML = '⏳ Sedang Memproses...';
        statusBox.className = 'status-box';
        statusBox.innerHTML = '';
        setProgress(5, 'Membaca struktur PDF...');

        try {
            const arrayBuffer = await selectedFile.arrayBuffer();
            setProgress(20, 'Menyiapkan memori...');

            const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
            const total = pdf.numPages;

            let pres = new PptxGenJS();
            pres.layout = 'LAYOUT_16x9';

            for (let i = 1; i &lt;= total; i++) {
                setProgress(20 + Math.round((i / total) * 60), `Membuat Slide ${i} dari ${total}...`);
                const page = await pdf.getPage(i);
                const content = await page.getTextContent();
                const lines = extractLines(content);

                let slide = pres.addSlide();

                if (lines.length &gt; 0) {
                    slide.addText(lines.join('\n'), {
                        x: 0.5, y: 0.4, w: "90%", h: "92%",
                        fontSize: 16, color: "363636", align: "left", valign: "top",
                        breakLine: true
                    });
                } else {
                    slide.addText("[Halaman ini berisi gambar atau kosong]", {
                        x: 0.5, y: "40%", w: "90%", fontSize: 14, color: "999999", italic: true, align: "center"
                    });
                }
            }

            setProgress(90, 'Menyusun file PPTX...');
            const outName = selectedFile.name.replace(/\.pdf$/i, '') + '.pptx';

            const rawBlob = await pres.write('blob');
            const blob = rawBlob.type === PPTX_MIME ? rawBlob : new Blob([rawBlob], { type: PPTX_MIME });

            let savedDirectly = false;

            if (window.showSaveFilePicker) {
                try {
                    const handle = await window.showSaveFilePicker({
                        suggestedName: outName,
                        types: [{
                            description: 'PowerPoint Presentation',
                            accept: { [PPTX_MIME]: ['.pptx'] }
                        }]
                    });
                    const writable = await handle.createWritable();
                    await writable.write(blob);
                    await writable.close();
                    savedDirectly = true;
                } catch (pickerErr) {
                    if (pickerErr &amp;&amp; pickerErr.name === 'AbortError') {
                        progressWrap.style.display = 'none';
                        statusBox.className = 'status-box';
                        statusBox.innerHTML = '';
                        resetButtonAfterRun();
                        return;
                    }
                }
            }

            if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); }
            currentBlobUrl = URL.createObjectURL(blob);

            if (!savedDirectly) {
                const tempLink = document.createElement('a');
                tempLink.href = currentBlobUrl;
                tempLink.download = outName;
                document.body.appendChild(tempLink);
                tempLink.click();
                document.body.removeChild(tempLink);
            }

            setProgress(100, 'Konversi Selesai!');

            if (savedDirectly) {
                statusBox.className = 'status-box success';
                statusBox.innerHTML = `✅ &lt;strong&gt;${total} slide berhasil dibuat.&lt;/strong&gt; File telah disimpan ke perangkat Anda.`;
            } else {
                statusBox.className = 'status-box warn';
                statusBox.innerHTML = `✅ &lt;strong&gt;${total} slide berhasil dibuat.&lt;/strong&gt; File otomatis terunduh. Jika akhirannya &lt;strong&gt;.zip&lt;/strong&gt;, cukup rename menjadi &lt;strong&gt;.pptx&lt;/strong&gt; di File Manager Anda.`;
            }

            convertBtn.style.display = 'none';
            downloadBtn.style.display = 'flex';
            downloadBtn.href = currentBlobUrl;
            downloadBtn.download = outName;
            return;

        } catch (err) {
            console.error(err);
            let msg = 'Maaf, terjadi kesalahan. Pastikan file PDF Anda tidak rusak.';
            const errText = (err &amp;&amp; err.message) ? err.message.toLowerCase() : '';
            if (errText.includes('password') || errText.includes('encrypt')) {
                msg = 'File PDF Anda terkunci oleh password. Buka kuncinya terlebih dahulu ya.';
            } else if (errText.includes('invalid') || errText.includes('corrupt')) {
                msg = 'Sepertinya file PDF Anda rusak atau formatnya tidak valid.';
            }
            statusBox.className = 'status-box';
            statusBox.innerHTML = `&lt;span class="error"&gt;❌ ${msg}&lt;/span&gt;`;
            progressWrap.style.display = 'none';
        }

        resetButtonAfterRun();
    });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/pdf-to-power-point.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-8521934822177203542</guid><pubDate>Thu, 09 Apr 2026 21:31:19 +0000</pubDate><atom:updated>2026-07-23T09:15:46.577-07:00</atom:updated><title>PDF To Word </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;meta name="description" content="Konversi PDF ke Word (DOCX) gratis, cepat, dan 100% aman diproses lokal di browser. Format rapi tanpa upload ke server."&gt;
    &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/pdf-to-word.html"&gt;
    &lt;title&gt;Ubah PDF ke Word (DOCX) Langsung di RAM | Kholil Tools&lt;/title&gt;

    &lt;!-- Font Modern --&gt;
    &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
    &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@500;600;700;800&amp;family=Inter:wght@400;500;600&amp;display=swap" rel="stylesheet"&gt;

    &lt;!-- Schema Markup --&gt;
    &lt;script type="application/ld+json"&gt;
    {
      "@context": "https://schema.org",
      "@type": "SoftwareApplication",
      "name": "PDF ke Word (DOCX) Converter",
      "operatingSystem": "Web",
      "applicationCategory": "BusinessApplication",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "IDR"
      },
      "description": "Utilitas ekstraksi teks PDF ke format DOCX yang dieksekusi 100% lokal di browser tanpa server."
    }
    &lt;/script&gt;

    &lt;style&gt;
        :root {
            --primary: #4F46E5; /* Indigo */
            --primary-light: #EEF2FF;
            --text-dark: #0F172A;
            --text-gray: #475569;
            --bg-body: #F8FAFC;
            --bg-card: #FFFFFF;
            --border-light: #E2E8F0;
            --green-bg: #DCFCE7;
            --green-text: #166534;
            --warning-bg: #FEF3C7;
            --warning-border: #F59E0B;
            --shadow-sm: 0 1px 3px rgba(0,0,0,0.05);
            --shadow-md: 0 10px 25px -5px rgba(0,0,0,0.05);
            --radius-lg: 16px;
            --radius-xl: 24px;
        }

        *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
        body { font-family: 'Inter', sans-serif; background-color: var(--bg-body); color: var(--text-dark); line-height: 1.6; }
        h1, h2, h3, h4 { font-family: 'Plus Jakarta Sans', sans-serif; }
        a { text-decoration: none; color: inherit; }

        /* HEADER */
        .header { background: var(--bg-card); padding: 16px 24px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); position: relative; z-index: 10; }
        .logo { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 1.25rem; font-weight: 800; color: var(--text-dark); }
        .logo span { color: #F97316; }
        .nav-links { display: flex; gap: 24px; font-size: 0.9rem; font-weight: 600; color: var(--text-gray); }
        .nav-links a:hover { color: var(--primary); }
        @media (max-width: 768px) { .nav-links { display: none; } }

        /* HERO &amp; GRADIENT BG */
        .hero-section {
            position: relative; padding: 60px 20px 100px; text-align: center;
            background: linear-gradient(135deg, #F1F5F9 0%, #E0E7FF 50%, #FCE7F3 100%);
        }
        .badge-top { display: inline-flex; align-items: center; background: rgba(255,255,255,0.8); backdrop-filter: blur(4px); padding: 6px 16px; border-radius: 99px; font-size: 0.8rem; font-weight: 700; color: var(--primary); margin-bottom: 20px; box-shadow: var(--shadow-sm); }
        .hero-section h1 { font-size: clamp(1.8rem, 4vw, 2.5rem); font-weight: 800; color: var(--text-dark); line-height: 1.2; max-width: 800px; margin: 0 auto 16px; }
        .hero-section h1 span { color: var(--primary); }
        .hero-section p { font-size: 1rem; color: var(--text-gray); max-width: 600px; margin: 0 auto 32px; }

        .feature-pills { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; }
        .pill { background: rgba(255,255,255,0.9); padding: 8px 16px; border-radius: 99px; font-size: 0.8rem; font-weight: 600; color: var(--text-dark); display: flex; align-items: center; gap: 6px; box-shadow: var(--shadow-sm); }

        /* MAIN GRID (TOOL + SIDEBAR) */
        .container { max-width: 1100px; margin: -60px auto 40px; padding: 0 20px; position: relative; z-index: 5; }
        .main-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 24px; }
        @media (max-width: 850px) { .main-grid { grid-template-columns: 1fr; } .container { margin-top: -40px; } }

        /* CARDS */
        .card { background: var(--bg-card); border-radius: var(--radius-xl); padding: 24px; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); }
        .card-header { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; border-bottom: 1px solid var(--border-light); padding-bottom: 16px; }
        .card-title { font-size: 1rem; font-weight: 700; color: var(--text-dark); }
        .icon-box { width: 36px; height: 36px; background: var(--primary-light); color: var(--primary); border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }

        /* TOOL AREA */
        .drop-zone { background: var(--primary-light); border: 2px dashed #94A3B8; border-radius: var(--radius-lg); padding: 40px 20px; text-align: center; cursor: pointer; transition: all 0.2s; position: relative; }
        .drop-zone:hover, .drop-zone.drag-over { border-color: var(--primary); background: #E0E7FF; }
        .drop-zone:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
        .drop-icon { font-size: 3rem; display: block; margin-bottom: 12px; }
        .drop-text { font-weight: 700; font-size: 1.05rem; margin-bottom: 4px; color: var(--text-dark); }
        .drop-sub { font-size: 0.85rem; color: var(--text-gray); }
        .drop-sub span { color: var(--primary); text-decoration: underline; }
        .file-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }

        /* LIBRARY STATUS BANNER */
        .lib-status { display: none; align-items: center; gap: 8px; font-size: 0.8rem; font-weight: 600; padding: 10px 14px; border-radius: 10px; margin-top: 16px; }
        .lib-status.loading { display: flex; background: #FEF3C7; color: #92400E; }
        .lib-status.error { display: flex; background: #FEE2E2; color: #991B1B; }
        .lib-status .spinner { width: 14px; height: 14px; border: 2px solid #92400E; border-top-color: transparent; border-radius: 50%; animation: spin 0.8s linear infinite; flex-shrink: 0; }
        .lib-status .retry-link { text-decoration: underline; cursor: pointer; font-weight: 700; }
        @keyframes spin { to { transform: rotate(360deg); } }

        /* STATUS &amp; BUTTON */
        .status-area { margin-top: 20px; text-align: center; font-size: 0.9rem; font-weight: 500; display: none; }
        .progress-bg { background: var(--border-light); height: 6px; border-radius: 99px; margin-top: 8px; overflow: hidden; }
        .progress-bar { background: var(--primary); height: 100%; width: 0%; transition: width 0.3s; }

        .btn-action { width: 100%; padding: 16px; border: none; border-radius: 12px; font-family: 'Plus Jakarta Sans', sans-serif; font-size: 1rem; font-weight: 700; color: white; background: #94A3B8; cursor: not-allowed; margin-top: 24px; transition: all 0.3s; }
        .btn-action.ready { background: var(--primary); cursor: pointer; box-shadow: 0 4px 14px rgba(79, 70, 229, 0.3); }
        .btn-action.ready:hover { background: #4338CA; transform: translateY(-2px); }

        .btn-download { display: none; width: 100%; padding: 16px; text-align: center; border-radius: 12px; font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 700; color: white; background: #10B981; margin-top: 24px; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.3); transition: transform 0.2s; cursor: pointer; }
        .btn-download:hover { transform: translateY(-2px); }

        /* RIGHT SIDEBAR (Green Card &amp; Steps) */
        .trust-card { background: var(--green-bg); text-align: center; padding: 24px; border-radius: var(--radius-lg); border: 1px solid #BBF7D0; margin-bottom: 24px; }
        .trust-icon { font-size: 2.5rem; margin-bottom: 12px; }
        .trust-card h3 { font-size: 1rem; color: var(--green-text); margin-bottom: 8px; }
        .trust-card p { font-size: 0.85rem; color: #15803D; line-height: 1.5; }

        .step-list { display: flex; flex-direction: column; gap: 16px; margin-top: 20px; }
        .step-item { display: flex; gap: 16px; align-items: flex-start; }
        .step-num { flex-shrink: 0; width: 28px; height: 28px; background: var(--primary); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.85rem; }
        .step-item p { font-size: 0.85rem; color: var(--text-gray); margin-top: 4px; }
        .step-item p strong { color: var(--text-dark); }

        /* PROOF GRID (Bukti Fungsi Nyata) */
        .proof-section { max-width: 1100px; margin: 0 auto 40px; padding: 0 20px; }
        .proof-card { background: var(--bg-card); border-radius: var(--radius-xl); padding: 28px; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); }
        .proof-title { font-size: 1.15rem; font-weight: 800; color: var(--text-dark); text-align: center; margin-bottom: 24px; }
        .proof-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
        @media (max-width: 700px) { .proof-grid { grid-template-columns: 1fr; } }
        .proof-item { text-align: center; }
        .proof-frame { border: 1px solid var(--border-light); border-radius: 14px; overflow: hidden; box-shadow: var(--shadow-sm); background: #F1F5F9; }
        .proof-frame img { width: 100%; height: auto; display: block; }
        .proof-badge { width: 30px; height: 30px; background: var(--primary); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.9rem; margin: 14px auto 8px; }
        .proof-caption { font-size: 0.9rem; font-weight: 600; color: var(--text-dark); }

        /* AUTHOR &amp; ARTICLE */
        .author-box { display: flex; align-items: center; gap: 16px; padding: 20px; background: var(--bg-card); border-radius: var(--radius-lg); border: 1px solid var(--border-light); margin: 40px auto; max-width: 800px; }
        .author-avatar { width: 48px; height: 48px; background: var(--primary); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1.2rem; }
        .author-info h4 { font-size: 1rem; margin-bottom: 2px; }
        .author-info p { font-size: 0.85rem; color: var(--text-gray); }

        .article-content { max-width: 800px; margin: 40px auto 60px; }
        .article-content h2 { font-size: 1.4rem; margin: 32px 0 16px; color: var(--text-dark); }
        .article-content p, .article-content li { font-size: 0.95rem; color: var(--text-gray); margin-bottom: 16px; }

        .warning-box { background: var(--primary-light); border-left: 4px solid var(--primary); padding: 20px; border-radius: 0 12px 12px 0; margin: 24px 0; }
        .warning-box strong { display: flex; align-items: center; gap: 8px; color: var(--primary); font-size: 0.95rem; margin-bottom: 8px; }

        /* FAQ */
        .faq-item { border-bottom: 1px solid var(--border-light); padding: 16px 0; }
        .faq-q { width: 100%; text-align: left; background: none; border: none; font-family: 'Plus Jakarta Sans', sans-serif; font-size: 1rem; font-weight: 600; color: var(--text-dark); cursor: pointer; display: flex; justify-content: space-between; align-items: center; }
        .faq-a { display: none; padding-top: 12px; font-size: 0.95rem; color: var(--text-gray); }
        .faq-q.open { color: var(--primary); }
        .faq-a.open { display: block; }

        /* FOOTER */
        footer { border-top: 1px solid var(--border-light); padding: 30px 20px; text-align: center; background: var(--bg-card); font-size: 0.85rem; color: var(--text-gray); }

        /* SR-ONLY for live status announcements */
        .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;!-- HEADER --&gt;
    &lt;header class="header"&gt;
        &lt;a href="https://www.kholil.biz.id/" class="logo"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/a&gt;
        &lt;nav class="nav-links"&gt;
            &lt;a href="https://www.kholil.biz.id/"&gt;Home&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;About Us&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Privacy Policy&lt;/a&gt;
            &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Utilitas Lainnya&lt;/a&gt;
        &lt;/nav&gt;
    &lt;/header&gt;

    &lt;!-- HERO GRADIENT --&gt;
    &lt;section class="hero-section"&gt;
        &lt;div class="badge-top"&gt;✨ Operasi 100% Client-Side&lt;/div&gt;
        &lt;h1&gt;Ekstraksi &lt;span&gt;PDF ke Word&lt;/span&gt; Langsung di RAM Browser&lt;/h1&gt;
        &lt;p&gt;Unggah dokumen .pdf, dan biarkan skrip peramban kami mengkomposisi ulang struktur teks Anda ke dalam file Word (DOCX) yang rapi.&lt;/p&gt;
        &lt;div class="feature-pills"&gt;
            &lt;div class="pill"&gt;&#128274; Privasi Dokumen Absolut&lt;/div&gt;
            &lt;div class="pill"&gt;⚡ Konversi JavaScript Lokal&lt;/div&gt;
            &lt;div class="pill"&gt;&#128241; Ramah Peramban Seluler&lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;

    &lt;!-- MAIN TOOL GRID --&gt;
    &lt;div class="container"&gt;
        &lt;div class="main-grid"&gt;

            &lt;!-- LEFT: TOOL CARD --&gt;
            &lt;div class="card"&gt;
                &lt;div class="card-header"&gt;
                    &lt;div class="icon-box"&gt;&#128221;&lt;/div&gt;
                    &lt;span class="card-title"&gt;PDF ke Word (DOCX) Parser&lt;/span&gt;
                &lt;/div&gt;

                &lt;!-- DROP ZONE (Accessible) --&gt;
                &lt;div class="drop-zone" id="dropZone" role="button" tabindex="0" aria-label="Area unggah file PDF, klik atau tekan Enter untuk memilih file"&gt;
                    &lt;span class="drop-icon" aria-hidden="true"&gt;&#128193;&lt;/span&gt;
                    &lt;p class="drop-text" id="dropText"&gt;Seret &amp; Lepas file .pdf Anda di sini&lt;/p&gt;
                    &lt;p class="drop-sub" id="dropSub"&gt;atau &lt;span&gt;klik untuk mencari file&lt;/span&gt;&lt;/p&gt;
                    &lt;input type="file" id="pdfInput" class="file-input" accept="application/pdf" aria-hidden="true" tabindex="-1"&gt;
                &lt;/div&gt;

                &lt;!-- LIBRARY LOAD STATUS --&gt;
                &lt;div class="lib-status loading" id="libStatus" role="status" aria-live="polite"&gt;
                    &lt;span class="spinner" aria-hidden="true"&gt;&lt;/span&gt;
                    &lt;span id="libStatusText"&gt;Memuat mesin konversi (PDF.js &amp; docx.js)...&lt;/span&gt;
                &lt;/div&gt;

                &lt;!-- PROGRESS &amp; STATUS --&gt;
                &lt;div class="status-area" id="statusArea" role="status" aria-live="polite"&gt;
                    &lt;div style="display: flex; justify-content: space-between; margin-bottom: 4px;"&gt;
                        &lt;span id="statusText"&gt;Memproses...&lt;/span&gt;
                        &lt;span id="statusPct"&gt;0%&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="progress-bg"&gt;
                        &lt;div class="progress-bar" id="progressBar"&gt;&lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;!-- BUTTONS --&gt;
                &lt;button class="btn-action" id="convertBtn" disabled&gt;⏳ Memuat pustaka sistem...&lt;/button&gt;
                &lt;a class="btn-download" id="downloadBtn" href="#" download="Hasil_Ekstraksi.docx"&gt;⬇ Unduh Hasil (.docx)&lt;/a&gt;
            &lt;/div&gt;

            &lt;!-- RIGHT: SIDEBAR INFO --&gt;
            &lt;div&gt;
                &lt;div class="trust-card"&gt;
                    &lt;div class="trust-icon"&gt;&#128737;️&lt;/div&gt;
                    &lt;h3&gt;Integritas Privasi Klien&lt;/h3&gt;
                    &lt;p&gt;Sistem ini beroperasi tertutup di dalam memori peramban Anda. Dokumen sensitif tidak pernah meninggalkan perangkat klien maupun diunggah ke server.&lt;/p&gt;
                &lt;/div&gt;

                &lt;div class="card"&gt;
                    &lt;div class="card-header" style="padding-bottom:12px; margin-bottom:0;"&gt;
                        &lt;span class="card-title" style="font-size:0.9rem;"&gt;&#128203; Langkah Ekstraksi&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="step-list"&gt;
                        &lt;div class="step-item"&gt;
                            &lt;div class="step-num"&gt;1&lt;/div&gt;
                            &lt;p&gt;Impor file PDF ke area unggah.&lt;/p&gt;
                        &lt;/div&gt;
                        &lt;div class="step-item"&gt;
                            &lt;div class="step-num"&gt;2&lt;/div&gt;
                            &lt;p&gt;Klik tombol &lt;strong&gt;Eksekusi Konversi&lt;/strong&gt;.&lt;/p&gt;
                        &lt;/div&gt;
                        &lt;div class="step-item"&gt;
                            &lt;div class="step-num"&gt;3&lt;/div&gt;
                            &lt;p&gt;Pustaka JS kami akan mem-parsing teks secara lokal.&lt;/p&gt;
                        &lt;/div&gt;
                        &lt;div class="step-item"&gt;
                            &lt;div class="step-num"&gt;4&lt;/div&gt;
                            &lt;p&gt;Simpan hasil akhir dokumen DOCX yang bisa diedit.&lt;/p&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;

        &lt;/div&gt;

        &lt;!-- PROOF GRID (Bukti Fungsi Nyata) --&gt;
        &lt;div class="proof-section"&gt;
            &lt;div class="proof-card"&gt;
                &lt;div class="proof-title"&gt;Bukti Tool Berfungsi 100% — Bukan Simulasi&lt;/div&gt;
                &lt;div class="proof-grid"&gt;
                    &lt;div class="proof-item"&gt;
                        &lt;div class="proof-frame"&gt;
                            &lt;img src="https://i.ibb.co.com/93s7T2Sm/Screenshot-2026-07-23-23-06-50-413-com-android-chrome.jpg" alt="Konversi PDF ke Word berhasil diproses" loading="lazy"&gt;
                        &lt;/div&gt;
                        &lt;div class="proof-badge"&gt;1&lt;/div&gt;
                        &lt;p class="proof-caption"&gt;Konversi Berhasil&lt;/p&gt;
                    &lt;/div&gt;
                    &lt;div class="proof-item"&gt;
                        &lt;div class="proof-frame"&gt;
                            &lt;img src="https://i.ibb.co.com/6JZvFTvB/Screenshot-2026-07-23-23-06-53-559-com-android-chrome.jpg" alt="Tombol unduh file DOCX berfungsi normal" loading="lazy"&gt;
                        &lt;/div&gt;
                        &lt;div class="proof-badge"&gt;2&lt;/div&gt;
                        &lt;p class="proof-caption"&gt;Download Berfungsi&lt;/p&gt;
                    &lt;/div&gt;
                    &lt;div class="proof-item"&gt;
                        &lt;div class="proof-frame"&gt;
                            &lt;img src="https://i.ibb.co.com/3P5tBfH/Screenshot-2026-07-23-23-07-55-564-com-google-android-apps-docs-editors-docs.jpg" alt="Hasil dokumen Word sempurna dibuka di Google Docs" loading="lazy"&gt;
                        &lt;/div&gt;
                        &lt;div class="proof-badge"&gt;3&lt;/div&gt;
                        &lt;p class="proof-caption"&gt;Hasil Sempurna&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- AUTHOR BOX --&gt;
        &lt;div class="author-box"&gt;
            &lt;div class="author-avatar"&gt;K&lt;/div&gt;
            &lt;div class="author-info"&gt;
                &lt;h4&gt;Ditulis oleh Kholil&lt;/h4&gt;
                &lt;p&gt;Arsitek Data Lokal &amp; Pengembang Kholil Tools | Pembaruan: Juli 2026&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- ARTICLE CONTENT (E-E-A-T &amp; Helpful Content) --&gt;
        &lt;div class="article-content"&gt;
            &lt;h2&gt;Arsitektur Ekstraksi Teks PDF ke Word (.docx)&lt;/h2&gt;
            &lt;p&gt;Format &lt;code&gt;.docx&lt;/code&gt; merupakan arsip ZIP yang berisi struktur XML bersarang. Alat konversi kami menggunakan pustaka JavaScript &lt;strong&gt;PDF.js&lt;/strong&gt; untuk membedah lapisan teks (&lt;em&gt;text layer&lt;/em&gt;) dari PDF Anda secara langsung di memori (RAM) browser klien.&lt;/p&gt;
            &lt;p&gt;Setelah tahap ekstraksi (&lt;em&gt;parsing&lt;/em&gt;) selesai, modul &lt;em&gt;docx.js&lt;/em&gt; akan menyusun ulang koordinat kalimat menjadi paragraf linier standar yang dapat disunting kembali di Microsoft Word.&lt;/p&gt;

            &lt;div class="warning-box"&gt;
                &lt;strong&gt;⚠️ Peringatan Limitasi Fungsionalitas&lt;/strong&gt;
                &lt;p&gt;Karena proses ini berjalan lokal di perangkat Anda (tanpa server), alat ini &lt;strong&gt;mengabaikan ekstraksi elemen grafis berat seperti gambar atau tabel bergaris kompleks&lt;/strong&gt; untuk memastikan browser seluler Anda tidak macet (&lt;em&gt;crash&lt;/em&gt;). Fitur ini dioptimalkan khusus untuk mengamankan teks dan struktur paragraf.&lt;/p&gt;
            &lt;/div&gt;

            &lt;p&gt;Untuk hasil paling maksimal, pastikan PDF Anda bukan hasil pindai (&lt;em&gt;scan&lt;/em&gt;) atau foto fisik, melainkan PDF yang diekspor dari perangkat lunak pengetikan, sehingga teks digitalnya dapat terbaca oleh ekstraktor kami.&lt;/p&gt;

            &lt;h2 style="margin-top: 48px;"&gt;Pemecahan Masalah Sistem Konversi (FAQ)&lt;/h2&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Mengapa gambar dari file PDF saya hilang di hasil Word? &lt;span&gt;+&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;Alat kami diprogram khusus untuk hanya menarik lapisan teks (&lt;em&gt;text layer&lt;/em&gt;). Merender gambar ke dalam XML Word membutuhkan proses memori yang lebih berat, yang biasanya dilakukan server komersial. Kami hilangkan demi menjaga alat ini tetap ringan di ponsel tanpa perlu mengunggah file Anda ke server mana pun.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Mengapa hasil teks terlihat sedikit berantakan? &lt;span&gt;+&lt;/span&gt;&lt;/button&gt;
                &lt;div class="faq-a"&gt;PDF menyimpan teks berdasarkan koordinat sumbu X/Y, bukan dalam bentuk kalimat yang mengalir. Jika PDF asli menggunakan tata letak kolom atau spasi kustom ekstrem, hasil Word-nya mungkin perlu sedikit "Clear Formatting" secara manual setelah dokumen dibuka.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Browser saya freeze (macet) saat klik eksekusi, apa solusinya?&lt;/button&gt;
                &lt;div class="faq-a"&gt;Ini bisa terjadi di smartphone saat merender file PDF ratusan halaman dengan RAM terbatas. Solusinya, muat ulang tab ini dan gunakan &lt;a href="https://www.kholil.biz.id/2026/04/split-pdf.html" style="color:var(--primary); font-weight:600;"&gt;Utilitas Split PDF&lt;/a&gt; kami untuk memecah PDF menjadi per 25 halaman sebelum mengkonversinya.&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="faq-item"&gt;
                &lt;button class="faq-q" aria-expanded="false"&gt;Tombol konversi tetap abu-abu meski file sudah dipilih, kenapa?&lt;/button&gt;
                &lt;div class="faq-a"&gt;Itu artinya mesin konversi (PDF.js/docx.js) masih dalam proses dimuat dari server pustaka — biasanya karena koneksi internet lambat. Tunggu beberapa detik hingga notifikasi kuning di bawah kotak unggah hilang, atau tekan tautan "coba lagi" jika muncul pesan gagal.&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- FOOTER --&gt;
    &lt;footer&gt;
        &lt;p&gt;&amp;copy; 2026 &lt;a href="https://www.kholil.biz.id/" style="color:var(--text-dark); font-weight:700;"&gt;Kholil Tools&lt;/a&gt; — Hak Cipta Dilindungi.&lt;/p&gt;
        &lt;p style="margin-top:4px;"&gt;Eksekusi data privasi terlindungi secara klien-sisi.&lt;/p&gt;
    &lt;/footer&gt;

    &lt;!-- LOGIKA JS --&gt;
    &lt;script&gt;
        document.addEventListener("DOMContentLoaded", () =&gt; {

            const pdfjsWorkerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';

            const dropZone = document.getElementById('dropZone');
            const pdfInput = document.getElementById('pdfInput');
            const convertBtn = document.getElementById('convertBtn');
            const downloadBtn = document.getElementById('downloadBtn');
            const dropText = document.getElementById('dropText');
            const dropSub = document.getElementById('dropSub');

            const statusArea = document.getElementById('statusArea');
            const statusText = document.getElementById('statusText');
            const statusPct = document.getElementById('statusPct');
            const progressBar = document.getElementById('progressBar');

            const libStatus = document.getElementById('libStatus');
            const libStatusText = document.getElementById('libStatusText');

            let selectedFile = null;
            let lastObjectUrl = null;

            // ================= LIBRARY LOADER (dengan fallback CDN) =================
            const LIB_SOURCES = {
                pdfjs: [
                    'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js',
                    'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/build/pdf.min.js'
                ],
                docx: [
                    'https://cdnjs.cloudflare.com/ajax/libs/docx/8.0.3/docx.min.js',
                    'https://cdn.jsdelivr.net/npm/docx@8.0.3/build/index.js'
                ]
            };

            const libsReady = { pdfjs: false, docx: false };

            function loadScriptChain(name, sources, index) {
                if (index &gt;= sources.length) {
                    showLibError(name);
                    return;
                }
                const s = document.createElement('script');
                s.src = sources[index];
                s.onload = () =&gt; {
                    libsReady[name] = true;
                    checkAllLibsReady();
                };
                s.onerror = () =&gt; {
                    // coba sumber berikutnya (fallback CDN)
                    loadScriptChain(name, sources, index + 1);
                };
                document.head.appendChild(s);
            }

            function showLibError(failedName) {
                libStatus.classList.remove('loading');
                libStatus.classList.add('error');
                libStatus.querySelector('.spinner')?.remove();
                libStatusText.innerHTML = `Gagal memuat pustaka (${failedName}). Periksa koneksi internet Anda lalu &lt;span class="retry-link" id="retryLibBtn"&gt;coba lagi&lt;/span&gt;.`;
                const retryBtn = document.getElementById('retryLibBtn');
                if (retryBtn) retryBtn.addEventListener('click', () =&gt; window.location.reload());
                convertBtn.disabled = true;
                convertBtn.classList.remove('ready');
                convertBtn.textContent = '⚠️ Pustaka gagal dimuat';
            }

            function checkAllLibsReady() {
                if (libsReady.pdfjs &amp;&amp; libsReady.docx) {
                    libStatus.style.display = 'none';
                    if (selectedFile) {
                        convertBtn.disabled = false;
                        convertBtn.classList.add('ready');
                        convertBtn.textContent = '&#128640; Eksekusi Konversi';
                    } else {
                        convertBtn.disabled = true;
                        convertBtn.classList.remove('ready');
                        convertBtn.textContent = 'Pilih file PDF terlebih dahulu';
                    }
                }
            }

            loadScriptChain('pdfjs', LIB_SOURCES.pdfjs, 0);
            loadScriptChain('docx', LIB_SOURCES.docx, 0);

            // ================= UI Interaksi File (drag &amp; drop) =================
            dropZone.addEventListener('dragover', (e) =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
            dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
            dropZone.addEventListener('drop', (e) =&gt; {
                e.preventDefault(); dropZone.classList.remove('drag-over');
                if (e.dataTransfer.files[0]) handleFile(e.dataTransfer.files[0]);
            });

            // Klik pada dropzone membuka file picker (selain lewat label overlay)
            dropZone.addEventListener('click', () =&gt; pdfInput.click());

            // Aksesibilitas keyboard pada dropzone
            dropZone.addEventListener('keydown', (e) =&gt; {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    pdfInput.click();
                }
            });

            pdfInput.addEventListener('change', (e) =&gt; {
                if (e.target.files[0]) handleFile(e.target.files[0]);
                // reset value supaya memilih file yang sama lagi tetap memicu 'change'
                e.target.value = '';
            });

            function resetToolUI() {
                convertBtn.style.display = 'block';
                if (libsReady.pdfjs &amp;&amp; libsReady.docx) {
                    convertBtn.disabled = false;
                    convertBtn.classList.add('ready');
                    convertBtn.textContent = '&#128640; Eksekusi Konversi';
                } else {
                    convertBtn.disabled = true;
                    convertBtn.classList.remove('ready');
                    convertBtn.textContent = '⏳ Menunggu pustaka termuat...';
                }
                downloadBtn.style.display = 'none';
                statusArea.style.display = 'none';
                progressBar.style.width = '0%';
                if (lastObjectUrl) {
                    URL.revokeObjectURL(lastObjectUrl);
                    lastObjectUrl = null;
                }
            }

            function handleFile(file) {
                if (file.type !== 'application/pdf') {
                    alert('Format ditolak! Harap pilih dokumen .pdf');
                    return;
                }
                selectedFile = file;
                dropText.innerHTML = `&lt;strong&gt;${file.name}&lt;/strong&gt;`;
                dropSub.textContent = `Ukuran: ${(file.size / 1024).toFixed(1)} KB — klik untuk ganti file`;
                resetToolUI();
            }

            function updateProgress(pct, text) {
                statusArea.style.display = 'block';
                progressBar.style.width = pct + '%';
                statusPct.textContent = pct + '%';
                statusText.textContent = text;
            }

            // Kelompokkan item teks per baris berdasarkan posisi Y dengan toleransi,
            // supaya teks pada baris yang sama tidak terpecah karena selisih desimal.
            function extractLines(content) {
                const items = content.items.filter(it =&gt; it.str &amp;&amp; it.str.trim().length &gt; 0);
                const Y_TOLERANCE = 2;
                const lines = [];
                let currentLine = null;
                let currentY = null;

                items.forEach(item =&gt; {
                    const y = item.transform[5];
                    if (currentY === null || Math.abs(y - currentY) &gt; Y_TOLERANCE) {
                        currentLine = [];
                        lines.push(currentLine);
                        currentY = y;
                    }
                    currentLine.push(item.str);
                });

                return lines.map(l =&gt; l.join(' ').replace(/\s+/g, ' ').trim()).filter(l =&gt; l.length &gt; 0);
            }

            // ================= Engine Konversi Utama =================
            convertBtn.addEventListener('click', async () =&gt; {
                if (!selectedFile) return;

                if (!libsReady.pdfjs || !libsReady.docx || typeof pdfjsLib === 'undefined' || typeof docx === 'undefined') {
                    alert('Pustaka sistem belum termuat sepenuhnya. Tunggu beberapa detik lalu coba lagi, atau muat ulang halaman jika masalah berlanjut.');
                    return;
                }

                pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorkerSrc;

                convertBtn.disabled = true;
                convertBtn.classList.remove('ready');
                convertBtn.textContent = '⏳ Mengurai Dokumen...';
                downloadBtn.style.display = 'none';

                try {
                    updateProgress(5, 'Membaca file PDF...');
                    const arrayBuffer = await selectedFile.arrayBuffer();

                    updateProgress(20, 'Membuka dokumen...');
                    const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
                    const totalPages = pdf.numPages;

                    const paragraphs = [];

                    paragraphs.push(
                        new docx.Paragraph({
                            children: [new docx.TextRun({ text: selectedFile.name.replace(/\.pdf$/i, ''), bold: true, size: 40 })],
                            spacing: { after: 400 }
                        })
                    );

                    let anyTextFound = false;

                    for (let i = 1; i &lt;= totalPages; i++) {
                        updateProgress(20 + Math.round((i / totalPages) * 60), `Memproses halaman ${i} dari ${totalPages}...`);

                        const page = await pdf.getPage(i);
                        const content = await page.getTextContent();
                        const lines = extractLines(content);

                        if (lines.length &gt; 0) {
                            anyTextFound = true;
                            lines.forEach(line =&gt; {
                                paragraphs.push(
                                    new docx.Paragraph({
                                        children: [new docx.TextRun({ text: line, size: 22 })],
                                        spacing: { after: 200 }
                                    })
                                );
                            });
                        } else {
                            paragraphs.push(
                                new docx.Paragraph({
                                    children: [new docx.TextRun({
                                        text: `[Halaman ${i}: tidak ada teks yang dapat diekstrak — kemungkinan berisi gambar atau hasil pindai]`,
                                        italics: true, size: 20, color: '999999'
                                    })],
                                    spacing: { after: 200 }
                                })
                            );
                        }

                        if (i &lt; totalPages) {
                            paragraphs.push(new docx.Paragraph({ children: [new docx.PageBreak()] }));
                        }
                    }

                    updateProgress(90, 'Membuat file DOCX...');
                    const doc = new docx.Document({ sections: [{ properties: {}, children: paragraphs }] });
                    const blob = await docx.Packer.toBlob(doc);

                    updateProgress(100, anyTextFound ? 'Berhasil dikonversi!' : 'Selesai — tidak ada teks yang ditemukan di file ini.');

                    if (lastObjectUrl) URL.revokeObjectURL(lastObjectUrl);
                    const outName = selectedFile.name.replace(/\.pdf$/i, '') + '_KholilTools.docx';
                    lastObjectUrl = URL.createObjectURL(blob);

                    convertBtn.style.display = 'none';

                    downloadBtn.href = lastObjectUrl;
                    downloadBtn.download = outName;
                    downloadBtn.style.display = 'block';

                    if (!anyTextFound) {
                        alert('Tidak ada teks yang bisa diekstrak dari PDF ini. File kemungkinan berupa hasil pindai/gambar (butuh OCR), bukan PDF berbasis teks.');
                    }

                } catch (error) {
                    console.error(error);
                    alert('Gagal memproses file. Pastikan PDF tidak terkunci password dan tidak rusak, lalu coba lagi.');
                    convertBtn.disabled = false;
                    convertBtn.classList.add('ready');
                    convertBtn.textContent = '&#128640; Eksekusi Ulang';
                    statusArea.style.display = 'none';
                }
            });

            // Logika FAQ Accordion
            const faqBtns = document.querySelectorAll('.faq-q');
            faqBtns.forEach(btn =&gt; {
                btn.addEventListener('click', () =&gt; {
                    const ans = btn.nextElementSibling;
                    const isOpen = btn.classList.contains('open');

                    faqBtns.forEach(b =&gt; {
                        b.classList.remove('open');
                        b.nextElementSibling.classList.remove('open');
                        const plus = b.querySelector('span');
                        if (plus) plus.textContent = '+';
                        b.setAttribute('aria-expanded', 'false');
                    });

                    if (!isOpen) {
                        btn.classList.add('open');
                        ans.classList.add('open');
                        const plus = btn.querySelector('span');
                        if (plus) plus.textContent = '-';
                        btn.setAttribute('aria-expanded', 'true');
                    }
                });
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/pdf-to-word.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-3296038324164570675</guid><pubDate>Thu, 09 Apr 2026 21:30:00 +0000</pubDate><atom:updated>2026-07-24T02:57:54.799-07:00</atom:updated><title>Compress Pdf</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;Compress PDF Online Gratis – Kholil Tools&lt;/title&gt;
  &lt;meta name="description" content="Kompres file PDF secara online gratis, cepat, dan aman. Tidak perlu upload ke server. Kurangi ukuran PDF langsung di browser tanpa instalasi apapun."/&gt;
  
  &lt;!-- Preconnect dioptimalkan untuk performa font --&gt;
  &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
  &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@600;700;800&amp;display=swap" rel="stylesheet"&gt;
  
  &lt;!-- Implementasi atribut defer untuk menghindari render-blocking --&gt;
  &lt;script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js" defer&gt;&lt;/script&gt;
  
  &lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --red:       #D63031;
      --red-dark:  #B02020;
      --red-light: #FFF0F0;
      --ink:       #1A1A2E;
      --muted:     #6B7280;
      --border:    #E5E7EB;
      --bg:        #F9FAFB;
      --white:     #FFFFFF;
      --success:   #059669;
      --warn:      #D97706;
      --radius:    12px;
    }

    body {
      font-family: 'Inter', sans-serif;
      background: var(--bg);
      color: var(--ink);
      line-height: 1.7;
      font-size: 16px;
    }

    /* ── HEADER ── */
    header {
      background: var(--white);
      border-bottom: 1px solid var(--border);
      padding: 0 24px;
    }
    .header-inner {
      max-width: 1100px;
      margin: auto;
      display: flex;
      align-items: center;
      height: 60px;
      gap: 10px;
    }
    .logo-mark {
      width: 32px; height: 32px;
      background: var(--red);
      border-radius: 8px;
      display: flex; align-items: center; justify-content: center;
    }
    .logo-mark svg { width: 18px; height: 18px; fill: #fff; }
    .logo-text { font-family: 'Sora', sans-serif; font-weight: 700; font-size: 18px; color: var(--ink); }
    .logo-text span { color: var(--red); }
    nav { margin-left: auto; display: flex; gap: 24px; }
    nav a { font-size: 14px; color: var(--muted); text-decoration: none; font-weight: 500; }
    nav a:hover { color: var(--red); }

    /* ── HERO ── */
    .hero {
      background: linear-gradient(135deg, #1A1A2E 0%, #2D2D44 100%);
      padding: 56px 24px 48px;
      text-align: center;
      color: #fff;
    }
    .hero-badge {
      display: inline-block;
      background: rgba(214,48,49,0.18);
      color: #FF9090;
      font-size: 12px;
      font-weight: 600;
      letter-spacing: .08em;
      text-transform: uppercase;
      padding: 4px 14px;
      border-radius: 20px;
      border: 1px solid rgba(214,48,49,0.3);
      margin-bottom: 18px;
    }
    .hero h1 {
      font-family: 'Sora', sans-serif;
      font-size: clamp(28px, 5vw, 48px);
      font-weight: 800;
      line-height: 1.15;
      margin-bottom: 16px;
    }
    .hero h1 span { color: var(--red); }
    .hero p {
      font-size: 17px;
      color: #BEC3CE;
      max-width: 560px;
      margin: 0 auto 28px;
    }
    .hero-stats {
      display: flex;
      justify-content: center;
      gap: 32px;
      flex-wrap: wrap;
    }
    .stat { text-align: center; }
    .stat-num {
      font-family: 'Sora', sans-serif;
      font-size: 26px;
      font-weight: 800;
      color: #fff;
    }
    .stat-label { font-size: 12px; color: #8D93A0; letter-spacing:.04em; }

    /* ── MAIN LAYOUT ── */
    .main-wrap {
      max-width: 1100px;
      margin: 0 auto;
      padding: 48px 24px;
      display: grid;
      grid-template-columns: 1fr 340px;
      gap: 32px;
      align-items: start;
    }
    @media(max-width:860px){
      .main-wrap { grid-template-columns: 1fr; }
      .sidebar { order: -1; }
    }

    /* ── TOOL CARD ── */
    .tool-card {
      background: var(--white);
      border-radius: var(--radius);
      border: 1px solid var(--border);
      box-shadow: 0 1px 4px rgba(0,0,0,.05);
      overflow: hidden;
    }
    .tool-card-header {
      padding: 20px 24px;
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      gap: 10px;
    }
    .tool-card-header h2 {
      font-family: 'Sora', sans-serif;
      font-size: 18px;
      font-weight: 700;
    }
    .badge-free {
      margin-left: auto;
      background: #ECFDF5;
      color: var(--success);
      font-size: 12px;
      font-weight: 600;
      padding: 3px 10px;
      border-radius: 20px;
      border: 1px solid #A7F3D0;
    }

    /* Drop Zone */
    .drop-zone {
      margin: 24px;
      border: 2px dashed var(--border);
      border-radius: var(--radius);
      padding: 44px 24px;
      text-align: center;
      cursor: pointer;
      transition: border-color .2s, background .2s;
      background: var(--bg);
    }
    .drop-zone:hover, .drop-zone.drag-over {
      border-color: var(--red);
      background: var(--red-light);
    }
    .drop-icon {
      width: 52px; height: 52px;
      background: var(--red-light);
      border-radius: 50%;
      display: inline-flex;
      align-items: center; justify-content: center;
      margin-bottom: 14px;
    }
    .drop-icon svg { width: 26px; height: 26px; }
    .drop-zone h3 { font-size: 16px; font-weight: 600; margin-bottom: 6px; }
    .drop-zone p { font-size: 13px; color: var(--muted); }
    .btn-choose {
      display: inline-block;
      margin-top: 16px;
      background: var(--red);
      color: #fff;
      padding: 10px 22px;
      border-radius: 30px;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
      transition: background .2s;
    }
    .btn-choose:hover { background: var(--red-dark); }
    #pdfFileInput { display: none; }

    /* File Info */
    #fileInfo {
      margin: 0 24px 16px;
      padding: 14px 16px;
      background: #F0F9FF;
      border: 1px solid #BAE6FD;
      border-radius: 8px;
      font-size: 14px;
      display: none;
      align-items: center;
      gap: 10px;
    }
    #fileInfo.visible { display: flex; }
    #fileInfo svg { flex-shrink: 0; color: #0284C7; width:20px; height:20px; }
    .file-name { font-weight: 600; color: var(--ink); }
    .file-size { color: var(--muted); font-size: 13px; }

    /* Options */
    .options-wrap {
      margin: 0 24px 20px;
    }
    .options-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 10px; }
    .level-grid {
      display: grid;
      grid-template-columns: repeat(3,1fr);
      gap: 8px;
    }
    .level-btn {
      padding: 10px 4px;
      border: 1.5px solid var(--border);
      border-radius: 8px;
      background: var(--white);
      cursor: pointer;
      text-align: center;
      transition: border-color .15s, background .15s;
      font-family: inherit;
    }
    .level-btn:hover { border-color: var(--red); }
    .level-btn.active { border-color: var(--red); background: var(--red-light); }
    .level-btn .lv-name { font-size: 13px; font-weight: 600; display: block; }
    .level-btn .lv-desc { font-size: 11px; color: var(--muted); }

    /* Action buttons */
    .actions {
      padding: 0 24px 24px;
      display: flex;
      gap: 10px;
    }
    .btn-primary {
      flex: 1;
      background: var(--red);
      color: #fff;
      border: none;
      border-radius: 30px;
      padding: 13px 20px;
      font-size: 15px;
      font-weight: 600;
      cursor: pointer;
      font-family: inherit;
      transition: background .2s, opacity .2s;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 7px;
    }
    .btn-primary:hover { background: var(--red-dark); }
    .btn-primary:disabled { opacity: .5; cursor: not-allowed; }
    .btn-reset {
      background: var(--bg);
      color: var(--muted);
      border: 1px solid var(--border);
      border-radius: 30px;
      padding: 13px 18px;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
      font-family: inherit;
      transition: background .2s;
    }
    .btn-reset:hover { background: var(--border); }

    /* Progress */
    #progressWrap {
      margin: 0 24px 16px;
      display: none;
    }
    #progressWrap.visible { display: block; }
    .progress-label { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
    .progress-bar-bg {
      background: var(--bg);
      border-radius: 20px;
      height: 8px;
      overflow: hidden;
      border: 1px solid var(--border);
    }
    .progress-bar-fill {
      height: 100%;
      background: linear-gradient(90deg, var(--red), #FF6B6B);
      border-radius: 20px;
      width: 0%;
      transition: width .3s;
    }

    /* Result */
    #resultCard {
      margin: 0 24px 24px;
      padding: 16px;
      border-radius: 10px;
      background: #ECFDF5;
      border: 1px solid #A7F3D0;
      display: none;
    }
    #resultCard.visible { display: block; }
    .result-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 12px;
      flex-wrap: wrap;
    }
    .result-meta { flex: 1; }
    .result-title { font-size: 14px; font-weight: 600; color: var(--success); margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
    .result-sizes {
      display: flex;
      gap: 16px;
      font-size: 13px;
      color: var(--muted);
    }
    .result-sizes strong { color: var(--ink); }
    .saved-badge {
      background: var(--success);
      color: #fff;
      font-size: 13px;
      font-weight: 700;
      padding: 5px 12px;
      border-radius: 20px;
    }
    .btn-download {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      background: var(--success);
      color: #fff;
      padding: 10px 20px;
      border-radius: 30px;
      font-size: 14px;
      font-weight: 600;
      text-decoration: none;
      margin-top: 12px;
      transition: background .2s;
    }
    .btn-download:hover { background: #047857; }

    /* ── SIDEBAR ── */
    .sidebar { display: flex; flex-direction: column; gap: 20px; }
    .info-card {
      background: var(--white);
      border-radius: var(--radius);
      border: 1px solid var(--border);
      padding: 20px;
    }
    .info-card h3 {
      font-family: 'Sora', sans-serif;
      font-size: 15px;
      font-weight: 700;
      margin-bottom: 14px;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .info-card h3 .ic {
      width: 30px; height: 30px;
      border-radius: 8px;
      background: var(--red-light);
      display: flex; align-items: center; justify-content: center;
      flex-shrink: 0;
    }
    .checklist { list-style: none; display: flex; flex-direction: column; gap: 9px; }
    .checklist li {
      font-size: 14px;
      display: flex;
      align-items: flex-start;
      gap: 9px;
      color: var(--muted);
    }
    .checklist li::before {
      content: '';
      flex-shrink: 0;
      width: 18px; height: 18px;
      margin-top: 2px;
      background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23059669'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z' clip-rule='evenodd'/%3E%3C/svg%3E") center/cover;
    }
    .tip-box {
      background: #FFFBEB;
      border: 1px solid #FDE68A;
      border-radius: 8px;
      padding: 12px 14px;
      font-size: 13px;
      color: #92400E;
      line-height: 1.6;
    }
    .tip-box strong { color: var(--warn); }

    /* ── CONTENT SECTION ── */
    .content-section {
      max-width: 1100px;
      margin: 0 auto;
      padding: 0 24px 64px;
    }
    .section-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      gap: 24px;
      margin-bottom: 48px;
    }
    .content-card {
      background: var(--white);
      border-radius: var(--radius);
      border: 1px solid var(--border);
      padding: 24px;
    }
    .content-card .cc-icon {
      width: 44px; height: 44px;
      background: var(--red-light);
      border-radius: 10px;
      display: flex; align-items: center; justify-content: center;
      margin-bottom: 14px;
    }
    .content-card h3 {
      font-family: 'Sora', sans-serif;
      font-size: 16px;
      font-weight: 700;
      margin-bottom: 8px;
    }
    .content-card p { font-size: 14px; color: var(--muted); line-height: 1.7; }

    .prose-section {
      background: var(--white);
      border-radius: var(--radius);
      border: 1px solid var(--border);
      padding: 36px;
      margin-bottom: 32px;
    }
    .prose-section h2 {
      font-family: 'Sora', sans-serif;
      font-size: 22px;
      font-weight: 700;
      margin-bottom: 14px;
      color: var(--ink);
    }
    .prose-section h3 {
      font-size: 17px;
      font-weight: 600;
      margin: 24px 0 10px;
      color: var(--ink);
    }
    .prose-section p {
      font-size: 15px;
      color: #374151;
      margin-bottom: 14px;
      line-height: 1.8;
    }
    .prose-section ul, .prose-section ol {
      padding-left: 22px;
      margin-bottom: 14px;
    }
    .prose-section li {
      font-size: 15px;
      color: #374151;
      margin-bottom: 8px;
      line-height: 1.7;
    }
    
    /* ── Image Proof Gallery CSS ── */
    .proof-gallery {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
      gap: 20px;
      margin: 24px 0;
    }
    .proof-figure {
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 16px;
      text-align: center;
    }
    .proof-figure img {
      width: 100%;
      height: auto;
      border-radius: 8px;
      margin-bottom: 12px;
      border: 1px solid #D1D5DB;
      box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    }
    .proof-figure figcaption {
      font-size: 14px;
      font-weight: 700;
      color: var(--ink);
      margin-bottom: 4px;
    }
    .proof-figure p {
      font-size: 13px !important;
      color: var(--muted) !important;
      margin-bottom: 0 !important;
      line-height: 1.5 !important;
    }

    .steps-list { list-style: none; counter-reset: steps; padding: 0; }
    .steps-list li {
      counter-increment: steps;
      padding: 14px 16px 14px 56px;
      position: relative;
      border-left: 3px solid var(--border);
      margin-bottom: 12px;
      border-radius: 0 8px 8px 0;
      background: var(--bg);
    }
    .steps-list li::before {
      content: counter(steps);
      position: absolute;
      left: 14px;
      top: 50%; transform: translateY(-50%);
      width: 26px; height: 26px;
      background: var(--red);
      color: #fff;
      border-radius: 50%;
      font-size: 13px;
      font-weight: 700;
      display: flex; align-items: center; justify-content: center;
    }
    .steps-list li strong { color: var(--ink); }

    /* FAQ */
    .faq-list { display: flex; flex-direction: column; gap: 10px; }
    .faq-item {
      border: 1px solid var(--border);
      border-radius: 10px;
      overflow: hidden;
      background: var(--white);
    }
    .faq-q {
      width: 100%;
      background: none;
      border: none;
      padding: 16px 20px;
      font-family: inherit;
      font-size: 15px;
      font-weight: 600;
      color: var(--ink);
      text-align: left;
      cursor: pointer;
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 12px;
    }
    .faq-q .arrow {
      width: 20px; height: 20px;
      flex-shrink: 0;
      transition: transform .25s;
      color: var(--muted);
    }
    .faq-item.open .faq-q .arrow { transform: rotate(180deg); }
    .faq-a {
      max-height: 0;
      overflow: hidden;
      transition: max-height .3s ease, padding .3s;
      padding: 0 20px;
      font-size: 14px;
      color: var(--muted);
      line-height: 1.7;
    }
    .faq-item.open .faq-a {
      max-height: 200px;
      padding: 0 20px 16px;
    }

    /* ── FOOTER ── */
    footer {
      background: var(--ink);
      color: #8D93A0;
      text-align: center;
      padding: 32px 24px;
      font-size: 13px;
    }
    footer a { color: #BEC3CE; text-decoration: none; }
    footer a:hover { color: #fff; }
    .footer-links { display: flex; justify-content: center; gap: 20px; margin-bottom: 12px; flex-wrap: wrap; }

    /* Spinner */
    .spinner {
      width: 18px; height: 18px;
      border: 2px solid rgba(255,255,255,.3);
      border-top-color: #fff;
      border-radius: 50%;
      animation: spin .7s linear infinite;
      display: none;
    }
    .btn-primary.loading .spinner { display: block; }
    .btn-primary.loading .btn-text { display: none; }
    @keyframes spin { to { transform: rotate(360deg); } }

    @media(max-width:600px){
      .hero-stats { gap: 20px; }
      .level-grid { grid-template-columns: 1fr; }
      .actions { flex-direction: column; }
      .prose-section { padding: 20px; }
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- HEADER --&gt;
&lt;header&gt;
  &lt;div class="header-inner"&gt;
    &lt;div class="logo-mark" aria-hidden="true"&gt;
      &lt;svg viewBox="0 0 24 24"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm4 18H6V4h7v5h5v11z"/&gt;&lt;/svg&gt;
    &lt;/div&gt;
    &lt;span class="logo-text"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/span&gt;
    
    &lt;!-- Navigasi Dibatasi Maksimal 3 Link Non-Esensial --&gt;
    &lt;nav aria-label="Navigasi Utama"&gt;
      &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Merge PDF&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/split-pdf.html"&gt;Split PDF&lt;/a&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;!-- HERO --&gt;
&lt;div class="hero"&gt;
  &lt;div class="hero-badge"&gt;100% Gratis &amp;amp; Online&lt;/div&gt;
  &lt;h1&gt;Compress PDF &lt;span&gt;Tanpa Batas&lt;/span&gt;&lt;br&gt;Langsung di Browser&lt;/h1&gt;
  &lt;p&gt;Kurangi ukuran file PDF hingga 90% tanpa mengorbankan kualitas. Tidak perlu instalasi — file Anda tidak pernah meninggalkan perangkat Anda.&lt;/p&gt;
  &lt;div class="hero-stats"&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;90%&lt;/div&gt;&lt;div class="stat-label"&gt;Pengurangan maks.&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;0&lt;/div&gt;&lt;div class="stat-label"&gt;Upload ke server&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;100%&lt;/div&gt;&lt;div class="stat-label"&gt;Gratis &amp;amp; privat&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;!-- MAIN CONTENT WRAPPER --&gt;
&lt;main&gt;
&lt;div class="main-wrap"&gt;
  &lt;div&gt;
    &lt;!-- TOOL CARD --&gt;
    &lt;div class="tool-card"&gt;
      &lt;div class="tool-card-header"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;path d="M14 2v6h6M12 18v-6M9 15l3 3 3-3"/&gt;&lt;/svg&gt;
        &lt;h2&gt;Kompres File PDF&lt;/h2&gt;
        &lt;span class="badge-free"&gt;GRATIS&lt;/span&gt;
      &lt;/div&gt;

      &lt;!-- Drop Zone --&gt;
      &lt;div class="drop-zone" id="dropZone" aria-label="Area Upload PDF"&gt;
        &lt;div class="drop-icon" aria-hidden="true"&gt;
          &lt;svg fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/&gt;&lt;/svg&gt;
        &lt;/div&gt;
        &lt;h3&gt;Seret &amp;amp; Lepas File PDF di Sini&lt;/h3&gt;
        &lt;p&gt;atau klik tombol di bawah untuk memilih file&lt;/p&gt;
        &lt;label class="btn-choose"&gt;
          Pilih File PDF
          &lt;input type="file" id="pdfFileInput" accept="application/pdf"&gt;
        &lt;/label&gt;
        &lt;p style="margin-top:10px; font-size:12px; color:var(--muted);"&gt;Maks. ukuran file: 50 MB&lt;/p&gt;
      &lt;/div&gt;

      &lt;!-- File Info --&gt;
      &lt;div id="fileInfo"&gt;
        &lt;svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;path d="M14 2v6h6"/&gt;&lt;/svg&gt;
        &lt;div&gt;
          &lt;div class="file-name" id="fileName"&gt;—&lt;/div&gt;
          &lt;div class="file-size" id="fileSizeLabel"&gt;—&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Compression Level --&gt;
      &lt;div class="options-wrap"&gt;
        &lt;div class="options-label"&gt;Level Kompresi&lt;/div&gt;
        &lt;div class="level-grid"&gt;
          &lt;button class="level-btn" data-level="light"&gt;
            &lt;span class="lv-name"&gt;&#128994; Ringan&lt;/span&gt;
            &lt;span class="lv-desc"&gt;Kualitas terjaga&lt;/span&gt;
          &lt;/button&gt;
          &lt;button class="level-btn active" data-level="medium"&gt;
            &lt;span class="lv-name"&gt;&#128993; Sedang&lt;/span&gt;
            &lt;span class="lv-desc"&gt;Seimbang ✓ Disarankan&lt;/span&gt;
          &lt;/button&gt;
          &lt;button class="level-btn" data-level="high"&gt;
            &lt;span class="lv-name"&gt;&#128308; Maksimal&lt;/span&gt;
            &lt;span class="lv-desc"&gt;File paling kecil&lt;/span&gt;
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Progress --&gt;
      &lt;div id="progressWrap"&gt;
        &lt;div class="progress-label" id="progressLabel"&gt;Memproses...&lt;/div&gt;
        &lt;div class="progress-bar-bg"&gt;
          &lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Result --&gt;
      &lt;div id="resultCard"&gt;
        &lt;div class="result-row"&gt;
          &lt;div class="result-meta"&gt;
            &lt;div class="result-title"&gt;
              &lt;svg width="16" height="16" fill="none" stroke="var(--success)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M22 11.08V12a10 10 0 11-5.93-9.14"/&gt;&lt;path d="M22 4L12 14.01l-3-3"/&gt;&lt;/svg&gt;
              Kompresi Berhasil!
            &lt;/div&gt;
            &lt;div class="result-sizes"&gt;
              &lt;div&gt;Sebelum: &lt;strong id="originalSize"&gt;—&lt;/strong&gt;&lt;/div&gt;
              &lt;div&gt;Sesudah: &lt;strong id="compressedSize"&gt;—&lt;/strong&gt;&lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
          &lt;span class="saved-badge" id="savedPct"&gt;−0%&lt;/span&gt;
        &lt;/div&gt;
        &lt;a id="downloadLink" class="btn-download" download="compressed.pdf" href="#"&gt;
          &lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/&gt;&lt;/svg&gt;
          Download PDF Terkompresi
        &lt;/a&gt;
      &lt;/div&gt;

      &lt;!-- Actions --&gt;
      &lt;div class="actions"&gt;
        &lt;button class="btn-primary" id="compressBtn" disabled&gt;
          &lt;span class="btn-text" style="display:flex;align-items:center;gap:7px;"&gt;
            &lt;svg width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 19V5M5 12l7 7 7-7"/&gt;&lt;/svg&gt;
            Mulai Kompres
          &lt;/span&gt;
          &lt;div class="spinner"&gt;&lt;/div&gt;
        &lt;/button&gt;
        &lt;button class="btn-reset" id="resetBtn"&gt;Reset&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;&lt;!-- /tool-card --&gt;
  &lt;/div&gt;

  &lt;!-- SIDEBAR --&gt;
  &lt;div class="sidebar"&gt;
    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Keamanan &amp;amp; Privasi
      &lt;/h3&gt;
      &lt;ul class="checklist"&gt;
        &lt;li&gt;File diproses 100% di browser Anda&lt;/li&gt;
        &lt;li&gt;Tidak ada data yang dikirim ke server kami&lt;/li&gt;
        &lt;li&gt;File otomatis terhapus saat tab ditutup&lt;/li&gt;
        &lt;li&gt;Aman untuk dokumen rahasia &amp;amp; sensitif&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;

    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M12 6v6l4 2"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Cara Penggunaan
      &lt;/h3&gt;
      &lt;ol class="checklist" style="list-style:none;counter-reset:st;padding:0"&gt;
        &lt;li style="counter-increment:st;padding-left:28px;position:relative;"&gt;
          &lt;span style="position:absolute;left:0;top:1px;background:var(--red);color:#fff;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;"&gt;1&lt;/span&gt;
          Upload file PDF Anda
        &lt;/li&gt;
        &lt;li style="counter-increment:st;padding-left:28px;position:relative;"&gt;
          &lt;span style="position:absolute;left:0;top:1px;background:var(--red);color:#fff;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;"&gt;2&lt;/span&gt;
          Pilih level kompresi
        &lt;/li&gt;
        &lt;li style="counter-increment:st;padding-left:28px;position:relative;"&gt;
          &lt;span style="position:absolute;left:0;top:1px;background:var(--red);color:#fff;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;"&gt;3&lt;/span&gt;
          Klik "Mulai Kompres"
        &lt;/li&gt;
        &lt;li style="counter-increment:st;padding-left:28px;position:relative;"&gt;
          &lt;span style="position:absolute;left:0;top:1px;background:var(--red);color:#fff;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;"&gt;4&lt;/span&gt;
          Download hasil file
        &lt;/li&gt;
      &lt;/ol&gt;
    &lt;/div&gt;

    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3M6.343 6.343l-.707-.707M12 19v1M8.93 16.93A7 7 0 1115.07 7.07"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Tips Kompresi PDF
      &lt;/h3&gt;
      &lt;div class="tip-box"&gt;
        &lt;strong&gt;&#128161; Tips Penting:&lt;/strong&gt; PDF yang banyak mengandung gambar beresolusi tinggi akan mengalami penurunan ukuran lebih signifikan. PDF berisi teks murni sudah cukup efisien sehingga hasilnya mungkin tidak terlalu berbeda.
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;!-- /main-wrap --&gt;

&lt;!-- CONTENT SECTION --&gt;
&lt;div class="content-section"&gt;

  &lt;!-- Feature Cards --&gt;
  &lt;div class="section-grid"&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9l-7-7z"/&gt;&lt;path d="M13 2v7h7"/&gt;&lt;/svg&gt;
      &lt;/div&gt;
      &lt;h3&gt;Kompresi Berbasis Browser&lt;/h3&gt;
      &lt;p&gt;Seluruh proses kompresi dilakukan langsung di browser Anda menggunakan teknologi PDF-lib. File tidak pernah dikirim ke internet sehingga data Anda 100% aman.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;
      &lt;/div&gt;
      &lt;h3&gt;Privasi Terjamin Penuh&lt;/h3&gt;
      &lt;p&gt;Dokumen keuangan, kontrak hukum, atau laporan bisnis Anda tidak pernah meninggalkan perangkat. Tidak ada server pihak ketiga yang terlibat dalam proses ini.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M2 12h20M12 2a15.3 15.3 0 010 20M12 2a15.3 15.3 0 000 20"/&gt;&lt;/svg&gt;
      &lt;/div&gt;
      &lt;h3&gt;Tanpa Instalasi Apapun&lt;/h3&gt;
      &lt;p&gt;Tidak perlu mengunduh aplikasi, tidak perlu mendaftar akun, dan tidak perlu membayar. Akses langsung dari browser di PC, laptop, maupun smartphone Anda.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/&gt;&lt;polyline points="17 6 23 6 23 12"/&gt;&lt;/svg&gt;
      &lt;/div&gt;
      &lt;h3&gt;Tiga Level Kompresi&lt;/h3&gt;
      &lt;p&gt;Pilih sesuai kebutuhan: kompresi ringan untuk menjaga kualitas cetak, sedang untuk keseimbangan terbaik, atau maksimal untuk ukuran file paling kecil sebelum dikirim via email.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- Prose: Cara Kerja &amp; Bukti Nyata (Updated E-E-A-T Content with Images) --&gt;
  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Panduan Teknis: Cara Kerja Kompresi PDF di Browser Anda&lt;/h2&gt;
    &lt;p&gt;
      Sebagian besar sistem pemberkasan digital di Indonesia (seperti portal SSCASN CPNS, Rekrutmen Bersama BUMN, hingga sistem e-Meterai Peruri) membatasi ukuran unggahan dokumen maksimal 1 MB hingga 2 MB. Memenuhi standar ini tanpa membuat dokumen buram membutuhkan algoritma kompresi yang tepat.
    &lt;/p&gt;
    &lt;p&gt;
      Tool kompresi Kholil.biz.id menggunakan arsitektur &lt;em&gt;Client-Side Processing&lt;/em&gt; via &lt;strong&gt;pdf-lib&lt;/strong&gt;. Berbeda dengan kompresor konvensional yang mengunggah file Anda ke server jarak jauh, skrip kami mengintervensi struktur PDF (seperti &lt;em&gt;Object Streams&lt;/em&gt; dan metadata yang tidak relevan) langsung pada RAM perangkat Anda. 
    &lt;/p&gt;
    
    &lt;h3&gt;Bukti Nyata Hasil Kompresi Kami&lt;/h3&gt;
    &lt;p&gt;Kami tidak hanya sekadar memberikan janji. Di bawah ini adalah dokumentasi pengujian langsung (screenshot) pengompresan PDF menggunakan tool di halaman ini melalui perangkat *mobile*:&lt;/p&gt;
    
    &lt;!-- Image Evidence Gallery --&gt;
    &lt;div class="proof-gallery"&gt;
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/nN1BSdR1/Screenshot-2026-07-24-16-53-04-871-com-android-chrome.jpg" alt="Ukuran file PDF asli sebelum dikompresi di browser Android" loading="lazy"&gt;
        &lt;figcaption&gt;1. File PDF Asli&lt;/figcaption&gt;
        &lt;p&gt;Tampilan dokumen awal sebelum dimasukkan ke dalam alat kompresi.&lt;/p&gt;
      &lt;/figure&gt;
      
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/wNcj5zk8/Screenshot-2026-07-24-16-53-11-472-com-android-chrome.jpg" alt="Ukuran file PDF yang menyusut drastis setelah dikompres" loading="lazy"&gt;
        &lt;figcaption&gt;2. Setelah Kompresi&lt;/figcaption&gt;
        &lt;p&gt;Hasil persentase penyusutan file yang signifikan dan langsung dapat diunduh.&lt;/p&gt;
      &lt;/figure&gt;
      
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/sJgkj6Lt/Screenshot-2026-07-24-16-53-21-659-com-intsig-camscanner.jpg" alt="Teks dokumen PDF tetap tajam dan jelas setelah dikompresi maksimal" loading="lazy"&gt;
        &lt;figcaption&gt;3. Kualitas Teks Tajam&lt;/figcaption&gt;
        &lt;p&gt;Resolusi teks dipertahankan secara optimal sehingga tetap nyaman dibaca.&lt;/p&gt;
      &lt;/figure&gt;
    &lt;/div&gt;

    &lt;h3&gt;Estimasi Penurunan Ukuran File&lt;/h3&gt;
    &lt;p&gt;Berdasarkan pengujian internal kami terhadap berbagai jenis dokumen PDF, berikut adalah estimasi rasio penurunannya:&lt;/p&gt;
    &lt;ul style="list-style-type: none; padding-left: 0;"&gt;
      &lt;li&gt;&#128201; &lt;strong&gt;PDF Hasil Scan (Ijazah/KTP):&lt;/strong&gt; Penurunan hingga &lt;strong&gt;70-85%&lt;/strong&gt; (sangat optimal jika disetel ke kompresi Maksimal).&lt;/li&gt;
      &lt;li&gt;&#128201; &lt;strong&gt;PDF Ekspor dari Canva/Photoshop:&lt;/strong&gt; Penurunan &lt;strong&gt;50-65%&lt;/strong&gt; (karena optimasi resolusi aset grafis internal).&lt;/li&gt;
      &lt;li&gt;&#128201; &lt;strong&gt;PDF Teks Murni (Ms. Word/Google Docs):&lt;/strong&gt; Penurunan &lt;strong&gt;10-15%&lt;/strong&gt; (file jenis ini umumnya sudah terkompresi dari sumbernya).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;

  &lt;!-- Prose: Kapan perlu kompres PDF --&gt;
  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Kapan Anda Perlu Mengompres File PDF?&lt;/h2&gt;

    &lt;h3&gt;1. Mengirim Lampiran Email&lt;/h3&gt;
    &lt;p&gt;
      Sebagian besar layanan email membatasi ukuran lampiran antara 10 MB hingga 25 MB. Jika file PDF Anda melebihi batas ini, Anda akan mendapat pesan error. Mengompres file sebelum mengirim adalah solusi paling cepat.
    &lt;/p&gt;

    &lt;h3&gt;2. Upload Dokumen ke Portal Instansi&lt;/h3&gt;
    &lt;p&gt;
      Portal pemerintah, universitas, atau platform rekrutmen sering membatasi ukuran file yang bisa diunggah. Ijazah, transkrip nilai, atau portofolio dalam format PDF perlu dikompres agar bisa dikirimkan tanpa error.
    &lt;/p&gt;

    &lt;h3&gt;3. Menghemat Ruang Penyimpanan&lt;/h3&gt;
    &lt;p&gt;
      Jika Anda menyimpan ratusan file PDF di perangkat atau cloud storage, mengompres setiap file bisa menghemat kapasitas penyimpanan secara signifikan.
    &lt;/p&gt;
  &lt;/div&gt;

  &lt;!-- FAQ (Updated Natural SEO Content) --&gt;
  &lt;div class="prose-section"&gt;
    &lt;h2&gt;FAQ &amp; Pemecahan Masalah (Troubleshooting)&lt;/h2&gt;
    &lt;div class="faq-list"&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false"&gt;
          Kenapa hasil kompresi PDF dari Canva/Photoshop tetap besar?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;File PDF yang diekspor dari perangkat lunak desain grafis biasanya mem-bypass kompresi objek dan menyimpan lapisan (layer) raster yang berat. Jika kompresi "Maksimal" di tool kami masih kurang, Anda disarankan melakukan "Flatten Image" pada software asal sebelum mengekspor ulang ke PDF.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false"&gt;
          Apakah hasil kompresi ini kompatibel dengan e-Meterai Peruri?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Ya. Tool ini mempertahankan arsitektur dasar PDF (PDF/A compliance) sehingga hasil outputnya tetap bisa dibaca oleh sistem pembubuhan e-Meterai tanpa merusak validasi digital dokumen Anda.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false"&gt;
          Browser saya menjadi lambat ("Not Responding") saat memproses file 40MB. Apa solusinya?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Karena proses terjadi sepenuhnya di RAM perangkat Anda (Client-Side), file berukuran besar membutuhkan memori yang cukup. Solusinya: tutup tab browser lain yang memakan banyak memori, atau gunakan laptop/PC alih-alih smartphone untuk file di atas 30 MB.&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- Trust Box / E-E-A-T Signal --&gt;
  &lt;div class="prose-section" style="background: #F8FAFC; border-color: #E2E8F0; display: flex; gap: 20px; align-items: center;"&gt;
    &lt;div style="flex-shrink: 0;"&gt;
      &lt;img src="https://ui-avatars.com/api/?name=Kholil&amp;background=0D8ABC&amp;color=fff" alt="Kholil - Pengelola Web" style="border-radius: 50%; width: 64px; height: 64px;"&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;h3 style="margin: 0 0 5px; font-size: 16px;"&gt;Dikembangkan oleh &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="color: var(--red); text-decoration: none;"&gt;Kholil&lt;/a&gt;&lt;/h3&gt;
      &lt;p style="margin: 0; font-size: 14px; color: var(--muted);"&gt;Kami berkomitmen menyediakan alat produktivitas dokumen yang aman dan berbasis privasi. Seluruh infrastruktur dikelola dari Indonesia. Punya pertanyaan atau menemui bug? &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="color: var(--ink); font-weight: 600;"&gt;Hubungi Tim Kami&lt;/a&gt;.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;!-- /content-section --&gt;
&lt;/main&gt;

&lt;!-- FOOTER --&gt;
&lt;footer&gt;
  &lt;!-- Navigasi Footer Dibatasi --&gt;
  &lt;div class="footer-links"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Merge PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/split-pdf.html"&gt;Split PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;Tentang Kami&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Kontak&lt;/a&gt;
  &lt;/div&gt;
  &lt;p&gt;© 2026 Kholil.biz.id — Tool PDF Online Gratis untuk Indonesia. Dibuat dengan ❤️ untuk memudahkan pekerjaan Anda.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
  const dropZone   = document.getElementById('dropZone');
  const fileInput  = document.getElementById('pdfFileInput');
  const fileInfo   = document.getElementById('fileInfo');
  const fileName   = document.getElementById('fileName');
  const fileSizeEl = document.getElementById('fileSizeLabel');
  const compressBtn = document.getElementById('compressBtn');
  const resetBtn   = document.getElementById('resetBtn');
  const progressWrap = document.getElementById('progressWrap');
  const progressLabel = document.getElementById('progressLabel');
  const progressFill = document.getElementById('progressFill');
  const resultCard = document.getElementById('resultCard');
  const downloadLink = document.getElementById('downloadLink');
  const originalSizeEl   = document.getElementById('originalSize');
  const compressedSizeEl = document.getElementById('compressedSize');
  const savedPctEl = document.getElementById('savedPct');

  let selectedFile = null;
  let selectedLevel = 'medium';

  // Level buttons
  document.querySelectorAll('.level-btn').forEach(btn =&gt; {
    btn.addEventListener('click', () =&gt; {
      document.querySelectorAll('.level-btn').forEach(b =&gt; b.classList.remove('active'));
      btn.classList.add('active');
      selectedLevel = btn.dataset.level;
    });
  });

  // Drag and drop
  dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
  dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
  dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('drag-over');
    const f = e.dataTransfer.files[0];
    if (f &amp;&amp; f.type === 'application/pdf') loadFile(f);
  });

  fileInput.addEventListener('change', e =&gt; {
    if (e.target.files[0]) loadFile(e.target.files[0]);
  });

  function fmtSize(bytes) {
    if (bytes &lt; 1024) return bytes + ' B';
    if (bytes &lt; 1024*1024) return (bytes/1024).toFixed(1) + ' KB';
    return (bytes/1024/1024).toFixed(2) + ' MB';
  }

  function loadFile(f) {
    if (f.size &gt; 50 * 1024 * 1024) { alert('Ukuran file melebihi batas 50 MB.'); return; }
    selectedFile = f;
    fileName.textContent = f.name;
    fileSizeEl.textContent = 'Ukuran asli: ' + fmtSize(f.size);
    fileInfo.classList.add('visible');
    resultCard.classList.remove('visible');
    progressWrap.classList.remove('visible');
    compressBtn.disabled = false;
  }

  resetBtn.addEventListener('click', () =&gt; {
    selectedFile = null;
    fileInput.value = '';
    fileInfo.classList.remove('visible');
    resultCard.classList.remove('visible');
    progressWrap.classList.remove('visible');
    compressBtn.disabled = true;
    progressFill.style.width = '0%';
  });

  compressBtn.addEventListener('click', async () =&gt; {
    // Validasi pengecekan eksekusi script eksternal
    if (typeof PDFLib === 'undefined') {
      alert('Sistem sedang memuat modul inti. Mohon tunggu beberapa detik dan coba lagi.');
      return;
    }

    if (!selectedFile) return;
    compressBtn.classList.add('loading');
    compressBtn.disabled = true;
    progressWrap.classList.add('visible');
    resultCard.classList.remove('visible');

    const steps = [
      {pct: 20, msg: 'Membaca file PDF…'},
      {pct: 45, msg: 'Menganalisis struktur dokumen…'},
      {pct: 70, msg: 'Mengoptimalkan objek PDF…'},
      {pct: 90, msg: 'Menyimpan hasil kompresi…'},
    ];

    let si = 0;
    const stepTimer = setInterval(() =&gt; {
      if (si &lt; steps.length) {
        progressLabel.textContent = steps[si].msg;
        progressFill.style.width  = steps[si].pct + '%';
        si++;
      }
    }, 300);

    try {
      const { PDFDocument } = PDFLib;
      const buffer = await selectedFile.arrayBuffer();
      const pdf = await PDFDocument.load(buffer, { ignoreEncryption: false });

      const useStreams = selectedLevel !== 'light';
      const compressed = await pdf.save({ useObjectStreams: useStreams });

      clearInterval(stepTimer);
      progressLabel.textContent = 'Selesai!';
      progressFill.style.width = '100%';

      const originalBytes   = selectedFile.size;
      const compressedBytes = compressed.byteLength;
      const savedPct = Math.round((1 - compressedBytes / originalBytes) * 100);

      originalSizeEl.textContent   = fmtSize(originalBytes);
      compressedSizeEl.textContent = fmtSize(compressedBytes);
      savedPctEl.textContent       = (savedPct &gt; 0 ? '−' : '+') + Math.abs(savedPct) + '%';

      const blob = new Blob([compressed], { type: 'application/pdf' });
      const url  = URL.createObjectURL(blob);
      downloadLink.href = url;
      downloadLink.download = selectedFile.name.replace('.pdf', '_compressed.pdf');

      setTimeout(() =&gt; {
        resultCard.classList.add('visible');
        progressWrap.classList.remove('visible');
        compressBtn.classList.remove('loading');
        compressBtn.disabled = false;
      }, 400);

    } catch (err) {
      clearInterval(stepTimer);
      alert('Gagal memproses file. Pastikan file bukan PDF terenkripsi.\n\nDetail: ' + err.message);
      compressBtn.classList.remove('loading');
      compressBtn.disabled = false;
      progressWrap.classList.remove('visible');
    }
  });

  // FAQ accordion dengan pembaruan Aksesibilitas (ARIA)
  document.querySelectorAll('.faq-q').forEach(btn =&gt; {
    btn.addEventListener('click', () =&gt; {
      const item = btn.closest('.faq-item');
      const isOpen = item.classList.contains('open');
      
      // Reset semua status aria-expanded
      document.querySelectorAll('.faq-item').forEach(i =&gt; {
        i.classList.remove('open');
        i.querySelector('.faq-q').setAttribute('aria-expanded', 'false');
      });
      
      // Update elemen yang diklik
      if (!isOpen) {
        item.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
      }
    });
  });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/compress-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-8460358654976201346</guid><pubDate>Thu, 09 Apr 2026 21:28:00 +0000</pubDate><atom:updated>2026-07-24T05:14:59.313-07:00</atom:updated><title>Split Pdf</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;Split PDF Online Gratis – Pisahkan Halaman PDF – Kholil Tools&lt;/title&gt;
  &lt;meta name="description" content="Pisahkan file PDF menjadi beberapa bagian secara online, gratis, dan aman. Pilih halaman tertentu atau rentang halaman. Proses langsung di browser tanpa upload ke server."/&gt;
  
  &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
  &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@600;700;800&amp;display=swap" rel="stylesheet"&gt;
  
  &lt;!-- Defer applied to external script --&gt;
  &lt;script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js" defer&gt;&lt;/script&gt;
  
  &lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --red:       #D63031;
      --red-dark:  #B02020;
      --red-light: #FFF0F0;
      --ink:       #1E293B; 
      --muted:     #64748B;
      --border:    #E2E8F0;
      --bg:        #F8FAFC;
      --white:     #FFFFFF;
      --success:   #059669;
      --warn:      #D97706;
      --radius:    12px;
    }

    body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--ink); line-height: 1.7; font-size: 16px; }

    /* HEADER */
    header { background: var(--white); border-bottom: 1px solid var(--border); padding: 0 24px; }
    .header-inner { max-width: 1100px; margin: auto; display: flex; align-items: center; height: 60px; gap: 10px; }
    .logo-mark { width: 32px; height: 32px; background: var(--red); border-radius: 8px; display: flex; align-items: center; justify-content: center; }
    .logo-mark svg { width: 18px; height: 18px; fill: #fff; }
    .logo-text { font-family: 'Sora', sans-serif; font-weight: 700; font-size: 18px; color: var(--ink); }
    .logo-text span { color: var(--red); }
    nav { margin-left: auto; display: flex; gap: 24px; }
    nav a { font-size: 14px; color: var(--muted); text-decoration: none; font-weight: 500; }
    nav a:hover, nav a.active { color: var(--red); }

    /* HERO DENGAN DIFERENSIASI VISUAL */
    .hero-split { background: linear-gradient(135deg, #0F172A 0%, #1E1B4B 100%); padding: 64px 24px 56px; text-align: center; color: #fff; border-bottom: 4px solid var(--red); }
    .hero-badge { display: inline-block; background: rgba(255,255,255,.1); color: #E2E8F0; font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; padding: 4px 14px; border-radius: 20px; border: 1px solid rgba(255,255,255,.2); margin-bottom: 18px; }
    .hero-split h1 { font-family: 'Sora', sans-serif; font-size: clamp(28px,5vw,48px); font-weight: 800; line-height: 1.15; margin-bottom: 16px; }
    .hero-split h1 span { color: #FCA5A5; }
    .hero-split p { font-size: 17px; color: #CBD5E1; max-width: 560px; margin: 0 auto 28px; }
    .hero-stats { display: flex; justify-content: center; gap: 32px; flex-wrap: wrap; }
    .stat { text-align: center; }
    .stat-num { font-family: 'Sora', sans-serif; font-size: 26px; font-weight: 800; color: #fff; }
    .stat-label { font-size: 12px; color: #94A3B8; letter-spacing: .04em; }

    /* MAIN LAYOUT */
    .main-wrap { max-width: 1100px; margin: 0 auto; padding: 48px 24px; display: grid; grid-template-columns: 1fr 340px; gap: 32px; align-items: start; }
    @media(max-width:860px){ .main-wrap { grid-template-columns: 1fr; } .sidebar { order: -1; } }

    /* TOOL CARD */
    .tool-card { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); box-shadow: 0 4px 6px -1px rgba(0,0,0,.05); overflow: hidden; }
    .tool-card-header { padding: 20px 24px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; background: #F8FAFC; }
    .tool-card-header h2 { font-family: 'Sora', sans-serif; font-size: 18px; font-weight: 700; }
    .badge-free { margin-left: auto; background: #ECFDF5; color: var(--success); font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 20px; border: 1px solid #A7F3D0; }

    /* Drop Zone */
    .drop-zone { margin: 24px; border: 2px dashed #CBD5E1; border-radius: var(--radius); padding: 44px 24px; text-align: center; cursor: pointer; transition: all .2s; background: var(--white); }
    .drop-zone:hover, .drop-zone.drag-over { border-color: var(--red); background: var(--red-light); }
    .drop-icon { width: 52px; height: 52px; background: var(--red-light); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 14px; }
    .drop-icon svg { width: 26px; height: 26px; }
    .drop-zone h3 { font-size: 16px; font-weight: 600; margin-bottom: 6px; }
    .drop-zone p { font-size: 13px; color: var(--muted); }
    .btn-choose { display: inline-block; margin-top: 16px; background: var(--red); color: #fff; padding: 10px 22px; border-radius: 30px; font-size: 14px; font-weight: 600; cursor: pointer; transition: background .2s; }
    .btn-choose:hover { background: var(--red-dark); }
    #pdfFileInput { display: none; }

    /* File Info */
    #fileInfo { margin: 0 24px 16px; padding: 14px 16px; background: #F0F9FF; border: 1px solid #BAE6FD; border-radius: 8px; font-size: 14px; display: none; align-items: center; gap: 10px; }
    #fileInfo.visible { display: flex; }
    #fileInfo svg { flex-shrink: 0; color: #0284C7; width: 20px; height: 20px; }
    .file-name { font-weight: 600; color: var(--ink); }
    .file-size { color: var(--muted); font-size: 13px; }

    /* Page Preview &amp; Chips */
    #pagePreview { margin: 0 24px 16px; display: none; }
    #pagePreview.visible { display: block; }
    .preview-label { font-size: 13px; font-weight: 600; color: var(--muted); margin-bottom: 8px; }
    .page-chips { display: flex; flex-wrap: wrap; gap: 6px; }
    button.page-chip { padding: 4px 12px; border-radius: 20px; font-size: 13px; font-family: 'Inter', sans-serif; font-weight: 600; border: 1px solid var(--border); background: var(--bg); color: var(--muted); cursor: pointer; transition: all .15s; outline: none; }
    button.page-chip:hover { border-color: var(--red); color: var(--red); }
    button.page-chip:focus-visible { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--red); }
    button.page-chip.selected { background: var(--red); border-color: var(--red); color: #fff; }

    /* Mode Toggle */
    .mode-wrap { margin: 0 24px 20px; }
    .mode-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 10px; }
    .mode-tabs { display: flex; gap: 0; border: 1.5px solid var(--border); border-radius: 10px; overflow: hidden; }
    .mode-tab { flex: 1; padding: 10px; background: var(--bg); border: none; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; transition: all .15s; display: flex; align-items: center; justify-content: center; gap: 6px; }
    .mode-tab:not(:last-child) { border-right: 1px solid var(--border); }
    .mode-tab.active { background: var(--red); color: #fff; }
    .mode-tab:focus-visible { outline: 2px solid var(--red); outline-offset: -2px; }

    /* Range Input */
    .range-wrap { margin: 0 24px 20px; }
    .range-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
    .range-hint { font-size: 12px; color: var(--muted); margin-bottom: 10px; }
    .range-input { width: 100%; padding: 10px 14px; border: 1.5px solid var(--border); border-radius: 8px; font-family: inherit; font-size: 14px; color: var(--ink); transition: border-color .2s; outline: none; }
    .range-input:focus { border-color: var(--red); box-shadow: 0 0 0 3px rgba(214,48,49,0.1); }

    /* Split Mode: Every N pages */
    .every-wrap { margin: 0 24px 20px; display: none; }
    .every-wrap.visible { display: block; }
    .every-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
    .every-row { display: flex; align-items: center; gap: 10px; }
    .every-row input[type=number] { width: 80px; padding: 10px 14px; border: 1.5px solid var(--border); border-radius: 8px; font-family: inherit; font-size: 14px; outline: none; }
    .every-row input[type=number]:focus { border-color: var(--red); }
    .every-row span { font-size: 14px; color: var(--muted); }

    /* Actions */
    .actions { padding: 0 24px 24px; display: flex; gap: 10px; }
    .btn-primary { flex: 1; background: var(--red); color: #fff; border: none; border-radius: 30px; padding: 13px 20px; font-size: 15px; font-weight: 600; cursor: pointer; font-family: inherit; transition: background .2s, opacity .2s; display: flex; align-items: center; justify-content: center; gap: 7px; }
    .btn-primary:hover { background: var(--red-dark); }
    .btn-primary:disabled { opacity: .5; cursor: not-allowed; }
    .btn-reset { background: var(--bg); color: var(--muted); border: 1px solid var(--border); border-radius: 30px; padding: 13px 18px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit; transition: background .2s; }
    .btn-reset:hover { background: var(--border); }

    /* Progress */
    #progressWrap { margin: 0 24px 16px; display: none; }
    #progressWrap.visible { display: block; }
    .progress-label { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
    .progress-bar-bg { background: var(--bg); border-radius: 20px; height: 8px; overflow: hidden; border: 1px solid var(--border); }
    .progress-bar-fill { height: 100%; background: linear-gradient(90deg, var(--red), #FF6B6B); border-radius: 20px; width: 0%; transition: width .3s; }

    /* Results */
    #resultCard { margin: 0 24px 24px; display: none; }
    #resultCard.visible { display: block; }
    .result-title { font-size: 14px; font-weight: 600; color: var(--success); margin-bottom: 12px; display: flex; align-items: center; gap: 6px; padding: 12px 16px; background: #ECFDF5; border: 1px solid #A7F3D0; border-radius: 10px; }
    .download-list { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow-y: auto; }
    .download-item { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; gap: 10px; }
    .download-item-info { font-size: 13px; flex: 1; }
    .download-item-name { font-weight: 600; color: var(--ink); }
    .download-item-pages { color: var(--muted); font-size: 12px; }
    .btn-dl-single { display: inline-flex; align-items: center; gap: 5px; background: var(--red); color: #fff; padding: 7px 14px; border-radius: 20px; font-size: 12px; font-weight: 600; text-decoration: none; transition: background .2s; white-space: nowrap; }
    .btn-dl-single:hover { background: var(--red-dark); }

    /* SIDEBAR */
    .sidebar { display: flex; flex-direction: column; gap: 20px; }
    .info-card { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); padding: 20px; }
    .info-card h3 { font-family: 'Sora', sans-serif; font-size: 15px; font-weight: 700; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
    .info-card h3 .ic { width: 30px; height: 30px; border-radius: 8px; background: var(--red-light); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .checklist { list-style: none; display: flex; flex-direction: column; gap: 9px; }
    .checklist li { font-size: 14px; display: flex; align-items: flex-start; gap: 9px; color: var(--muted); }
    .checklist li::before { content: ''; flex-shrink: 0; width: 18px; height: 18px; margin-top: 2px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23059669'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z' clip-rule='evenodd'/%3E%3C/svg%3E") center/cover; }
    .tip-box { background: #FFFBEB; border: 1px solid #FDE68A; border-radius: 8px; padding: 12px 14px; font-size: 13px; color: #92400E; line-height: 1.6; }
    .tip-box strong { color: var(--warn); }
    .example-box { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
    .example-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
    .example-row:last-child { margin-bottom: 0; }
    .ex-code { background: var(--ink); color: #7DD3FC; font-size: 13px; font-family: monospace; padding: 3px 8px; border-radius: 4px; flex-shrink: 0; }
    .ex-desc { font-size: 13px; color: var(--muted); }

    /* CONTENT */
    .content-section { max-width: 1100px; margin: 0 auto; padding: 0 24px 64px; }
    .section-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; margin-bottom: 48px; }
    .content-card { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.02); }
    .content-card .cc-icon { width: 44px; height: 44px; background: #F0F9FF; color: #0284C7; border-radius: 10px; display: flex; align-items: center; justify-content: center; margin-bottom: 14px; }
    .content-card h3 { font-family: 'Sora', sans-serif; font-size: 16px; font-weight: 700; margin-bottom: 8px; }
    .content-card p { font-size: 14px; color: var(--muted); line-height: 1.7; }
    
    /* Prose Section */
    .prose-section { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); padding: 36px; margin-bottom: 32px; border-top: 3px solid var(--ink); }
    .prose-section h2 { font-family: 'Sora', sans-serif; font-size: 22px; font-weight: 700; margin-bottom: 14px; color: var(--ink); }
    .prose-section h3 { font-size: 17px; font-weight: 600; margin: 24px 0 10px; color: var(--ink); }
    .prose-section p { font-size: 15px; color: #334155; margin-bottom: 14px; line-height: 1.8; }
    .prose-section ul, .prose-section ol { padding-left: 22px; margin-bottom: 14px; }
    .prose-section li { font-size: 15px; color: #334155; margin-bottom: 8px; line-height: 1.7; }

    /* ── Image Proof Gallery CSS ── */
    .proof-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; margin: 24px 0; }
    .proof-figure { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; text-align: center; }
    .proof-figure img { width: 100%; height: auto; border-radius: 8px; margin-bottom: 12px; border: 1px solid #CBD5E1; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
    .proof-figure figcaption { font-size: 14px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
    .proof-figure p { font-size: 13px !important; color: var(--muted) !important; margin-bottom: 0 !important; line-height: 1.5 !important; }
    
    .steps-list { list-style: none; counter-reset: steps; padding: 0; }
    .steps-list li { counter-increment: steps; padding: 14px 16px 14px 56px; position: relative; border-bottom: 1px dashed var(--border); margin-bottom: 12px; }
    .steps-list li:last-child { border-bottom: none; }
    .steps-list li::before { content: counter(steps); position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 28px; height: 28px; background: var(--ink); color: #fff; border-radius: 8px; font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
    .steps-list li strong { color: var(--ink); }
    
    .faq-list { display: flex; flex-direction: column; gap: 10px; }
    .faq-item { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--bg); }
    .faq-q { width: 100%; background: none; border: none; padding: 16px 20px; font-family: inherit; font-size: 15px; font-weight: 600; color: var(--ink); text-align: left; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
    .faq-q .arrow { width: 20px; height: 20px; flex-shrink: 0; transition: transform .25s; color: var(--muted); }
    .faq-item.open .faq-q .arrow { transform: rotate(180deg); }
    .faq-a { max-height: 0; overflow: hidden; transition: max-height .3s ease, padding .3s; padding: 0 20px; font-size: 14px; color: var(--muted); line-height: 1.7; }
    .faq-item.open .faq-a { max-height: 220px; padding: 0 20px 16px; }

    /* FOOTER */
    footer { background: var(--ink); color: #94A3B8; text-align: center; padding: 40px 24px; font-size: 14px; border-top: 4px solid var(--red); }
    footer a { color: #CBD5E1; text-decoration: none; font-weight: 500; }
    footer a:hover { color: #fff; text-decoration: underline; }
    .footer-links { display: flex; justify-content: center; gap: 24px; margin-bottom: 16px; flex-wrap: wrap; }

    /* Spinner */
    .spinner { width: 18px; height: 18px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .7s linear infinite; display: none; }
    .btn-primary.loading .spinner { display: block; }
    .btn-primary.loading .btn-text { display: none; }
    @keyframes spin { to { transform: rotate(360deg); } }

    @media(max-width:600px){ .hero-stats { gap: 20px; } .actions { flex-direction: column; } .prose-section { padding: 20px; } .mode-tab { font-size: 12px; } }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;header&gt;
  &lt;div class="header-inner"&gt;
    &lt;div class="logo-mark" aria-hidden="true"&gt;
      &lt;svg viewBox="0 0 24 24"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm4 18H6V4h7v5h5v11z"/&gt;&lt;/svg&gt;
    &lt;/div&gt;
    &lt;span class="logo-text"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/span&gt;
    
    &lt;!-- Navigasi Dibatasi Maksimal 3 Link Non-Esensial --&gt;
    &lt;nav aria-label="Menu Utama"&gt;
      &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html"&gt;Compress PDF&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Merge PDF&lt;/a&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;div class="hero-split"&gt;
  &lt;div class="hero-badge"&gt;Alat Ekstraksi Dokumen Aman&lt;/div&gt;
  &lt;h1&gt;Split PDF — &lt;span&gt;Pisahkan Halaman&lt;/span&gt;&lt;br&gt;Sesuai Kebutuhan Anda&lt;/h1&gt;
  &lt;p&gt;Ekstrak halaman tertentu, pisah per rentang, atau bagi per halaman tunggal. Semua proses terjadi di browser Anda — file tidak pernah diunggah ke server.&lt;/p&gt;
  &lt;div class="hero-stats"&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;∞&lt;/div&gt;&lt;div class="stat-label"&gt;Tanpa Batas Halaman&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;0&lt;/div&gt;&lt;div class="stat-label"&gt;Upload ke Server&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;main&gt;
&lt;div class="main-wrap"&gt;
  &lt;div&gt;
    &lt;div class="tool-card"&gt;
      &lt;div class="tool-card-header"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--ink)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;path d="M14 2v6h6"/&gt;&lt;line x1="8" y1="13" x2="16" y2="13"/&gt;&lt;line x1="8" y1="17" x2="13" y2="17"/&gt;&lt;/svg&gt;
        &lt;h2&gt;Pisahkan File PDF&lt;/h2&gt;
        &lt;span class="badge-free"&gt;GRATIS&lt;/span&gt;
      &lt;/div&gt;

      &lt;!-- Drop Zone --&gt;
      &lt;div class="drop-zone" id="dropZone" aria-label="Area Upload PDF"&gt;
        &lt;div class="drop-icon" aria-hidden="true"&gt;
          &lt;svg fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/&gt;&lt;/svg&gt;
        &lt;/div&gt;
        &lt;h3&gt;Seret &amp;amp; Lepas File PDF di Sini&lt;/h3&gt;
        &lt;p&gt;atau klik tombol di bawah untuk memilih file&lt;/p&gt;
        &lt;label class="btn-choose"&gt;
          Pilih File PDF
          &lt;input type="file" id="pdfFileInput" accept="application/pdf"&gt;
        &lt;/label&gt;
        &lt;p style="margin-top:10px;font-size:12px;color:var(--muted);"&gt;Maks. ukuran file: 50 MB&lt;/p&gt;
      &lt;/div&gt;

      &lt;!-- File Info --&gt;
      &lt;div id="fileInfo"&gt;
        &lt;svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;path d="M14 2v6h6"/&gt;&lt;/svg&gt;
        &lt;div&gt;
          &lt;div class="file-name" id="fileName"&gt;—&lt;/div&gt;
          &lt;div class="file-size" id="fileSizeLabel"&gt;—&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Page Visual Preview --&gt;
      &lt;div id="pagePreview"&gt;
        &lt;div style="margin: 0 24px 10px;"&gt;
          &lt;div class="preview-label" id="previewInst"&gt;Pratinjau Halaman — Klik untuk memilih/batalkan&lt;/div&gt;
          &lt;div class="page-chips" id="pageChips" aria-labelledby="previewInst"&gt;&lt;/div&gt;
          &lt;p style="font-size:12px;color:var(--muted);margin-top:8px;" id="selectionHint" aria-live="polite"&gt;Belum ada halaman dipilih&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Mode --&gt;
      &lt;div class="mode-wrap"&gt;
        &lt;div class="mode-label"&gt;Mode Pemisahan&lt;/div&gt;
        &lt;div class="mode-tabs" role="tablist"&gt;
          &lt;button class="mode-tab active" data-mode="range" role="tab" aria-selected="true" type="button"&gt;
            &lt;svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9 18H5a2 2 0 01-2-2V6a2 2 0 012-2h4"/&gt;&lt;path d="M15 18h4a2 2 0 002-2V6a2 2 0 00-2-2h-4"/&gt;&lt;line x1="12" y1="2" x2="12" y2="22"/&gt;&lt;/svg&gt;
            Rentang / Pilihan
          &lt;/button&gt;
          &lt;button class="mode-tab" data-mode="every" role="tab" aria-selected="false" type="button"&gt;
            &lt;svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;rect x="3" y="3" width="7" height="7"/&gt;&lt;rect x="14" y="3" width="7" height="7"/&gt;&lt;rect x="3" y="14" width="7" height="7"/&gt;&lt;rect x="14" y="14" width="7" height="7"/&gt;&lt;/svg&gt;
            Per N Halaman
          &lt;/button&gt;
          &lt;button class="mode-tab" data-mode="single" role="tab" aria-selected="false" type="button"&gt;
            &lt;svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;/svg&gt;
            Pisah Semua
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Range Input (mode: range) --&gt;
      &lt;div class="range-wrap" id="rangeWrap"&gt;
        &lt;label for="pageRange" class="range-label" style="display:block;"&gt;Halaman yang Ingin Dipisahkan&lt;/label&gt;
        &lt;div class="range-hint"&gt;Contoh: &lt;code&gt;1-3&lt;/code&gt; (rentang), &lt;code&gt;2,4,6&lt;/code&gt; (pilihan), atau &lt;code&gt;1-3,5,7-9&lt;/code&gt; (kombinasi)&lt;/div&gt;
        &lt;input type="text" class="range-input" id="pageRange" placeholder="Masukkan halaman, mis: 1-5,8,10-12"&gt;
      &lt;/div&gt;

      &lt;!-- Every N pages (mode: every) --&gt;
      &lt;div class="every-wrap" id="everyWrap"&gt;
        &lt;div class="every-label"&gt;Pisah Setiap&lt;/div&gt;
        &lt;div class="every-row"&gt;
          &lt;label for="everyN" class="sr-only" style="display:none;"&gt;Jumlah halaman per bagian&lt;/label&gt;
          &lt;input type="number" id="everyN" value="1" min="1" max="999" aria-label="Jumlah halaman yang akan dipisah per bagian"&gt;
          &lt;span&gt;halaman — akan menghasilkan beberapa file PDF terpisah&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Progress --&gt;
      &lt;div id="progressWrap"&gt;
        &lt;div class="progress-label" id="progressLabel" aria-live="assertive"&gt;Memproses...&lt;/div&gt;
        &lt;div class="progress-bar-bg"&gt;&lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Result --&gt;
      &lt;div id="resultCard" aria-live="polite"&gt;
        &lt;div class="result-title"&gt;
          &lt;svg width="16" height="16" fill="none" stroke="var(--success)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M22 11.08V12a10 10 0 11-5.93-9.14"/&gt;&lt;path d="M22 4L12 14.01l-3-3"/&gt;&lt;/svg&gt;
          &lt;span id="resultTitle"&gt;Berhasil memisahkan PDF!&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="download-list" id="downloadList"&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Actions --&gt;
      &lt;div class="actions"&gt;
        &lt;button class="btn-primary" id="splitBtn" disabled type="button"&gt;
          &lt;span class="btn-text" style="display:flex;align-items:center;gap:7px;"&gt;
            &lt;svg width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9 18H5a2 2 0 01-2-2V6a2 2 0 012-2h4"/&gt;&lt;path d="M15 18h4a2 2 0 002-2V6a2 2 0 00-2-2h-4"/&gt;&lt;line x1="12" y1="2" x2="12" y2="22"/&gt;&lt;/svg&gt;
            Mulai Split PDF
          &lt;/span&gt;
          &lt;div class="spinner" aria-hidden="true"&gt;&lt;/div&gt;
        &lt;/button&gt;
        &lt;button class="btn-reset" id="resetBtn" type="button"&gt;Reset&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- SIDEBAR --&gt;
  &lt;aside class="sidebar"&gt;
    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Keamanan &amp;amp; Privasi
      &lt;/h3&gt;
      &lt;ul class="checklist"&gt;
        &lt;li&gt;File diproses 100% di browser Anda&lt;/li&gt;
        &lt;li&gt;Tidak ada data yang dikirim ke server&lt;/li&gt;
        &lt;li&gt;File otomatis hilang saat tab ditutup&lt;/li&gt;
        &lt;li&gt;Aman untuk dokumen rahasia &amp;amp; sensitif&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;

    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M8 12h8M12 8v8"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Format Input Halaman
      &lt;/h3&gt;
      &lt;div class="example-box"&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;1-5&lt;/span&gt;&lt;span class="ex-desc"&gt;Halaman 1 sampai 5&lt;/span&gt;&lt;/div&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;2,4,6&lt;/span&gt;&lt;span class="ex-desc"&gt;Halaman 2, 4, dan 6&lt;/span&gt;&lt;/div&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;1-3,7&lt;/span&gt;&lt;span class="ex-desc"&gt;Hal. 1-3 dan halaman 7&lt;/span&gt;&lt;/div&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;5-&lt;/span&gt;&lt;span class="ex-desc"&gt;Halaman 5 hingga akhir&lt;/span&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M12 6v6l4 2"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Tips Aksesibilitas
      &lt;/h3&gt;
      &lt;div class="tip-box"&gt;
        &lt;strong&gt;&#128161; Dukungan Keyboard:&lt;/strong&gt; Mode &lt;em&gt;Pratinjau Halaman&lt;/em&gt; kini mendukung navigasi menggunakan tombol `Tab` dan pemilihan menggunakan `Space` atau `Enter`.
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/aside&gt;
&lt;/div&gt;

&lt;!-- CONTENT --&gt;
&lt;div class="content-section"&gt;

  &lt;div class="section-grid"&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9 18H5a2 2 0 01-2-2V6a2 2 0 012-2h4"/&gt;&lt;path d="M15 18h4a2 2 0 002-2V6a2 2 0 00-2-2h-4"/&gt;&lt;line x1="12" y1="2" x2="12" y2="22"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Tiga Mode Pemisahan Aktif&lt;/h3&gt;
      &lt;p&gt;Pilih rentang halaman spesifik, belah setiap N halaman berulang, atau cerai-beraikan seluruh dokumen menjadi halaman tunggal secara presisi.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;rect x="3" y="3" width="7" height="7"/&gt;&lt;rect x="14" y="3" width="7" height="7"/&gt;&lt;rect x="3" y="14" width="7" height="7"/&gt;&lt;rect x="14" y="14" width="7" height="7"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Pratinjau UI Interaktif&lt;/h3&gt;
      &lt;p&gt;Arsitektur front-end kami merender pratinjau halaman visual sebagai &lt;em&gt;chips&lt;/em&gt;, meminimalisir kesalahan pengetikan nomor secara manual.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Protokol Zero-Upload&lt;/h3&gt;
      &lt;p&gt;Semua operasi bitwise PDF dilakukan melalui CPU &amp; RAM lokal perangkat Anda. Integritas data sensitif perusahaan dijamin utuh.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Generasi File Instan&lt;/h3&gt;
      &lt;p&gt;Kapasitas mengunduh objek partisi secara individual di titik akhir, tanpa perlu menunggu layanan kompresi arsip (.zip) pihak ketiga.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Panduan Teknis: Ekstraksi Dokumen PDF Tersegmentasi&lt;/h2&gt;
    &lt;p&gt;
      Dalam administrasi digital di Indonesia (seperti pemisahan lampiran ijazah dari transkrip nilai untuk pendaftaran CPNS, atau memisahkan halaman kontrak spesifik untuk dibubuhi e-Meterai), mengekstrak halaman PDF tanpa merusak kualitas aslinya adalah sebuah keharusan.
    &lt;/p&gt;
    &lt;p&gt;
      Sistem &lt;em&gt;Split PDF&lt;/em&gt; kami mengungguli alat konvensional dengan fitur &lt;strong&gt;Pratinjau Visual (Page Chips)&lt;/strong&gt;. Alih-alih menebak nomor halaman secara buta, algoritma &lt;code&gt;pdf-lib&lt;/code&gt; kami membaca arsitektur dokumen Anda secara lokal di RAM browser, lalu memetakan setiap halaman menjadi elemen interaktif yang bisa Anda klik.
    &lt;/p&gt;
    
    &lt;!-- Penambahan Bukti Pengujian (E-E-A-T Evidence) --&gt;
    &lt;h3&gt;Bukti Nyata Pengujian Alat Split PDF&lt;/h3&gt;
    &lt;p&gt;Untuk membuktikan bahwa sistem &lt;em&gt;Client-Side&lt;/em&gt; kami benar-benar berfungsi dan bukan sekadar &lt;em&gt;mockup&lt;/em&gt;, berikut adalah dokumentasi pemisahan PDF secara *real-time* via browser mobile:&lt;/p&gt;
    
    &lt;div class="proof-gallery"&gt;
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/BK777FFt/Screenshot-2026-07-24-17-16-15-142-com-android-chrome.jpg" alt="Pratinjau visual halaman (page chips) aktif di browser" loading="lazy"&gt;
        &lt;figcaption&gt;1. Pratinjau Interaktif&lt;/figcaption&gt;
        &lt;p&gt;Halaman PDF di-render sebagai chip yang bisa dipilih (klik) satu per satu.&lt;/p&gt;
      &lt;/figure&gt;
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/yncdPMYv/Screenshot-2026-07-24-17-16-19-723-com-android-chrome.jpg" alt="Daftar tautan download per halaman PDF" loading="lazy"&gt;
        &lt;figcaption&gt;2. Hasil Ekstraksi Instan&lt;/figcaption&gt;
        &lt;p&gt;Sistem memisahkan halaman menjadi file independen dengan tombol unduh masing-masing.&lt;/p&gt;
      &lt;/figure&gt;
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/Q3FN58W8/Screenshot-2026-07-24-17-16-24-091-com-android-chrome.jpg" alt="Notifikasi unduhan browser membuktikan proses native" loading="lazy"&gt;
        &lt;figcaption&gt;3. Unduhan Native Browser&lt;/figcaption&gt;
        &lt;p&gt;File langsung disimpan ke memori internal tanpa proses zip atau server pihak ketiga.&lt;/p&gt;
      &lt;/figure&gt;
      &lt;figure class="proof-figure"&gt;
        &lt;img src="https://i.ibb.co.com/gLHzgwZX/Screenshot-2026-07-24-17-23-49-607-com-android-chrome.jpg" alt="Validitas halaman PDF yang diunduh sesuai dengan chip yang dipilih" loading="lazy"&gt;
        &lt;figcaption&gt;4. Validasi Dokumen Akhir&lt;/figcaption&gt;
        &lt;p&gt;Hasil akhir dokumen PDF akurat 100% sesuai dengan halaman yang ditargetkan.&lt;/p&gt;
      &lt;/figure&gt;
    &lt;/div&gt;

    &lt;h3&gt;Studi Kasus: Memisahkan Laporan Keuangan&lt;/h3&gt;
    &lt;p&gt;Jika Anda memiliki &lt;em&gt;Annual Report&lt;/em&gt; setebal 150 halaman dan hanya membutuhkan neraca keuangan di halaman &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html" style="color:var(--red);"&gt;45-50&lt;/a&gt;, Anda cukup menggunakan &lt;strong&gt;Mode Rentang&lt;/strong&gt;, mengetikkan &lt;code&gt;45-50&lt;/code&gt;, dan sistem akan mengisolasi objek data tersebut menjadi file baru yang jauh lebih ringan dalam hitungan milidetik.&lt;/p&gt;
  &lt;/div&gt;

  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Langkah Konfigurasi Operasi Pemisahan&lt;/h2&gt;
    &lt;ol class="steps-list"&gt;
      &lt;li&gt;&lt;strong&gt;Inisiasi File&lt;/strong&gt; — Tempatkan dokumen PDF ke dalam zona area yang disediakan. Sistem akan membaca metadata file tanpa memicu koneksi internet keluar.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Identifikasi Interaktif&lt;/strong&gt; — Visualisasi chip halaman akan tampil. Anda bisa menekan (klik/tap) elemen tersebut untuk memilih atau membatalkan pilihan ekstraksi.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Pilih Algoritma Mode&lt;/strong&gt; — &lt;em&gt;Mode Rentang&lt;/em&gt; cocok untuk halaman spesifik. Gunakan &lt;em&gt;Per N Halaman&lt;/em&gt; untuk pembagian volume merata, atau &lt;em&gt;Pisah Semua&lt;/em&gt; untuk partisi ekstrem.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Validasi Input Manual&lt;/strong&gt; — Jika Anda lebih memilih pengetikan, pastikan mematuhi tata bahasa sistem (misal: &lt;code&gt;2,5,7-9&lt;/code&gt;).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Eksekusi Render&lt;/strong&gt; — Tekan tombol pemrosesan. *Browser* akan melakukan dekode biner sesuai instruksi Anda.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Distribusi Hasil&lt;/strong&gt; — Modul *download* otomatis tersaji untuk menyimpan fragmen dokumen.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/div&gt;

  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Troubleshooting &amp; Tanya Jawab Teknis&lt;/h2&gt;
    &lt;div class="faq-list"&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Kenapa hasil file yang dipisah (Split) ukurannya masih sangat besar?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Jika dokumen PDF sumber Anda dibuat dari hasil &lt;em&gt;scan&lt;/em&gt; beresolusi sangat tinggi, mengekstrak satu halaman pun akan membawa metadata dan lapisan gambar yang berat. Untuk mengatasinya, Anda disarankan menjalankan hasil pemisahan ini ke fitur &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html" style="color:var(--red);"&gt;Compress PDF&lt;/a&gt; kami terlebih dahulu.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Apakah pemisahan halaman merusak *digital signature* (tanda tangan digital) dokumen?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Ya. Memisahkan atau memodifikasi dokumen PDF yang sudah memiliki sertifikat digital terenkripsi (seperti sertifikat BSSN atau tanda tangan digital resmi) akan merusak validitas &lt;em&gt;hash&lt;/em&gt; dokumen tersebut. Lakukan pemisahan &lt;strong&gt;sebelum&lt;/strong&gt; dokumen ditandatangani secara digital.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Bisakah saya menyatukan kembali file yang sudah dipisah?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Tentu saja. Semua objek PDF hasil partisi kami mengadopsi standar &lt;em&gt;PDF/A&lt;/em&gt; murni yang kompatibel untuk direkonstruksi. Anda dapat menyatukan mereka kembali dengan mudah menggunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html" style="color:var(--red);"&gt;Merge PDF&lt;/a&gt; kami.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Apakah PDF yang dilindungi kata sandi dapat diekstrak halamannya?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Algoritma *Client-Side* kami secara sengaja dikunci untuk menolak operasi pemisahan pada dokumen yang memiliki level enkripsi DRM aktif. Anda wajib menonaktifkan gembok keamanan tersebut sebelum mengunggah.&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- Trust Box / E-E-A-T Signal --&gt;
  &lt;div class="prose-section" style="background: #F0F9FF; border: 1px solid #BAE6FD; display: flex; gap: 20px; align-items: center;"&gt;
    &lt;div style="flex-shrink: 0;"&gt;
      &lt;img src="https://ui-avatars.com/api/?name=Kholil&amp;background=0D8ABC&amp;color=fff" alt="Kholil - Pengembang Alat" style="border-radius: 50%; width: 64px; height: 64px;"&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;h3 style="margin: 0 0 5px; font-size: 16px;"&gt;Direkayasa oleh &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="color: #0284C7; text-decoration: none;"&gt;Kholil&lt;/a&gt;&lt;/h3&gt;
      &lt;p style="margin: 0; font-size: 14px; color: var(--muted);"&gt;Semua skrip manipulasi PDF di halaman ini dieksekusi secara &lt;em&gt;Client-Side&lt;/em&gt; demi menjamin privasi absolut. Data Anda tetap berada di perangkat Anda. Menemukan kendala fungsi alat? &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="color: var(--ink); font-weight: 600;"&gt;Hubungi Saya&lt;/a&gt;.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;
&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="footer-links"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html"&gt;Compress PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Merge PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;Tentang Kami&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Kontak&lt;/a&gt;
  &lt;/div&gt;
  &lt;p&gt;© 2026 Kholil Tools — Infrastruktur Produktivitas PDF untuk Indonesia.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
  const dropZone    = document.getElementById('dropZone');
  const fileInput   = document.getElementById('pdfFileInput');
  const fileInfoEl  = document.getElementById('fileInfo');
  const fileNameEl  = document.getElementById('fileName');
  const fileSizeEl  = document.getElementById('fileSizeLabel');
  const pagePreview = document.getElementById('pagePreview');
  const pageChips   = document.getElementById('pageChips');
  const selHint     = document.getElementById('selectionHint');
  const rangeWrap   = document.getElementById('rangeWrap');
  const everyWrap   = document.getElementById('everyWrap');
  const pageRangeIn = document.getElementById('pageRange');
  const everyNIn    = document.getElementById('everyN');
  const splitBtn    = document.getElementById('splitBtn');
  const resetBtn    = document.getElementById('resetBtn');
  const progressWrap = document.getElementById('progressWrap');
  const progressLabel = document.getElementById('progressLabel');
  const progressFill  = document.getElementById('progressFill');
  const resultCard  = document.getElementById('resultCard');
  const resultTitle = document.getElementById('resultTitle');
  const downloadList = document.getElementById('downloadList');

  let selectedFile = null;
  let totalPages   = 0;
  let selectedMode = 'range';
  let chipSelected = new Set();

  function fmtSize(b) {
    if (b &lt; 1024) return b + ' B';
    if (b &lt; 1048576) return (b/1024).toFixed(1) + ' KB';
    return (b/1048576).toFixed(2) + ' MB';
  }

  // Drag &amp; drop
  dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
  dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
  dropZone.addEventListener('drop', e =&gt; { e.preventDefault(); dropZone.classList.remove('drag-over'); const f = e.dataTransfer.files[0]; if (f &amp;&amp; f.type === 'application/pdf') loadFile(f); });
  fileInput.addEventListener('change', e =&gt; { if (e.target.files[0]) loadFile(e.target.files[0]); });

  async function loadFile(f) {
    if (f.size &gt; 50 * 1024 * 1024) { alert('Ukuran file melebihi batas 50 MB.'); return; }
    selectedFile = f;
    fileNameEl.textContent = f.name;

    try {
      const { PDFDocument } = PDFLib;
      const buf = await f.arrayBuffer();
      const pdf = await PDFDocument.load(buf);
      totalPages = pdf.getPageCount();
    } catch(e) { totalPages = 0; }

    fileSizeEl.textContent = fmtSize(f.size) + (totalPages ? ` · ${totalPages} halaman` : '');
    fileInfoEl.classList.add('visible');
    resultCard.classList.remove('visible');
    progressWrap.classList.remove('visible');
    splitBtn.disabled = false;
    chipSelected.clear();
    buildChips();
    pagePreview.classList.add('visible');
  }

  function buildChips() {
    pageChips.innerHTML = '';
    for (let i = 1; i &lt;= Math.min(totalPages, 100); i++) {
      const c = document.createElement('button'); // Diganti button demi aksesibilitas
      c.type = 'button';
      c.className = 'page-chip';
      c.textContent = i;
      c.dataset.page = i;
      c.setAttribute('aria-pressed', 'false');
      
      c.addEventListener('click', () =&gt; {
        if (chipSelected.has(i)) { 
          chipSelected.delete(i); 
          c.classList.remove('selected'); 
          c.setAttribute('aria-pressed', 'false');
        }
        else { 
          chipSelected.add(i); 
          c.classList.add('selected'); 
          c.setAttribute('aria-pressed', 'true');
        }
        syncChipsToInput();
        updateSelHint();
      });
      pageChips.appendChild(c);
    }
    if (totalPages &gt; 100) {
      const more = document.createElement('button');
      more.type = 'button';
      more.className = 'page-chip';
      more.style.cursor = 'default';
      more.textContent = `+${totalPages - 100} lagi`;
      more.setAttribute('disabled', 'true');
      pageChips.appendChild(more);
    }
  }

  function syncChipsToInput() {
    if (chipSelected.size === 0) { pageRangeIn.value = ''; return; }
    const sorted = Array.from(chipSelected).sort((a,b) =&gt; a-b);
    const ranges = [];
    let start = sorted[0], end = sorted[0];
    for (let i = 1; i &lt; sorted.length; i++) {
      if (sorted[i] === end + 1) { end = sorted[i]; }
      else { ranges.push(start === end ? `${start}` : `${start}-${end}`); start = end = sorted[i]; }
    }
    ranges.push(start === end ? `${start}` : `${start}-${end}`);
    pageRangeIn.value = ranges.join(',');
  }

  function updateSelHint() {
    if (chipSelected.size === 0) { selHint.textContent = 'Belum ada halaman dipilih'; }
    else { selHint.textContent = `${chipSelected.size} halaman dipilih: ${Array.from(chipSelected).sort((a,b)=&gt;a-b).join(', ')}`; }
  }

  // Mode tabs (ARIA compliance fix)
  document.querySelectorAll('.mode-tab').forEach(tab =&gt; {
    tab.addEventListener('click', () =&gt; {
      document.querySelectorAll('.mode-tab').forEach(t =&gt; {
        t.classList.remove('active');
        t.setAttribute('aria-selected', 'false');
      });
      tab.classList.add('active');
      tab.setAttribute('aria-selected', 'true');
      selectedMode = tab.dataset.mode;
      rangeWrap.style.display = selectedMode === 'range' ? 'block' : 'none';
      everyWrap.classList.toggle('visible', selectedMode === 'every');
    });
  });

  function parseRange(str, max) {
    const pages = [];
    str.split(',').forEach(part =&gt; {
      part = part.trim();
      if (part.includes('-')) {
        let [s, e] = part.split('-').map(v =&gt; v.trim());
        const start = parseInt(s);
        const end   = e === '' ? max : parseInt(e);
        if (!isNaN(start) &amp;&amp; !isNaN(end)) for (let i = start; i &lt;= end; i++) pages.push(i-1);
      } else {
        const n = parseInt(part);
        if (!isNaN(n)) pages.push(n-1);
      }
    });
    return [...new Set(pages)].filter(p =&gt; p &gt;= 0 &amp;&amp; p &lt; max);
  }

  resetBtn.addEventListener('click', () =&gt; {
    selectedFile = null; totalPages = 0; chipSelected.clear();
    fileInput.value = '';
    fileInfoEl.classList.remove('visible');
    pagePreview.classList.remove('visible');
    resultCard.classList.remove('visible');
    progressWrap.classList.remove('visible');
    splitBtn.disabled = true;
    progressFill.style.width = '0%';
    pageRangeIn.value = '';
    document.querySelectorAll('.page-chip').forEach(c =&gt; c.setAttribute('aria-pressed', 'false'));
  });

  splitBtn.addEventListener('click', async () =&gt; {
    if (typeof PDFLib === 'undefined') {
      alert('Sistem inti PDF sedang memuat, harap tunggu sejenak dan coba lagi.');
      return;
    }
    
    if (!selectedFile) return;
    splitBtn.classList.add('loading');
    splitBtn.disabled = true;
    progressWrap.classList.add('visible');
    resultCard.classList.remove('visible');
    downloadList.innerHTML = '';

    const steps = [
      {pct: 20, msg: 'Membaca struktur PDF…'},
      {pct: 50, msg: 'Menyalin halaman yang dipilih…'},
      {pct: 80, msg: 'Membuat file hasil…'},
    ];
    let si = 0;
    const timer = setInterval(() =&gt; { if (si &lt; steps.length) { progressLabel.textContent = steps[si].msg; progressFill.style.width = steps[si].pct + '%'; si++; } }, 350);

    try {
      const { PDFDocument } = PDFLib;
      const buffer = await selectedFile.arrayBuffer();
      const srcPdf = await PDFDocument.load(buffer);
      const max = srcPdf.getPageCount();

      let segments = []; 

      if (selectedMode === 'single') {
        for (let i = 0; i &lt; max; i++) segments.push({ label: `Halaman ${i+1}`, pages: [i] });
      } else if (selectedMode === 'every') {
        const n = Math.max(1, parseInt(everyNIn.value) || 1);
        for (let i = 0; i &lt; max; i += n) {
          const chunk = [];
          for (let j = i; j &lt; Math.min(i+n, max); j++) chunk.push(j);
          const from = i+1, to = Math.min(i+n, max);
          segments.push({ label: from === to ? `Halaman ${from}` : `Halaman ${from}–${to}`, pages: chunk });
        }
      } else {
        const rangeStr = pageRangeIn.value.trim();
        if (!rangeStr) { clearInterval(timer); alert('Masukkan rentang halaman terlebih dahulu.'); splitBtn.classList.remove('loading'); splitBtn.disabled = false; progressWrap.classList.remove('visible'); return; }
        const pages = parseRange(rangeStr, max);
        if (!pages.length) { clearInterval(timer); alert('Tidak ada halaman valid yang ditemukan. Pastikan nomor halaman sesuai dengan dokumen.'); splitBtn.classList.remove('loading'); splitBtn.disabled = false; progressWrap.classList.remove('visible'); return; }
        segments.push({ label: `Halaman ${rangeStr}`, pages });
      }

      if (segments.length &gt; 50) segments = segments.slice(0, 50);

      const results = [];
      for (const seg of segments) {
        const newPdf = await PDFDocument.create();
        const copied = await newPdf.copyPages(srcPdf, seg.pages);
        copied.forEach(p =&gt; newPdf.addPage(p));
        const bytes = await newPdf.save();
        const blob  = new Blob([bytes], { type: 'application/pdf' });
        const url   = URL.createObjectURL(blob);
        results.push({ label: seg.label, url, size: bytes.byteLength });
      }

      clearInterval(timer);
      progressFill.style.width = '100%';
      progressLabel.textContent = 'Selesai!';

      results.forEach((r, idx) =&gt; {
        const item = document.createElement('div');
        item.className = 'download-item';
        item.innerHTML = `
          &lt;div class="download-item-info"&gt;
            &lt;div class="download-item-name"&gt;File ${idx+1} — ${r.label}&lt;/div&gt;
            &lt;div class="download-item-pages"&gt;${fmtSize(r.size)}&lt;/div&gt;
          &lt;/div&gt;
          &lt;a class="btn-dl-single" href="${r.url}" download="split_${idx+1}.pdf"&gt;
            &lt;svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/&gt;&lt;/svg&gt;
            Download
          &lt;/a&gt;`;
        downloadList.appendChild(item);
      });

      resultTitle.textContent = `Berhasil membuat ${results.length} file PDF!`;

      setTimeout(() =&gt; {
        resultCard.classList.add('visible');
        progressWrap.classList.remove('visible');
        splitBtn.classList.remove('loading');
        splitBtn.disabled = false;
      }, 400);

    } catch (err) {
      clearInterval(timer);
      alert('Gagal memproses file. Pastikan file bukan PDF terenkripsi.\n\nDetail: ' + err.message);
      splitBtn.classList.remove('loading');
      splitBtn.disabled = false;
      progressWrap.classList.remove('visible');
    }
  });

  // FAQ Accordion (ARIA compliance)
  document.querySelectorAll('.faq-q').forEach(btn =&gt; {
    btn.addEventListener('click', () =&gt; {
      const item = btn.closest('.faq-item');
      const isOpen = item.classList.contains('open');
      
      document.querySelectorAll('.faq-item').forEach(i =&gt; {
        i.classList.remove('open');
        i.querySelector('.faq-q').setAttribute('aria-expanded', 'false');
      });
      
      if (!isOpen) {
        item.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
      }
    });
  });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
​&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;Split PDF Online Gratis – Pisahkan Halaman PDF – Kholil Tools&lt;/title&gt;
  &lt;meta name="description" content="Pisahkan file PDF menjadi beberapa bagian secara online, gratis, dan aman. Pilih halaman tertentu atau rentang halaman. Proses langsung di browser tanpa upload ke server."/&gt;
  
  &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
  &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@600;700;800&amp;display=swap" rel="stylesheet"&gt;
  
  &lt;!-- Defer applied to external script --&gt;
  &lt;script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js" defer&gt;&lt;/script&gt;
  
  &lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --red:       #D63031;
      --red-dark:  #B02020;
      --red-light: #FFF0F0;
      --ink:       #1E293B; /* Sedikit diubah dari halaman Compress untuk beda template */
      --muted:     #64748B;
      --border:    #E2E8F0;
      --bg:        #F8FAFC;
      --white:     #FFFFFF;
      --success:   #059669;
      --warn:      #D97706;
      --radius:    12px;
    }

    body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--ink); line-height: 1.7; font-size: 16px; }

    /* HEADER */
    header { background: var(--white); border-bottom: 1px solid var(--border); padding: 0 24px; }
    .header-inner { max-width: 1100px; margin: auto; display: flex; align-items: center; height: 60px; gap: 10px; }
    .logo-mark { width: 32px; height: 32px; background: var(--red); border-radius: 8px; display: flex; align-items: center; justify-content: center; }
    .logo-mark svg { width: 18px; height: 18px; fill: #fff; }
    .logo-text { font-family: 'Sora', sans-serif; font-weight: 700; font-size: 18px; color: var(--ink); }
    .logo-text span { color: var(--red); }
    nav { margin-left: auto; display: flex; gap: 24px; }
    nav a { font-size: 14px; color: var(--muted); text-decoration: none; font-weight: 500; }
    nav a:hover, nav a.active { color: var(--red); }

    /* HERO DENGAN DIFERENSIASI VISUAL */
    .hero-split { background: linear-gradient(135deg, #0F172A 0%, #1E1B4B 100%); padding: 64px 24px 56px; text-align: center; color: #fff; border-bottom: 4px solid var(--red); }
    .hero-badge { display: inline-block; background: rgba(255,255,255,.1); color: #E2E8F0; font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; padding: 4px 14px; border-radius: 20px; border: 1px solid rgba(255,255,255,.2); margin-bottom: 18px; }
    .hero-split h1 { font-family: 'Sora', sans-serif; font-size: clamp(28px,5vw,48px); font-weight: 800; line-height: 1.15; margin-bottom: 16px; }
    .hero-split h1 span { color: #FCA5A5; }
    .hero-split p { font-size: 17px; color: #CBD5E1; max-width: 560px; margin: 0 auto 28px; }
    .hero-stats { display: flex; justify-content: center; gap: 32px; flex-wrap: wrap; }
    .stat { text-align: center; }
    .stat-num { font-family: 'Sora', sans-serif; font-size: 26px; font-weight: 800; color: #fff; }
    .stat-label { font-size: 12px; color: #94A3B8; letter-spacing: .04em; }

    /* MAIN LAYOUT */
    .main-wrap { max-width: 1100px; margin: 0 auto; padding: 48px 24px; display: grid; grid-template-columns: 1fr 340px; gap: 32px; align-items: start; }
    @media(max-width:860px){ .main-wrap { grid-template-columns: 1fr; } .sidebar { order: -1; } }

    /* TOOL CARD */
    .tool-card { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); box-shadow: 0 4px 6px -1px rgba(0,0,0,.05); overflow: hidden; }
    .tool-card-header { padding: 20px 24px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; background: #F8FAFC; }
    .tool-card-header h2 { font-family: 'Sora', sans-serif; font-size: 18px; font-weight: 700; }
    .badge-free { margin-left: auto; background: #ECFDF5; color: var(--success); font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 20px; border: 1px solid #A7F3D0; }

    /* Drop Zone */
    .drop-zone { margin: 24px; border: 2px dashed #CBD5E1; border-radius: var(--radius); padding: 44px 24px; text-align: center; cursor: pointer; transition: all .2s; background: var(--white); }
    .drop-zone:hover, .drop-zone.drag-over { border-color: var(--red); background: var(--red-light); }
    .drop-icon { width: 52px; height: 52px; background: var(--red-light); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 14px; }
    .drop-icon svg { width: 26px; height: 26px; }
    .drop-zone h3 { font-size: 16px; font-weight: 600; margin-bottom: 6px; }
    .drop-zone p { font-size: 13px; color: var(--muted); }
    .btn-choose { display: inline-block; margin-top: 16px; background: var(--red); color: #fff; padding: 10px 22px; border-radius: 30px; font-size: 14px; font-weight: 600; cursor: pointer; transition: background .2s; }
    .btn-choose:hover { background: var(--red-dark); }
    #pdfFileInput { display: none; }

    /* File Info */
    #fileInfo { margin: 0 24px 16px; padding: 14px 16px; background: #F0F9FF; border: 1px solid #BAE6FD; border-radius: 8px; font-size: 14px; display: none; align-items: center; gap: 10px; }
    #fileInfo.visible { display: flex; }
    #fileInfo svg { flex-shrink: 0; color: #0284C7; width: 20px; height: 20px; }
    .file-name { font-weight: 600; color: var(--ink); }
    .file-size { color: var(--muted); font-size: 13px; }

    /* Page Preview &amp; Chips (Accessibility Fixed) */
    #pagePreview { margin: 0 24px 16px; display: none; }
    #pagePreview.visible { display: block; }
    .preview-label { font-size: 13px; font-weight: 600; color: var(--muted); margin-bottom: 8px; }
    .page-chips { display: flex; flex-wrap: wrap; gap: 6px; }
    /* Diubah menjadi style untuk button */
    button.page-chip { padding: 4px 12px; border-radius: 20px; font-size: 13px; font-family: 'Inter', sans-serif; font-weight: 600; border: 1px solid var(--border); background: var(--bg); color: var(--muted); cursor: pointer; transition: all .15s; outline: none; }
    button.page-chip:hover { border-color: var(--red); color: var(--red); }
    button.page-chip:focus-visible { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--red); }
    button.page-chip.selected { background: var(--red); border-color: var(--red); color: #fff; }

    /* Mode Toggle */
    .mode-wrap { margin: 0 24px 20px; }
    .mode-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 10px; }
    .mode-tabs { display: flex; gap: 0; border: 1.5px solid var(--border); border-radius: 10px; overflow: hidden; }
    .mode-tab { flex: 1; padding: 10px; background: var(--bg); border: none; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; transition: all .15s; display: flex; align-items: center; justify-content: center; gap: 6px; }
    .mode-tab:not(:last-child) { border-right: 1px solid var(--border); }
    .mode-tab.active { background: var(--red); color: #fff; }
    .mode-tab:focus-visible { outline: 2px solid var(--red); outline-offset: -2px; }

    /* Range Input */
    .range-wrap { margin: 0 24px 20px; }
    .range-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
    .range-hint { font-size: 12px; color: var(--muted); margin-bottom: 10px; }
    .range-input { width: 100%; padding: 10px 14px; border: 1.5px solid var(--border); border-radius: 8px; font-family: inherit; font-size: 14px; color: var(--ink); transition: border-color .2s; outline: none; }
    .range-input:focus { border-color: var(--red); box-shadow: 0 0 0 3px rgba(214,48,49,0.1); }

    /* Split Mode: Every N pages */
    .every-wrap { margin: 0 24px 20px; display: none; }
    .every-wrap.visible { display: block; }
    .every-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
    .every-row { display: flex; align-items: center; gap: 10px; }
    .every-row input[type=number] { width: 80px; padding: 10px 14px; border: 1.5px solid var(--border); border-radius: 8px; font-family: inherit; font-size: 14px; outline: none; }
    .every-row input[type=number]:focus { border-color: var(--red); }
    .every-row span { font-size: 14px; color: var(--muted); }

    /* Actions */
    .actions { padding: 0 24px 24px; display: flex; gap: 10px; }
    .btn-primary { flex: 1; background: var(--red); color: #fff; border: none; border-radius: 30px; padding: 13px 20px; font-size: 15px; font-weight: 600; cursor: pointer; font-family: inherit; transition: background .2s, opacity .2s; display: flex; align-items: center; justify-content: center; gap: 7px; }
    .btn-primary:hover { background: var(--red-dark); }
    .btn-primary:disabled { opacity: .5; cursor: not-allowed; }
    .btn-reset { background: var(--bg); color: var(--muted); border: 1px solid var(--border); border-radius: 30px; padding: 13px 18px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit; transition: background .2s; }
    .btn-reset:hover { background: var(--border); }

    /* Progress */
    #progressWrap { margin: 0 24px 16px; display: none; }
    #progressWrap.visible { display: block; }
    .progress-label { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
    .progress-bar-bg { background: var(--bg); border-radius: 20px; height: 8px; overflow: hidden; border: 1px solid var(--border); }
    .progress-bar-fill { height: 100%; background: linear-gradient(90deg, var(--red), #FF6B6B); border-radius: 20px; width: 0%; transition: width .3s; }

    /* Results */
    #resultCard { margin: 0 24px 24px; display: none; }
    #resultCard.visible { display: block; }
    .result-title { font-size: 14px; font-weight: 600; color: var(--success); margin-bottom: 12px; display: flex; align-items: center; gap: 6px; padding: 12px 16px; background: #ECFDF5; border: 1px solid #A7F3D0; border-radius: 10px; }
    .download-list { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow-y: auto; }
    .download-item { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; gap: 10px; }
    .download-item-info { font-size: 13px; flex: 1; }
    .download-item-name { font-weight: 600; color: var(--ink); }
    .download-item-pages { color: var(--muted); font-size: 12px; }
    .btn-dl-single { display: inline-flex; align-items: center; gap: 5px; background: var(--red); color: #fff; padding: 7px 14px; border-radius: 20px; font-size: 12px; font-weight: 600; text-decoration: none; transition: background .2s; white-space: nowrap; }
    .btn-dl-single:hover { background: var(--red-dark); }

    /* SIDEBAR */
    .sidebar { display: flex; flex-direction: column; gap: 20px; }
    .info-card { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); padding: 20px; }
    .info-card h3 { font-family: 'Sora', sans-serif; font-size: 15px; font-weight: 700; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
    .info-card h3 .ic { width: 30px; height: 30px; border-radius: 8px; background: var(--red-light); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .checklist { list-style: none; display: flex; flex-direction: column; gap: 9px; }
    .checklist li { font-size: 14px; display: flex; align-items: flex-start; gap: 9px; color: var(--muted); }
    .checklist li::before { content: ''; flex-shrink: 0; width: 18px; height: 18px; margin-top: 2px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23059669'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z' clip-rule='evenodd'/%3E%3C/svg%3E") center/cover; }
    .tip-box { background: #FFFBEB; border: 1px solid #FDE68A; border-radius: 8px; padding: 12px 14px; font-size: 13px; color: #92400E; line-height: 1.6; }
    .tip-box strong { color: var(--warn); }
    .example-box { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
    .example-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
    .example-row:last-child { margin-bottom: 0; }
    .ex-code { background: var(--ink); color: #7DD3FC; font-size: 13px; font-family: monospace; padding: 3px 8px; border-radius: 4px; flex-shrink: 0; }
    .ex-desc { font-size: 13px; color: var(--muted); }

    /* CONTENT */
    .content-section { max-width: 1100px; margin: 0 auto; padding: 0 24px 64px; }
    .section-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; margin-bottom: 48px; }
    .content-card { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.02); }
    .content-card .cc-icon { width: 44px; height: 44px; background: #F0F9FF; color: #0284C7; border-radius: 10px; display: flex; align-items: center; justify-content: center; margin-bottom: 14px; }
    .content-card h3 { font-family: 'Sora', sans-serif; font-size: 16px; font-weight: 700; margin-bottom: 8px; }
    .content-card p { font-size: 14px; color: var(--muted); line-height: 1.7; }
    
    /* Prose Section diformat ulang agar tidak identik */
    .prose-section { background: var(--white); border-radius: var(--radius); border: 1px solid var(--border); padding: 36px; margin-bottom: 32px; border-top: 3px solid var(--ink); }
    .prose-section h2 { font-family: 'Sora', sans-serif; font-size: 22px; font-weight: 700; margin-bottom: 14px; color: var(--ink); }
    .prose-section h3 { font-size: 17px; font-weight: 600; margin: 24px 0 10px; color: var(--ink); }
    .prose-section p { font-size: 15px; color: #334155; margin-bottom: 14px; line-height: 1.8; }
    .prose-section ul, .prose-section ol { padding-left: 22px; margin-bottom: 14px; }
    .prose-section li { font-size: 15px; color: #334155; margin-bottom: 8px; line-height: 1.7; }
    
    .steps-list { list-style: none; counter-reset: steps; padding: 0; }
    .steps-list li { counter-increment: steps; padding: 14px 16px 14px 56px; position: relative; border-bottom: 1px dashed var(--border); margin-bottom: 12px; }
    .steps-list li:last-child { border-bottom: none; }
    .steps-list li::before { content: counter(steps); position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 28px; height: 28px; background: var(--ink); color: #fff; border-radius: 8px; font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
    .steps-list li strong { color: var(--ink); }
    
    .faq-list { display: flex; flex-direction: column; gap: 10px; }
    .faq-item { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--bg); }
    .faq-q { width: 100%; background: none; border: none; padding: 16px 20px; font-family: inherit; font-size: 15px; font-weight: 600; color: var(--ink); text-align: left; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
    .faq-q .arrow { width: 20px; height: 20px; flex-shrink: 0; transition: transform .25s; color: var(--muted); }
    .faq-item.open .faq-q .arrow { transform: rotate(180deg); }
    .faq-a { max-height: 0; overflow: hidden; transition: max-height .3s ease, padding .3s; padding: 0 20px; font-size: 14px; color: var(--muted); line-height: 1.7; }
    .faq-item.open .faq-a { max-height: 220px; padding: 0 20px 16px; }

    /* FOOTER */
    footer { background: var(--ink); color: #94A3B8; text-align: center; padding: 40px 24px; font-size: 14px; border-top: 4px solid var(--red); }
    footer a { color: #CBD5E1; text-decoration: none; font-weight: 500; }
    footer a:hover { color: #fff; text-decoration: underline; }
    .footer-links { display: flex; justify-content: center; gap: 24px; margin-bottom: 16px; flex-wrap: wrap; }

    /* Spinner */
    .spinner { width: 18px; height: 18px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .7s linear infinite; display: none; }
    .btn-primary.loading .spinner { display: block; }
    .btn-primary.loading .btn-text { display: none; }
    @keyframes spin { to { transform: rotate(360deg); } }

    @media(max-width:600px){ .hero-stats { gap: 20px; } .actions { flex-direction: column; } .prose-section { padding: 20px; } .mode-tab { font-size: 12px; } }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;header&gt;
  &lt;div class="header-inner"&gt;
    &lt;div class="logo-mark" aria-hidden="true"&gt;
      &lt;svg viewBox="0 0 24 24"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm4 18H6V4h7v5h5v11z"/&gt;&lt;/svg&gt;
    &lt;/div&gt;
    &lt;span class="logo-text"&gt;Kholil&lt;span&gt;Tools&lt;/span&gt;&lt;/span&gt;
    
    &lt;!-- Navigasi Dibatasi Maksimal 3 Link Non-Esensial --&gt;
    &lt;nav aria-label="Menu Utama"&gt;
      &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html"&gt;Compress PDF&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Merge PDF&lt;/a&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;div class="hero-split"&gt;
  &lt;div class="hero-badge"&gt;Alat Ekstraksi Dokumen Aman&lt;/div&gt;
  &lt;h1&gt;Split PDF — &lt;span&gt;Pisahkan Halaman&lt;/span&gt;&lt;br&gt;Sesuai Kebutuhan Anda&lt;/h1&gt;
  &lt;p&gt;Ekstrak halaman tertentu, pisah per rentang, atau bagi per halaman tunggal. Semua proses terjadi di browser Anda — file tidak pernah diunggah ke server.&lt;/p&gt;
  &lt;div class="hero-stats"&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;∞&lt;/div&gt;&lt;div class="stat-label"&gt;Tanpa Batas Halaman&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="stat-num"&gt;0&lt;/div&gt;&lt;div class="stat-label"&gt;Upload ke Server&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;main&gt;
&lt;div class="main-wrap"&gt;
  &lt;div&gt;
    &lt;div class="tool-card"&gt;
      &lt;div class="tool-card-header"&gt;
        &lt;svg width="22" height="22" fill="none" stroke="var(--ink)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;path d="M14 2v6h6"/&gt;&lt;line x1="8" y1="13" x2="16" y2="13"/&gt;&lt;line x1="8" y1="17" x2="13" y2="17"/&gt;&lt;/svg&gt;
        &lt;h2&gt;Pisahkan File PDF&lt;/h2&gt;
        &lt;span class="badge-free"&gt;GRATIS&lt;/span&gt;
      &lt;/div&gt;

      &lt;!-- Drop Zone --&gt;
      &lt;div class="drop-zone" id="dropZone" aria-label="Area Upload PDF"&gt;
        &lt;div class="drop-icon" aria-hidden="true"&gt;
          &lt;svg fill="none" stroke="var(--red)" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/&gt;&lt;/svg&gt;
        &lt;/div&gt;
        &lt;h3&gt;Seret &amp;amp; Lepas File PDF di Sini&lt;/h3&gt;
        &lt;p&gt;atau klik tombol di bawah untuk memilih file&lt;/p&gt;
        &lt;label class="btn-choose"&gt;
          Pilih File PDF
          &lt;input type="file" id="pdfFileInput" accept="application/pdf"&gt;
        &lt;/label&gt;
        &lt;p style="margin-top:10px;font-size:12px;color:var(--muted);"&gt;Maks. ukuran file: 50 MB&lt;/p&gt;
      &lt;/div&gt;

      &lt;!-- File Info --&gt;
      &lt;div id="fileInfo"&gt;
        &lt;svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;path d="M14 2v6h6"/&gt;&lt;/svg&gt;
        &lt;div&gt;
          &lt;div class="file-name" id="fileName"&gt;—&lt;/div&gt;
          &lt;div class="file-size" id="fileSizeLabel"&gt;—&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Page Visual Preview --&gt;
      &lt;div id="pagePreview"&gt;
        &lt;div style="margin: 0 24px 10px;"&gt;
          &lt;div class="preview-label" id="previewInst"&gt;Pratinjau Halaman — Klik untuk memilih/batalkan&lt;/div&gt;
          &lt;div class="page-chips" id="pageChips" aria-labelledby="previewInst"&gt;&lt;/div&gt;
          &lt;p style="font-size:12px;color:var(--muted);margin-top:8px;" id="selectionHint" aria-live="polite"&gt;Belum ada halaman dipilih&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Mode --&gt;
      &lt;div class="mode-wrap"&gt;
        &lt;div class="mode-label"&gt;Mode Pemisahan&lt;/div&gt;
        &lt;div class="mode-tabs" role="tablist"&gt;
          &lt;button class="mode-tab active" data-mode="range" role="tab" aria-selected="true" type="button"&gt;
            &lt;svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9 18H5a2 2 0 01-2-2V6a2 2 0 012-2h4"/&gt;&lt;path d="M15 18h4a2 2 0 002-2V6a2 2 0 00-2-2h-4"/&gt;&lt;line x1="12" y1="2" x2="12" y2="22"/&gt;&lt;/svg&gt;
            Rentang / Pilihan
          &lt;/button&gt;
          &lt;button class="mode-tab" data-mode="every" role="tab" aria-selected="false" type="button"&gt;
            &lt;svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;rect x="3" y="3" width="7" height="7"/&gt;&lt;rect x="14" y="3" width="7" height="7"/&gt;&lt;rect x="3" y="14" width="7" height="7"/&gt;&lt;rect x="14" y="14" width="7" height="7"/&gt;&lt;/svg&gt;
            Per N Halaman
          &lt;/button&gt;
          &lt;button class="mode-tab" data-mode="single" role="tab" aria-selected="false" type="button"&gt;
            &lt;svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z"/&gt;&lt;/svg&gt;
            Pisah Semua
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Range Input (mode: range) --&gt;
      &lt;div class="range-wrap" id="rangeWrap"&gt;
        &lt;label for="pageRange" class="range-label" style="display:block;"&gt;Halaman yang Ingin Dipisahkan&lt;/label&gt;
        &lt;div class="range-hint"&gt;Contoh: &lt;code&gt;1-3&lt;/code&gt; (rentang), &lt;code&gt;2,4,6&lt;/code&gt; (pilihan), atau &lt;code&gt;1-3,5,7-9&lt;/code&gt; (kombinasi)&lt;/div&gt;
        &lt;input type="text" class="range-input" id="pageRange" placeholder="Masukkan halaman, mis: 1-5,8,10-12"&gt;
      &lt;/div&gt;

      &lt;!-- Every N pages (mode: every) --&gt;
      &lt;div class="every-wrap" id="everyWrap"&gt;
        &lt;div class="every-label"&gt;Pisah Setiap&lt;/div&gt;
        &lt;div class="every-row"&gt;
          &lt;label for="everyN" class="sr-only" style="display:none;"&gt;Jumlah halaman per bagian&lt;/label&gt;
          &lt;input type="number" id="everyN" value="1" min="1" max="999" aria-label="Jumlah halaman yang akan dipisah per bagian"&gt;
          &lt;span&gt;halaman — akan menghasilkan beberapa file PDF terpisah&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Progress --&gt;
      &lt;div id="progressWrap"&gt;
        &lt;div class="progress-label" id="progressLabel" aria-live="assertive"&gt;Memproses...&lt;/div&gt;
        &lt;div class="progress-bar-bg"&gt;&lt;div class="progress-bar-fill" id="progressFill"&gt;&lt;/div&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Result --&gt;
      &lt;div id="resultCard" aria-live="polite"&gt;
        &lt;div class="result-title"&gt;
          &lt;svg width="16" height="16" fill="none" stroke="var(--success)" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M22 11.08V12a10 10 0 11-5.93-9.14"/&gt;&lt;path d="M22 4L12 14.01l-3-3"/&gt;&lt;/svg&gt;
          &lt;span id="resultTitle"&gt;Berhasil memisahkan PDF!&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="download-list" id="downloadList"&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;!-- Actions --&gt;
      &lt;div class="actions"&gt;
        &lt;button class="btn-primary" id="splitBtn" disabled type="button"&gt;
          &lt;span class="btn-text" style="display:flex;align-items:center;gap:7px;"&gt;
            &lt;svg width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9 18H5a2 2 0 01-2-2V6a2 2 0 012-2h4"/&gt;&lt;path d="M15 18h4a2 2 0 002-2V6a2 2 0 00-2-2h-4"/&gt;&lt;line x1="12" y1="2" x2="12" y2="22"/&gt;&lt;/svg&gt;
            Mulai Split PDF
          &lt;/span&gt;
          &lt;div class="spinner" aria-hidden="true"&gt;&lt;/div&gt;
        &lt;/button&gt;
        &lt;button class="btn-reset" id="resetBtn" type="button"&gt;Reset&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- SIDEBAR --&gt;
  &lt;aside class="sidebar"&gt;
    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Keamanan &amp;amp; Privasi
      &lt;/h3&gt;
      &lt;ul class="checklist"&gt;
        &lt;li&gt;File diproses 100% di browser Anda&lt;/li&gt;
        &lt;li&gt;Tidak ada data yang dikirim ke server&lt;/li&gt;
        &lt;li&gt;File otomatis hilang saat tab ditutup&lt;/li&gt;
        &lt;li&gt;Aman untuk dokumen rahasia &amp;amp; sensitif&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;

    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M8 12h8M12 8v8"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Format Input Halaman
      &lt;/h3&gt;
      &lt;div class="example-box"&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;1-5&lt;/span&gt;&lt;span class="ex-desc"&gt;Halaman 1 sampai 5&lt;/span&gt;&lt;/div&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;2,4,6&lt;/span&gt;&lt;span class="ex-desc"&gt;Halaman 2, 4, dan 6&lt;/span&gt;&lt;/div&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;1-3,7&lt;/span&gt;&lt;span class="ex-desc"&gt;Hal. 1-3 dan halaman 7&lt;/span&gt;&lt;/div&gt;
        &lt;div class="example-row"&gt;&lt;span class="ex-code"&gt;5-&lt;/span&gt;&lt;span class="ex-desc"&gt;Halaman 5 hingga akhir&lt;/span&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="info-card"&gt;
      &lt;h3&gt;
        &lt;span class="ic"&gt;&lt;svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M12 6v6l4 2"/&gt;&lt;/svg&gt;&lt;/span&gt;
        Tips Aksesibilitas
      &lt;/h3&gt;
      &lt;div class="tip-box"&gt;
        &lt;strong&gt;&#128161; Dukungan Keyboard:&lt;/strong&gt; Mode &lt;em&gt;Pratinjau Halaman&lt;/em&gt; kini mendukung navigasi menggunakan tombol `Tab` dan pemilihan menggunakan `Space` atau `Enter`.
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/aside&gt;
&lt;/div&gt;

&lt;!-- CONTENT --&gt;
&lt;div class="content-section"&gt;

  &lt;div class="section-grid"&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M9 18H5a2 2 0 01-2-2V6a2 2 0 012-2h4"/&gt;&lt;path d="M15 18h4a2 2 0 002-2V6a2 2 0 00-2-2h-4"/&gt;&lt;line x1="12" y1="2" x2="12" y2="22"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Tiga Mode Pemisahan Aktif&lt;/h3&gt;
      &lt;p&gt;Pilih rentang halaman spesifik, belah setiap N halaman berulang, atau cerai-beraikan seluruh dokumen menjadi halaman tunggal secara presisi.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;rect x="3" y="3" width="7" height="7"/&gt;&lt;rect x="14" y="3" width="7" height="7"/&gt;&lt;rect x="3" y="14" width="7" height="7"/&gt;&lt;rect x="14" y="14" width="7" height="7"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Pratinjau UI Interaktif&lt;/h3&gt;
      &lt;p&gt;Arsitektur front-end kami merender pratinjau halaman visual sebagai &lt;em&gt;chips&lt;/em&gt;, meminimalisir kesalahan pengetikan nomor secara manual.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Protokol Zero-Upload&lt;/h3&gt;
      &lt;p&gt;Semua operasi bitwise PDF dilakukan melalui CPU &amp; RAM lokal perangkat Anda. Integritas data sensitif perusahaan dijamin utuh.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="content-card"&gt;
      &lt;div class="cc-icon"&gt;&lt;svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/&gt;&lt;/svg&gt;&lt;/div&gt;
      &lt;h3&gt;Generasi File Instan&lt;/h3&gt;
      &lt;p&gt;Kapasitas mengunduh objek partisi secara individual di titik akhir, tanpa perlu menunggu layanan kompresi arsip (.zip) pihak ketiga.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Panduan Teknis: Ekstraksi Dokumen PDF Tersegmentasi&lt;/h2&gt;
    &lt;p&gt;
      Dalam administrasi digital di Indonesia (seperti pemisahan lampiran ijazah dari transkrip nilai untuk pendaftaran CPNS, atau memisahkan halaman kontrak spesifik untuk dibubuhi e-Meterai), mengekstrak halaman PDF tanpa merusak kualitas aslinya adalah sebuah keharusan.
    &lt;/p&gt;
    &lt;p&gt;
      Sistem &lt;em&gt;Split PDF&lt;/em&gt; kami mengungguli alat konvensional dengan fitur &lt;strong&gt;Pratinjau Visual (Page Chips)&lt;/strong&gt;. Alih-alih menebak nomor halaman secara buta, algoritma &lt;code&gt;pdf-lib&lt;/code&gt; kami membaca arsitektur dokumen Anda secara lokal di RAM browser, lalu memetakan setiap halaman menjadi elemen interaktif yang bisa Anda klik.
    &lt;/p&gt;
    
    &lt;h3&gt;Studi Kasus: Memisahkan Laporan Keuangan&lt;/h3&gt;
    &lt;p&gt;Jika Anda memiliki &lt;em&gt;Annual Report&lt;/em&gt; setebal 150 halaman dan hanya membutuhkan neraca keuangan di halaman &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html" style="color:var(--red);"&gt;45-50&lt;/a&gt;, Anda cukup menggunakan &lt;strong&gt;Mode Rentang&lt;/strong&gt;, mengetikkan &lt;code&gt;45-50&lt;/code&gt;, dan sistem akan mengisolasi objek data tersebut menjadi file baru yang jauh lebih ringan dalam hitungan milidetik.&lt;/p&gt;
  &lt;/div&gt;

  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Langkah Konfigurasi Operasi Pemisahan&lt;/h2&gt;
    &lt;ol class="steps-list"&gt;
      &lt;li&gt;&lt;strong&gt;Inisiasi File&lt;/strong&gt; — Tempatkan dokumen PDF ke dalam zona area yang disediakan. Sistem akan membaca metadata file tanpa memicu koneksi internet keluar.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Identifikasi Interaktif&lt;/strong&gt; — Visualisasi chip halaman akan tampil. Anda bisa menekan (klik/tap) elemen tersebut untuk memilih atau membatalkan pilihan ekstraksi.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Pilih Algoritma Mode&lt;/strong&gt; — &lt;em&gt;Mode Rentang&lt;/em&gt; cocok untuk halaman spesifik. Gunakan &lt;em&gt;Per N Halaman&lt;/em&gt; untuk pembagian volume merata, atau &lt;em&gt;Pisah Semua&lt;/em&gt; untuk partisi ekstrem.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Validasi Input Manual&lt;/strong&gt; — Jika Anda lebih memilih pengetikan, pastikan mematuhi tata bahasa sistem (misal: &lt;code&gt;2,5,7-9&lt;/code&gt;).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Eksekusi Render&lt;/strong&gt; — Tekan tombol pemrosesan. *Browser* akan melakukan dekode biner sesuai instruksi Anda.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Distribusi Hasil&lt;/strong&gt; — Modul *download* otomatis tersaji untuk menyimpan fragmen dokumen.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/div&gt;

  &lt;div class="prose-section"&gt;
    &lt;h2&gt;Troubleshooting &amp; Tanya Jawab Teknis&lt;/h2&gt;
    &lt;div class="faq-list"&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Kenapa hasil file yang dipisah (Split) ukurannya masih sangat besar?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Jika dokumen PDF sumber Anda dibuat dari hasil &lt;em&gt;scan&lt;/em&gt; beresolusi sangat tinggi, mengekstrak satu halaman pun akan membawa metadata dan lapisan gambar yang berat. Untuk mengatasinya, Anda disarankan menjalankan hasil pemisahan ini ke fitur &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html" style="color:var(--red);"&gt;Compress PDF&lt;/a&gt; kami terlebih dahulu.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Apakah pemisahan halaman merusak *digital signature* (tanda tangan digital) dokumen?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Ya. Memisahkan atau memodifikasi dokumen PDF yang sudah memiliki sertifikat digital terenkripsi (seperti sertifikat BSSN atau tanda tangan digital resmi) akan merusak validitas &lt;em&gt;hash&lt;/em&gt; dokumen tersebut. Lakukan pemisahan &lt;strong&gt;sebelum&lt;/strong&gt; dokumen ditandatangani secara digital.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Bisakah saya menyatukan kembali file yang sudah dipisah?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Tentu saja. Semua objek PDF hasil partisi kami mengadopsi standar &lt;em&gt;PDF/A&lt;/em&gt; murni yang kompatibel untuk direkonstruksi. Anda dapat menyatukan mereka kembali dengan mudah menggunakan utilitas &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html" style="color:var(--red);"&gt;Merge PDF&lt;/a&gt; kami.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button class="faq-q" aria-expanded="false" type="button"&gt;
          Apakah PDF yang dilindungi kata sandi dapat diekstrak halamannya?
          &lt;svg class="arrow" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"&gt;&lt;path d="M6 9l6 6 6-6"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
        &lt;div class="faq-a"&gt;Algoritma *Client-Side* kami secara sengaja dikunci untuk menolak operasi pemisahan pada dokumen yang memiliki level enkripsi DRM aktif. Anda wajib menonaktifkan gembok keamanan tersebut sebelum mengunggah.&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- Trust Box / E-E-A-T Signal --&gt;
  &lt;div class="prose-section" style="background: #F0F9FF; border: 1px solid #BAE6FD; display: flex; gap: 20px; align-items: center;"&gt;
    &lt;div style="flex-shrink: 0;"&gt;
      &lt;img src="https://ui-avatars.com/api/?name=Kholil&amp;background=0D8ABC&amp;color=fff" alt="Kholil - Pengembang Alat" style="border-radius: 50%; width: 64px; height: 64px;"&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;h3 style="margin: 0 0 5px; font-size: 16px;"&gt;Direkayasa oleh &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="color: #0284C7; text-decoration: none;"&gt;Kholil&lt;/a&gt;&lt;/h3&gt;
      &lt;p style="margin: 0; font-size: 14px; color: var(--muted);"&gt;Semua skrip manipulasi PDF di halaman ini dieksekusi secara &lt;em&gt;Client-Side&lt;/em&gt; demi menjamin privasi absolut. Data Anda tetap berada di perangkat Anda. Menemukan kendala fungsi alat? &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="color: var(--ink); font-weight: 600;"&gt;Hubungi Saya&lt;/a&gt;.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;
&lt;/main&gt;

&lt;footer&gt;
  &lt;div class="footer-links"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html"&gt;Compress PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/2026/04/merge-pdf.html"&gt;Merge PDF&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;Tentang Kami&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Kontak&lt;/a&gt;
  &lt;/div&gt;
  &lt;p&gt;© 2026 Kholil Tools — Infrastruktur Produktivitas PDF untuk Indonesia.&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
  const dropZone    = document.getElementById('dropZone');
  const fileInput   = document.getElementById('pdfFileInput');
  const fileInfoEl  = document.getElementById('fileInfo');
  const fileNameEl  = document.getElementById('fileName');
  const fileSizeEl  = document.getElementById('fileSizeLabel');
  const pagePreview = document.getElementById('pagePreview');
  const pageChips   = document.getElementById('pageChips');
  const selHint     = document.getElementById('selectionHint');
  const rangeWrap   = document.getElementById('rangeWrap');
  const everyWrap   = document.getElementById('everyWrap');
  const pageRangeIn = document.getElementById('pageRange');
  const everyNIn    = document.getElementById('everyN');
  const splitBtn    = document.getElementById('splitBtn');
  const resetBtn    = document.getElementById('resetBtn');
  const progressWrap = document.getElementById('progressWrap');
  const progressLabel = document.getElementById('progressLabel');
  const progressFill  = document.getElementById('progressFill');
  const resultCard  = document.getElementById('resultCard');
  const resultTitle = document.getElementById('resultTitle');
  const downloadList = document.getElementById('downloadList');

  let selectedFile = null;
  let totalPages   = 0;
  let selectedMode = 'range';
  let chipSelected = new Set();

  function fmtSize(b) {
    if (b &lt; 1024) return b + ' B';
    if (b &lt; 1048576) return (b/1024).toFixed(1) + ' KB';
    return (b/1048576).toFixed(2) + ' MB';
  }

  // Drag &amp; drop
  dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
  dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'));
  dropZone.addEventListener('drop', e =&gt; { e.preventDefault(); dropZone.classList.remove('drag-over'); const f = e.dataTransfer.files[0]; if (f &amp;&amp; f.type === 'application/pdf') loadFile(f); });
  fileInput.addEventListener('change', e =&gt; { if (e.target.files[0]) loadFile(e.target.files[0]); });

  async function loadFile(f) {
    if (f.size &gt; 50 * 1024 * 1024) { alert('Ukuran file melebihi batas 50 MB.'); return; }
    selectedFile = f;
    fileNameEl.textContent = f.name;

    try {
      const { PDFDocument } = PDFLib;
      const buf = await f.arrayBuffer();
      const pdf = await PDFDocument.load(buf);
      totalPages = pdf.getPageCount();
    } catch(e) { totalPages = 0; }

    fileSizeEl.textContent = fmtSize(f.size) + (totalPages ? ` · ${totalPages} halaman` : '');
    fileInfoEl.classList.add('visible');
    resultCard.classList.remove('visible');
    progressWrap.classList.remove('visible');
    splitBtn.disabled = false;
    chipSelected.clear();
    buildChips();
    pagePreview.classList.add('visible');
  }

  function buildChips() {
    pageChips.innerHTML = '';
    for (let i = 1; i &lt;= Math.min(totalPages, 100); i++) {
      const c = document.createElement('button'); // Diganti button demi aksesibilitas
      c.type = 'button';
      c.className = 'page-chip';
      c.textContent = i;
      c.dataset.page = i;
      c.setAttribute('aria-pressed', 'false');
      
      c.addEventListener('click', () =&gt; {
        if (chipSelected.has(i)) { 
          chipSelected.delete(i); 
          c.classList.remove('selected'); 
          c.setAttribute('aria-pressed', 'false');
        }
        else { 
          chipSelected.add(i); 
          c.classList.add('selected'); 
          c.setAttribute('aria-pressed', 'true');
        }
        syncChipsToInput();
        updateSelHint();
      });
      pageChips.appendChild(c);
    }
    if (totalPages &gt; 100) {
      const more = document.createElement('button');
      more.type = 'button';
      more.className = 'page-chip';
      more.style.cursor = 'default';
      more.textContent = `+${totalPages - 100} lagi`;
      more.setAttribute('disabled', 'true');
      pageChips.appendChild(more);
    }
  }

  function syncChipsToInput() {
    if (chipSelected.size === 0) { pageRangeIn.value = ''; return; }
    const sorted = Array.from(chipSelected).sort((a,b) =&gt; a-b);
    const ranges = [];
    let start = sorted[0], end = sorted[0];
    for (let i = 1; i &lt; sorted.length; i++) {
      if (sorted[i] === end + 1) { end = sorted[i]; }
      else { ranges.push(start === end ? `${start}` : `${start}-${end}`); start = end = sorted[i]; }
    }
    ranges.push(start === end ? `${start}` : `${start}-${end}`);
    pageRangeIn.value = ranges.join(',');
  }

  function updateSelHint() {
    if (chipSelected.size === 0) { selHint.textContent = 'Belum ada halaman dipilih'; }
    else { selHint.textContent = `${chipSelected.size} halaman dipilih: ${Array.from(chipSelected).sort((a,b)=&gt;a-b).join(', ')}`; }
  }

  // Mode tabs (ARIA compliance fix)
  document.querySelectorAll('.mode-tab').forEach(tab =&gt; {
    tab.addEventListener('click', () =&gt; {
      document.querySelectorAll('.mode-tab').forEach(t =&gt; {
        t.classList.remove('active');
        t.setAttribute('aria-selected', 'false');
      });
      tab.classList.add('active');
      tab.setAttribute('aria-selected', 'true');
      selectedMode = tab.dataset.mode;
      rangeWrap.style.display = selectedMode === 'range' ? 'block' : 'none';
      everyWrap.classList.toggle('visible', selectedMode === 'every');
    });
  });

  function parseRange(str, max) {
    const pages = [];
    str.split(',').forEach(part =&gt; {
      part = part.trim();
      if (part.includes('-')) {
        let [s, e] = part.split('-').map(v =&gt; v.trim());
        const start = parseInt(s);
        const end   = e === '' ? max : parseInt(e);
        if (!isNaN(start) &amp;&amp; !isNaN(end)) for (let i = start; i &lt;= end; i++) pages.push(i-1);
      } else {
        const n = parseInt(part);
        if (!isNaN(n)) pages.push(n-1);
      }
    });
    return [...new Set(pages)].filter(p =&gt; p &gt;= 0 &amp;&amp; p &lt; max);
  }

  resetBtn.addEventListener('click', () =&gt; {
    selectedFile = null; totalPages = 0; chipSelected.clear();
    fileInput.value = '';
    fileInfoEl.classList.remove('visible');
    pagePreview.classList.remove('visible');
    resultCard.classList.remove('visible');
    progressWrap.classList.remove('visible');
    splitBtn.disabled = true;
    progressFill.style.width = '0%';
    pageRangeIn.value = '';
    document.querySelectorAll('.page-chip').forEach(c =&gt; c.setAttribute('aria-pressed', 'false'));
  });

  splitBtn.addEventListener('click', async () =&gt; {
    if (typeof PDFLib === 'undefined') {
      alert('Sistem inti PDF sedang memuat, harap tunggu sejenak dan coba lagi.');
      return;
    }
    
    if (!selectedFile) return;
    splitBtn.classList.add('loading');
    splitBtn.disabled = true;
    progressWrap.classList.add('visible');
    resultCard.classList.remove('visible');
    downloadList.innerHTML = '';

    const steps = [
      {pct: 20, msg: 'Membaca struktur PDF…'},
      {pct: 50, msg: 'Menyalin halaman yang dipilih…'},
      {pct: 80, msg: 'Membuat file hasil…'},
    ];
    let si = 0;
    const timer = setInterval(() =&gt; { if (si &lt; steps.length) { progressLabel.textContent = steps[si].msg; progressFill.style.width = steps[si].pct + '%'; si++; } }, 350);

    try {
      const { PDFDocument } = PDFLib;
      const buffer = await selectedFile.arrayBuffer();
      const srcPdf = await PDFDocument.load(buffer);
      const max = srcPdf.getPageCount();

      let segments = []; 

      if (selectedMode === 'single') {
        for (let i = 0; i &lt; max; i++) segments.push({ label: `Halaman ${i+1}`, pages: [i] });
      } else if (selectedMode === 'every') {
        const n = Math.max(1, parseInt(everyNIn.value) || 1);
        for (let i = 0; i &lt; max; i += n) {
          const chunk = [];
          for (let j = i; j &lt; Math.min(i+n, max); j++) chunk.push(j);
          const from = i+1, to = Math.min(i+n, max);
          segments.push({ label: from === to ? `Halaman ${from}` : `Halaman ${from}–${to}`, pages: chunk });
        }
      } else {
        const rangeStr = pageRangeIn.value.trim();
        if (!rangeStr) { clearInterval(timer); alert('Masukkan rentang halaman terlebih dahulu.'); splitBtn.classList.remove('loading'); splitBtn.disabled = false; progressWrap.classList.remove('visible'); return; }
        const pages = parseRange(rangeStr, max);
        if (!pages.length) { clearInterval(timer); alert('Tidak ada halaman valid yang ditemukan. Pastikan nomor halaman sesuai dengan dokumen.'); splitBtn.classList.remove('loading'); splitBtn.disabled = false; progressWrap.classList.remove('visible'); return; }
        segments.push({ label: `Halaman ${rangeStr}`, pages });
      }

      if (segments.length &gt; 50) segments = segments.slice(0, 50);

      const results = [];
      for (const seg of segments) {
        const newPdf = await PDFDocument.create();
        const copied = await newPdf.copyPages(srcPdf, seg.pages);
        copied.forEach(p =&gt; newPdf.addPage(p));
        const bytes = await newPdf.save();
        const blob  = new Blob([bytes], { type: 'application/pdf' });
        const url   = URL.createObjectURL(blob);
        results.push({ label: seg.label, url, size: bytes.byteLength });
      }

      clearInterval(timer);
      progressFill.style.width = '100%';
      progressLabel.textContent = 'Selesai!';

      results.forEach((r, idx) =&gt; {
        const item = document.createElement('div');
        item.className = 'download-item';
        item.innerHTML = `
          &lt;div class="download-item-info"&gt;
            &lt;div class="download-item-name"&gt;File ${idx+1} — ${r.label}&lt;/div&gt;
            &lt;div class="download-item-pages"&gt;${fmtSize(r.size)}&lt;/div&gt;
          &lt;/div&gt;
          &lt;a class="btn-dl-single" href="${r.url}" download="split_${idx+1}.pdf"&gt;
            &lt;svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"&gt;&lt;path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/&gt;&lt;/svg&gt;
            Download
          &lt;/a&gt;`;
        downloadList.appendChild(item);
      });

      resultTitle.textContent = `Berhasil membuat ${results.length} file PDF!`;

      setTimeout(() =&gt; {
        resultCard.classList.add('visible');
        progressWrap.classList.remove('visible');
        splitBtn.classList.remove('loading');
        splitBtn.disabled = false;
      }, 400);

    } catch (err) {
      clearInterval(timer);
      alert('Gagal memproses file. Pastikan file bukan PDF terenkripsi.\n\nDetail: ' + err.message);
      splitBtn.classList.remove('loading');
      splitBtn.disabled = false;
      progressWrap.classList.remove('visible');
    }
  });

  // FAQ Accordion (ARIA compliance)
  document.querySelectorAll('.faq-q').forEach(btn =&gt; {
    btn.addEventListener('click', () =&gt; {
      const item = btn.closest('.faq-item');
      const isOpen = item.classList.contains('open');
      
      document.querySelectorAll('.faq-item').forEach(i =&gt; {
        i.classList.remove('open');
        i.querySelector('.faq-q').setAttribute('aria-expanded', 'false');
      });
      
      if (!isOpen) {
        item.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
      }
    });
  });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/split-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3643028523075417648.post-5209187968330203593</guid><pubDate>Thu, 09 Apr 2026 21:25:00 +0000</pubDate><atom:updated>2026-07-24T06:41:36.130-07:00</atom:updated><title>Merge Pdf</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;Merge PDF Online Gratis – Gabungkan PDF Tanpa Upload | Kholil Tools&lt;/title&gt;
  &lt;meta name="description" content="Gabungkan beberapa file PDF menjadi satu dokumen secara gratis, cepat, dan aman. Proses langsung di browser – file Anda tidak pernah dikirim ke server." /&gt;
  &lt;meta name="robots" content="index, follow" /&gt;
  &lt;link rel="canonical" href="https://www.kholil.biz.id/2026/04/merge-pdf.html" /&gt;

  &lt;!-- Open Graph --&gt;
  &lt;meta property="og:title" content="Merge PDF Online Gratis | Kholil Tools" /&gt;
  &lt;meta property="og:description" content="Gabungkan PDF langsung di browser. 100% gratis, tanpa login, tanpa upload ke server." /&gt;
  &lt;meta property="og:type" content="website" /&gt;

  &lt;link rel="preconnect" href="https://fonts.googleapis.com" /&gt;
  &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /&gt;
  &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Sora:wght@700;800&amp;display=swap" rel="stylesheet" /&gt;
  
  &lt;script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js" defer&gt;&lt;/script&gt;

  &lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --red:      #E53935;
      --red-dark: #B71C1C;
      --red-soft: #FFEBEE;
      --ink:      #1A1A2E;
      --ink-2:    #3D3D5C;
      --ink-3:    #6B6B8A;
      --bg:       #F7F8FC;
      --white:    #FFFFFF;
      --border:   #E2E4EF;
      --shadow:   0 4px 24px rgba(26,26,46,.08);
      --radius:   14px;
    }

    body {
      font-family: 'Inter', sans-serif;
      background: var(--bg);
      color: var(--ink);
      line-height: 1.65;
    }

    /* ── HEADER ── */
    header {
      background: var(--white);
      border-bottom: 1px solid var(--border);
      padding: 0 24px;
      position: sticky;
      top: 0;
      z-index: 100;
    }
    .header-inner {
      max-width: 960px;
      margin: auto;
      height: 56px;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .logo {
      font-family: 'Sora', sans-serif;
      font-size: 1.1rem;
      color: var(--red);
      text-decoration: none;
      font-weight: 800;
      letter-spacing: -.5px;
    }
    nav a {
      color: var(--ink-2);
      text-decoration: none;
      font-size: .875rem;
      font-weight: 500;
      margin-left: 20px;
      transition: color .2s;
    }
    nav a:hover { color: var(--red); }

    /* ── HERO ── */
    .hero {
      background: linear-gradient(135deg, #fff 0%, var(--red-soft) 100%);
      padding: 56px 24px 40px;
      text-align: center;
      border-bottom: 1px solid var(--border);
    }
    .hero-badge {
      display: inline-block;
      background: var(--red-soft);
      color: var(--red-dark);
      font-size: .75rem;
      font-weight: 700;
      letter-spacing: .06em;
      text-transform: uppercase;
      padding: 4px 14px;
      border-radius: 99px;
      margin-bottom: 14px;
    }
    .hero h1 {
      font-family: 'Sora', sans-serif;
      font-size: clamp(1.8rem, 4vw, 2.6rem);
      font-weight: 800;
      color: var(--ink);
      line-height: 1.2;
      margin-bottom: 14px;
    }
    .hero h1 span { color: var(--red); }
    .hero p {
      max-width: 560px;
      margin: 0 auto 28px;
      color: var(--ink-2);
      font-size: 1rem;
    }
    .trust-row {
      display: flex;
      justify-content: center;
      gap: 24px;
      flex-wrap: wrap;
    }
    .trust-item {
      display: flex;
      align-items: center;
      gap: 7px;
      font-size: .82rem;
      color: var(--ink-3);
      font-weight: 500;
    }
    .trust-item svg { flex-shrink: 0; }

    /* ── MAIN LAYOUT ── */
    .page-wrap {
      max-width: 960px;
      margin: 0 auto;
      padding: 40px 20px 60px;
      display: grid;
      grid-template-columns: 1fr 280px;
      gap: 32px;
      align-items: start;
    }
    @media (max-width: 720px) {
      .page-wrap { grid-template-columns: 1fr; }
      .sidebar { order: -1; }
    }

    /* ── TOOL CARD ── */
    .tool-card {
      background: var(--white);
      border-radius: var(--radius);
      box-shadow: var(--shadow);
      padding: 28px;
      border: 1px solid var(--border);
    }
    .tool-card h2 {
      font-family: 'Sora', sans-serif;
      font-size: 1.1rem;
      font-weight: 700;
      margin-bottom: 18px;
      color: var(--ink);
    }

    /* Drop zone */
    .drop-zone {
      border: 2px dashed var(--border);
      border-radius: var(--radius);
      padding: 36px 20px;
      text-align: center;
      cursor: pointer;
      transition: border-color .25s, background .25s;
      background: var(--bg);
    }
    .drop-zone:hover, .drop-zone.drag-over {
      border-color: var(--red);
      background: var(--red-soft);
    }
    .drop-icon {
      width: 48px;
      height: 48px;
      margin: 0 auto 12px;
      color: var(--red);
    }
    .drop-zone p {
      color: var(--ink-2);
      font-size: .9rem;
      margin-bottom: 14px;
    }
    .btn-pick {
      display: inline-block;
      background: var(--red);
      color: #fff;
      padding: 10px 24px;
      border-radius: 99px;
      font-size: .875rem;
      font-weight: 600;
      cursor: pointer;
      transition: background .2s;
    }
    .btn-pick:hover { background: var(--red-dark); }
    #file-input { display: none; }

    /* File list &amp; accessibility buttons */
    #file-list {
      margin-top: 20px;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .file-row {
      display: flex;
      align-items: center;
      gap: 10px;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 10px 14px;
      cursor: grab;
      user-select: none;
      transition: box-shadow .15s;
    }
    .file-row.dragging { opacity: .5; }
    .file-row:hover { box-shadow: 0 2px 10px rgba(229,57,53,.12); }
    .file-icon { color: var(--red); flex-shrink: 0; }
    .file-name {
      flex: 1;
      font-size: .875rem;
      font-weight: 500;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      color: var(--ink);
    }
    .file-size { font-size: .75rem; color: var(--ink-3); flex-shrink: 0; }
    
    .btn-up, .btn-down, .file-del {
      background: none;
      border: 1px solid var(--border);
      cursor: pointer;
      color: var(--ink-3);
      padding: 4px 8px;
      border-radius: 6px;
      line-height: 1;
      transition: all .15s;
    }
    .btn-up:hover:not(:disabled), .btn-down:hover:not(:disabled) { border-color: var(--ink-2); color: var(--ink); }
    .btn-up:disabled, .btn-down:disabled { opacity: 0.3; cursor: not-allowed; }
    
    .file-del { border: none; padding: 4px; }
    .file-del:hover { color: var(--red); }
    
    .drag-hint {
      font-size: .75rem;
      color: var(--ink-3);
      text-align: center;
      margin-top: 8px;
    }

    /* Action row */
    .action-row {
      display: flex;
      gap: 12px;
      margin-top: 20px;
      flex-wrap: wrap;
    }
    .btn-merge {
      flex: 1;
      padding: 13px 24px;
      background: var(--red);
      color: #fff;
      border: none;
      border-radius: 99px;
      font-size: .95rem;
      font-weight: 700;
      cursor: pointer;
      transition: background .2s, transform .15s;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
    }
    .btn-merge:hover:not(:disabled) { background: var(--red-dark); transform: translateY(-1px); }
    .btn-merge:disabled { background: #ccc; cursor: not-allowed; }
    .btn-reset {
      padding: 13px 18px;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: 99px;
      font-size: .875rem;
      font-weight: 600;
      cursor: pointer;
      color: var(--ink-2);
      transition: border-color .2s;
    }
    .btn-reset:hover { border-color: var(--red); color: var(--red); }

    /* Progress */
    .progress-wrap {
      margin-top: 16px;
      display: none;
    }
    .progress-bar-bg {
      background: var(--border);
      border-radius: 99px;
      height: 6px;
      overflow: hidden;
    }
    .progress-bar-fill {
      height: 100%;
      background: var(--red);
      border-radius: 99px;
      width: 0%;
      transition: width .3s ease;
    }
    .progress-label {
      font-size: .78rem;
      color: var(--ink-3);
      margin-top: 6px;
    }

    /* Result */
    .result-box {
      display: none;
      margin-top: 20px;
      background: #F1F8E9;
      border: 1px solid #AED581;
      border-radius: var(--radius);
      align-items: center;
      gap: 14px;
      padding: 18px 20px;
    }
    .result-box.show { display: flex; }
    .result-info { flex: 1; }
    .result-info strong { font-size: .9rem; color: #33691E; display: block; margin-bottom: 2px; }
    .result-info span { font-size: .78rem; color: #558B2F; }
    .btn-download {
      padding: 10px 22px;
      background: #388E3C;
      color: #fff;
      border-radius: 99px;
      text-decoration: none;
      font-size: .875rem;
      font-weight: 700;
      white-space: nowrap;
      transition: background .2s;
    }
    .btn-download:hover { background: #2E7D32; }

    /* ── SIDEBAR ── */
    .sidebar { display: flex; flex-direction: column; gap: 20px; }
    .side-card {
      background: var(--white);
      border-radius: var(--radius);
      box-shadow: var(--shadow);
      padding: 20px;
      border: 1px solid var(--border);
    }
    .side-card h3 {
      font-family: 'Sora', sans-serif;
      font-size: .95rem;
      font-weight: 700;
      color: var(--ink);
      margin-bottom: 12px;
    }
    .step-list { list-style: none; counter-reset: steps; }
    .step-list li {
      counter-increment: steps;
      display: flex;
      align-items: flex-start;
      gap: 12px;
      margin-bottom: 12px;
      font-size: .875rem;
      color: var(--ink-2);
    }
    .step-list li::before {
      content: counter(steps);
      min-width: 26px;
      height: 26px;
      background: var(--red);
      color: #fff;
      font-size: .75rem;
      font-weight: 700;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      margin-top: 1px;
    }
    .tool-links { list-style: none; }
    .tool-links li { margin-bottom: 8px; }
    .tool-links a {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: .875rem;
      color: var(--ink-2);
      text-decoration: none;
      padding: 8px 10px;
      border-radius: 8px;
      transition: background .15s, color .15s;
    }
    .tool-links a:hover { background: var(--red-soft); color: var(--red); }

    /* ── ARTICLE ── */
    .article-section {
      max-width: 960px;
      margin: 0 auto;
      padding: 0 20px 60px;
    }
    .article-section h2 {
      font-family: 'Sora', sans-serif;
      font-size: 1.3rem;
      font-weight: 700;
      color: var(--ink);
      margin: 36px 0 12px;
      padding-bottom: 8px;
      border-bottom: 2px solid var(--red-soft);
    }
    .article-section h3 {
      font-size: 1rem;
      font-weight: 600;
      color: var(--ink);
      margin: 20px 0 8px;
    }
    .article-section p {
      color: var(--ink-2);
      margin-bottom: 14px;
      font-size: .95rem;
    }
    .article-section ul, .article-section ol {
      padding-left: 20px;
      color: var(--ink-2);
      font-size: .95rem;
      margin-bottom: 14px;
    }
    .article-section li { margin-bottom: 6px; }

    .info-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 16px;
      margin: 20px 0;
    }
    .info-card {
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 18px;
    }
    .info-card .ic-icon { font-size: 1.6rem; margin-bottom: 8px; }
    .info-card strong { display: block; font-size: .9rem; color: var(--ink); margin-bottom: 4px; }
    .info-card span { font-size: .82rem; color: var(--ink-3); }

    .faq-item {
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      margin-bottom: 10px;
      overflow: hidden;
    }
    .faq-q {
      width: 100%;
      text-align: left;
      background: none;
      border: none;
      padding: 16px 20px;
      font-size: .9rem;
      font-weight: 600;
      color: var(--ink);
      cursor: pointer;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .faq-q .arrow { transition: transform .25s; font-size: 1rem; color: var(--red); }
    .faq-q.open .arrow { transform: rotate(180deg); }
    .faq-a {
      display: none;
      padding: 0 20px 16px;
      font-size: .875rem;
      color: var(--ink-2);
    }
    .faq-a.open { display: block; }
    
    /* ── Image Proof Gallery CSS ── */
    .proof-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; margin: 24px 0; }
    .proof-figure { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; text-align: center; }
    .proof-figure img { width: 100%; height: auto; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
    .proof-figure figcaption { font-size: 14px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
    .proof-figure p { font-size: 13px !important; color: var(--ink-3) !important; margin-bottom: 0 !important; line-height: 1.5 !important; }

    /* ── FOOTER ── */
    footer {
      background: var(--ink);
      color: rgba(255,255,255,.6);
      text-align: center;
      padding: 28px 20px;
      font-size: .82rem;
    }
    footer a { color: rgba(255,255,255,.8); text-decoration: none; margin: 0 10px; }
    footer a:hover { color: #fff; }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;header&gt;
  &lt;div class="header-inner"&gt;
    &lt;a class="logo" href="https://www.kholil.biz.id/"&gt;&#128196; Kholil Tools&lt;/a&gt;
    &lt;nav aria-label="Menu Utama"&gt;
      &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html"&gt;Compress PDF&lt;/a&gt;
      &lt;a href="https://www.kholil.biz.id/2026/04/split-pdf.html"&gt;Split PDF&lt;/a&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
&lt;/header&gt;

&lt;section class="hero"&gt;
  &lt;div class="hero-badge"&gt;100% Gratis &amp;amp; Aman&lt;/div&gt;
  &lt;h1&gt;Gabungkan &lt;span&gt;PDF Online&lt;/span&gt;&lt;br&gt;Tanpa Upload ke Server&lt;/h1&gt;
  &lt;p&gt;Seret dan tata ulang file PDF Anda, lalu unduh hasilnya dalam hitungan detik. Semua proses berjalan langsung di browser Anda.&lt;/p&gt;
  &lt;div class="trust-row"&gt;
    &lt;div class="trust-item"&gt;
      &lt;svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"&gt;&lt;path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/&gt;&lt;/svg&gt;
      File tidak diunggah ke server
    &lt;/div&gt;
    &lt;div class="trust-item"&gt;
      &lt;svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"&gt;&lt;circle cx="12" cy="12" r="10"/&gt;&lt;path d="M12 8v4l3 3"/&gt;&lt;/svg&gt;
      Proses dalam hitungan detik
    &lt;/div&gt;
    &lt;div class="trust-item"&gt;
      &lt;svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"&gt;&lt;path d="M20 7H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z"/&gt;&lt;path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/&gt;&lt;/svg&gt;
      Tanpa instalasi aplikasi
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/section&gt;

&lt;div class="page-wrap"&gt;
  &lt;main&gt;
    &lt;div class="tool-card"&gt;
      &lt;h2&gt;Gabungkan File PDF Anda&lt;/h2&gt;

      &lt;div class="drop-zone" id="drop-zone" tabindex="0" role="button" aria-label="Upload PDF Area"&gt;
        &lt;svg class="drop-icon" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"&gt;
          &lt;rect x="8" y="4" width="24" height="32" rx="3" stroke-width="2"/&gt;
          &lt;path d="M32 4l8 8v28a3 3 0 0 1-3 3H11" /&gt;
          &lt;path d="M32 4v8h8" /&gt;
          &lt;path d="M20 38v6m0 0l-3-3m3 3l3-3" /&gt;
        &lt;/svg&gt;
        &lt;p&gt;Seret file PDF ke sini atau klik tombol di bawah&lt;/p&gt;
        &lt;label class="btn-pick" for="file-input"&gt;
          Pilih File PDF
          &lt;input type="file" id="file-input" multiple accept="application/pdf" /&gt;
        &lt;/label&gt;
      &lt;/div&gt;

      &lt;div id="file-list" aria-live="polite"&gt;&lt;/div&gt;
      &lt;p class="drag-hint" id="drag-hint" style="display:none;"&gt;☰ Seret atau klik panah untuk mengubah urutan halaman&lt;/p&gt;

      &lt;div class="action-row"&gt;
        &lt;button type="button" class="btn-merge" id="mergeBtn" disabled&gt;
          &lt;svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"&gt;&lt;path d="M8 6H21M8 12H21M8 18H21M3 6h.01M3 12h.01M3 18h.01"/&gt;&lt;/svg&gt;
          Gabungkan PDF
        &lt;/button&gt;
        &lt;button type="button" class="btn-reset" id="resetBtn"&gt;Reset&lt;/button&gt;
      &lt;/div&gt;

      &lt;div class="progress-wrap" id="progress-wrap" aria-live="assertive"&gt;
        &lt;div class="progress-bar-bg"&gt;&lt;div class="progress-bar-fill" id="progress-fill"&gt;&lt;/div&gt;&lt;/div&gt;
        &lt;p class="progress-label" id="progress-label"&gt;Memproses…&lt;/p&gt;
      &lt;/div&gt;

      &lt;div class="result-box" id="result-box" aria-live="polite"&gt;
        &lt;div class="result-info"&gt;
          &lt;strong&gt;✅ PDF berhasil digabungkan!&lt;/strong&gt;
          &lt;span id="result-meta"&gt;Siap diunduh&lt;/span&gt;
        &lt;/div&gt;
        &lt;a id="downloadBtn" class="btn-download" download="merged.pdf"&gt;⬇ Download PDF&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="article-section"&gt;
      &lt;h2&gt;Panduan Praktis Penggabungan PDF untuk Dokumen Indonesia&lt;/h2&gt;
      &lt;p&gt;Dalam urusan administratif di Indonesia—seperti seleksi CPNS, pengajuan KPR, atau pendaftaran beasiswa LPDP—sering kali Anda diwajibkan untuk mengunggah berbagai dokumen yang terpisah ke dalam satu berkas berformat PDF. Fitur Merge PDF secara spesifik mengatasi kendala ini.&lt;/p&gt;
      
      &lt;h3&gt;Studi Kasus Penggunaan Nyata&lt;/h3&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;Sistem SSCASN CPNS:&lt;/strong&gt; Persyaratan dokumen sering mensyaratkan Surat Lamaran, Surat Pernyataan, KTP, dan Ijazah untuk digabung dalam satu file. Sistem kami memastikan resolusi tetap terjaga agar terbaca oleh sistem verifikasi.&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Kompilasi Laporan Bulanan Bisnis:&lt;/strong&gt; Staf administrasi dapat menggabungkan puluhan lembar faktur (*invoice*) bulanan dan bukti bayar yang tersebar menjadi satu berkas pelaporan pajak terpusat, menghemat waktu kurasi manual.&lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;Alat Merge PDF dari Kholil Tools menjamin setiap proses penggabungan struktur PDF Anda (dari berbagai versi PDF, misalnya v1.4 dan v1.7) kompatibel secara penuh tanpa merusak isi (*client-side manipulation*).&lt;/p&gt;

      &lt;!-- Bukti Pengujian (E-E-A-T Evidence) --&gt;
      &lt;h3&gt;Bukti Nyata Pengujian Alat Merge PDF&lt;/h3&gt;
      &lt;p&gt;Untuk membuktikan bahwa sistem kami benar-benar berfungsi dengan baik, berikut adalah dokumentasi proses penggabungan PDF secara &lt;em&gt;real-time&lt;/em&gt; via browser perangkat mobile:&lt;/p&gt;
      
      &lt;div class="proof-gallery"&gt;
        &lt;figure class="proof-figure"&gt;
          &lt;img src="https://i.ibb.co.com/Rpq3wMVB/Screenshot-2026-07-24-20-32-37-659-com-android-chrome.jpg" alt="Mengatur urutan file PDF yang akan digabungkan" loading="lazy"&gt;
          &lt;figcaption&gt;1. Mengatur Urutan File&lt;/figcaption&gt;
          &lt;p&gt;Anda dapat menggeser file (drag &amp; drop) atau menggunakan panah navigasi untuk menentukan urutan penggabungan dokumen.&lt;/p&gt;
        &lt;/figure&gt;
        &lt;figure class="proof-figure"&gt;
          &lt;img src="https://i.ibb.co.com/9k88g0n2/Screenshot-2026-07-24-20-32-43-113-com-android-chrome.jpg" alt="Notifikasi PDF berhasil digabungkan dan siap diunduh" loading="lazy"&gt;
          &lt;figcaption&gt;2. Penggabungan Berhasil&lt;/figcaption&gt;
          &lt;p&gt;Setelah diproses, sistem menampilkan notifikasi sukses dan tombol unduh instan untuk menyimpan hasil ke perangkat lokal.&lt;/p&gt;
        &lt;/figure&gt;
        &lt;figure class="proof-figure"&gt;
          &lt;img src="https://i.ibb.co.com/cS1M930k/Screenshot-2026-07-24-20-32-52-789-com-intsig-camscanner.jpg" alt="Hasil akhir dokumen PDF gabungan yang rapi" loading="lazy"&gt;
          &lt;figcaption&gt;3. Hasil Akhir Dokumen&lt;/figcaption&gt;
          &lt;p&gt;Dokumen akhir yang diunduh tergabung dengan rapi, menjaga kualitas dan resolusi aslinya tanpa distorsi.&lt;/p&gt;
        &lt;/figure&gt;
      &lt;/div&gt;

      &lt;h2&gt;Keunggulan Arsitektur Alat Kholil Tools&lt;/h2&gt;
      &lt;div class="info-grid"&gt;
        &lt;div class="info-card"&gt;
          &lt;div class="ic-icon" aria-hidden="true"&gt;&#128274;&lt;/div&gt;
          &lt;strong&gt;Privasi Tingkat Enkripsi&lt;/strong&gt;
          &lt;span&gt;Seluruh pemrosesan PDF-lib dikendalikan di RAM Anda, kami tidak punya log file satupun.&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="info-card"&gt;
          &lt;div class="ic-icon" aria-hidden="true"&gt;⚡&lt;/div&gt;
          &lt;strong&gt;Proses Instan&lt;/strong&gt;
          &lt;span&gt;Tidak ada penundaan lalu lintas unggah/unduh jaringan. PDF siap secara instan.&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="info-card"&gt;
          &lt;div class="ic-icon" aria-hidden="true"&gt;♿&lt;/div&gt;
          &lt;strong&gt;Aksesibilitas Tinggi&lt;/strong&gt;
          &lt;span&gt;Mendukung penuh pemilihan urutan via &lt;em&gt;Keyboard Navigation&lt;/em&gt; bagi kemudahan operasional disabilitas.&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;h2&gt;FAQ &amp; Pemecahan Masalah&lt;/h2&gt;
      &lt;div class="faq-item"&gt;
        &lt;button type="button" class="faq-q" data-faq="1" aria-expanded="false"&gt;Kenapa ukuran file gabungan menjadi sangat besar dibandingkan aslinya? &lt;span class="arrow"&gt;▾&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-a" id="faq-1"&gt;Hal ini terjadi jika Anda menggabungkan PDF yang merupakan hasil pemindaian (scan) gambar. Jika ukurannya melebihi batas (misalnya batas 1MB untuk situs pemerintah), segera turunkan ukurannya menggunakan alat &lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html" style="color:var(--red);"&gt;Compress PDF&lt;/a&gt; kami.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button type="button" class="faq-q" data-faq="2" aria-expanded="false"&gt;Apakah saya dapat menyortir urutan halaman secara manual di alat ini? &lt;span class="arrow"&gt;▾&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-a" id="faq-2"&gt;Tentu saja. Anda bisa mengurutkan file dengan melakukan proses Seret &amp; Lepas (Drag-and-Drop) menggunakan tetikus, atau menggunakan tombol navigasi (▲/▼) yang kami sediakan untuk pengguna papan ketik.&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="faq-item"&gt;
        &lt;button type="button" class="faq-q" data-faq="3" aria-expanded="false"&gt;Apakah saya bisa menggabungkan dokumen rahasia perusahaan yang telah dipassword? &lt;span class="arrow"&gt;▾&lt;/span&gt;&lt;/button&gt;
        &lt;div class="faq-a" id="faq-3"&gt;Tidak. Sistem *Client-side* kami dirancang menolak enkripsi DRM berlapis untuk menghormati perlindungan cipta. Anda wajib menonaktifkan kata sandi pada properti PDF sebelum digabungkan.&lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="info-card" style="margin-top: 30px; background: #F0F9FF; display: flex; gap: 15px; align-items: center; border-color: #BAE6FD;"&gt;
        &lt;img src="https://ui-avatars.com/api/?name=Kholil&amp;background=0D8ABC&amp;color=fff" alt="Kholil - Pengembang Kholil Tools" style="border-radius: 50%; width: 50px; height: 50px;"&gt;
        &lt;div&gt;
          &lt;h3 style="margin:0 0 5px; font-size:16px;"&gt;Pengembangan oleh &lt;a href="https://www.kholil.biz.id/p/about-us.html" style="color: #0284C7; text-decoration:none;"&gt;Kholil&lt;/a&gt;&lt;/h3&gt;
          &lt;p style="margin:0; font-size:14px; color: var(--ink-2);"&gt;Sistem penggabungan ini difokuskan pada manipulasi data memori internal, sehingga melindungi Anda dari kerentanan intersepsi jaringan. Ada laporan masalah fungsionalitas? &lt;a href="https://www.kholil.biz.id/p/contact-us.html" style="color: var(--ink); font-weight:600;"&gt;Hubungi Kami&lt;/a&gt;.&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/main&gt;

  &lt;aside class="sidebar"&gt;
    &lt;div class="side-card"&gt;
      &lt;h3&gt;Prosedur Operasi&lt;/h3&gt;
      &lt;ol class="step-list"&gt;
        &lt;li&gt;Pilih beberapa File PDF&lt;/li&gt;
        &lt;li&gt;Validasi dan sesuaikan hirarki dokumen&lt;/li&gt;
        &lt;li&gt;Eksekusi proses Merge&lt;/li&gt;
        &lt;li&gt;Simpan hasil di penyimpanan lokal&lt;/li&gt;
      &lt;/ol&gt;
    &lt;/div&gt;

    &lt;div class="side-card"&gt;
      &lt;h3&gt;&#128736; Utilitas PDF Lainnya&lt;/h3&gt;
      &lt;ul class="tool-links"&gt;
        &lt;li&gt;&lt;a href="https://www.kholil.biz.id/2026/04/compress-pdf.html"&gt;&#128476;️ Compress PDF&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="https://www.kholil.biz.id/2026/04/split-pdf.html"&gt;✂️ Split PDF&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/aside&gt;
&lt;/div&gt;

&lt;footer&gt;
  &lt;div style="display:flex; justify-content:center; gap:20px; flex-wrap:wrap; margin-bottom:12px;"&gt;
    &lt;a href="https://www.kholil.biz.id/"&gt;Beranda&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/privacy-policy.html"&gt;Kebijakan Privasi &amp; Cookies&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/about-us.html"&gt;Tentang Kami&lt;/a&gt;
    &lt;a href="https://www.kholil.biz.id/p/contact-us.html"&gt;Hubungi Kami&lt;/a&gt;
  &lt;/div&gt;
  &lt;p&gt;© 2026 Kholil.biz.id · Pemrosesan 100% Client-Side&lt;/p&gt;
&lt;/footer&gt;

&lt;script&gt;
(function(){
  const input      = document.getElementById('file-input');
  const dropZone   = document.getElementById('drop-zone');
  const fileList   = document.getElementById('file-list');
  const dragHint   = document.getElementById('drag-hint');
  const mergeBtn   = document.getElementById('mergeBtn');
  const resetBtn   = document.getElementById('resetBtn');
  const progressWrap = document.getElementById('progress-wrap');
  const progressFill = document.getElementById('progress-fill');
  const progressLabel = document.getElementById('progress-label');
  const resultBox  = document.getElementById('result-box');
  const resultMeta = document.getElementById('result-meta');
  const downloadBtn= document.getElementById('downloadBtn');

  let files = []; 

  /* ── FILE INPUT ── */
  input.addEventListener('change', e =&gt; {
    addFiles([...e.target.files]);
    e.target.value = '';
  });

  /* ── DRAG &amp; DROP on drop-zone ── */
  dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.classList.add('drag-over'); });
  dropZone.addEventListener('dragleave', () =&gt; dropZone.classList.remove('drag-over'); });
  dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault();
    dropZone.classList.remove('drag-over');
    const dropped = [...e.dataTransfer.files].filter(f =&gt; f.type === 'application/pdf');
    addFiles(dropped);
  });
  dropZone.addEventListener('click', () =&gt; input.click());
  dropZone.addEventListener('keydown', e =&gt; {
    if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); input.click(); }
  });

  function addFiles(newFiles) {
    newFiles.forEach(f =&gt; { if(f.type === 'application/pdf') files.push(f); });
    renderList();
  }

  function formatBytes(b) {
    if(b &lt; 1024) return b + ' B';
    if(b &lt; 1048576) return (b/1024).toFixed(1) + ' KB';
    return (b/1048576).toFixed(1) + ' MB';
  }

  /* ── RENDER FILE LIST ── */
  function renderList() {
    fileList.innerHTML = '';
    if(files.length === 0){ dragHint.style.display='none'; mergeBtn.disabled=true; return; }
    dragHint.style.display = files.length &gt; 1 ? 'block' : 'none';
    mergeBtn.disabled = false;

    files.forEach((f, i) =&gt; {
      const row = document.createElement('div');
      row.className = 'file-row';
      row.draggable = true;
      row.dataset.index = i;
      row.innerHTML = `
        &lt;svg class="file-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"&gt;
          &lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/&gt;&lt;polyline points="14 2 14 8 20 8"/&gt;
        &lt;/svg&gt;
        &lt;span class="file-name" title="${f.name}"&gt;${f.name}&lt;/span&gt;
        &lt;span class="file-size"&gt;${formatBytes(f.size)}&lt;/span&gt;
        &lt;button type="button" class="btn-up" data-idx="${i}" aria-label="Naikkan file ini" ${i === 0 ? 'disabled' : ''}&gt;▲&lt;/button&gt;
        &lt;button type="button" class="btn-down" data-idx="${i}" aria-label="Turunkan file ini" ${i === files.length - 1 ? 'disabled' : ''}&gt;▼&lt;/button&gt;
        &lt;button type="button" class="file-del" data-del="${i}" aria-label="Hapus file" title="Hapus"&gt;
          &lt;svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"&gt;&lt;line x1="18" y1="6" x2="6" y2="18"/&gt;&lt;line x1="6" y1="6" x2="18" y2="18"/&gt;&lt;/svg&gt;
        &lt;/button&gt;
      `;
      fileList.appendChild(row);
    });

    /* Delete buttons */
    fileList.querySelectorAll('.file-del').forEach(btn =&gt; {
      btn.addEventListener('click', e =&gt; {
        e.stopPropagation();
        files.splice(+btn.dataset.del, 1);
        renderList();
      });
    });

    /* Keyboard Reorder Navigation */
    fileList.querySelectorAll('.btn-up').forEach(btn =&gt; {
      btn.addEventListener('click', e =&gt; {
        e.stopPropagation();
        const idx = +btn.dataset.idx;
        if (idx &gt; 0) {
          [files[idx], files[idx - 1]] = [files[idx - 1], files[idx]];
          renderList();
        }
      });
    });

    fileList.querySelectorAll('.btn-down').forEach(btn =&gt; {
      btn.addEventListener('click', e =&gt; {
        e.stopPropagation();
        const idx = +btn.dataset.idx;
        if (idx &lt; files.length - 1) {
          [files[idx], files[idx + 1]] = [files[idx + 1], files[idx]];
          renderList();
        }
      });
    });

    /* Drag-to-reorder */
    let dragSrc = null;
    fileList.querySelectorAll('.file-row').forEach(row =&gt; {
      row.addEventListener('dragstart', () =&gt; { dragSrc = +row.dataset.index; row.classList.add('dragging'); });
      row.addEventListener('dragend', () =&gt; row.classList.remove('dragging'));
      row.addEventListener('dragover', e =&gt; e.preventDefault());
      row.addEventListener('drop', e =&gt; {
        e.preventDefault();
        const target = +row.dataset.index;
        if(dragSrc === null || dragSrc === target) return;
        const moved = files.splice(dragSrc, 1)[0];
        files.splice(target, 0, moved);
        renderList();
      });
    });
  }

  /* ── RESET ── */
  resetBtn.addEventListener('click', () =&gt; {
    files = [];
    renderList();
    resultBox.classList.remove('show');
    progressWrap.style.display = 'none';
    downloadBtn.removeAttribute('href');
  });

  /* ── MERGE ── */
  mergeBtn.addEventListener('click', async () =&gt; {
    if (typeof PDFLib === 'undefined') {
      alert('Sistem penggabung PDF sedang memuat. Mohon tunggu beberapa detik.');
      return;
    }

    if(!files.length) return;
    mergeBtn.disabled = true;
    resultBox.classList.remove('show');
    progressWrap.style.display = 'block';
    progressFill.style.width = '0%';
    progressLabel.textContent = 'Memulai penggabungan…';

    try {
      const { PDFDocument } = PDFLib;
      const merged = await PDFDocument.create();
      let totalPages = 0;

      for(let i = 0; i &lt; files.length; i++){
        progressLabel.textContent = `Memproses file ${i+1} dari ${files.length}: ${files[i].name}`;
        progressFill.style.width = ((i / files.length) * 85) + '%';
        const buffer = await files[i].arrayBuffer();
        const pdf = await PDFDocument.load(buffer, { ignoreEncryption: true });
        const pages = await merged.copyPages(pdf, pdf.getPageIndices());
        pages.forEach(p =&gt; { merged.addPage(p); totalPages++; });
      }

      progressLabel.textContent = 'Menyimpan dokumen…';
      progressFill.style.width = '95%';
      const bytes = await merged.save();
      progressFill.style.width = '100%';
      progressLabel.textContent = 'Selesai!';

      const blob = new Blob([bytes], { type: 'application/pdf' });
      const url  = URL.createObjectURL(blob);
      downloadBtn.href = url;
      downloadBtn.download = 'merged.pdf';
      resultMeta.textContent = `${files.length} file · ${totalPages} halaman · ${formatBytes(bytes.byteLength)}`;
      resultBox.classList.add('show');

      setTimeout(() =&gt; { progressWrap.style.display = 'none'; }, 800);
    } catch(err) {
      progressLabel.textContent = '❌ Gagal: Pastikan PDF tidak di-enkripsi password. ' + err.message;
      console.error(err);
    } finally {
      mergeBtn.disabled = false;
    }
  });

  /* ── FAQ accordion (Aksesibilitas Ditambahkan) ── */
  document.querySelectorAll('.faq-q').forEach(btn =&gt; {
    btn.addEventListener('click', () =&gt; {
      const id = btn.dataset.faq;
      const ans = document.getElementById('faq-' + id);
      const isOpen = btn.classList.contains('open');
      
      document.querySelectorAll('.faq-q').forEach(b =&gt; { 
        b.classList.remove('open'); 
        b.setAttribute('aria-expanded', 'false');
      });
      document.querySelectorAll('.faq-a').forEach(a =&gt; { 
        a.classList.remove('open'); 
      });
      
      if(!isOpen){ 
        btn.classList.add('open'); 
        ans.classList.add('open'); 
        btn.setAttribute('aria-expanded', 'true');
      }
    });
  });

})();
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</description><link>https://www.kholil.biz.id/2026/04/merge-pdf.html</link><author>noreply@blogger.com (tews)</author><thr:total>0</thr:total></item></channel></rss>