Duolingo Auto Create Link Super

Duolingo Super Manager - Create referral links, activate Super, manage accounts

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Duolingo Auto Create Link Super 
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Duolingo Super Manager - Create referral links, activate Super, manage accounts
// @author       MeowWoof
// @match        https://www.duolingo.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @connect      duolingo-super022.vercel.app
// @run-at       document-idle
// @license      MIT
// @icon         https://d35aaqx5ub95lt.cloudfront.net/vendor/38dc6a042b0de3f6aeb44ff2aa70de73.svg
// ==/UserScript==

(function () {
'use strict';

const API_BASE    = 'https://duolingo-super022.vercel.app';
const FREE_KEY    = 'DS-FREE-PUBLIC-KEY-2025';
const DISCORD_URL = 'https://discord.gg/ufBrcGemBH';
const CD_STORE    = 'duods_cd_expire';

let _sess     = null;
let _keyData  = null;
let _cdTimer  = null;
let _hist     = [];
let _hidden   = false;
let _animLock = false;
let _pgLock   = false;
let _curPg    = 'auth';
let _ntTimer;
let _isFree   = false;

function _detectDark() {
    const html = document.documentElement;
    if (html.dataset.theme === 'dark') return true;
    if (html.dataset.theme === 'light') return false;
    const htmlCl = html.className || '';
    const bodyCl = document.body.className || '';
    if (htmlCl.includes('dark') || bodyCl.includes('dark')) return true;
    if (htmlCl.includes('light') || bodyCl.includes('light')) return false;
    const bodyStyle = window.getComputedStyle(document.body);
    const htmlStyle = window.getComputedStyle(html);
    for (const s of [bodyStyle, htmlStyle]) {
        const bg = s.backgroundColor;
        if (bg) {
            const m = bg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
            if (m) {
                const lum = 0.299 * +m[1] + 0.587 * +m[2] + 0.114 * +m[3];
                if (lum < 60) return true;
                if (lum > 180) return false;
            }
        }
    }
    try {
        const testEl = document.querySelector('[class*="duo"][class*="dark"], [data-color-mode="dark"], [color-scheme="dark"]');
        if (testEl) return true;
    } catch {}
    return window.matchMedia('(prefers-color-scheme: dark)').matches;
}

function _applyTheme() {
    const root = document.getElementById('DDS_Root');
    if (!root) return;
    if (_detectDark()) {
        root.setAttribute('data-dds-dark', '1');
    } else {
        root.removeAttribute('data-dds-dark');
    }
}

function _watchTheme() {
    _applyTheme();
    const obs = new MutationObserver(() => _applyTheme());
    obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme', 'class', 'data-color-mode'], subtree: false });
    obs.observe(document.body, { attributes: true, attributeFilter: ['data-theme', 'class', 'data-color-mode'], subtree: false });
    const duoAppRoot = document.getElementById('root') || document.getElementById('app') || document.querySelector('[id^="__next"]');
    if (duoAppRoot) {
        obs.observe(duoAppRoot, { attributes: true, attributeFilter: ['class', 'data-theme'] });
    }
    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', _applyTheme);
    setInterval(_applyTheme, 500);
}

function _req(path, method, body) {
    return new Promise(resolve => {
        const hdrs = _hdrs();
        if (!hdrs && path !== '/api/key/info') return resolve({ error: 'Unauthorized' });
        const cfg = {
            method: method || 'POST',
            url: API_BASE + path,
            headers: hdrs || { 'Content-Type': 'application/json' },
            timeout: 90000,
            onload: r => {
                try { resolve(JSON.parse(r.responseText)); }
                catch { resolve({ error: 'Parse error: ' + r.responseText.slice(0, 120) }); }
            },
            onerror: () => resolve({ error: 'Network error' }),
            ontimeout: () => resolve({ error: 'Timeout (90s)' })
        };
        if (body) cfg.data = JSON.stringify(body);
        GM_xmlhttpRequest(cfg);
    });
}

function _hdrs() {
    if (!_sess) return null;
    if (Date.now() > _sess.exp) { _sess = null; return null; }
    return {
        'Content-Type': 'application/json',
        'x-ds-key': _sess.key,
        'x-ds-token': _sess.tok
    };
}

function _fetchKey(key) {
    return new Promise(resolve => {
        GM_xmlhttpRequest({
            method: 'GET',
            url: `${API_BASE}/api/key/info?key=${encodeURIComponent(key)}`,
            headers: { 'Content-Type': 'application/json' },
            timeout: 30000,
            onload: r => {
                try {
                    const d = JSON.parse(r.responseText);
                    if (d.ok) {
                        _sess = {
                            key,
                            tok: d.session_token || _fnv(key),
                            exp: Date.now() + (d.session_ttl_ms || 3600000)
                        };
                    }
                    resolve(d);
                } catch { resolve({ error: 'Parse error' }); }
            },
            onerror: () => resolve({ error: 'Network error' }),
            ontimeout: () => resolve({ error: 'Timeout' })
        });
    });
}

function _fnv(s) {
    let h = 0x811c9dc5;
    for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = (h * 0x01000193) >>> 0; }
    return h.toString(16).padStart(8, '0') + Date.now().toString(36);
}

const _set = (k, v) => GM_setValue(k, v);
const _get = (k, d) => GM_getValue(k, d);

GM_addStyle(`
#DDS_Root {
    --dds-blue:    0, 122, 255;
    --dds-green:   52, 199, 89;
    --dds-red:     255, 59, 48;
    --dds-orange:  255, 149, 0;
    --dds-purple:  88, 101, 242;
    --dds-card:    255, 255, 255;
    --dds-border:  200, 200, 210;
    --dds-text1:   30, 30, 40;
    --dds-text2:   100, 100, 115;
    --dds-inbg:    0, 122, 255;
    --dds-cd-bg:   255, 149, 0;
    --dds-tab-bg:  180, 180, 195;
    --dds-surface: 245, 245, 250;
    --dds-shadow:  0, 0, 0;
    --dds-spin-fg: 0, 122, 255;
    --dds-spin-bg: 0, 122, 255;
}
#DDS_Root[data-dds-dark] {
    --dds-blue:    10, 132, 255;
    --dds-green:   48, 209, 88;
    --dds-red:     255, 69, 58;
    --dds-orange:  255, 159, 10;
    --dds-purple:  100, 115, 255;
    --dds-card:    28, 28, 32;
    --dds-border:  60, 60, 70;
    --dds-text1:   240, 240, 248;
    --dds-text2:   160, 160, 175;
    --dds-inbg:    10, 132, 255;
    --dds-cd-bg:   255, 159, 10;
    --dds-tab-bg:  70, 70, 80;
    --dds-surface: 38, 38, 44;
    --dds-shadow:  0, 0, 0;
    --dds-spin-fg: 255, 255, 255;
    --dds-spin-bg: 255, 255, 255;
}

#DDS_Root * { box-sizing: border-box; }
#DDS_Root p, #DDS_Root span, #DDS_Root button,
#DDS_Root input, #DDS_Root label, #DDS_Root div, #DDS_Root textarea {
    font-family: 'din-round', -apple-system, sans-serif !important;
}
#DDS_Root p, #DDS_Root span { margin: 0; padding: 0; }
#DDS_Root svg { flex-shrink: 0; }
#DDS_Root a { text-decoration: none; }

.dds-shell {
    display: inline-flex; flex-direction: column;
    justify-content: flex-end; align-items: flex-end;
    gap: 8px; position: fixed; right: 16px; bottom: 16px;
    z-index: 2147483647;
}
@media (max-width: 699px) { .dds-shell { margin-bottom: 80px; } }

.dds-box {
    display: flex; width: 320px; padding: 16px;
    flex-direction: column; align-items: center; gap: 8px;
    overflow: hidden; border-radius: 24px;
    outline: 1.5px solid rgba(var(--dds-border), 0.55); outline-offset: -1px;
    background: rgba(var(--dds-card), 0.97);
    backdrop-filter: blur(28px); -webkit-backdrop-filter: blur(28px);
    box-shadow: 0 12px 48px rgba(var(--dds-shadow),.22), 0 2px 8px rgba(var(--dds-shadow),.10);
    transition: height .6s cubic-bezier(.34,1.56,.64,1),
                width  .6s cubic-bezier(.34,1.56,.64,1);
}

.dds-row    { display: flex; align-items: center; justify-content: space-between; align-self: stretch; }
.dds-hstack { display: flex; align-items: center; gap: 8px; align-self: stretch; }
.dds-vstack { display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 8px; align-self: stretch; }
.dds-nosel  { user-select: none; -webkit-user-select: none; }
.dds-hr     { align-self: stretch; height: 1px; background: rgba(var(--dds-border), .35); flex-shrink: 0; }

.dds-t1 { font-size: 15px; font-weight: 700; line-height: normal; color: rgb(var(--dds-text1)); margin: 0; }
.dds-t2 { font-size: 13px; font-weight: 600; line-height: normal; color: rgba(var(--dds-text2), .80); margin: 0; }

.dds-input-wrap {
    display: flex; height: 48px; padding: 0 14px;
    align-items: center; flex: 1 0 0; gap: 8px; border-radius: 12px;
    outline: 2px solid rgba(var(--dds-inbg), .30); outline-offset: -2px;
    background: rgba(var(--dds-inbg), .10);
    position: relative; overflow: hidden;
}
.dds-input-wrap.ta {
    height: auto; min-height: 72px; padding: 12px 14px; align-items: flex-start;
}
.dds-input {
    border: none !important; outline: none !important; background: none !important;
    text-align: right; font-size: 14px !important; font-weight: 600 !important;
    color: rgb(var(--dds-blue)) !important; font-family: inherit !important;
    width: 100%; -moz-appearance: textfield; line-height: 1.4;
}
.dds-input::placeholder { color: rgba(var(--dds-blue), .40) !important; }
.dds-input::-webkit-outer-spin-button,
.dds-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
textarea.dds-input {
    resize: none; text-align: left; overflow-y: auto;
    font-size: 12px !important; min-height: 48px; padding-top: 2px;
}

.dds-main-btn {
    display: flex; height: 48px; padding: 12px 18px;
    justify-content: center; align-items: center; gap: 8px;
    border-radius: 14px; border: none; cursor: pointer;
    user-select: none; -webkit-user-select: none;
    outline: 2px solid rgba(var(--dds-shadow),.18); outline-offset: -2px;
    background: rgb(var(--dds-blue)); white-space: nowrap; flex-shrink: 0;
    transition: width .7s cubic-bezier(.77,0,.18,1),
                background .7s cubic-bezier(.16,1,.32,1),
                outline .7s cubic-bezier(.16,1,.32,1),
                filter .35s cubic-bezier(.16,1,.32,1),
                transform .35s cubic-bezier(.16,1,.32,1);
}
.dds-main-btn:hover  { filter: brightness(.88); transform: scale(1.03); }
.dds-main-btn:active { filter: brightness(.80); transform: scale(.97); }
.dds-main-btn:disabled { opacity: .38; pointer-events: none; }

.dds-lbl {
    font-size: 15px; font-weight: 800; letter-spacing: .2px;
    transition: opacity .35s, filter .35s, color .35s;
    white-space: nowrap;
}

.dds-sm-btn {
    display: flex; height: 40px; padding: 0 16px;
    justify-content: center; align-items: center; gap: 8px;
    border-radius: 12px; border: none; cursor: pointer;
    user-select: none; flex-shrink: 0;
    outline: 2px solid rgba(var(--dds-shadow),.18); outline-offset: -2px;
    background: rgb(var(--dds-blue)); white-space: nowrap;
    transition: background .7s cubic-bezier(.16,1,.32,1),
                filter .35s cubic-bezier(.16,1,.32,1),
                transform .35s cubic-bezier(.16,1,.32,1);
}
.dds-sm-btn:hover  { filter: brightness(.88); transform: scale(1.04); }
.dds-sm-btn:active { filter: brightness(.80); transform: scale(.96); }
.dds-sm-btn:disabled { opacity: .38; pointer-events: none; }
.dds-sm-lbl { font-size: 13px; font-weight: 800; transition: opacity .35s, filter .35s, color .35s; white-space: nowrap; }

.dds-prog-wrap {
    align-self: stretch; height: 0; border-radius: 4px;
    background: rgba(var(--dds-blue), .12); overflow: hidden;
    transition: height .4s cubic-bezier(.16,1,.32,1);
}
.dds-prog-wrap.on { height: 5px; }
.dds-prog-fill {
    height: 100%; border-radius: 4px; background: rgb(var(--dds-blue));
    width: 0%; transition: width .5s cubic-bezier(.16,1,.32,1);
    box-shadow: 0 0 8px rgba(var(--dds-blue), .45);
}
.dds-prog-fill.warn { background: rgb(var(--dds-red)); }

.dds-log {
    background: rgba(var(--dds-blue), .06);
    border: 1.5px solid rgba(var(--dds-blue), .18);
    border-radius: 12px; padding: 10px 14px;
    font-size: 11px !important; font-weight: 600 !important;
    color: rgba(var(--dds-text1), .75) !important;
    max-height: 160px; overflow-y: auto;
    line-height: 1.8; font-family: monospace !important;
    white-space: pre-wrap; word-break: break-all; align-self: stretch;
}
.dds-log::-webkit-scrollbar { width: 3px; }
.dds-log::-webkit-scrollbar-thumb { background: rgba(var(--dds-blue), .28); border-radius: 3px; }
.lo { color: rgb(var(--dds-green)); }
.le { color: rgb(var(--dds-red)); }
.li { color: rgb(var(--dds-blue)); }
.lw { color: rgb(var(--dds-orange)); }

.dds-badge {
    display: flex; align-items: center; justify-content: space-between;
    align-self: stretch; padding: 10px 14px; border-radius: 16px;
    outline: 1.5px solid rgba(var(--dds-blue), .25); outline-offset: -1px;
    background: rgba(var(--dds-blue), .08);
}
.dds-badge-key  { font-size: 12px; font-weight: 800; color: rgb(var(--dds-blue)); font-family: monospace !important; letter-spacing: .5px; }
.dds-badge-meta { font-size: 10px !important; font-weight: 600 !important; color: rgba(var(--dds-text2), .75) !important; margin-top: 2px; }

.dds-free-tag {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 3px 10px; min-width: 44px;
    border-radius: 7px; font-size: 10px !important; font-weight: 800 !important;
    background: rgba(var(--dds-green), .16);
    outline: 1.5px solid rgba(var(--dds-green), .32); outline-offset: -1px;
    color: rgb(var(--dds-green)) !important; letter-spacing: .5px;
    flex-shrink: 0; white-space: nowrap;
}

.dds-quota-wrap {
    align-self: stretch; display: flex; flex-direction: column; gap: 6px;
    padding: 10px 14px; border-radius: 16px;
    background: rgba(var(--dds-surface), .90);
    outline: 1.5px solid rgba(var(--dds-border), .35); outline-offset: -1px;
}
.dds-quota-row   { display: flex; justify-content: space-between; align-items: center; }
.dds-quota-lbl   { font-size: 10px !important; font-weight: 700 !important; text-transform: uppercase; letter-spacing: .8px; color: rgba(var(--dds-text2), .65) !important; }
.dds-quota-val   { font-size: 11px !important; font-weight: 800 !important; color: rgb(var(--dds-blue)) !important; }
.dds-track   { height: 5px; background: rgba(var(--dds-blue), .14); border-radius: 4px; overflow: hidden; }
.dds-tfill   { height: 100%; background: rgb(var(--dds-blue)); border-radius: 4px; transition: width .6s cubic-bezier(.34,1.56,.64,1); }
.dds-tfill.warn { background: rgb(var(--dds-red)); }

.dds-cd {
    display: none; align-items: center; justify-content: space-between;
    align-self: stretch; padding: 8px 14px; border-radius: 14px;
    background: rgba(var(--dds-cd-bg), .12);
    outline: 1.5px solid rgba(var(--dds-cd-bg), .28); outline-offset: -1px;
}
.dds-cd.on    { display: flex; animation: dds-slidein .35s cubic-bezier(.34,1.56,.64,1); }
.dds-cd-lbl   { display: flex; align-items: center; gap: 5px; font-size: 11px !important; font-weight: 700 !important; color: rgb(var(--dds-orange)) !important; }
.dds-cd-time  { font-size: 14px !important; font-weight: 800 !important; color: rgb(var(--dds-orange)) !important; font-family: monospace !important; }

.dds-tabs {
    display: flex; gap: 2px; align-self: stretch;
    background: rgba(var(--dds-tab-bg), .15);
    border-radius: 14px; padding: 3px;
    outline: 1.5px solid rgba(var(--dds-border), .25); outline-offset: -1px;
}
.dds-tab {
    flex: 1; padding: 7px 4px; border: none; border-radius: 11px;
    background: none; color: rgba(var(--dds-text2), .65);
    font-size: 10px !important; font-weight: 700 !important; cursor: pointer;
    transition: all .28s cubic-bezier(.34,1.56,.64,1);
}
.dds-tab.on {
    background: rgb(var(--dds-blue)); color: #fff;
    box-shadow: 0 2px 10px rgba(var(--dds-blue), .42);
    transform: scale(1.05);
}
.dds-tab:hover:not(.on) { color: rgb(var(--dds-text1)); background: rgba(var(--dds-blue), .08); transform: scale(1.02); }

.dds-pane        { display: none; flex-direction: column; gap: 8px; align-self: stretch; }
.dds-pane.active { display: flex; animation: dds-fadein .22s cubic-bezier(.16,1,.32,1); }
.dds-page        { display: none; }
.dds-page.active { display: flex; flex-direction: column; gap: 8px; align-self: stretch; align-items: center; }

@keyframes dds-fadein {
    from { opacity: 0; transform: translateY(6px) scale(.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes dds-slidein {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes dds-float {
    0%, 100% { transform: translateY(0) rotate(-1deg); }
    50%       { transform: translateY(-6px) rotate(1deg); }
}

.dds-lock-ring {
    width: 58px; height: 58px; border-radius: 18px;
    background: rgba(var(--dds-blue), .12);
    outline: 1.5px solid rgba(var(--dds-blue), .28); outline-offset: -1px;
    display: flex; align-items: center; justify-content: center;
    animation: dds-float 3.4s ease-in-out infinite;
}

.dds-copy-tag {
    display: inline-flex; align-items: center;
    padding: 2px 8px; border-radius: 6px;
    background: rgba(var(--dds-blue), .12);
    outline: 1px solid rgba(var(--dds-blue), .25); outline-offset: -1px;
    font-size: 9px !important; font-weight: 700 !important;
    cursor: pointer; color: rgb(var(--dds-blue)) !important;
    margin-left: 6px; transition: background .15s, transform .15s; vertical-align: middle;
}
.dds-copy-tag:hover { background: rgba(var(--dds-blue), .24); transform: scale(1.08); }
.dds-copy-tag:active { transform: scale(.94); }

.dds-err {
    font-size: 11px !important; font-weight: 600 !important;
    color: rgb(var(--dds-red)) !important; text-align: center;
    min-height: 14px; align-self: stretch;
}

.dds-ico-box {
    width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
}

.dds-danger-btn {
    display: flex; height: 30px; padding: 0 12px;
    justify-content: center; align-items: center; border-radius: 10px; border: none;
    cursor: pointer; font-size: 11px !important; font-weight: 800 !important;
    background: rgba(var(--dds-red), .10);
    outline: 1.5px solid rgba(var(--dds-red), .25); outline-offset: -1px;
    color: rgb(var(--dds-red)) !important; white-space: nowrap;
    transition: filter .3s, transform .3s, background .3s, color .3s;
}
.dds-danger-btn:hover  { background: rgb(var(--dds-red)); color: #fff !important; transform: scale(1.04); }
.dds-danger-btn:active { transform: scale(.95); }

.dds-hide-btn {
    display: flex; height: 40px; padding: 0 16px;
    align-items: center; gap: 8px;
    border-radius: 14px; border: none; cursor: pointer; user-select: none;
    outline: 2px solid rgba(var(--dds-shadow),.18); outline-offset: -2px;
    background: rgb(var(--dds-blue));
    transition: filter .35s cubic-bezier(.16,1,.32,1),
                transform .35s cubic-bezier(.16,1,.32,1),
                background .4s, outline .4s;
}
.dds-hide-btn:hover  { filter: brightness(.88); transform: scale(1.04); }
.dds-hide-btn:active { filter: brightness(.80); transform: scale(.96); }

.dds-ntf-shell {
    display: flex; justify-content: center; align-items: center;
    width: 300px; position: fixed; left: calc(50% - 150px);
    z-index: 2147483647; bottom: 16px; border-radius: 18px;
    transition: .8s cubic-bezier(.16,1,.32,1); pointer-events: none;
}
.dds-ntf-box {
    display: flex; width: 300px; padding: 14px 16px;
    flex-direction: column; gap: 3px; border-radius: 18px;
    outline: 1.5px solid rgba(var(--dds-border), .40); outline-offset: -1px;
    background: rgba(var(--dds-card), 0.97);
    backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
    box-shadow: 0 8px 32px rgba(var(--dds-shadow),.16);
    transition: .8s cubic-bezier(.16,1,.32,1); filter: blur(16px); opacity: 0;
    pointer-events: auto;
}
.dds-ntf-box.show { filter: blur(0); opacity: 1; }

.dds-discord-card {
    align-self: stretch; padding: 10px 14px; border-radius: 14px;
    background: rgba(var(--dds-purple),.10);
    outline: 1.5px solid rgba(var(--dds-purple),.28); outline-offset: -1px;
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.dds-discord-link {
    font-size: 11px !important; font-weight: 800 !important;
    color: rgb(var(--dds-purple)) !important;
    padding: 5px 12px; border-radius: 9px;
    background: rgba(var(--dds-purple),.14);
    outline: 1px solid rgba(var(--dds-purple),.25); outline-offset: -1px;
    white-space: nowrap; flex-shrink: 0;
    transition: background .2s, transform .2s;
}
.dds-discord-link:hover { background: rgba(var(--dds-purple),.26); transform: scale(1.05); }

.dds-sec-lbl {
    font-size: 10px !important; font-weight: 700 !important;
    text-transform: uppercase; letter-spacing: .7px;
    color: rgba(var(--dds-text2), .65) !important; align-self: flex-start;
}

.dds-info-card {
    padding: 10px 14px; border-radius: 14px;
    background: rgba(var(--dds-surface), .90);
    outline: 1.5px solid rgba(var(--dds-border), .30); outline-offset: -1px;
    align-self: stretch;
}

.dds-dot-ring {
    display: inline-flex; gap: 4px; align-items: center;
}
.dds-dot-ring span {
    width: 5px; height: 5px; border-radius: 50%; background: currentColor;
    animation: dds-dot-pulse 1.2s ease-in-out infinite;
}
.dds-dot-ring span:nth-child(2) { animation-delay: .18s; }
.dds-dot-ring span:nth-child(3) { animation-delay: .36s; }
@keyframes dds-dot-pulse {
    0%, 80%, 100% { transform: scale(.55); opacity: .35; }
    40%            { transform: scale(1.1); opacity: 1; }
}

.dds-spin-ring {
    width: 16px; height: 16px; border-radius: 50%;
    border: 2.5px solid rgba(var(--dds-spin-bg), .28);
    border-top-color: rgb(var(--dds-spin-fg));
    animation: dds-spin 0.75s linear infinite;
    flex-shrink: 0;
}
@keyframes dds-spin { to { transform: rotate(360deg); } }

.dds-pulse-dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: rgb(var(--dds-green));
    animation: dds-pulse-glow 2s ease-in-out infinite;
    flex-shrink: 0;
}
@keyframes dds-pulse-glow {
    0%, 100% { box-shadow: 0 0 0 0 rgba(var(--dds-green), .60); transform: scale(1); }
    50%       { box-shadow: 0 0 0 5px rgba(var(--dds-green), 0); transform: scale(1.18); }
}

.dds-shimmer {
    position: absolute; top: 0; left: -120px; width: 80px; height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.22), transparent);
    animation: dds-shimmer-move 2.8s ease-in-out infinite 1.4s;
    pointer-events: none;
}
@keyframes dds-shimmer-move {
    0%   { left: -120px; }
    38%  { left: 360px; }
    100% { left: 360px; }
}

.dds-ripple-btn { position: relative; overflow: hidden; }
.dds-ripple-btn::after {
    content: ''; position: absolute; border-radius: 50%;
    width: 4px; height: 4px;
    background: rgba(255,255,255,.38);
    transform: scale(0); opacity: 1;
    pointer-events: none;
}
.dds-ripple-btn.rippling::after { animation: dds-ripple .55s linear; }
@keyframes dds-ripple { to { transform: scale(80); opacity: 0; } }
`);

const _root = document.createElement('div');
_root.id = 'DDS_Root';
_root.innerHTML = `

<div class="dds-ntf-shell" id="dds-ntf">
    <div class="dds-ntf-box" id="dds-ntf-box">
        <p class="dds-t1 dds-nosel" id="dds-ntf-title"></p>
        <p class="dds-t2 dds-nosel" id="dds-ntf-body" style="align-self:stretch;overflow-wrap:break-word;"></p>
    </div>
</div>

<div class="dds-shell" id="dds-shell">

    <div class="dds-hstack" style="align-self:flex-end;">
        <button class="dds-hide-btn dds-nosel" id="dds-toggle">
            <div class="dds-ico-box" id="dds-ico-wrap" style="background:rgba(255,255,255,.20);">
                <svg id="dds-ico-eye" width="14" height="14" viewBox="0 0 24 18" fill="none">
                    <path d="M12 3C7.5 3 3.73 5.5 2 9c1.73 3.5 5.5 6 10 6s8.27-2.5 10-6c-1.73-3.5-5.5-6-10-6zm0 10a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z" fill="#fff"/>
                    <line x1="2" y1="2" x2="22" y2="16" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
                </svg>
                <svg id="dds-ico-eyeoff" width="14" height="14" viewBox="0 0 24 18" fill="none" style="display:none;">
                    <path d="M12 3C7.5 3 3.73 5.5 2 9c1.73 3.5 5.5 6 10 6s8.27-2.5 10-6c-1.73-3.5-5.5-6-10-6zm0 10a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z" fill="rgb(var(--dds-blue))"/>
                </svg>
            </div>
            <p class="dds-t1 dds-nosel" id="dds-toggle-txt" style="color:#fff;font-size:14px;">Hide</p>
        </button>
    </div>

    <div class="dds-box" id="dds-box">

        <!-- PAGE: Auth -->
        <div class="dds-page active" id="dds-page-auth">
            <div class="dds-lock-ring">
                <svg width="28" height="28" viewBox="0 0 24 24" fill="none">
                    <rect x="3" y="11" width="18" height="11" rx="3" fill="none" stroke="rgb(var(--dds-blue))" stroke-width="2"/>
                    <path d="M7 11V7a5 5 0 0 1 10 0v4" stroke="rgb(var(--dds-blue))" stroke-width="2" stroke-linecap="round"/>
                    <circle cx="12" cy="16.5" r="1.5" fill="rgb(var(--dds-blue))"/>
                </svg>
            </div>
            <div style="display:flex;flex-direction:column;gap:4px;align-items:center;align-self:stretch;">
                <p class="dds-t1 dds-nosel" style="font-size:16px;text-align:center;">Authenticate to continue</p>
                <p class="dds-t2 dds-nosel" style="text-align:center;font-size:12px;">Enter your key to unlock all features</p>
            </div>
            <div class="dds-hr"></div>
            <div class="dds-vstack" style="align-self:stretch;">
                <div class="dds-hstack">
                    <div class="dds-input-wrap">
                        <div class="dds-ico-box" style="background:rgba(var(--dds-blue),.16);">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none">
                                <path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" stroke="rgba(var(--dds-blue),.75)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                            </svg>
                        </div>
                        <input id="dds-key-in" class="dds-input dds-nosel" type="password" placeholder="DS-XXXXXXXXXXXXXXXX" style="text-align:left;" autocomplete="off">
                    </div>
                    <button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-unlock-btn" style="flex:none;height:48px;border-radius:14px;padding:0 18px;position:relative;overflow:hidden;">
                        <div class="dds-shimmer"></div>
                        <span class="dds-lbl" id="dds-unlock-lbl" style="color:#fff;">UNLOCK</span>
                    </button>
                </div>
                <p class="dds-err" id="dds-auth-err"></p>
                <div id="dds-auth-spin" style="display:none;justify-content:center;">
                    <div class="dds-dot-ring" style="color:rgb(var(--dds-blue));"><span></span><span></span><span></span></div>
                </div>
            </div>
            <button class="dds-main-btn dds-nosel" id="dds-free-btn" style="width:100%;background:rgba(var(--dds-green),.13);outline:2px solid rgba(var(--dds-green),.30);outline-offset:-2px;">
                <div class="dds-pulse-dot"></div>
                <span class="dds-lbl" style="color:rgb(var(--dds-green));">Use Free Key</span>
            </button>
            <div class="dds-discord-card">
                <div style="display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;">
                    <p class="dds-t1 dds-nosel" style="font-size:11px;color:rgb(var(--dds-purple));">Get your own key</p>
                    <p class="dds-t2 dds-nosel" style="font-size:10px;">Join our Discord server</p>
                </div>
                <a class="dds-discord-link" href="${DISCORD_URL}" target="_blank" rel="noopener">Join</a>
            </div>
            <div class="dds-row" style="align-self:stretch;padding-top:2px;">
                <p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">discord.gg/ufBrcGemBH</p>
                <p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">v1.0</p>
            </div>
        </div>

        <!-- PAGE: Dashboard -->
        <div class="dds-page" id="dds-page-dash">
            <div class="dds-badge">
                <div style="display:flex;flex-direction:column;gap:4px;min-width:0;flex:1;">
                    <div style="display:flex;align-items:center;gap:7px;flex-wrap:nowrap;">
                        <p class="dds-badge-key dds-nosel" id="dds-key-disp" style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">DS-XXXX...</p>
                        <span class="dds-free-tag dds-nosel" id="dds-free-tag" style="display:none;">FREE</span>
                    </div>
                    <p class="dds-badge-meta dds-nosel" id="dds-key-meta">Batch: ? · CD: ?s</p>
                </div>
                <button class="dds-danger-btn dds-nosel" id="dds-chkey-btn">Change key</button>
            </div>
            <div class="dds-quota-wrap">
                <div class="dds-quota-row">
                    <span class="dds-quota-lbl dds-nosel">DAILY QUOTA</span>
                    <span class="dds-quota-val dds-nosel" id="dds-quota-val">0 / 0</span>
                </div>
                <div class="dds-track"><div class="dds-tfill" id="dds-quota-fill" style="width:0%"></div></div>
            </div>
            <div class="dds-cd" id="dds-cd">
                <span class="dds-cd-lbl dds-nosel">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none">
                        <circle cx="12" cy="12" r="9" stroke="rgb(var(--dds-orange))" stroke-width="2"/>
                        <path d="M12 7v5l3 3" stroke="rgb(var(--dds-orange))" stroke-width="2" stroke-linecap="round"/>
                    </svg>
                    Cooldown
                </span>
                <span class="dds-cd-time dds-nosel" id="dds-cd-time">00:00</span>
            </div>
            <div class="dds-tabs">
                <button class="dds-tab on dds-nosel" data-tab="link">Links</button>
                <button class="dds-tab dds-nosel"    data-tab="active">Active</button>
                <button class="dds-tab dds-nosel"    data-tab="hist">History</button>
            </div>

            <!-- TAB: Links -->
            <div class="dds-pane active" id="dds-pane-link">
                <div class="dds-info-card">
                    <p class="dds-t2 dds-nosel" style="font-size:11px;line-height:1.65;">
                        Create <span id="dds-batch-n" style="color:rgb(var(--dds-blue));font-weight:800;">?</span> links per batch. System runs in parallel.
                    </p>
                </div>
                <button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-create-btn" style="width:100%;position:relative;overflow:hidden;">
                    <div class="dds-shimmer"></div>
                    <div class="dds-ico-box" id="dds-create-ico" style="background:rgba(255,255,255,.20);">
                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
                            <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
                        </svg>
                    </div>
                    <span class="dds-lbl" id="dds-create-lbl" style="color:#fff;">Create Link Batch</span>
                </button>
                <div class="dds-prog-wrap" id="dds-create-prog">
                    <div class="dds-prog-fill" id="dds-create-fill"></div>
                </div>
                <p class="dds-sec-lbl">Output</p>
                <div class="dds-log" id="dds-log-link">Ready...</div>
            </div>

            <!-- TAB: Active -->
            <div class="dds-pane" id="dds-pane-active">
                <p class="dds-sec-lbl">Duolingo JWT</p>
                <div class="dds-input-wrap ta">
                    <textarea id="dds-jwt-in" class="dds-input dds-nosel" placeholder="eyJhbGci..." rows="3"></textarea>
                </div>
                <button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-activate-btn" style="width:100%;position:relative;overflow:hidden;">
                    <div class="dds-shimmer"></div>
                    <div class="dds-ico-box" id="dds-activate-ico" style="background:rgba(255,255,255,.20);">
                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
                            <path d="M5 12h14M12 5l7 7-7 7"/>
                        </svg>
                    </div>
                    <span class="dds-lbl" id="dds-activate-lbl" style="color:#fff;">Activate Super</span>
                </button>
                <div class="dds-log" id="dds-log-active">Waiting for command...</div>
            </div>

            <!-- TAB: History -->
            <div class="dds-pane" id="dds-pane-hist">
                <div class="dds-hstack">
                    <button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-loadhist-btn" style="flex:1;height:40px;border-radius:12px;padding:0 14px;position:relative;overflow:hidden;">
                        <div class="dds-ico-box" id="dds-loadhist-ico" style="background:rgba(255,255,255,.20);">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
                                <polyline points="1 4 1 10 7 10"/>
                                <path d="M3.51 15a9 9 0 1 0 .49-3.68"/>
                            </svg>
                        </div>
                        <span class="dds-lbl" id="dds-loadhist-lbl" style="color:#fff;font-size:13px;">Load history</span>
                    </button>
                    <button class="dds-sm-btn dds-nosel" id="dds-copyall-btn">
                        <div class="dds-ico-box" style="background:rgba(255,255,255,.20);">
                            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
                                <rect x="9" y="9" width="13" height="13" rx="2" stroke-linejoin="round"/>
                                <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
                            </svg>
                        </div>
                        <span class="dds-sm-lbl" style="color:#fff;">Copy all</span>
                    </button>
                </div>
                <div class="dds-log" id="dds-hist-log" style="max-height:200px;">Press load to view...</div>
            </div>

            <div class="dds-row" style="align-self:stretch;padding-top:2px;">
                <p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">discord.gg/ufBrcGemBH</p>
                <p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">v2.0</p>
            </div>
        </div>

        <!-- PAGE: Change Key -->
        <div class="dds-page" id="dds-page-chkey">
            <div style="display:flex;flex-direction:column;gap:4px;align-items:center;align-self:stretch;">
                <p class="dds-t1 dds-nosel" style="font-size:15px;text-align:center;">Switch Key</p>
                <p class="dds-t2 dds-nosel" style="text-align:center;font-size:12px;">Enter a new key or return to free tier</p>
            </div>
            <div class="dds-hr"></div>
            <div class="dds-vstack" style="align-self:stretch;">
                <div class="dds-hstack">
                    <div class="dds-input-wrap">
                        <div class="dds-ico-box" style="background:rgba(var(--dds-blue),.16);">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none">
                                <path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" stroke="rgba(var(--dds-blue),.75)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                            </svg>
                        </div>
                        <input id="dds-newkey-in" class="dds-input dds-nosel" type="password" placeholder="DS-XXXXXXXXXXXXXXXX" style="text-align:left;" autocomplete="off">
                    </div>
                    <button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-newkey-btn" style="flex:none;height:48px;border-radius:14px;padding:0 18px;position:relative;overflow:hidden;">
                        <div class="dds-shimmer"></div>
                        <span class="dds-lbl" id="dds-newkey-lbl" style="color:#fff;">Apply</span>
                    </button>
                </div>
                <p class="dds-err" id="dds-chkey-err"></p>
                <div id="dds-chkey-spin" style="display:none;justify-content:center;">
                    <div class="dds-dot-ring" style="color:rgb(var(--dds-blue));"><span></span><span></span><span></span></div>
                </div>
            </div>
            <button class="dds-main-btn dds-nosel" id="dds-activefree-btn" style="width:100%;background:rgba(var(--dds-green),.13);outline:2px solid rgba(var(--dds-green),.30);outline-offset:-2px;">
                <div class="dds-pulse-dot"></div>
                <span class="dds-lbl" style="color:rgb(var(--dds-green));">Activate Free Key</span>
            </button>
            <div class="dds-discord-card">
                <div style="display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;">
                    <p class="dds-t1 dds-nosel" style="font-size:11px;color:rgb(var(--dds-purple));">Get a premium key</p>
                    <p class="dds-t2 dds-nosel" style="font-size:10px;">More quota, faster cooldown</p>
                </div>
                <a class="dds-discord-link" href="${DISCORD_URL}" target="_blank" rel="noopener">Discord</a>
            </div>
            <button class="dds-sm-btn dds-nosel" id="dds-back-btn" style="width:100%;background:rgba(var(--dds-surface),.95);outline:2px solid rgba(var(--dds-border),.35);outline-offset:-2px;">
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="rgba(var(--dds-text2),.75)" stroke-width="2.5">
                    <path d="M19 12H5M12 19l-7-7 7-7"/>
                </svg>
                <span class="dds-sm-lbl" style="color:rgba(var(--dds-text2),.75);">Back</span>
            </button>
            <div class="dds-row" style="align-self:stretch;padding-top:2px;">
                <p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">duods.vercel.app</p>
                <p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">v1.0</p>
            </div>
        </div>

    </div>
</div>
`;
document.body.appendChild(_root);

_watchTheme();

function _ripple(btn) {
    btn.classList.remove('rippling');
    void btn.offsetWidth;
    btn.classList.add('rippling');
    setTimeout(() => btn.classList.remove('rippling'), 560);
}

function _notify(title, body, dur = 5) {
    const box = document.getElementById('dds-ntf-box');
    document.getElementById('dds-ntf-title').textContent = title;
    document.getElementById('dds-ntf-body').textContent  = body;
    box.classList.add('show');
    clearTimeout(_ntTimer);
    _ntTimer = setTimeout(() => box.classList.remove('show'), dur * 1000);
}

function _toggleVis(hide) {
    if (_animLock) return;
    _animLock = true;
    _hidden   = hide;

    const shell = document.getElementById('dds-shell');
    const box   = document.getElementById('dds-box');
    const icoE  = document.getElementById('dds-ico-eye');
    const icoH  = document.getElementById('dds-ico-eyeoff');
    const icoW  = document.getElementById('dds-ico-wrap');
    const txt   = document.getElementById('dds-toggle-txt');
    const btn   = document.getElementById('dds-toggle');
    const h     = box.offsetHeight;

    shell.style.transition = box.style.transition = '.8s cubic-bezier(.16,1,.32,1)';

    if (hide) {
        btn.style.background = 'rgba(var(--dds-card),.95)';
        btn.style.outline    = '2px solid rgba(var(--dds-blue),.25)';
        if (icoW) { icoW.style.background = 'rgba(var(--dds-blue),.16)'; }
        if (icoE) icoE.style.display = 'none';
        if (icoH) icoH.style.display = '';
        txt.textContent = 'Show';
        txt.style.color = 'rgb(var(--dds-blue))';
        shell.style.bottom = `-${h - 8}px`;
        box.style.filter   = 'blur(8px)';
        box.style.opacity  = '0';
    } else {
        btn.style.background = 'rgb(var(--dds-blue))';
        btn.style.outline    = '2px solid rgba(0,0,0,.18)';
        if (icoW) { icoW.style.background = 'rgba(255,255,255,.20)'; }
        if (icoH) icoH.style.display = 'none';
        if (icoE) icoE.style.display = '';
        txt.textContent = 'Hide';
        txt.style.color = '#fff';
        shell.style.bottom = '16px';
        box.style.filter   = '';
        box.style.opacity  = '';
    }

    setTimeout(() => {
        shell.style.transition = '';
        box.style.transition   = '';
        _animLock = false;
    }, 800);
}

document.getElementById('dds-toggle').addEventListener('click', () => _toggleVis(!_hidden));

function _goPage(to) {
    if (_pgLock || _curPg === to) return;
    _pgLock = true;

    const box    = document.getElementById('dds-box');
    const fromEl = document.getElementById(`dds-page-${_curPg}`);
    const toEl   = document.getElementById(`dds-page-${to}`);
    if (!fromEl || !toEl) { _pgLock = false; return; }

    const oldH = box.offsetHeight;
    fromEl.style.display = 'none';
    toEl.classList.add('active');
    toEl.style.opacity = '0';
    const newH = box.offsetHeight;
    toEl.classList.remove('active');
    toEl.style.opacity = '';
    fromEl.style.display = '';

    box.style.height     = oldH + 'px';
    box.style.transition = 'height .6s cubic-bezier(.34,1.56,.64,1)';

    fromEl.style.transition = 'opacity .35s, filter .35s, transform .35s';
    fromEl.style.opacity    = '0';
    fromEl.style.filter     = 'blur(6px)';
    fromEl.style.transform  = 'scale(.96)';
    requestAnimationFrame(() => { box.style.height = newH + 'px'; });

    setTimeout(() => {
        fromEl.classList.remove('active');
        fromEl.style.cssText = '';
        toEl.classList.add('active');
        toEl.style.opacity   = '0';
        toEl.style.filter    = 'blur(6px)';
        toEl.style.transform = 'scale(1.04)';
        void toEl.offsetWidth;
        toEl.style.transition = 'opacity .4s, filter .4s, transform .5s cubic-bezier(.34,1.56,.64,1)';
        toEl.style.opacity    = '1';
        toEl.style.filter     = 'blur(0)';
        toEl.style.transform  = 'scale(1)';
        _curPg = to;
        setTimeout(() => {
            box.style.height     = '';
            box.style.transition = '';
            toEl.style.cssText   = '';
            _pgLock = false;
        }, 400);
    }, 380);
}

document.querySelectorAll('#DDS_Root .dds-tab').forEach(btn => {
    btn.addEventListener('click', () => {
        document.querySelectorAll('#DDS_Root .dds-tab').forEach(b => b.classList.remove('on'));
        btn.classList.add('on');
        ['link', 'active', 'hist'].forEach(t => {
            const el = document.getElementById(`dds-pane-${t}`);
            if (el) el.classList.toggle('active', btn.dataset.tab === t);
        });
    });
});

function _log(id, msg, type = 'i') {
    const el = document.getElementById(id);
    if (!el) return;
    const ts  = new Date().toLocaleTimeString('en-US', { hour12: false });
    const cls = { o: 'lo', e: 'le', i: 'li', w: 'lw' }[type] || 'li';
    el.innerHTML += `<span class="${cls}">[${ts}] ${msg}</span>\n`;
    el.scrollTop = el.scrollHeight;
}
function _logClear(id) {
    const el = document.getElementById(id);
    if (el) el.innerHTML = '';
}

const C_BLUE  = { bg: 'rgb(var(--dds-blue))',        out: 'rgba(0,0,0,.18)',             tc: '#fff' };
const C_GREEN = { bg: 'rgba(var(--dds-green),.12)',   out: 'rgba(var(--dds-green),.28)',  tc: 'rgb(var(--dds-green))' };
const C_GRAY  = { bg: 'rgba(var(--dds-surface),.95)', out: 'rgba(var(--dds-border),.40)', tc: 'rgba(var(--dds-text2),.80)' };

function _setLoading(btnId, icoId, on) {
    const ico = document.getElementById(icoId);
    if (!ico) return;
    if (on) {
        ico.innerHTML = `<div class="dds-spin-ring"></div>`;
    } else {
        const svgMap = {
            'dds-create-ico':   `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`,
            'dds-activate-ico': `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg>`,
            'dds-loadhist-ico': `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 .49-3.68"/></svg>`
        };
        if (svgMap[icoId]) ico.innerHTML = svgMap[icoId];
    }
}

function _btnState(id, clr, text, icoId) {
    const btn = document.getElementById(id);
    if (!btn) return;
    const lbl = btn.querySelector('.dds-lbl') || btn.querySelector('.dds-sm-lbl');
    if (!lbl) return;

    const prev = lbl.textContent;
    lbl.textContent = text;
    const nw = btn.offsetWidth;
    lbl.textContent = prev;
    btn.style.width = btn.offsetWidth + 'px';

    requestAnimationFrame(() => {
        lbl.style.opacity = '0';
        lbl.style.filter  = 'blur(4px)';
        btn.style.width        = nw + 'px';
        btn.style.background   = clr.bg;
        btn.style.outline      = `solid 2px ${clr.out}`;
        btn.style.outlineOffset = '-2px';
        if (icoId) {
            const icoEl = document.getElementById(icoId);
            if (icoEl) icoEl.style.background = clr.tc === '#fff' ? 'rgba(255,255,255,.20)' : 'rgba(0,0,0,.06)';
        }
    });

    setTimeout(() => {
        lbl.style.transition = '0s';
        lbl.style.color = clr.tc;
        void lbl.offsetWidth;
        lbl.style.transition = '.35s';
        lbl.textContent = text;
        requestAnimationFrame(() => {
            lbl.style.opacity = '1';
            lbl.style.filter  = 'blur(0)';
        });
        setTimeout(() => { btn.style.width = ''; }, 400);
    }, 380);
}

function _refreshQuota(used, limit) {
    const pct  = limit > 0 ? Math.min((used / limit) * 100, 100) : 0;
    const fill = document.getElementById('dds-quota-fill');
    if (fill) { fill.style.width = pct + '%'; fill.classList.toggle('warn', pct > 85); }
    const val = document.getElementById('dds-quota-val');
    if (val) val.textContent = `${used} / ${limit}`;
}

function _saveCD(sec) {
    if (sec > 0) _set(CD_STORE, Date.now() + sec * 1000);
}

function _loadCD() {
    const exp = _get(CD_STORE, 0);
    if (!exp) return 0;
    const rem = Math.ceil((exp - Date.now()) / 1000);
    return rem > 0 ? rem : 0;
}

function _startCD(sec, save = true) {
    if (_cdTimer) clearInterval(_cdTimer);
    let rem = Math.ceil(sec);
    if (save) _saveCD(rem);

    const cdEl = document.getElementById('dds-cd');
    const cdTx = document.getElementById('dds-cd-time');
    const cBtn = document.getElementById('dds-create-btn');

    if (cdEl) cdEl.classList.add('on');
    if (cBtn) cBtn.disabled = true;

    function tick() {
        if (rem <= 0) {
            clearInterval(_cdTimer);
            if (cdEl) cdEl.classList.remove('on');
            if (cBtn) cBtn.disabled = false;
            return;
        }
        const mm = String(Math.floor(rem / 60)).padStart(2, '0');
        const ss = String(rem % 60).padStart(2, '0');
        if (cdTx) cdTx.textContent = `${mm}:${ss}`;
        rem--;
    }
    tick();
    _cdTimer = setInterval(tick, 1000);
}

function _applyKey(data, key) {
    _keyData = data;
    _isFree  = key === FREE_KEY;

    const disp    = document.getElementById('dds-key-disp');
    const meta    = document.getElementById('dds-key-meta');
    const batchN  = document.getElementById('dds-batch-n');
    const freeTag = document.getElementById('dds-free-tag');

    if (disp) disp.textContent = key.slice(0, 16) + '...';
    if (freeTag) freeTag.style.display = _isFree ? '' : 'none';

    let expStr = '';
    if (data.expires_in_seconds != null) {
        const h = Math.floor(data.expires_in_seconds / 3600);
        const m = Math.floor((data.expires_in_seconds % 3600) / 60);
        expStr = ` · Exp: ${h}h${m}m`;
    }
    if (meta) meta.textContent =
        `Batch: ${data.batch_size} · CD: ${data.cd_seconds}s · Uses: ${data.used_total}/${data.total_limit}${expStr}`;
    if (batchN) batchN.textContent = data.batch_size;

    _refreshQuota(data.used_today || 0, data.daily_limit);

    const cdRemain = data.cooldown_remain && data.cooldown_remain > 0
        ? data.cooldown_remain : _loadCD();
    if (cdRemain > 0) _startCD(cdRemain, false);
}

async function _unlock(key, errId, spinId, btnId, lblId) {
    const errEl = document.getElementById(errId);
    const spin  = document.getElementById(spinId);
    const btn   = document.getElementById(btnId);
    const lbl   = lblId ? document.getElementById(lblId) : null;

    if (!key) return;
    if (btn) { btn.disabled = true; if (btn.classList.contains('dds-ripple-btn')) _ripple(btn); }
    if (errEl) errEl.textContent = '';
    if (spin) spin.style.display = 'flex';
    if (lbl) { lbl.style.opacity = '0'; lbl.style.filter = 'blur(3px)'; }

    const r = await _fetchKey(key);

    if (btn) btn.disabled = false;
    if (spin) spin.style.display = 'none';
    if (lbl) {
        setTimeout(() => {
            lbl.style.transition = '.35s';
            lbl.style.opacity    = '1';
            lbl.style.filter     = 'blur(0)';
        }, 80);
    }

    if (r.ok && _sess) {
        _set('dds_key', key);
        _applyKey(r, key);
        _goPage('dash');
        _notify('Unlocked 🎉', `Welcome, ${r.label || 'user'}!`);
    } else {
        _sess = null;
        if (errEl) errEl.textContent = 'Invalid key: ' + (r.error || 'Unknown error');
    }
}

document.getElementById('dds-unlock-btn').addEventListener('click', () => {
    const key = document.getElementById('dds-key-in').value.trim();
    _unlock(key, 'dds-auth-err', 'dds-auth-spin', 'dds-unlock-btn', 'dds-unlock-lbl');
});
document.getElementById('dds-key-in').addEventListener('keydown', e => {
    if (e.key === 'Enter') {
        const key = document.getElementById('dds-key-in').value.trim();
        _unlock(key, 'dds-auth-err', 'dds-auth-spin', 'dds-unlock-btn', 'dds-unlock-lbl');
    }
});
document.getElementById('dds-free-btn').addEventListener('click', () => {
    _unlock(FREE_KEY, 'dds-auth-err', 'dds-auth-spin', 'dds-free-btn', null);
});
document.getElementById('dds-chkey-btn').addEventListener('click', () => {
    document.getElementById('dds-newkey-in').value = '';
    document.getElementById('dds-chkey-err').textContent = '';
    _goPage('chkey');
});
document.getElementById('dds-back-btn').addEventListener('click', () => {
    _goPage('dash');
});
document.getElementById('dds-newkey-btn').addEventListener('click', () => {
    const key = document.getElementById('dds-newkey-in').value.trim();
    _unlock(key, 'dds-chkey-err', 'dds-chkey-spin', 'dds-newkey-btn', 'dds-newkey-lbl');
});
document.getElementById('dds-newkey-in').addEventListener('keydown', e => {
    if (e.key === 'Enter') {
        const key = document.getElementById('dds-newkey-in').value.trim();
        _unlock(key, 'dds-chkey-err', 'dds-chkey-spin', 'dds-newkey-btn', 'dds-newkey-lbl');
    }
});
document.getElementById('dds-activefree-btn').addEventListener('click', async () => {
    const cdSaved = _loadCD();
    const r = await _fetchKey(FREE_KEY);
    if (r.ok && _sess) {
        _set('dds_key', FREE_KEY);
        _applyKey(r, FREE_KEY);
        if (cdSaved > 0) _startCD(cdSaved, false);
        _goPage('dash');
        _notify('Free Key Active ✅', 'Now using the free public key.');
    } else {
        _sess = null;
        document.getElementById('dds-chkey-err').textContent = 'Could not activate free key. Try again.';
    }
});

document.getElementById('dds-create-btn').addEventListener('click', async () => {
    if (!_sess) return;
    const key = _get('dds_key', '');
    if (!key) return;

    const btn  = document.getElementById('dds-create-btn');
    const prog = document.getElementById('dds-create-prog');
    const fill = document.getElementById('dds-create-fill');

    _ripple(btn);
    btn.disabled = true;
    _setLoading('dds-create-btn', 'dds-create-ico', true);
    _btnState('dds-create-btn', C_GRAY, 'Working...', null);
    prog.classList.add('on');
    fill.style.width = '15%';
    _logClear('dds-log-link');
    _log('dds-log-link', `Creating ${_keyData?.batch_size || '?'} links in parallel...`, 'i');
    fill.style.width = '50%';

    const r = await _req('/api/link/create', 'POST', { key });
    fill.style.width = '100%';
    setTimeout(() => { prog.classList.remove('on'); }, 600);
    btn.disabled = false;
    _setLoading('dds-create-btn', 'dds-create-ico', false);

    if (r.ok && r.urls && r.urls.length > 0) {
        _btnState('dds-create-btn', C_GREEN, `Done (${r.count})`, 'dds-create-ico');
        _log('dds-log-link', `Created ${r.count} link${r.failed > 0 ? ` (${r.failed} failed)` : ''}`, 'o');
        r.urls.forEach((url, i) => {
            _log('dds-log-link', `#${i + 1}  ${url}`, 'o');
            _hist.unshift(url);
        });
        if (_keyData) {
            _keyData.used_today = (_keyData.used_today || 0) + r.count;
            _refreshQuota(_keyData.used_today, _keyData.daily_limit);
        }
        if (_keyData?.cd_seconds) _startCD(_keyData.cd_seconds);
        setTimeout(() => _btnState('dds-create-btn', C_BLUE, 'Create Link Batch', 'dds-create-ico'), 3000);
    } else {
        _btnState('dds-create-btn', C_BLUE, 'Create Link Batch', 'dds-create-ico');
        const msg = r.error || 'Unknown error';
        _log('dds-log-link', `Error: ${msg}`, 'e');
        const cdM = msg.match(/Cooldown.*?(\d+):(\d+)/);
        if (cdM) _startCD(parseInt(cdM[1]) * 60 + parseInt(cdM[2]));
        else if (r.cooldown_remain > 0) _startCD(r.cooldown_remain);
    }
});

document.getElementById('dds-activate-btn').addEventListener('click', async () => {
    if (!_sess) return;

    const jwt = document.getElementById('dds-jwt-in').value.trim();
    if (!jwt) return _log('dds-log-active', 'Enter your JWT token!', 'e');

    if (!_hist.length) return _log('dds-log-active', 'No links in history! Create or load links first.', 'e');
    const code = _hist[0].split('/').pop();

    let uid = '';
    try {
        const seg = jwt.split('.')[1];
        const pad = seg + '='.repeat((4 - seg.length % 4) % 4);
        uid = String(JSON.parse(atob(pad)).sub || '');
    } catch { return _log('dds-log-active', 'Invalid JWT format!', 'e'); }
    if (!uid) return _log('dds-log-active', 'Could not extract UID from JWT.', 'e');

    const btn = document.getElementById('dds-activate-btn');
    _ripple(btn);
    btn.disabled = true;
    _setLoading('dds-activate-btn', 'dds-activate-ico', true);
    _btnState('dds-activate-btn', C_GRAY, 'Activating...', null);
    _log('dds-log-active', `UID: ${uid} · Code: ${code}`, 'i');

    const r = await _req('/api/active', 'POST', { jwt, duo_uid: uid, invite_code: code });
    btn.disabled = false;
    _setLoading('dds-activate-btn', 'dds-activate-ico', false);
    _btnState('dds-activate-btn', C_BLUE, 'Activate Super', 'dds-activate-ico');

    if (r.ok) {
        _log('dds-log-active',
            r.already_member
                ? 'Account already has Super Duolingo.'
                : 'Activated successfully! Open the app to verify.',
            'o');
        _notify('Super Activated 🦉', 'Free Super Duolingo activated!');
    } else {
        _log('dds-log-active', `Error: ${r.error || 'Failed'}`, 'e');
    }
});

document.getElementById('dds-loadhist-btn').addEventListener('click', async () => {
    if (!_sess) return;
    const key = _get('dds_key', '');
    if (!key) return;

    const btn = document.getElementById('dds-loadhist-btn');
    _ripple(btn);
    btn.disabled = true;
    _setLoading('dds-loadhist-btn', 'dds-loadhist-ico', true);
    _btnState('dds-loadhist-btn', C_GRAY, 'Loading...', null);

    const r = await _req(`/api/link/list?key=${encodeURIComponent(key)}`, 'GET');
    btn.disabled = false;
    _setLoading('dds-loadhist-btn', 'dds-loadhist-ico', false);
    _btnState('dds-loadhist-btn', C_BLUE, 'Load history', 'dds-loadhist-ico');

    if (r.ok && r.links) {
        _hist = r.links;
        _renderHist();
        _notify('Loaded ✓', `${r.links.length} links in history.`, 3);
    } else {
        document.getElementById('dds-hist-log').innerHTML =
            `<span class="le">Error: ${r.error || 'Unknown'}</span>`;
    }
});

function _renderHist() {
    const el = document.getElementById('dds-hist-log');
    el.innerHTML = '';
    if (!_hist.length) { el.innerHTML = '<span class="lw">No links yet.</span>'; return; }
    _hist.forEach((url, i) => {
        const code = url.split('/').pop();
        el.innerHTML +=
            `<span class="lo">#${String(i + 1).padStart(3, '0')} ${url}</span>` +
            `<span class="dds-copy-tag" data-url="${url}" data-code="${code}">copy</span>\n`;
    });
    el.querySelectorAll('.dds-copy-tag').forEach(tag => {
        tag.addEventListener('click', e => {
            e.stopPropagation();
            navigator.clipboard?.writeText(tag.dataset.url).catch(() => {});
            tag.textContent = 'ok ✓';
            setTimeout(() => { tag.textContent = 'copy'; }, 1200);
        });
    });
}

document.getElementById('dds-copyall-btn').addEventListener('click', () => {
    if (!_hist.length) return;
    navigator.clipboard?.writeText(_hist.join('\n')).catch(() => {});
    const lbl = document.getElementById('dds-copyall-btn').querySelector('.dds-sm-lbl');
    lbl.textContent = 'Copied!';
    setTimeout(() => { lbl.textContent = 'Copy all'; }, 1500);
});

(function _boot() {
    const saved = _get('dds_key', '');
    const key   = saved || FREE_KEY;

    const shell = document.getElementById('dds-shell');
    const box   = document.getElementById('dds-box');
    shell.style.bottom = `-${box.offsetHeight - 8}px`;
    box.style.opacity  = '0';
    box.style.filter   = 'blur(8px)';

    _fetchKey(key).then(r => {
        if (r.ok && _sess) {
            if (!saved) _set('dds_key', FREE_KEY);
            _applyKey(r, key);
            _goPage('dash');
        } else {
            _sess = null;
            if (key !== FREE_KEY) _set('dds_key', '');
            document.getElementById('dds-key-in').value = saved || '';
        }
    });

    _toggleVis(false);
    setTimeout(() => {
        shell.style.transition = box.style.transition = '.8s cubic-bezier(.16,1,.32,1)';
        shell.style.bottom = '16px';
        box.style.opacity  = '';
        box.style.filter   = '';
        setTimeout(() => { shell.style.transition = ''; box.style.transition = ''; }, 800);
    }, 600);
})();

})();