{"id":14909,"date":"2026-04-10T13:22:36","date_gmt":"2026-04-10T13:22:36","guid":{"rendered":"https:\/\/vocgems.com\/?page_id=14909"},"modified":"2026-07-13T14:38:07","modified_gmt":"2026-07-13T14:38:07","slug":"%d0%ba%d0%be%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%82%d0%be%d1%80-%d0%b8%d0%b8","status":"publish","type":"page","link":"https:\/\/vocgems.com\/ru\/ai-constructor\/","title":{"rendered":"\u0418\u0418-\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"14909\" class=\"elementor elementor-14909\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f780e47 e-flex e-con-boxed e-con e-parent\" data-id=\"f780e47\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-8c27381 elementor-widget elementor-widget-html\" data-id=\"8c27381\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"en\" id=\"vocHtmlRoot\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>VOC Gems \u2014 AI Jewelry Constructor<\/title>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=DM+Sans:wght@300;400;500;600;700&display=swap\" rel=\"stylesheet\">\n    <style>\n        :root {\n            --primary: #45BDB0;\n            --primary-dark: #3AA89C;\n            --primary-light: #E5F8F6;\n            --primary-glow: rgba(69, 189, 176, 0.12);\n            --primary-50: rgba(69, 189, 176, 0.05);\n            --gold: #B8965A;\n            --gold-light: #F7F1E5;\n            --gold-glow: rgba(184, 150, 90, 0.15);\n            --rose: #C4848A;\n            --platinum: #9BA4AB;\n            --dark: #141414;\n            --text: #2A2A2A;\n            --text-mid: #555;\n            --text-light: #777;\n            --gray: #999;\n            --border: #E8E6E3;\n            --border-light: #F0EEEB;\n            --bg: #FBFAF9;\n            --bg-card: #FFFFFF;\n            --bg-warm: #F5F3F0;\n            --bg-panel: #FEFEFE;\n            --shadow-sm: 0 2px 8px rgba(0,0,0,0.04);\n            --shadow-md: 0 8px 30px rgba(0,0,0,0.06);\n            --shadow-lg: 0 20px 60px rgba(0,0,0,0.08);\n            --shadow-glow: 0 8px 30px var(--primary-glow);\n            --radius-sm: 10px;\n            --radius-md: 16px;\n            --radius-lg: 24px;\n            --radius-xl: 32px;\n            --transition: 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n        }\n\n        * { margin: 0; padding: 0; box-sizing: border-box; }\n\n        body {\n            font-family: 'DM Sans', sans-serif;\n            background: var(--bg);\n            color: var(--text);\n            min-height: 100vh;\n            overflow-x: hidden;\n            -webkit-font-smoothing: antialiased;\n        }\n\n        \/* Header & breadcrumb removed \u2014 handled by WP Theme Builder *\/\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 HERO *\/\n        .page-hero {\n            text-align: center;\n            padding: 36px 48px 36px;\n            position: relative;\n        }\n\n        .page-hero::before {\n            content: '';\n            position: absolute;\n            top: 0; left: 50%;\n            transform: translateX(-50%);\n            width: 200px; height: 200px;\n            background: radial-gradient(ellipse, var(--primary-glow), transparent 70%);\n            pointer-events: none;\n        }\n\n        .page-title {\n            font-family: 'Playfair Display', serif;\n            font-size: 42px;\n            font-weight: 400;\n            color: var(--dark);\n            letter-spacing: 1px;\n            margin-bottom: 10px;\n            animation: heroIn 0.8s ease both;\n        }\n\n        .page-subtitle {\n            font-size: 15px;\n            color: var(--text-light);\n            font-weight: 300;\n            letter-spacing: 0.3px;\n            animation: heroIn 0.8s ease 0.1s both;\n        }\n\n        @keyframes heroIn {\n            from { opacity: 0; transform: translateY(16px); }\n            to { opacity: 1; transform: translateY(0); }\n        }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 STEP INDICATOR *\/\n        .steps-bar {\n            display: flex;\n            justify-content: center;\n            gap: 0;\n            padding: 0 48px 32px;\n            animation: heroIn 0.8s ease 0.15s both;\n        }\n\n        .step-item {\n            display: flex; align-items: center; gap: 10px;\n            padding: 10px 24px;\n            font-size: 12px;\n            font-weight: 500;\n            letter-spacing: 0.8px;\n            text-transform: uppercase;\n            color: var(--gray);\n            position: relative;\n            cursor: pointer;\n            transition: color var(--transition);\n        }\n\n        .step-item.active { color: var(--primary); }\n        .step-item.completed { color: var(--primary); }\n\n        .step-num {\n            width: 28px; height: 28px;\n            border-radius: 50%;\n            border: 1.5px solid var(--border);\n            display: flex; align-items: center; justify-content: center;\n            font-size: 11px; font-weight: 600;\n            transition: all var(--transition);\n        }\n\n        .step-item.active .step-num {\n            background: var(--primary);\n            border-color: var(--primary);\n            color: white;\n        }\n\n        .step-item.completed .step-num {\n            background: var(--primary-light);\n            border-color: var(--primary);\n            color: var(--primary);\n        }\n\n        .step-connector {\n            width: 40px; height: 1px;\n            background: var(--border);\n            align-self: center;\n        }\n\n        .step-connector.active { background: var(--primary); }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 MAIN LAYOUT *\/\n        .main {\n            display: grid;\n            grid-template-columns: 380px 1fr;\n            max-width: 1520px;\n            margin: 0 auto;\n            padding: 0 32px 80px;\n            gap: 28px;\n        }\n        .panel-right { display: none !important; }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 LEFT PANEL *\/\n        .panel-left {\n            background: var(--bg-card);\n            border: 1px solid var(--border-light);\n            border-radius: var(--radius-lg);\n            padding: 28px;\n            height: fit-content;\n            animation: panelIn 0.7s ease 0.2s both;\n            transition: box-shadow var(--transition);\n        }\n\n        .panel-left:hover { box-shadow: var(--shadow-md); }\n\n        @keyframes panelIn {\n            from { opacity: 0; transform: translateY(20px); }\n            to { opacity: 1; transform: translateY(0); }\n        }\n\n        .panel-title {\n            font-family: 'Playfair Display', serif;\n            font-size: 22px;\n            font-weight: 500;\n            color: var(--dark);\n            margin-bottom: 24px;\n            padding-bottom: 16px;\n            border-bottom: 1px solid var(--border-light);\n            display: flex; align-items: center; gap: 10px;\n        }\n\n        .panel-title-icon {\n            width: 20px; height: 20px;\n            stroke: var(--primary);\n            stroke-width: 1.5;\n            fill: none;\n        }\n\n        .param-section { margin-bottom: 26px; }\n\n        .param-label {\n            font-size: 10px;\n            text-transform: uppercase;\n            letter-spacing: 2.5px;\n            color: var(--gray);\n            margin-bottom: 12px;\n            font-weight: 600;\n        }\n\n        \/* \u2500\u2500\u2500 Stone Card \u2500\u2500\u2500 *\/\n        .stone-card {\n            background: linear-gradient(135deg, var(--bg-warm), var(--bg-card));\n            border-radius: var(--radius-md);\n            padding: 20px;\n            margin-bottom: 26px;\n            border: 1px solid var(--border);\n            cursor: pointer;\n            transition: all var(--transition);\n            position: relative;\n            overflow: hidden;\n        }\n\n        .stone-card::before {\n            content: '';\n            position: absolute;\n            top: -50%; right: -50%;\n            width: 100%; height: 100%;\n            background: radial-gradient(circle, rgba(69,189,176,0.06), transparent 60%);\n            transition: transform var(--transition);\n        }\n\n        .stone-card:hover {\n            border-color: var(--primary);\n            box-shadow: var(--shadow-glow);\n        }\n\n        .stone-card:hover::before { transform: translate(-20%, 20%); }\n\n        .stone-visual {\n            width: 100%; height: 160px;\n            background: linear-gradient(160deg, #1a2a3a, #0d1b2a);\n            border-radius: var(--radius-sm);\n            display: flex; align-items: center; justify-content: center;\n            margin-bottom: 16px;\n            position: relative;\n            overflow: hidden;\n        }\n\n        .stone-visual::before {\n            content: '';\n            position: absolute; inset: 0;\n            background: radial-gradient(ellipse at 40% 40%, rgba(69,189,176,0.15), transparent 60%);\n            pointer-events: none;\n        }\n\n        .stone-gem-icon {\n            width: 70px; height: 70px;\n            filter: drop-shadow(0 8px 25px rgba(69,189,176,0.4));\n            animation: gemFloat 4s ease-in-out infinite, gemRotate 8s ease-in-out infinite;\n        }\n\n        @keyframes gemRotate {\n            0%, 100% { transform: rotate(-2deg); }\n            50% { transform: rotate(2deg); }\n        }\n\n        .gem-sparkles { position: absolute; inset: 0; pointer-events: none; }\n        .gem-sparkle {\n            position: absolute;\n            font-size: 10px;\n            color: rgba(255,255,255,0.7);\n            animation: sparkleGem 2s ease-in-out infinite;\n        }\n        @keyframes sparkleGem {\n            0%, 100% { opacity: 0; transform: scale(0.5); }\n            50% { opacity: 1; transform: scale(1.2); }\n        }\n\n        @keyframes gemFloat {\n            0%, 100% { transform: translateY(0) rotate(0deg); }\n            50% { transform: translateY(-6px) rotate(2deg); }\n        }\n\n        .stone-badge {\n            position: absolute;\n            top: 10px; right: 10px;\n            background: rgba(255,255,255,0.9);\n            backdrop-filter: blur(8px);\n            padding: 4px 10px;\n            border-radius: 20px;\n            font-size: 10px;\n            font-weight: 600;\n            color: var(--primary);\n            letter-spacing: 0.5px;\n        }\n\n        .stone-details {\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-start;\n        }\n\n        .stone-name {\n            font-family: 'Playfair Display', serif;\n            font-size: 20px;\n            font-weight: 600;\n            color: var(--dark);\n            margin-bottom: 4px;\n        }\n\n        .stone-specs {\n            font-size: 12px;\n            color: var(--text-light);\n            line-height: 1.6;\n        }\n\n        .stone-price {\n            font-family: 'Playfair Display', serif;\n            font-size: 22px;\n            font-weight: 600;\n            color: var(--primary);\n        }\n\n        .stone-change {\n            display: flex; \n            align-items: center; \n            justify-content: space-between;\n            padding: 12px 16px;\n            margin: -28px -28px 16px -28px;\n            background: var(--primary-light);\n            border-radius: var(--radius-lg) var(--radius-lg) 0 0;\n            font-size: 13px;\n            color: var(--primary-dark);\n            font-weight: 600;\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n\n        .stone-change:hover { \n            background: var(--primary);\n            color: white;\n        }\n\n        .stone-change svg {\n            width: 14px; height: 14px;\n            stroke: currentColor; stroke-width: 2; fill: none;\n        }\n        \n        .stone-change-btn {\n            width: 100%;\n            padding: 14px 20px;\n            background: #45BDB0;\n            border: 2px solid #45BDB0;\n            border-radius: var(--radius-md);\n            font-family: 'DM Sans', sans-serif;\n            font-size: 14px;\n            font-weight: 600;\n            color: #ffffff !important;\n            cursor: pointer;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 8px;\n            transition: all var(--transition);\n        }\n        \n        .stone-change-btn:hover {\n            background: #3AA89C;\n            border-color: #3AA89C;\n            color: #ffffff !important;\n            transform: translateY(-1px);\n            box-shadow: 0 4px 15px rgba(69,189,176,0.3);\n        }\n        \n        .stone-change-btn svg {\n            width: 16px; height: 16px;\n            stroke: currentColor;\n            stroke-width: 2;\n            fill: none;\n        }\n\n        \/* \u2500\u2500\u2500 Type Selector (6 types) \u2500\u2500\u2500 *\/\n        .type-grid {\n            display: grid;\n            grid-template-columns: repeat(3, 1fr);\n            gap: 8px;\n        }\n\n        .type-btn {\n            background: var(--bg-card);\n            border: 1.5px solid var(--border);\n            border-radius: var(--radius-sm);\n            padding: 14px 6px;\n            text-align: center;\n            cursor: pointer;\n            transition: all var(--transition);\n            position: relative;\n        }\n\n        .type-btn:hover {\n            border-color: var(--primary);\n            transform: translateY(-2px);\n            box-shadow: var(--shadow-glow);\n        }\n\n        .type-btn.active {\n            background: var(--primary-light);\n            border-color: var(--primary);\n        }\n\n        .type-btn-icon {\n            width: 28px; height: 28px;\n            margin: 0 auto 6px;\n            stroke: var(--text-light);\n            stroke-width: 1.5;\n            fill: none;\n            transition: all var(--transition);\n        }\n\n        .type-btn.active .type-btn-icon,\n        .type-btn:hover .type-btn-icon {\n            stroke: var(--primary);\n            transform: scale(1.1);\n        }\n\n        .type-btn-label {\n            font-size: 11px;\n            font-weight: 600;\n            color: var(--text-mid);\n            letter-spacing: 0.3px;\n        }\n\n        .type-btn.active .type-btn-label { color: var(--primary-dark); }\n\n        \/* \u2500\u2500\u2500 Gender \u2500\u2500\u2500 *\/\n        .gender-grid {\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: 8px;\n        }\n\n        .gender-btn {\n            display: flex; align-items: center; justify-content: center; gap: 8px;\n            background: var(--bg-card);\n            border: 1.5px solid var(--border);\n            border-radius: var(--radius-sm);\n            padding: 13px;\n            cursor: pointer;\n            transition: all var(--transition);\n            font-size: 13px;\n            font-weight: 500;\n            color: var(--text-mid);\n        }\n\n        .gender-btn:hover {\n            border-color: var(--primary);\n            background: var(--primary-50);\n        }\n\n        .gender-btn.active {\n            background: var(--primary-light);\n            border-color: var(--primary);\n            color: var(--primary-dark);\n        }\n\n        .gender-btn svg {\n            width: 18px; height: 18px;\n            stroke: var(--gray); stroke-width: 2; fill: none;\n            transition: stroke var(--transition);\n        }\n\n        .gender-btn.active svg,\n        .gender-btn:hover svg { stroke: var(--primary); }\n\n        \/* \u2500\u2500\u2500 Ring Size \u2500\u2500\u2500 *\/\n        .ring-size-grid {\n            display: grid;\n            grid-template-columns: repeat(5, 1fr);\n            gap: 6px;\n        }\n\n        .size-btn {\n            background: var(--bg-card);\n            border: 1.5px solid var(--border);\n            border-radius: 8px;\n            padding: 9px 4px;\n            text-align: center;\n            cursor: pointer;\n            transition: all var(--transition);\n            font-size: 12px;\n            font-weight: 600;\n            color: var(--text-mid);\n        }\n\n        .size-btn:hover {\n            border-color: var(--primary);\n            background: var(--primary-50);\n        }\n\n        .size-btn.active {\n            background: var(--primary);\n            border-color: var(--primary);\n            color: white;\n        }\n\n        \/* \u2500\u2500\u2500 Metal Selector \u2500\u2500\u2500 *\/\n        .metal-options { display: flex; flex-direction: column; gap: 8px; }\n\n        .metal-btn {\n            display: flex; align-items: center; gap: 14px;\n            background: var(--bg-card);\n            border: 1.5px solid var(--border);\n            border-radius: var(--radius-sm);\n            padding: 13px 16px;\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n\n        .metal-btn:hover {\n            border-color: var(--primary);\n            background: var(--primary-50);\n            transform: translateX(3px);\n        }\n\n        .metal-btn.active {\n            background: var(--primary-light);\n            border-color: var(--primary);\n        }\n\n        .metal-circle {\n            width: 30px; height: 30px;\n            border-radius: 50%;\n            flex-shrink: 0;\n            box-shadow: inset 0 -2px 4px rgba(0,0,0,0.1), 0 2px 8px rgba(0,0,0,0.1);\n            transition: transform var(--transition);\n        }\n\n        .metal-btn:hover .metal-circle { transform: scale(1.1); }\n\n        .metal-circle.gold { background: linear-gradient(145deg, #F5D76E, #D4A520, #AA8420); }\n        .metal-circle.white-gold { background: linear-gradient(145deg, #FAFAFA, #E0E0E0, #B8B8B8); }\n        .metal-circle.rose-gold { background: linear-gradient(145deg, #F0B8BC, #D4919A, #B87080); }\n        .metal-circle.platinum { background: linear-gradient(145deg, #E0E4E8, #C0C8D0, #98A4B0); }\n\n        .metal-name {\n            font-size: 13px;\n            font-weight: 600;\n            color: var(--text);\n        }\n\n        \/* \u2500\u2500\u2500 Gold Grade \u2500\u2500\u2500 *\/\n        .grade-grid {\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: 8px;\n        }\n\n        .grade-btn {\n            background: var(--bg-card);\n            border: 1.5px solid var(--border);\n            border-radius: var(--radius-sm);\n            padding: 14px;\n            text-align: center;\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n\n        .grade-btn:hover {\n            border-color: var(--gold);\n            background: var(--gold-light);\n        }\n\n        .grade-btn.active {\n            background: var(--gold-light);\n            border-color: var(--gold);\n            box-shadow: 0 4px 15px var(--gold-glow);\n        }\n\n        .grade-btn-label {\n            font-size: 14px; font-weight: 600;\n            color: var(--text);\n        }\n\n        .grade-btn-desc {\n            font-size: 11px;\n            color: var(--text-light);\n            margin-top: 2px;\n        }\n\n        \/* \u2500\u2500\u2500 Style Selector \u2500\u2500\u2500 *\/\n        .style-scroll {\n            padding-right: 4px;\n            overflow: visible;\n        }\n\n        .style-scroll::-webkit-scrollbar { width: 4px; }\n        .style-scroll::-webkit-scrollbar-track { background: transparent; }\n        .style-scroll::-webkit-scrollbar-thumb {\n            background: var(--primary);\n            border-radius: 4px;\n        }\n\n        .style-grid-expanded {\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: 8px;\n        }\n\n        .style-category {\n            grid-column: 1 \/ -1;\n            font-size: 10px;\n            text-transform: uppercase;\n            letter-spacing: 2px;\n            color: var(--primary);\n            font-weight: 600;\n            margin-top: 16px;\n            margin-bottom: 4px;\n            padding-bottom: 6px;\n            border-bottom: 1px solid var(--primary-light);\n        }\n\n        .style-category:first-child { margin-top: 0; }\n\n        .style-btn {\n            background: #ffffff;\n            border: 1.5px solid #E8E6E3;\n            border-radius: 10px;\n            padding: 12px 10px;\n            text-align: center;\n            cursor: pointer;\n            transition: border-color 0.2s, background 0.2s, transform 0.2s;\n            min-height: 58px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: 3px;\n        }\n\n        .style-btn:hover {\n            border-color: #45BDB0;\n            background: rgba(69,189,176,0.05);\n            transform: translateY(-1px);\n        }\n\n        .style-btn.active {\n            background: #E5F8F6;\n            border-color: #45BDB0;\n        }\n\n        .style-btn-label {\n            font-size: 12px;\n            font-weight: 600;\n            color: #2A2A2A;\n            line-height: 1.3;\n        }\n\n        .style-btn-desc {\n            font-size: 10px;\n            color: #777;\n            margin-top: 1px;\n            line-height: 1.3;\n        }\n\n        .style-btn.active .style-btn-label { color: #3AA89C; }\n\n        \/* \u2500\u2500\u2500 Diamond Amount \u2500\u2500\u2500 *\/\n        .diamond-grid {\n            display: grid;\n            grid-template-columns: repeat(3, 1fr);\n            gap: 8px;\n        }\n\n        .diamond-btn {\n            background: var(--bg-card);\n            border: 1.5px solid var(--border);\n            border-radius: var(--radius-sm);\n            padding: 14px 8px;\n            text-align: center;\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n\n        .diamond-btn:hover {\n            border-color: var(--primary);\n            background: var(--primary-50);\n        }\n\n        .diamond-btn.active {\n            background: var(--primary-light);\n            border-color: var(--primary);\n        }\n\n        .diamond-btn-icon { font-size: 18px; margin-bottom: 4px; }\n        .diamond-btn-label {\n            font-size: 11px; font-weight: 600;\n            color: var(--text-mid);\n        }\n\n        .diamond-btn.active .diamond-btn-label { color: var(--primary-dark); }\n\n        \/* \u2500\u2500\u2500 Custom Wishes \u2500\u2500\u2500 *\/\n        .wishes-textarea {\n            width: 100%;\n            min-height: 90px;\n            background: #ffffff;\n            border: 1.5px solid #E8E6E3;\n            border-radius: 10px;\n            padding: 14px;\n            font-family: 'DM Sans', sans-serif;\n            font-size: 13px;\n            color: #2A2A2A;\n            resize: vertical;\n            transition: border-color 0.3s, box-shadow 0.3s;\n            outline: none;\n            line-height: 1.5;\n            display: block;\n            box-sizing: border-box;\n            -webkit-appearance: none;\n            appearance: none;\n        }\n        .wishes-textarea::placeholder {\n            color: #999;\n            opacity: 1;\n            font-size: 13px;\n            line-height: 1.5;\n        }\n        \/* \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u043e\u0442 WP\/Elementor *\/\n        .wishes-textarea::-webkit-input-placeholder { color: #999; opacity: 1; }\n        .wishes-textarea::-moz-placeholder { color: #999; opacity: 1; }\n        .wishes-textarea:-ms-input-placeholder { color: #999; opacity: 1; }\n        .wishes-textarea:focus {\n            border-color: #45BDB0;\n            box-shadow: 0 0 0 3px rgba(69,189,176,0.12);\n        }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 CENTER PANEL *\/\n        .panel-center {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            animation: panelIn 0.7s ease 0.3s both;\n        }\n\n        .preview-container {\n            position: relative;\n            width: 100%;\n            aspect-ratio: 1;\n            background: var(--bg-card);\n            border-radius: var(--radius-xl);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            border: 1px solid var(--border-light);\n            overflow: hidden;\n            transition: box-shadow var(--transition);\n        }\n\n        .preview-container:hover { box-shadow: var(--shadow-lg); }\n\n        \/* Decorative corner accents *\/\n        .corner-accent {\n            position: absolute;\n            width: 48px; height: 48px;\n            border: 1.5px solid var(--primary);\n            opacity: 0.15;\n            pointer-events: none;\n        }\n\n        .corner-accent.tl { top: 20px; left: 20px; border-right: none; border-bottom: none; border-radius: 6px 0 0 0; }\n        .corner-accent.tr { top: 20px; right: 20px; border-left: none; border-bottom: none; border-radius: 0 6px 0 0; }\n        .corner-accent.bl { bottom: 20px; left: 20px; border-right: none; border-top: none; border-radius: 0 0 0 6px; }\n        .corner-accent.br { bottom: 20px; right: 20px; border-left: none; border-top: none; border-radius: 0 0 6px 0; }\n\n        \/* Diamond pattern background *\/\n        .diamond-pattern {\n            position: absolute; inset: 0;\n            background-image: radial-gradient(circle at 1px 1px, rgba(69,189,176,0.03) 1px, transparent 0);\n            background-size: 24px 24px;\n            pointer-events: none;\n        }\n\n        .preview-placeholder {\n            text-align: center;\n            padding: 40px;\n            position: relative;\n            z-index: 1;\n        }\n\n        .placeholder-gem {\n            width: 80px; height: 80px;\n            fill: var(--border);\n            margin: 0 auto 24px;\n            opacity: 0.35;\n            animation: gemFloat 5s ease-in-out infinite;\n        }\n\n        .placeholder-title {\n            font-family: 'Playfair Display', serif;\n            font-size: 22px;\n            font-weight: 400;\n            color: var(--text-mid);\n            margin-bottom: 8px;\n        }\n\n        .placeholder-hint {\n            font-size: 13px;\n            color: var(--gray);\n        }\n\n        \/* Generated result *\/\n        .preview-result {\n            display: none;\n            width: 100%; height: 100%;\n            position: relative;\n            z-index: 1;\n        }\n\n        .preview-result.show {\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .preview-image-wrapper {\n            width: 82%; height: 82%;\n            background: linear-gradient(145deg, #fff, #f8f8f6);\n            border-radius: var(--radius-lg);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            box-shadow: var(--shadow-md);\n            position: relative;\n            animation: imageReveal 0.9s cubic-bezier(0.34, 1.56, 0.64, 1) both;\n        }\n\n        @keyframes imageReveal {\n            from { opacity: 0; transform: scale(0.85); }\n            to { opacity: 1; transform: scale(1); }\n        }\n\n        .preview-jewelry-render {\n            width: 100%; height: 100%;\n            display: flex; align-items: center; justify-content: center;\n        }\n        .jewelry-svg {\n            width: 70%; height: 70%;\n            animation: jewelryReveal 0.9s cubic-bezier(0.34, 1.56, 0.64, 1) both;\n        }\n        @keyframes jewelryReveal {\n            from { opacity: 0; transform: scale(0.7) rotate(-5deg); }\n            to { opacity: 1; transform: scale(1) rotate(0deg); }\n        }\n\n        \/* Watermark overlay on generated image *\/\n        .watermark-overlay {\n            position: absolute;\n            inset: 0;\n            display: flex; align-items: center; justify-content: center;\n            pointer-events: none;\n            border-radius: var(--radius-lg);\n            overflow: hidden;\n        }\n\n        .watermark-text {\n            font-family: 'Playfair Display', serif;\n            font-size: 28px;\n            font-weight: 700;\n            color: rgba(69,189,176,0.08);\n            letter-spacing: 12px;\n            text-transform: uppercase;\n            transform: rotate(-25deg);\n            white-space: nowrap;\n        }\n\n        .watermark-bar {\n            position: absolute;\n            bottom: 0; left: 0; right: 0;\n            background: rgba(0,0,0,0.04);\n            backdrop-filter: blur(4px);\n            padding: 8px 16px;\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            font-size: 9px;\n            letter-spacing: 1px;\n            color: rgba(0,0,0,0.25);\n            text-transform: uppercase;\n            font-weight: 600;\n        }\n\n        \/* Angle switcher *\/\n        .angle-bar {\n            position: absolute;\n            bottom: 24px; left: 50%;\n            transform: translateX(-50%);\n            display: none;\n            gap: 8px;\n            background: rgba(255,255,255,0.95);\n            backdrop-filter: blur(12px);\n            padding: 8px 12px;\n            border-radius: var(--radius-md);\n            box-shadow: var(--shadow-md);\n            z-index: 2;\n        }\n\n        .preview-result.show ~ .angle-bar-wrapper .angle-bar { display: flex; }\n\n        .angle-btn {\n            width: 44px; height: 44px;\n            background: var(--bg);\n            border: 1.5px solid var(--border);\n            border-radius: var(--radius-sm);\n            cursor: pointer;\n            transition: all var(--transition);\n            display: flex; align-items: center; justify-content: center;\n            font-size: 11px; font-weight: 600;\n            color: var(--text-light);\n        }\n\n        .angle-btn:hover {\n            border-color: var(--primary);\n            background: var(--primary-50);\n        }\n\n        .angle-btn.active {\n            border-color: var(--primary);\n            background: var(--primary-light);\n            color: var(--primary);\n        }\n\n        \/* Loading state *\/\n        .loading-overlay {\n            position: absolute; inset: 0;\n            background: rgba(255,255,255,0.96);\n            display: none;\n            align-items: center; justify-content: center;\n            flex-direction: column;\n            border-radius: var(--radius-xl);\n            z-index: 10;\n        }\n\n        .loading-overlay.show { display: flex; }\n\n        .loading-ring {\n            width: 56px; height: 56px;\n            border: 2.5px solid var(--border-light);\n            border-top-color: var(--primary);\n            border-radius: 50%;\n            animation: spin 0.9s linear infinite;\n            margin-bottom: 24px;\n        }\n\n        @keyframes spin { to { transform: rotate(360deg); } }\n\n        .loading-text {\n            font-size: 15px;\n            color: var(--text);\n            font-weight: 500;\n            margin-bottom: 6px;\n        }\n\n        .loading-subtext {\n            font-size: 12px;\n            color: var(--gray);\n            margin-bottom: 20px;\n        }\n\n        .loading-progress {\n            width: 200px; height: 3px;\n            background: var(--border-light);\n            border-radius: 3px;\n            overflow: hidden;\n        }\n\n        .loading-progress-bar {\n            width: 0%; height: 100%;\n            background: linear-gradient(90deg, var(--primary), var(--primary-dark));\n            border-radius: 3px;\n            transition: width 0.3s ease;\n        }\n\n        \/* Generate button *\/\n        .generate-section {\n            margin-top: 28px;\n            text-align: center;\n            width: 100%;\n        }\n\n        .generate-btn {\n            display: inline-flex; align-items: center; gap: 10px;\n            background: linear-gradient(135deg, var(--primary), var(--primary-dark));\n            color: white;\n            border: none;\n            padding: 18px 56px;\n            font-family: 'DM Sans', sans-serif;\n            font-size: 14px;\n            font-weight: 600;\n            letter-spacing: 0.8px;\n            text-transform: uppercase;\n            border-radius: 60px;\n            cursor: pointer;\n            transition: all var(--transition);\n            box-shadow: 0 8px 30px rgba(69,189,176,0.35);\n            position: relative;\n            overflow: hidden;\n        }\n\n        .generate-btn::after {\n            content: '';\n            position: absolute;\n            top: 0; left: -100%;\n            width: 100%; height: 100%;\n            background: linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n            transition: left 0.6s;\n        }\n\n        .generate-btn:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 12px 40px rgba(69,189,176,0.45);\n        }\n\n        .generate-btn:hover::after { left: 100%; }\n\n        .generate-btn:active { transform: translateY(0); }\n\n        .generate-btn svg {\n            width: 18px; height: 18px;\n            stroke: white; stroke-width: 2; fill: none;\n        }\n\n        .generate-hint {\n            margin-top: 14px;\n            font-size: 12px;\n            color: var(--gray);\n        }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 GENERATION LIMIT BADGE *\/\n        .gen-limit-badge {\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 8px;\n            margin-top: 12px;\n            padding: 8px 16px;\n            border-radius: 50px;\n            font-size: 12px;\n            font-weight: 500;\n            color: var(--text-mid);\n            background: var(--bg-warm);\n            border: 1px solid var(--border);\n            transition: all var(--transition);\n        }\n        .gen-limit-badge .gen-count {\n            font-weight: 700;\n            color: var(--primary);\n        }\n        .gen-limit-badge.low .gen-count { color: #E67E22; }\n        .gen-limit-badge.exhausted {\n            background: #FFF5F5;\n            border-color: #FEB2B2;\n            color: #C53030;\n        }\n        .gen-limit-badge.exhausted .gen-count { color: #C53030; }\n\n        .generate-btn:disabled {\n            opacity: 0.5;\n            cursor: not-allowed;\n            transform: none !important;\n        }\n        .generate-btn:disabled::after { display: none; }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 ACCESS GATE OVERLAY *\/\n        .access-gate {\n            display: none;\n            position: fixed;\n            inset: 0;\n            z-index: 10000;\n            background: rgba(20, 20, 20, 0.6);\n            backdrop-filter: blur(8px);\n            -webkit-backdrop-filter: blur(8px);\n            align-items: center;\n            justify-content: center;\n            animation: fadeIn 0.4s ease;\n        }\n        .access-gate.show { display: flex; }\n\n        .access-gate-card {\n            background: var(--bg-card);\n            border-radius: var(--radius-lg);\n            padding: 48px 40px;\n            max-width: 440px;\n            width: 90%;\n            text-align: center;\n            box-shadow: var(--shadow-lg);\n            animation: heroIn 0.5s ease both;\n        }\n        .access-gate-icon {\n            width: 64px; height: 64px;\n            margin: 0 auto 20px;\n            background: var(--primary-light);\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n        .access-gate-icon svg {\n            width: 28px; height: 28px;\n            stroke: var(--primary);\n            stroke-width: 2;\n            fill: none;\n        }\n        .access-gate-title {\n            font-family: 'Playfair Display', serif;\n            font-size: 26px;\n            font-weight: 500;\n            color: var(--dark);\n            margin-bottom: 10px;\n        }\n        .access-gate-text {\n            font-size: 14px;\n            color: var(--text-light);\n            line-height: 1.6;\n            margin-bottom: 28px;\n        }\n        .access-gate-text strong {\n            color: var(--text);\n            font-weight: 600;\n        }\n        .access-gate-actions {\n            display: flex;\n            gap: 12px;\n            justify-content: center;\n        }\n        .access-gate-btn {\n            padding: 12px 28px;\n            border-radius: 50px;\n            font-family: 'DM Sans', sans-serif;\n            font-size: 14px;\n            font-weight: 600;\n            cursor: pointer;\n            transition: all var(--transition);\n            text-decoration: none;\n            display: inline-flex;\n            align-items: center;\n            gap: 6px;\n            border: none;\n        }\n        .access-gate-btn.primary {\n            background: var(--primary);\n            color: white;\n        }\n        .access-gate-btn.primary:hover {\n            background: var(--primary-dark);\n            transform: translateY(-1px);\n            box-shadow: var(--shadow-glow);\n        }\n        .access-gate-btn.secondary {\n            background: var(--bg-warm);\n            color: var(--text);\n            border: 1px solid var(--border);\n        }\n        .access-gate-btn.secondary:hover {\n            background: var(--border-light);\n        }\n        .access-gate-days {\n            display: inline-flex;\n            align-items: center;\n            justify-content: center;\n            gap: 4px;\n            margin-top: 16px;\n            padding: 10px 20px;\n            background: var(--primary-light);\n            border-radius: 12px;\n            font-size: 14px;\n            color: var(--primary-dark);\n            font-weight: 500;\n        }\n        .access-gate-days strong {\n            font-size: 22px;\n            font-weight: 700;\n            color: var(--primary);\n        }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 RIGHT PANEL *\/\n        .panel-right {\n            background: var(--bg-card);\n            border: 1px solid var(--border-light);\n            border-radius: var(--radius-lg);\n            padding: 28px;\n            height: fit-content;\n            animation: panelIn 0.7s ease 0.4s both;\n            transition: box-shadow var(--transition);\n        }\n\n        .panel-right:hover { box-shadow: var(--shadow-md); }\n\n        .price-card {\n            background: linear-gradient(160deg, var(--bg-warm), var(--bg-card));\n            border-radius: var(--radius-md);\n            padding: 22px;\n            border: 1px solid var(--border);\n            margin-bottom: 22px;\n        }\n\n        .price-section-label {\n            font-size: 10px;\n            text-transform: uppercase;\n            letter-spacing: 2.5px;\n            color: var(--gray);\n            margin-bottom: 16px;\n            font-weight: 600;\n        }\n\n        .price-row {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            padding: 10px 0;\n            border-bottom: 1px solid var(--border-light);\n            transition: padding-left var(--transition);\n        }\n\n        .price-row:hover { padding-left: 4px; }\n        .price-row:last-of-type { border-bottom: none; }\n\n        .price-label {\n            font-size: 13px;\n            color: var(--text-light);\n        }\n\n        .price-value {\n            font-size: 13px;\n            font-weight: 600;\n            color: var(--text);\n            font-variant-numeric: tabular-nums;\n        }\n\n        .price-total {\n            margin-top: 16px;\n            padding-top: 16px;\n            border-top: 2px solid var(--primary-light);\n            display: flex;\n            justify-content: space-between;\n            align-items: baseline;\n        }\n\n        .price-total-label {\n            font-family: 'Playfair Display', serif;\n            font-size: 18px;\n            color: var(--dark);\n        }\n\n        .price-total-value {\n            font-family: 'Playfair Display', serif;\n            font-size: 32px;\n            font-weight: 600;\n            color: var(--primary);\n        }\n\n        \/* Action buttons *\/\n        .action-buttons {\n            display: flex; flex-direction: column;\n            gap: 10px;\n            margin-bottom: 24px;\n        }\n\n        .btn-order {\n            width: 100%;\n            display: flex; align-items: center; justify-content: center; gap: 10px;\n            background: linear-gradient(135deg, var(--primary), var(--primary-dark));\n            color: white;\n            border: none;\n            padding: 16px 20px;\n            font-family: 'DM Sans', sans-serif;\n            font-size: 13px;\n            font-weight: 600;\n            letter-spacing: 0.5px;\n            border-radius: var(--radius-sm);\n            cursor: pointer;\n            transition: all var(--transition);\n            box-shadow: 0 6px 20px rgba(69,189,176,0.3);\n        }\n\n        .btn-order:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 10px 30px rgba(69,189,176,0.4);\n        }\n\n        .btn-order svg {\n            width: 18px; height: 18px;\n            stroke: white; stroke-width: 2; fill: none;\n        }\n\n        .btn-save {\n            width: 100%;\n            display: flex; align-items: center; justify-content: center; gap: 10px;\n            background: var(--bg-card);\n            color: var(--text);\n            border: 1.5px solid var(--border);\n            padding: 14px 20px;\n            font-family: 'DM Sans', sans-serif;\n            font-size: 13px;\n            font-weight: 500;\n            border-radius: var(--radius-sm);\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n\n        .btn-save:hover {\n            border-color: var(--primary);\n            background: var(--primary-50);\n            transform: translateY(-1px);\n        }\n\n        .btn-save svg {\n            width: 16px; height: 16px;\n            stroke: var(--primary); stroke-width: 2; fill: none;\n        }\n\n        .btn-stone-only {\n            width: 100%;\n            display: flex; align-items: center; justify-content: center; gap: 6px;\n            background: transparent;\n            color: var(--primary);\n            border: none;\n            padding: 12px;\n            font-family: 'DM Sans', sans-serif;\n            font-size: 13px;\n            font-weight: 500;\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n\n        .btn-stone-only:hover { color: var(--primary-dark); gap: 10px; }\n\n        .btn-stone-only svg {\n            width: 16px; height: 16px;\n            stroke: currentColor; stroke-width: 2; fill: none;\n            transition: transform var(--transition);\n        }\n\n        .btn-stone-only:hover svg { transform: translateX(3px); }\n\n        \/* Features list *\/\n        .features-list {\n            padding-top: 20px;\n            border-top: 1px solid var(--border-light);\n        }\n\n        .feature-row {\n            display: flex; align-items: center; gap: 12px;\n            padding: 8px 0;\n            font-size: 12px;\n            color: var(--text-light);\n            transition: all var(--transition);\n        }\n\n        .feature-row:hover {\n            color: var(--text-mid);\n            padding-left: 4px;\n        }\n\n        .feature-check {\n            width: 20px; height: 20px;\n            background: var(--primary-light);\n            border-radius: 50%;\n            display: flex; align-items: center; justify-content: center;\n            flex-shrink: 0;\n        }\n\n        .feature-check svg {\n            width: 11px; height: 11px;\n            stroke: var(--primary);\n            stroke-width: 2.5;\n            fill: none;\n        }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 FOOTER *\/\n        .footer-bar {\n            text-align: center;\n            padding: 32px;\n            color: var(--gray);\n            font-size: 13px;\n            border-top: 1px solid var(--border-light);\n            margin-top: 20px;\n        }\n\n        .footer-bar a {\n            color: var(--primary);\n            text-decoration: none;\n            font-weight: 500;\n            transition: color var(--transition);\n        }\n\n        .footer-bar a:hover { color: var(--primary-dark); }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 RESPONSIVE *\/\n        @media (max-width: 1280px) {\n            .header { display: none; }\n            .main {\n                grid-template-columns: 1fr;\n                padding: 0 20px 60px;\n                max-width: 600px;\n            }\n            .page-hero { padding-left: 20px; padding-right: 20px; }\n            .steps-bar { display: none; }\n        }\n\n        \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 HIDDEN HELPERS *\/\n        .ring-only-section { transition: all var(--transition); }\n        .ring-only-section.hidden {\n            opacity: 0;\n            max-height: 0;\n            margin: 0; padding: 0;\n            overflow: hidden;\n            pointer-events: none;\n        }\n\n        .gold-grade-section { transition: all var(--transition); }\n        .gold-grade-section.hidden {\n            opacity: 0;\n            max-height: 0;\n            margin: 0; padding: 0;\n            overflow: hidden;\n            pointer-events: none;\n        }\n    <\/style>\n<\/head>\n<body style=\"padding-top: clamp(55px, 5vw, 75px);\">\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 VOC HEADER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <style>\n\/* ===== VOC GEMS HEADER V3 \u2014 PREMIUM ===== *\/\n.voc-header-wrap * { margin: 0; padding: 0; box-sizing: border-box; }\n.voc-header-wrap img { max-width: none !important; height: auto; }\n.voc-header-wrap { font-family: 'DM Sans', sans-serif; position: fixed; top: 0; left: 0; width: 100%; z-index: 99999; transition: background 0.4s ease, box-shadow 0.4s ease; }\n.voc-header-wrap.scrolled { background: rgba(255,255,255,0.95); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); box-shadow: 0 1px 0 rgba(0,0,0,0.07); }\n.voc-header { max-width: 1600px; margin: 0 auto; padding: clamp(1px,0.2vw,4px) clamp(24px,4vw,60px); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; }\n.voc-header-left { display: flex; align-items: center; }\n.voc-hamburger { display: flex; flex-direction: column; gap: 5px; cursor: pointer; padding: 8px; margin: -8px; position: relative; z-index: 100001; }\n.voc-hamburger span { display: block; height: 1.5px; border-radius: 2px; transition: all 0.4s cubic-bezier(0.4,0,0.2,1); transform-origin: center; }\n.voc-hamburger span:nth-child(1) { width: 26px; } .voc-hamburger span:nth-child(2) { width: 18px; } .voc-hamburger span:nth-child(3) { width: 22px; }\n.voc-hamburger:hover span { width: 26px !important; }\n.voc-menu-label { font-family: 'DM Sans', sans-serif; font-size: clamp(11px,0.9vw,13px); font-weight: 500; letter-spacing: 1.5px; text-transform: uppercase; margin-left: 10px; cursor: pointer; transition: opacity 0.25s ease; }\n.voc-header-wrap.header-dark .voc-menu-label { color: rgba(255,255,255,0.8); }\n.voc-header-wrap.header-dark.scrolled .voc-menu-label { color: #1A1A1A; }\n.voc-header-wrap.header-light .voc-menu-label { color: #1A1A1A; }\n.voc-menu-label:hover { opacity: 0.6; }\n.voc-hamburger.active span:nth-child(2) { opacity: 0; transform: scaleX(0); }\n.voc-hamburger.active span:nth-child(3) { width: 22px !important; transform: rotate(-45deg) translate(5px,-5px); }\n.voc-header-center { display: flex; justify-content: center; }\n.voc-header-logo { display: flex; align-items: center; text-decoration: none; transition: opacity 0.3s ease; }\n.voc-header-logo:hover { opacity: 0.8; }\n.voc-logo-img { height: clamp(92px,9.6vw,130px) !important; max-height: 130px !important; width: auto !important; max-width: 260px !important; object-fit: contain; transition: all 0.4s cubic-bezier(0.4,0,0.2,1); }\n.voc-header-right { display: flex; align-items: center; justify-content: flex-end; gap: clamp(2px,0.4vw,6px); }\n.voc-header-icon { width: clamp(36px,2.8vw,42px); height: clamp(36px,2.8vw,42px); border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all 0.25s ease; cursor: pointer; text-decoration: none; position: relative; }\n.voc-header-icon svg { width: clamp(17px,1.35vw,20px); height: clamp(17px,1.35vw,20px); stroke-width: 1.7; }\n.voc-selection-badge { position: absolute; top: 4px; right: 4px; width: 14px; height: 14px; background: #45BDB0; border-radius: 50%; font-size: 9px; font-weight: 700; color: #fff; display: none; align-items: center; justify-content: center; }\n.voc-header-wrap.header-light .voc-hamburger span { background: #1A1A1A; }\n.voc-header-wrap.header-light .voc-logo-img { filter: none; }\n.voc-header-wrap.header-light .voc-header-icon { color: #1A1A1A; }\n.voc-header-wrap.header-light .voc-header-icon:hover { background: rgba(0,0,0,0.05); }\n.voc-header-wrap.header-light.scrolled .voc-hamburger span { background: #1A1A1A; }\n\/* Fullscreen menu *\/\n.voc-fullmenu { position: fixed; inset: 0; z-index: 100000; display: flex; pointer-events: none; visibility: hidden; }\n.voc-fullmenu.open { pointer-events: all; visibility: visible; }\n.voc-fullmenu-panel { width: 58%; height: 100%; background: #0D0F0E; transform: translateX(-100%); transition: transform 0.65s cubic-bezier(0.76,0,0.24,1); display: flex; flex-direction: column; justify-content: center; padding: clamp(60px,7vw,110px) clamp(48px,6vw,96px); position: relative; overflow: hidden; }\n.voc-fullmenu-panel::after { content: ''; position: absolute; bottom: -80px; right: -60px; width: 380px; height: 380px; background: radial-gradient(circle, rgba(69,189,176,0.1) 0%, transparent 70%); pointer-events: none; }\n.voc-fullmenu.open .voc-fullmenu-panel { transform: translateX(0); }\n.voc-fullmenu-aside { width: 42%; height: 100%; background: #EFF9F8; transform: translateX(100%); transition: transform 0.65s cubic-bezier(0.76,0,0.24,1) 0.04s; display: flex; flex-direction: column; justify-content: flex-end; padding: clamp(40px,5vw,80px); position: relative; overflow: hidden; }\n.voc-fullmenu.open .voc-fullmenu-aside { transform: translateX(0); }\n.voc-fullmenu-gem { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-58%); opacity: 0.12; pointer-events: none; }\n.voc-fullmenu-close { position: absolute; top: clamp(20px,2.5vw,36px); right: clamp(20px,2.5vw,36px); width: 44px; height: 44px; border-radius: 50%; background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.1); color: rgba(255,255,255,0.5); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.25s ease; z-index: 2; }\n.voc-fullmenu-close:hover { background: rgba(255,255,255,0.14); color: #fff; }\n.voc-fullmenu-close svg { width: 18px; height: 18px; stroke-width: 1.5; }\n.voc-fullmenu-nav { display: flex; flex-direction: column; }\n.voc-fullmenu-nav > a, .voc-fullmenu-item { font-family: 'Playfair Display', serif; font-size: clamp(18px,2vw,30px); font-weight: 400; color: rgba(255,255,255,0.45); text-decoration: none; padding: clamp(3px,0.4vw,7px) 0; display: flex; align-items: baseline; gap: 14px; cursor: pointer; border: none; background: none; width: 100%; text-align: left; transition: color 0.3s ease; position: relative; line-height: 1.2; }\n.voc-fullmenu-nav > a::before, .voc-fullmenu-item::before { content: ''; position: absolute; left: 0; bottom: 0; width: 0; height: 1px; background: #45BDB0; transition: width 0.45s cubic-bezier(0.4,0,0.2,1); }\n.voc-fullmenu-nav > a:hover, .voc-fullmenu-item:hover { color: #fff; }\n.voc-fullmenu-nav > a:hover::before, .voc-fullmenu-item:hover::before { width: 100%; }\n.voc-fullmenu-num { font-family: 'DM Sans', sans-serif; font-size: clamp(10px,0.85vw,13px); font-weight: 400; color: rgba(255,255,255,0.18); letter-spacing: 0.5px; transition: color 0.3s ease; min-width: 22px; padding-bottom: 4px; }\n.voc-fullmenu-nav > a:hover .voc-fullmenu-num, .voc-fullmenu-item:hover .voc-fullmenu-num { color: #45BDB0; }\n.voc-fullmenu-arrow { font-family: 'DM Sans', sans-serif; font-size: clamp(13px,1.1vw,17px); margin-left: auto; color: rgba(255,255,255,0.18); transition: transform 0.35s ease, color 0.3s ease; padding-bottom: 4px; }\n.voc-fullmenu-item.open .voc-fullmenu-arrow { transform: rotate(180deg); color: #45BDB0; }\n.voc-fullmenu-sub { display: none; flex-direction: column; padding: clamp(6px,0.8vw,12px) 0 clamp(4px,0.5vw,8px) clamp(28px,3vw,44px); border-left: 1px solid rgba(69,189,176,0.2); margin-left: clamp(18px,2vw,28px); gap: 2px; }\n.voc-fullmenu-sub.open { display: flex; }\n.voc-fullmenu-sub a { font-family: 'DM Sans', sans-serif !important; font-size: clamp(13px,1.05vw,16px) !important; font-weight: 400; color: rgba(255,255,255,0.35) !important; text-decoration: none; padding: clamp(4px,0.45vw,7px) 0 !important; transition: color 0.25s ease; display: block !important; width: 100%; }\n.voc-fullmenu-sub a:hover { color: #45BDB0 !important; }\n.voc-fullmenu-sub a::before { display: none !important; }\n.voc-fullmenu-auth { margin-top: clamp(20px,2.5vw,40px); padding-top: clamp(14px,1.8vw,24px); border-top: 1px solid rgba(255,255,255,0.07); display: flex; align-items: center; gap: 12px; }\n.voc-fullmenu-auth a { font-family: 'DM Sans', sans-serif; font-size: clamp(13px,1.05vw,15px); font-weight: 500; text-decoration: none; transition: all 0.25s ease; white-space: nowrap; }\n.voc-auth-login { color: #fff !important; padding: 9px 20px; border: 1.5px solid rgba(255,255,255,0.9); border-radius: 50px; }\n.voc-auth-login:hover { color: #fff !important; border-color: #fff !important; }\n.voc-auth-register { color: #fff !important; padding: 9px 22px; background: #45BDB0; border-radius: 50px; }\n.voc-auth-register:hover { background: #3AA89C !important; transform: translateY(-1px); box-shadow: 0 6px 18px rgba(69,189,176,0.35); }\n.voc-aside-label { font-family: 'DM Sans', sans-serif; font-size: clamp(10px,0.85vw,12px); font-weight: 500; color: #45BDB0; letter-spacing: 2.5px; text-transform: uppercase; margin-bottom: 10px; }\n.voc-aside-title { font-family: 'Playfair Display', serif; font-size: clamp(18px,1.9vw,28px); color: #1A1A1A; font-weight: 400; line-height: 1.35; margin-bottom: 20px; }\n.voc-aside-cta { display: inline-flex; align-items: center; gap: 8px; font-family: 'DM Sans', sans-serif; font-size: clamp(13px,1.05vw,15px); color: #45BDB0; font-weight: 500; text-decoration: none; transition: gap 0.3s ease; }\n.voc-aside-cta:hover { gap: 14px; }\n.voc-search-overlay { position: fixed; inset: 0; background: rgba(13,15,14,0.88); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); z-index: 200000; display: none; align-items: flex-start; justify-content: center; padding-top: 15vh; }\n.voc-search-overlay.active { display: flex; }\n.voc-search-box { width: 90%; max-width: 620px; position: relative; }\n.voc-search-box input { width: 100%; padding: 20px 58px 20px 26px; font-family: 'DM Sans', sans-serif; font-size: clamp(15px,1.4vw,19px); border: none; border-radius: 14px; background: #fff; color: #1A1A1A; box-shadow: 0 30px 80px rgba(0,0,0,0.4); outline: none; }\n.voc-search-box input::placeholder { color: #aaa; }\n.voc-search-close { position: absolute; right: 18px; top: 50%; transform: translateY(-50%); background: none; border: none; font-size: 20px; color: #999; cursor: pointer; transition: color 0.2s; line-height: 1; }\n.voc-search-close:hover { color: #1A1A1A; }\n.voc-lang-switcher { display: flex; align-items: center; gap: 2px; margin-right: clamp(4px,0.5vw,8px); }\n.voc-lang-btn { font-family: \"DM Sans\", sans-serif; font-size: clamp(11px,0.9vw,13px); font-weight: 600; letter-spacing: 0.5px; padding: 4px 8px; border-radius: 6px; cursor: pointer; transition: all 0.2s; text-decoration: none; border: none; background: none; }\n.voc-lang-btn.active { background: rgba(0,0,0,0.08); }\n.voc-lang-btn:hover:not(.active) { background: rgba(0,0,0,0.04); }\n.voc-header-wrap.header-light .voc-lang-btn { color: #1A1A1A; }\n.voc-lang-sep { color: rgba(0,0,0,0.2); font-size: 11px; }\n@media (max-width: 700px) { .voc-fullmenu-aside { display: none; } .voc-fullmenu-panel { width: 100%; } .voc-fullmenu-nav > a, .voc-fullmenu-item { font-size: clamp(18px,5vw,26px); } }\n    <\/style>\n\n    <div class=\"voc-header-wrap header-light\" id=\"vocHeader\">\n        <div class=\"voc-header\">\n            <div class=\"voc-header-left\">\n                <div class=\"voc-hamburger\" id=\"vocHamburger\" onclick=\"vocToggleMenu()\"><span><\/span><span><\/span><span><\/span><\/div>\n                <span class=\"voc-menu-label\" onclick=\"vocToggleMenu()\" data-i18n=\"menu\">Menu<\/span>\n            <\/div>\n            <div class=\"voc-header-center\">\n                <a href=\"\/\" class=\"voc-header-logo\">\n                    <img decoding=\"async\" src=\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png\" alt=\"VOC Gems\" class=\"voc-logo-img\" \/>\n                <\/a>\n            <\/div>\n            <div class=\"voc-header-right\">\n                <div class=\"voc-lang-switcher\" id=\"vocLangSwitcher\">\n                    <a href=\"\/\" class=\"voc-lang-btn\" id=\"langEN\">EN<\/a>\n                    <span class=\"voc-lang-sep\">|<\/span>\n                    <a href=\"\/ru\/\" class=\"voc-lang-btn\" id=\"langRU\">RU<\/a>\n                <\/div>\n                <a href=\"\/your-selection\/\" class=\"voc-header-icon\" title=\"Your Selection\">\n                    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><path d=\"M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z\"\/><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"\/><path d=\"M16 10a4 4 0 0 1-8 0\"\/><\/svg>\n                    <span class=\"voc-selection-badge\" id=\"vocSelectionBadge\">0<\/span>\n                <\/a>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Fullscreen menu -->\n    <div class=\"voc-fullmenu\" id=\"vocFullMenu\">\n        <div class=\"voc-fullmenu-panel\">\n            <button class=\"voc-fullmenu-close\" onclick=\"vocToggleMenu()\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><path d=\"M18 6L6 18M6 6l12 12\"\/><\/svg><\/button>\n            <nav class=\"voc-fullmenu-nav\">\n                <a href=\"\/\"><span class=\"voc-fullmenu-num\">01<\/span><span data-i18n=\"nav_home\">Home<\/span><\/a>\n                <a href=\"\/catalog\/\"><span class=\"voc-fullmenu-num\">02<\/span><span data-i18n=\"nav_gemstones\">Loose Gemstones<\/span><\/a>\n                <a href=\"\/catalog-jewelry\/\"><span class=\"voc-fullmenu-num\">03<\/span><span data-i18n=\"nav_jewelry\">Fine Jewelry<\/span><\/a>\n                <a href=\"\/subscriptions\/auction\/\"><span class=\"voc-fullmenu-num\">04<\/span><span data-i18n=\"nav_auction\">Auction<\/span><\/a>\n                <a href=\"\/ai-constructor\/\"><span class=\"voc-fullmenu-num\">05<\/span><span data-i18n=\"nav_constructor\">Constructor<\/span><\/a>\n                <a href=\"\/home\/contact\/\"><span class=\"voc-fullmenu-num\">06<\/span><span data-i18n=\"nav_contact\">Contact<\/span><\/a>\n                <a href=\"\/gemstone-info\/\"><span class=\"voc-fullmenu-num\">07<\/span><span data-i18n=\"nav_news\">News<\/span><\/a>\n                <div>\n                    <div class=\"voc-fullmenu-item\" onclick=\"vocToggleSub('aboutSub',this)\"><span class=\"voc-fullmenu-num\">08<\/span><span data-i18n=\"nav_about\">About<\/span><span class=\"voc-fullmenu-arrow\">\u2193<\/span><\/div>\n                    <div class=\"voc-fullmenu-sub\" id=\"aboutSub\">\n                        <a href=\"\/home\/about\/\" data-i18n=\"nav_about_us\">About Us<\/a>\n                        <a href=\"\/home\/payment\/\" data-i18n=\"nav_payment\">Payment & Delivery<\/a>\n                        <a href=\"\/home\/security\/\" data-i18n=\"nav_security\">Security<\/a>\n                        <a href=\"\/faq\/\">FAQ<\/a>\n                        <a href=\"\/gemstone-guide\/\" data-i18n=\"nav_guide\">Gemstone Guide<\/a>\n                        <a href=\"\/terms\/\" data-i18n=\"nav_terms\">Terms & Conditions<\/a>\n                    <\/div>\n                <\/div>\n                <div>\n                    <div class=\"voc-fullmenu-item\" onclick=\"vocToggleSub('accountSub',this)\"><span class=\"voc-fullmenu-num\">09<\/span><span data-i18n=\"nav_account\">Account<\/span><span class=\"voc-fullmenu-arrow\">\u2193<\/span><\/div>\n                    <div class=\"voc-fullmenu-sub\" id=\"accountSub\">\n                        <a href=\"\/dash\/\" id=\"headerDashLink\" data-i18n=\"nav_dashboard\">Dashboard<\/a>\n                        <a href=\"\/your-selection\/\" data-i18n=\"nav_selection\">Your Selection<\/a>\n                    <\/div>\n                <\/div>\n            <\/nav>\n            <div class=\"voc-fullmenu-auth\">\n                <a href=\"\/wp-login.php?redirect_to=%2Fdash%2F\" class=\"voc-auth-login\" data-i18n=\"nav_login\">Log In<\/a>\n                <a href=\"\/wp-login.php?action=register&redirect_to=%2Fdash%2F\" class=\"voc-auth-register\" data-i18n=\"nav_signup\">Sign Up<\/a>\n                <!-- 23.06.2026 \u2014 \u0431\u043b\u043e\u043a \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u044e\u0437\u0435\u0440\u0430; \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 JS \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u043d\u043e\u043f\u043e\u043a \u0432\u0445\u043e\u0434\u0430 -->\n                <a href=\"\/dash\/\" class=\"voc-auth-account\" id=\"vocCtorAccount\" style=\"display:none; color:#fff !important; padding:9px 22px; background:#45BDB0; border-radius:50px; font-weight:600;\">\n                    <span id=\"vocCtorAccountName\">Account<\/span>\n                <\/a>\n            <\/div>\n        <\/div>\n        <div class=\"voc-fullmenu-aside\">\n            <svg class=\"voc-fullmenu-gem\" width=\"300\" height=\"300\" viewBox=\"0 0 200 200\" fill=\"none\">\n                <polygon points=\"100,10 170,60 170,140 100,190 30,140 30,60\" stroke=\"#45BDB0\" stroke-width=\"1\" fill=\"none\"\/>\n                <polygon points=\"100,10 140,60 100,50 60,60\" stroke=\"#45BDB0\" stroke-width=\"0.6\" fill=\"none\" opacity=\"0.6\"\/>\n                <line x1=\"30\" y1=\"60\" x2=\"170\" y2=\"60\" stroke=\"#45BDB0\" stroke-width=\"0.6\" opacity=\"0.4\"\/>\n                <line x1=\"30\" y1=\"140\" x2=\"170\" y2=\"140\" stroke=\"#45BDB0\" stroke-width=\"0.6\" opacity=\"0.4\"\/>\n            <\/svg>\n            <div>\n                <div class=\"voc-aside-label\" data-i18n=\"aside_collections\">Collections<\/div>\n                <div class=\"voc-aside-title\" data-i18n=\"aside_title\">Rare gemstones,<br>certified & verified<\/div>\n                <a href=\"\/catalog\/\" class=\"voc-aside-cta\" onclick=\"vocToggleMenu()\" data-i18n=\"aside_cta\">Explore catalog \u2192<\/a>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Search overlay -->\n    <div class=\"voc-search-overlay\" id=\"vocSearch\" onclick=\"if(event.target===this) vocCloseSearch()\">\n        <div class=\"voc-search-box\">\n            <input type=\"text\" id=\"vocSearchInput\" placeholder=\"Search gemstones, articles...\" onkeydown=\"if(event.key==='Enter') window.location.href='\/search-results\/?q='+this.value\">\n            <button class=\"voc-search-close\" onclick=\"vocCloseSearch()\">\u2715<\/button>\n        <\/div>\n    <\/div>\n\n    <script>\n    \/\/ ===== Header scripts =====\n    \/\/ 23.06.2026 \u2014 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0448\u0430\u043f\u043a\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430 \u043c\u0435\u0436\u0434\u0443 \u00ab\u0412\u043e\u0439\u0442\u0438\/\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f\u00bb\n    \/\/ \u0438 \u0431\u043b\u043e\u043a\u043e\u043c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430. \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0448\u0430\u043f\u043a\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u0412\u0421\u0415\u0413\u0414\u0410 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 \u0432\u0445\u043e\u0434\u0430\n    \/\/ (\u043b\u043e\u0433\u0438\u043a\u0430 \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0430). \u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0451\u043c \u0438\u0437 window.VOC_ACCOUNT\n    \/\/ (\u0438\u043c\u044f) \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0442\u0430\u0432\u0438\u0442 refreshSession() \u0432 VOC_API.\n    window.vocUpdateHeaderAuth = function(loggedIn) {\n        var login  = document.querySelector('.voc-fullmenu-auth .voc-auth-login');\n        var reg    = document.querySelector('.voc-fullmenu-auth .voc-auth-register');\n        var acc    = document.getElementById('vocCtorAccount');\n        var accNm  = document.getElementById('vocCtorAccountName');\n        if (loggedIn) {\n            if (login) login.style.display = 'none';\n            if (reg)   reg.style.display   = 'none';\n            if (acc)   acc.style.display   = 'inline-block';\n            var nm = (window.VOC_ACCOUNT && (window.VOC_ACCOUNT.displayName || window.VOC_ACCOUNT.userName)) || 'Account';\n            if (accNm) accNm.textContent = nm;\n        } else {\n            if (login) login.style.display = '';\n            if (reg)   reg.style.display   = '';\n            if (acc)   acc.style.display   = 'none';\n        }\n    };\n\n    window.vocToggleMenu = function() { var m=document.getElementById('vocFullMenu'),b=document.getElementById('vocHamburger'),o=m.classList.contains('open'); m.classList.toggle('open'); b.classList.toggle('active'); document.body.style.overflow=o?'':'hidden'; };\n    window.vocToggleSub = function(id,el) { var s=document.getElementById(id),o=s.classList.contains('open'); document.querySelectorAll('.voc-fullmenu-sub').forEach(function(x){x.classList.remove('open')}); document.querySelectorAll('.voc-fullmenu-item').forEach(function(x){x.classList.remove('open')}); if(!o){s.classList.add('open');el.classList.add('open');} };\n    document.querySelectorAll('.voc-fullmenu-nav > a, .voc-fullmenu-sub a').forEach(function(l){ l.addEventListener('click',function(){document.getElementById('vocFullMenu').classList.remove('open');document.getElementById('vocHamburger').classList.remove('active');document.body.style.overflow='';}); });\n    window.vocOpenSearch = function() { document.getElementById('vocSearch').classList.add('active'); setTimeout(function(){document.getElementById('vocSearchInput').focus();},100); };\n    window.vocCloseSearch = function() { document.getElementById('vocSearch').classList.remove('active'); };\n    \/\/ Scroll effect\n    window.addEventListener('scroll', function() { var h=document.getElementById('vocHeader'); if(h) h.classList.toggle('scrolled', window.scrollY>60); });\n    \/\/ Escape key\n    document.addEventListener('keydown', function(e) { if(e.key==='Escape'){ vocCloseSearch(); document.getElementById('vocFullMenu').classList.remove('open'); document.getElementById('vocHamburger').classList.remove('active'); document.body.style.overflow=''; } });\n    <\/script>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 HEADER END \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 HERO \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <div class=\"page-hero\">\n        <h1 class=\"page-title\" data-i18n=\"hero_title\">Design Your Jewelry<\/h1>\n        <p class=\"page-subtitle\" data-i18n=\"hero_subtitle\">Set your parameters \u2014 AI will generate a unique design just for you<\/p>\n        <!-- \u0411\u043b\u043e\u043a 9.3: \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 -->\n        <p style=\"margin-top:10px;font-size:13px;opacity:0.7;\">\n            <a href=\"\/catalog\/\" id=\"linkBrowseCatalog\" style=\"color:inherit;text-decoration:underline;opacity:0.8;\">Or browse our gemstones first \u2192<\/a>\n        <\/p>\n    <\/div>\n\n    <!-- \u0411\u043b\u043e\u043a 9.2: \u0431\u0430\u043d\u043d\u0435\u0440 \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u043b\u0438 \u0441 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043a\u0430\u043c\u043d\u044f -->\n    <div id=\"stoneFromCardBanner\" style=\"display:none;background:linear-gradient(135deg,#1B5E4F,#2E7D5A);color:#fff;padding:14px 24px;text-align:center;font-size:14px;\">\n        <span id=\"stoneFromCardText\"><\/span>\n        <a href=\"#\" id=\"stoneFromCardLink\" style=\"color:#C9A86C;font-weight:600;margin-left:8px;text-decoration:underline;\">Continue with this stone \u2192<\/a>\n    <\/div>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 ACCESS GATE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <div class=\"access-gate\" id=\"accessGate\">\n        <div class=\"access-gate-card\" id=\"accessGateCard\">\n            <!-- Content injected by JS based on reason -->\n        <\/div>\n    <\/div>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 STEPS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <div class=\"steps-bar\">\n        <div class=\"step-item active\">\n            <span class=\"step-num\">1<\/span>\n            <span data-i18n=\"step_params\">Parameters<\/span>\n        <\/div>\n        <div class=\"step-connector active\"><\/div>\n        <div class=\"step-item\">\n            <span class=\"step-num\">2<\/span>\n            <span data-i18n=\"step_gen\">Generation<\/span>\n        <\/div>\n        <div class=\"step-connector\"><\/div>\n        <div class=\"step-item\">\n            <span class=\"step-num\">3<\/span>\n            <span data-i18n=\"step_order\">Order<\/span>\n        <\/div>\n    <\/div>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 MAIN LAYOUT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <main class=\"main\">\n\n        <!-- \u2500\u2500\u2500\u2500\u2500 LEFT: Parameters \u2500\u2500\u2500\u2500\u2500 -->\n        <aside class=\"panel-left\">\n            <h2 class=\"panel-title\">\n                <svg class=\"panel-title-icon\" viewBox=\"0 0 24 24\"><path d=\"M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z\"\/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z\"\/><\/svg>\n                <span data-i18n=\"panel_params\">Jewelry Parameters<\/span>\n            <\/h2>\n\n            <!-- Stone Card -->\n            <div class=\"param-section\">\n                <div class=\"param-label\" data-i18n=\"lbl_gemstone\">Selected Gemstone<\/div>\n                <button class=\"stone-change-btn\" id=\"chooseStoneBtn\">\n                    \ud83d\udc8e <span data-i18n=\"btn_choose_gem\">Choose Gemstone<\/span>\n                    <svg viewBox=\"0 0 24 24\"><polyline points=\"6 9 12 15 18 9\"\/><\/svg>\n                <\/button>\n            <\/div>\n            \n            <div class=\"stone-card\">\n                <div class=\"stone-visual\" id=\"stoneVisual\">\n                    <svg class=\"stone-gem-icon\" id=\"gemSvg\" viewBox=\"0 0 100 100\">\n                        <defs>\n                            <linearGradient id=\"gemGrad\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n                                <stop offset=\"0%\" id=\"gemColor1\" stop-color=\"#6FCF97\" stop-opacity=\"0.9\"\/>\n                                <stop offset=\"50%\" id=\"gemColor2\" stop-color=\"#2D9F6F\" stop-opacity=\"0.95\"\/>\n                                <stop offset=\"100%\" id=\"gemColor3\" stop-color=\"#1B7A4E\" stop-opacity=\"1\"\/>\n                            <\/linearGradient>\n                            <linearGradient id=\"gemShine\" x1=\"0%\" y1=\"0%\" x2=\"50%\" y2=\"50%\">\n                                <stop offset=\"0%\" stop-color=\"white\" stop-opacity=\"0.5\"\/>\n                                <stop offset=\"100%\" stop-color=\"white\" stop-opacity=\"0\"\/>\n                            <\/linearGradient>\n                            <filter id=\"gemGlow\">\n                                <feGaussianBlur stdDeviation=\"3\" result=\"blur\"\/>\n                                <feComposite in=\"SourceGraphic\" in2=\"blur\" operator=\"over\"\/>\n                            <\/filter>\n                        <\/defs>\n                        <!-- Emerald cut (default) -->\n                        <g id=\"gemShape\" filter=\"url(#gemGlow)\">\n                            <polygon id=\"gemMain\" points=\"25,15 75,15 85,35 50,90 15,35\" fill=\"url(#gemGrad)\" stroke=\"rgba(255,255,255,0.3)\" stroke-width=\"0.5\"\/>\n                            <polygon points=\"25,15 75,15 65,35 35,35\" fill=\"url(#gemGrad)\" opacity=\"0.7\"\/>\n                            <polygon points=\"35,35 65,35 50,90\" fill=\"url(#gemGrad)\" opacity=\"0.5\"\/>\n                            <line x1=\"15\" y1=\"35\" x2=\"85\" y2=\"35\" stroke=\"rgba(255,255,255,0.2)\" stroke-width=\"0.5\"\/>\n                            <line x1=\"50\" y1=\"15\" x2=\"50\" y2=\"35\" stroke=\"rgba(255,255,255,0.15)\" stroke-width=\"0.5\"\/>\n                            <line x1=\"35\" y1=\"35\" x2=\"50\" y2=\"90\" stroke=\"rgba(255,255,255,0.1)\" stroke-width=\"0.5\"\/>\n                            <line x1=\"65\" y1=\"35\" x2=\"50\" y2=\"90\" stroke=\"rgba(255,255,255,0.1)\" stroke-width=\"0.5\"\/>\n                            <!-- Shine highlight -->\n                            <polygon points=\"25,15 50,15 40,32 18,32\" fill=\"url(#gemShine)\" opacity=\"0.6\"\/>\n                        <\/g>\n                    <\/svg>\n                    <div class=\"gem-sparkles\">\n                        <span class=\"gem-sparkle\" style=\"top:20%;left:30%;animation-delay:0s\">\u2726<\/span>\n                        <span class=\"gem-sparkle\" style=\"top:40%;left:70%;animation-delay:0.7s\">\u2726<\/span>\n                        <span class=\"gem-sparkle\" style=\"top:60%;left:25%;animation-delay:1.4s\">\u2726<\/span>\n                    <\/div>\n                    <span class=\"stone-badge\" style=\"opacity:0\">\u2014<\/span>\n                <\/div>\n                <div class=\"stone-details\">\n                    <div>\n                        <div class=\"stone-name\" style=\"color:var(--text-light);font-style:italic\" data-i18n=\"no_stone\">No gemstone selected<\/div>\n                        <div class=\"stone-specs\" style=\"color:var(--gray)\" data-i18n=\"no_stone_hint\">Choose a gemstone from the catalog to continue<\/div>\n                    <\/div>\n                    <div class=\"stone-price\" style=\"color:var(--gray)\">\u2014<\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Jewelry Type -->\n            <div class=\"param-section\">\n                <div class=\"param-label\" data-i18n=\"lbl_type\">Jewelry Type<\/div>\n                <div class=\"type-grid\">\n                    <div class=\"type-btn\" data-type=\"ring\">\n                        <svg class=\"type-btn-icon\" viewBox=\"0 0 28 28\"><circle cx=\"14\" cy=\"14\" r=\"9\" \/><circle cx=\"14\" cy=\"14\" r=\"5\" \/><circle cx=\"14\" cy=\"5\" r=\"2.5\" \/><\/svg>\n                        <div class=\"type-btn-label\" data-i18n=\"type_ring\">Ring<\/div>\n                    <\/div>\n                    <div class=\"type-btn\" data-type=\"earrings\">\n                        <svg class=\"type-btn-icon\" viewBox=\"0 0 28 28\"><circle cx=\"14\" cy=\"8\" r=\"3\" \/><path d=\"M14 11V18\"\/><path d=\"M10 22L14 18L18 22\"\/><\/svg>\n                        <div class=\"type-btn-label\" data-i18n=\"type_earrings\">Earrings<\/div>\n                    <\/div>\n                    <div class=\"type-btn\" data-type=\"pendant\">\n                        <svg class=\"type-btn-icon\" viewBox=\"0 0 28 28\"><path d=\"M8 4C8 4 14 6 20 4\"\/><path d=\"M14 6V12\"\/><path d=\"M14 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12Z\"\/><\/svg>\n                        <div class=\"type-btn-label\" data-i18n=\"type_pendant\">Pendant<\/div>\n                    <\/div>\n                    <div class=\"type-btn\" data-type=\"bracelet\">\n                        <svg class=\"type-btn-icon\" viewBox=\"0 0 28 28\"><ellipse cx=\"14\" cy=\"14\" rx=\"11\" ry=\"6\"\/><circle cx=\"14\" cy=\"8\" r=\"2\"\/><\/svg>\n                        <div class=\"type-btn-label\" data-i18n=\"type_bracelet\">Bracelet<\/div>\n                    <\/div>\n                    <div class=\"type-btn\" data-type=\"brooch\">\n                        <svg class=\"type-btn-icon\" viewBox=\"0 0 28 28\"><circle cx=\"14\" cy=\"12\" r=\"7\"\/><circle cx=\"14\" cy=\"12\" r=\"3\"\/><line x1=\"14\" y1=\"19\" x2=\"14\" y2=\"26\"\/><\/svg>\n                        <div class=\"type-btn-label\" data-i18n=\"type_brooch\">Brooch<\/div>\n                    <\/div>\n                    <div class=\"type-btn\" data-type=\"necklace\">\n                        <svg class=\"type-btn-icon\" viewBox=\"0 0 28 28\"><path d=\"M6 8C6 8 10 16 14 18C18 16 22 8 22 8\"\/><path d=\"M3 6C3 6 6 4 14 4C22 4 25 6 25 6\"\/><circle cx=\"14\" cy=\"20\" r=\"3\"\/><\/svg>\n                        <div class=\"type-btn-label\" data-i18n=\"type_necklace\">Necklace<\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Gender -->\n            <!-- Metal -->\n            <div class=\"param-section\">\n                <div class=\"param-label\" data-i18n=\"lbl_metal\">Metal<\/div>\n                <div class=\"metal-options\">\n                    <div class=\"metal-btn\" data-metal=\"yellow-gold\">\n                        <div class=\"metal-circle gold\"><\/div>\n                        <div class=\"metal-name\" data-i18n=\"metal_yellow\">Yellow Gold<\/div>\n                    <\/div>\n                    <div class=\"metal-btn\" data-metal=\"white-gold\">\n                        <div class=\"metal-circle white-gold\"><\/div>\n                        <div class=\"metal-name\" data-i18n=\"metal_white\">White Gold<\/div>\n                    <\/div>\n                    <div class=\"metal-btn\" data-metal=\"rose-gold\">\n                        <div class=\"metal-circle rose-gold\"><\/div>\n                        <div class=\"metal-name\" data-i18n=\"metal_rose\">Rose Gold<\/div>\n                    <\/div>\n                    <div class=\"metal-btn\" data-metal=\"platinum\">\n                        <div class=\"metal-circle platinum\"><\/div>\n                        <div class=\"metal-name\" data-i18n=\"metal_plat\">Platinum 950<\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Gold Grade (hidden for platinum) -->\n            <div class=\"param-section gold-grade-section\" id=\"gradeSection\">\n                <div class=\"param-label\" data-i18n=\"lbl_grade\">Gold Purity<\/div>\n                <div class=\"grade-grid\">\n                    <div class=\"grade-btn\" data-grade=\"750\">\n                        <div class=\"grade-btn-label\">18 Karat<\/div>\n                        <div class=\"grade-btn-desc\">750 fineness<\/div>\n                    <\/div>\n                    <div class=\"grade-btn\" data-grade=\"585\">\n                        <div class=\"grade-btn-label\">14 Karat<\/div>\n                        <div class=\"grade-btn-desc\">585 fineness<\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Style -->\n            <div class=\"param-section\">\n                <div class=\"param-label\" data-i18n=\"lbl_style\">Design Style<\/div>\n                <div class=\"style-scroll\">\n                    <div class=\"style-grid-expanded\">\n                        <div class=\"style-btn\" data-style=\"classic\"><div class=\"style-btn-label\" data-i18n=\"style_classic\">Classic<\/div><div class=\"style-btn-desc\" data-i18n=\"style_classic_d\">Timeless elegance<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"modern\"><div class=\"style-btn-label\" data-i18n=\"style_modern\">Modern<\/div><div class=\"style-btn-desc\" data-i18n=\"style_modern_d\">Clean minimal lines<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"vintage\"><div class=\"style-btn-label\" data-i18n=\"style_vintage\">Vintage<\/div><div class=\"style-btn-desc\" data-i18n=\"style_vintage_d\">Art Deco charm<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"halo\"><div class=\"style-btn-label\" data-i18n=\"style_halo\">Halo<\/div><div class=\"style-btn-desc\" data-i18n=\"style_halo_d\">Diamond halo<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"solitaire\"><div class=\"style-btn-label\" data-i18n=\"style_solitaire\">Solitaire<\/div><div class=\"style-btn-desc\" data-i18n=\"style_solitaire_d\">Single stone<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"statement\"><div class=\"style-btn-label\" data-i18n=\"style_statement\">Statement<\/div><div class=\"style-btn-desc\" data-i18n=\"style_statement_d\">Bold high jewelry<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"nature\"><div class=\"style-btn-label\" data-i18n=\"style_nature\">Nature<\/div><div class=\"style-btn-desc\" data-i18n=\"style_nature_d\">Organic forms<\/div><\/div>\n                        <div class=\"style-btn\" data-style=\"geometric\"><div class=\"style-btn-label\" data-i18n=\"style_geometric\">Geometric<\/div><div class=\"style-btn-desc\" data-i18n=\"style_geometric_d\">Sharp angles<\/div><\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Diamond amount -->\n            <div class=\"param-section\">\n                <div class=\"param-label\" data-i18n=\"lbl_diamonds\">Accent Diamonds<\/div>\n                <div class=\"diamond-grid\">\n                    <div class=\"diamond-btn\" data-diamonds=\"none\">\n                        <div class=\"diamond-btn-icon\">\u2014<\/div>\n                        <div class=\"diamond-btn-label\" data-i18n=\"dia_none\">None<\/div>\n                    <\/div>\n                    <div class=\"diamond-btn\" data-diamonds=\"few\">\n                        <div class=\"diamond-btn-icon\">\u2726<\/div>\n                        <div class=\"diamond-btn-label\">Few<\/div>\n                    <\/div>\n                    <div class=\"diamond-btn\" data-diamonds=\"many\">\n                        <div class=\"diamond-btn-icon\">\u2726\u2726\u2726<\/div>\n                        <div class=\"diamond-btn-label\" data-i18n=\"dia_many\">Many<\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Custom wishes -->\n            <div class=\"param-section\">\n                <div class=\"param-label\" data-i18n=\"lbl_wishes\">Your Wishes<\/div>\n                <textarea class=\"wishes-textarea\" placeholder=\"Describe what you want in the design. E.g.: thin band, bold setting, side stones...\"><\/textarea>\n            <\/div>\n        <\/aside>\n\n        <!-- \u2500\u2500\u2500\u2500\u2500 CENTER: Preview \u2500\u2500\u2500\u2500\u2500 -->\n        <section class=\"panel-center\">\n            <div class=\"preview-container\">\n                <div class=\"diamond-pattern\"><\/div>\n                <div class=\"corner-accent tl\"><\/div>\n                <div class=\"corner-accent tr\"><\/div>\n                <div class=\"corner-accent bl\"><\/div>\n                <div class=\"corner-accent br\"><\/div>\n\n                <div class=\"preview-placeholder\" id=\"placeholder\">\n                    <svg class=\"placeholder-gem\" viewBox=\"0 0 80 80\">\n                        <path d=\"M40 6L10 30L40 74L70 30L40 6Z\" fill=\"currentColor\" opacity=\"0.15\"\/>\n                        <path d=\"M40 6L10 30L40 74L70 30L40 6Z\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\/>\n                        <path d=\"M10 30H70M40 6L25 30L40 74M40 6L55 30L40 74\" stroke=\"currentColor\" stroke-width=\"1\" fill=\"none\" opacity=\"0.3\"\/>\n                    <\/svg>\n                    <div class=\"placeholder-title\" data-i18n=\"ph_title\">Your jewelry will appear here<\/div>\n                    <div class=\"placeholder-hint\" data-i18n=\"ph_hint\">Set parameters and click \"Generate Design\"<\/div>\n                <\/div>\n\n                <div class=\"preview-result\" id=\"result\">\n                    <div class=\"preview-image-wrapper\">\n                        <div class=\"preview-jewelry-render\" id=\"jewelryRender\">\n                            <svg viewBox=\"0 0 300 300\" class=\"jewelry-svg\" id=\"jewelrySvg\">\n                                <defs>\n                                    <linearGradient id=\"metalGrad\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n                                        <stop offset=\"0%\" id=\"metalC1\" stop-color=\"#F5D76E\"\/>\n                                        <stop offset=\"50%\" id=\"metalC2\" stop-color=\"#D4A520\"\/>\n                                        <stop offset=\"100%\" id=\"metalC3\" stop-color=\"#AA8420\"\/>\n                                    <\/linearGradient>\n                                    <linearGradient id=\"stoneGrad2\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n                                        <stop offset=\"0%\" id=\"stoneC1\" stop-color=\"#6FCF97\"\/>\n                                        <stop offset=\"100%\" id=\"stoneC2\" stop-color=\"#1B7A4E\"\/>\n                                    <\/linearGradient>\n                                    <filter id=\"softGlow2\">\n                                        <feGaussianBlur stdDeviation=\"4\" result=\"blur\"\/>\n                                        <feComposite in=\"SourceGraphic\" in2=\"blur\" operator=\"over\"\/>\n                                    <\/filter>\n                                <\/defs>\n                                <!-- Ring preview (default) -->\n                                <g id=\"jewelryShape\" filter=\"url(#softGlow2)\">\n                                    <!-- Band -->\n                                    <ellipse cx=\"150\" cy=\"180\" rx=\"70\" ry=\"25\" fill=\"none\" stroke=\"url(#metalGrad)\" stroke-width=\"12\" opacity=\"0.9\"\/>\n                                    <ellipse cx=\"150\" cy=\"180\" rx=\"70\" ry=\"25\" fill=\"none\" stroke=\"rgba(255,255,255,0.15)\" stroke-width=\"2\"\/>\n                                    <!-- Prongs -->\n                                    <line x1=\"138\" y1=\"160\" x2=\"135\" y2=\"140\" stroke=\"url(#metalGrad)\" stroke-width=\"3\" stroke-linecap=\"round\"\/>\n                                    <line x1=\"162\" y1=\"160\" x2=\"165\" y2=\"140\" stroke=\"url(#metalGrad)\" stroke-width=\"3\" stroke-linecap=\"round\"\/>\n                                    <!-- Center stone -->\n                                    <polygon points=\"150,105 130,130 140,148 160,148 170,130\" fill=\"url(#stoneGrad2)\" stroke=\"rgba(255,255,255,0.3)\" stroke-width=\"0.5\"\/>\n                                    <polygon points=\"150,105 130,130 170,130\" fill=\"url(#stoneGrad2)\" opacity=\"0.7\"\/>\n                                    <polygon points=\"130,130 170,130 160,148 140,148\" fill=\"url(#stoneGrad2)\" opacity=\"0.5\"\/>\n                                    <polygon points=\"150,105 138,120 130,130\" fill=\"rgba(255,255,255,0.15)\"\/>\n                                    <!-- Accent diamonds -->\n                                    <circle cx=\"128\" cy=\"168\" r=\"4\" fill=\"rgba(255,255,255,0.7)\" id=\"accentL\"\/>\n                                    <circle cx=\"172\" cy=\"168\" r=\"4\" fill=\"rgba(255,255,255,0.7)\" id=\"accentR\"\/>\n                                <\/g>\n                                <!-- Sparkle effects -->\n                                <circle cx=\"145\" cy=\"110\" r=\"2\" fill=\"white\" opacity=\"0.8\">\n                                    <animate attributeName=\"opacity\" values=\"0;0.9;0\" dur=\"2s\" repeatCount=\"indefinite\"\/>\n                                <\/circle>\n                                <circle cx=\"165\" cy=\"125\" r=\"1.5\" fill=\"white\" opacity=\"0.6\">\n                                    <animate attributeName=\"opacity\" values=\"0;0.7;0\" dur=\"2.5s\" begin=\"0.5s\" repeatCount=\"indefinite\"\/>\n                                <\/circle>\n                                <circle cx=\"135\" cy=\"135\" r=\"1\" fill=\"white\" opacity=\"0.5\">\n                                    <animate attributeName=\"opacity\" values=\"0;0.6;0\" dur=\"1.8s\" begin=\"1s\" repeatCount=\"indefinite\"\/>\n                                <\/circle>\n                            <\/svg>\n                        <\/div>\n                        <div class=\"watermark-overlay\">\n                            <div class=\"watermark-text\">VOC GEMS<\/div>\n                            <div class=\"watermark-bar\">\n                                <span>VOC GEMS \u00b7 AI Constructor<\/span>\n                                <span id=\"designId\">VOC-2025-00001<\/span>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <div class=\"loading-overlay\" id=\"loading\">\n                    <div class=\"loading-ring\"><\/div>\n                    <div class=\"loading-text\" data-i18n=\"load_text\">AI is creating your design<\/div>\n                    <div class=\"loading-subtext\" data-i18n=\"load_sub\">This usually takes 10\u201315 seconds<\/div>\n                    <div class=\"loading-progress\">\n                        <div class=\"loading-progress-bar\" id=\"progressBar\"><\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Angle buttons shown after generation -->\n            <div style=\"position:relative; width:100%;\">\n                <div class=\"angle-bar\" id=\"angleBar\">\n                    <button class=\"angle-btn active\" data-angle=\"front\" data-i18n=\"angle_front\">Front<\/button>\n                    <button class=\"angle-btn\" data-angle=\"side\" data-i18n=\"angle_side\">Side<\/button>\n                    <button class=\"angle-btn\" data-angle=\"three_quarter\">3\/4<\/button>\n                <\/div>\n            <\/div>\n\n            <!-- Save by email \u2014 \u0422\u0417 \u0412\u043e\u043b\u043d\u0430 3 (\u043b\u0438\u0434-\u043c\u0430\u0433\u043d\u0438\u0442) -->\n            <div class=\"voc-save-row\" id=\"vocSaveRow\">\n                <button class=\"voc-save-btn\" id=\"vocSaveBtn\" type=\"button\">\n                    <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n                        <path d=\"M4 4h16v16H4z\"\/>\n                        <path d=\"M4 8l8 5 8-5\"\/>\n                    <\/svg>\n                    <span id=\"vocSaveBtnLabel\">Save this preview by email<\/span>\n                <\/button>\n                <div class=\"voc-save-hint\" id=\"vocSaveHint\">Free, with watermark \u2014 for inspiration. No commitment.<\/div>\n            <\/div>\n\n            <div class=\"generate-section\">\n                <button class=\"generate-btn\" id=\"generateBtn\">\n                    <svg viewBox=\"0 0 24 24\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"\/><\/svg>\n                    <span data-i18n=\"btn_generate\">Generate Design<\/span>\n                <\/button>\n                <div id=\"genLimitBadge\" class=\"gen-limit-badge\" style=\"display:none\">\n                    <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><polyline points=\"12 6 12 12 16 14\"\/><\/svg>\n                    <span><span class=\"gen-count\" id=\"genRemaining\">\u2014<\/span> \/ <span id=\"genLimit\">20<\/span> <span data-i18n=\"gen_week\">generations this week<\/span><\/span>\n                <\/div>\n                <div class=\"generate-hint\" data-i18n=\"gen_hint\">AI will generate a unique design based on your selection<\/div>\n            <\/div>\n        <\/section>\n\n        <!-- \u2500\u2500\u2500\u2500\u2500 RIGHT: Price \u2500\u2500\u2500\u2500\u2500 -->\n        <aside class=\"panel-right\">\n            <h2 class=\"panel-title\">\n                <svg class=\"panel-title-icon\" viewBox=\"0 0 24 24\"><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"23\"\/><path d=\"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6\"\/><\/svg>\n                <span data-i18n=\"price_title\">Price Estimate<\/span>\n            <\/h2>\n\n            <div id=\"priceSection\" style=\"display:none\">\n            <div class=\"price-card\">\n                <div class=\"price-section-label\" data-i18n=\"price_breakdown\">Price Breakdown<\/div>\n                <div class=\"price-row\" id=\"gemstoneRow\">\n                    <span class=\"price-label\" data-i18n=\"price_gem\">Gemstone<\/span>\n                    <span class=\"price-value\" id=\"gemstonePrice\">\u2014<\/span>\n                <\/div>\n                <div class=\"price-row\">\n                    <span class=\"price-label\" id=\"metalLabel\">Gold 750 (~8g)<\/span>\n                    <span class=\"price-value\" id=\"metalPrice\">$1,850<\/span>\n                <\/div>\n                <div class=\"price-row\">\n                    <span class=\"price-label\" data-i18n=\"price_craft\">Craftsmanship<\/span>\n                    <span class=\"price-value\">$800<\/span>\n                <\/div>\n                <div class=\"price-row\" id=\"diamondRow\">\n                    <span class=\"price-label\">Diamonds (0.5ct)<\/span>\n                    <span class=\"price-value\" id=\"diamondsPrice\">$650<\/span>\n                <\/div>\n\n                <div class=\"price-total\">\n                    <span class=\"price-total-label\">Total<\/span>\n                    <span class=\"price-total-value\" id=\"totalPrice\">\u2014<\/span>\n                <\/div>\n            <\/div>\n\n            <div class=\"action-buttons\">\n                <button class=\"btn-order\">\n                    <svg viewBox=\"0 0 24 24\"><circle cx=\"9\" cy=\"21\" r=\"1\"\/><circle cx=\"20\" cy=\"21\" r=\"1\"\/><path d=\"m1 1 4 4 2.5 9.5a2 2 0 0 0 2 1.5h9a2 2 0 0 0 2-1.5L23 6H6\"\/><\/svg>\n                    Order Production\n                <\/button>\n                <button class=\"btn-save\">\n                    <svg viewBox=\"0 0 24 24\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 0 2-2z\"\/><polyline points=\"17 21 17 13 7 13 7 21\"\/><polyline points=\"7 3 7 8 15 8\"\/><\/svg>\n                    Save Design\n                <\/button>\n                <button class=\"btn-stone-only\">\n                    Buy Stone Only\n                    <svg viewBox=\"0 0 24 24\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"\/><polyline points=\"12 5 19 12 12 19\"\/><\/svg>\n                <\/button>\n            <\/div>\n\n            <div class=\"features-list\">\n                <div class=\"feature-row\">\n                    <span class=\"feature-check\"><svg viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"\/><\/svg><\/span>\n                    Free worldwide shipping\n                <\/div>\n                <div class=\"feature-row\">\n                    <span class=\"feature-check\"><svg viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"\/><\/svg><\/span>\n                    <span data-i18n=\"cert_note\">Gemstone authenticity certificate<\/span>\n                <\/div>\n                <div class=\"feature-row\">\n                    <span class=\"feature-check\"><svg viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"\/><\/svg><\/span>\n                    2-year jewelry warranty\n                <\/div>\n                <div class=\"feature-row\">\n                    <span class=\"feature-check\"><svg viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"\/><\/svg><\/span>\n                    Production 2\u20133 weeks\n                <\/div>\n            <\/div>\n            <\/div><!-- \/priceSection -->\n        <\/aside>\n    <\/main>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 FOOTER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <div class=\"footer-bar\">\n        Questions? <a href=\"mailto:info@vocgems.com\">info@vocgems.com<\/a> \u00b7 \"VOC gems\" \u2014 Gemstone Store &amp; Auction \u00a9 2026\n    <\/div>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 STONE SELECTION MODAL \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <div id=\"stoneModal\" class=\"stone-modal\">\n        <div class=\"stone-modal-overlay\" onclick=\"VOC_API.hideStoneModal()\"><\/div>\n        <div class=\"stone-modal-content\">\n            <div class=\"stone-modal-header\">\n                <h3><span data-i18n=\"modal_title\">Select Gemstone<\/span><\/h3>\n                <button class=\"stone-modal-close\" onclick=\"VOC_API.hideStoneModal()\">\u00d7<\/button>\n            <\/div>\n            <!-- Filters -->\n            <div class=\"stone-modal-filters\">\n                <div class=\"stone-search-wrap\">\n                    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"\/><path d=\"M21 21l-4.35-4.35\"\/><\/svg>\n                    <input type=\"text\" id=\"stoneSearch\" placeholder=\"Search by name...\" oninput=\"VOC_API.onSearch(this.value)\">\n                <\/div>\n                <div class=\"stone-category-tabs\" id=\"stoneCategoryTabs\">\n                    <button class=\"scat-btn active\" data-cat=\"\">All<\/button>\n                    <button class=\"scat-btn\" data-cat=\"Emerald\">Emerald<\/button>\n                    <button class=\"scat-btn\" data-cat=\"Ruby\">Ruby<\/button>\n                    <button class=\"scat-btn\" data-cat=\"Sapphire\">Sapphire<\/button>\n                    <button class=\"scat-btn\" data-cat=\"Spinel\">Spinel<\/button>\n                    <button class=\"scat-btn\" data-cat=\"Alexandrite\">Alexandrite<\/button>\n                    <button class=\"scat-btn\" data-cat=\"Tourmaline\">Tourmaline<\/button>\n                <\/div>\n            <\/div>\n            <!-- Grid -->\n            <div class=\"stone-modal-grid\" id=\"stoneGrid\">\n                <div class=\"stone-modal-loading\" id=\"stoneModalLoading\">\n                    <div class=\"stone-modal-spinner\"><\/div>\n                    <span>Loading catalog...<\/span>\n                <\/div>\n            <\/div>\n            <!-- Pagination -->\n            <div class=\"stone-modal-pagination\" id=\"stoneModalPagination\" style=\"display:none\">\n                <button class=\"spag-btn\" id=\"stonePrevBtn\" onclick=\"VOC_API.loadPage(VOC_API.stonePage - 1)\" disabled>\u2190 Prev<\/button>\n                <span id=\"stonePageInfo\">Page 1<\/span>\n                <button class=\"spag-btn\" id=\"stoneNextBtn\" onclick=\"VOC_API.loadPage(VOC_API.stonePage + 1)\">Next \u2192<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <!-- \u2550\u2550\u2550 Watermark-preview email modal \u2014 \u0422\u0417 \u0412\u043e\u043b\u043d\u0430 3 \u2550\u2550\u2550 -->\n    <div class=\"voc-wm-modal\" id=\"vocWmModal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"vocWmTitle\">\n        <div class=\"voc-wm-card\">\n            <button class=\"voc-wm-close\" type=\"button\" id=\"vocWmClose\" aria-label=\"Close\">\u00d7<\/button>\n            <h3 id=\"vocWmTitle\">Get this preview by email<\/h3>\n            <p class=\"voc-wm-sub\" id=\"vocWmSub\">We'll send you the rendered image with a soft watermark \u2014 for your reference and inspiration. No spam, ever.<\/p>\n            <input type=\"email\" class=\"voc-wm-field\" id=\"vocWmEmail\" placeholder=\"your@email.com\" autocomplete=\"email\" required>\n            <label class=\"voc-wm-consent\">\n                <input type=\"checkbox\" id=\"vocWmConsent\">\n                <span id=\"vocWmConsentText\">I agree to the <a href=\"\/privacy-policy\/\" target=\"_blank\" rel=\"noopener\">Privacy Policy<\/a> and consent to receiving the preview by email.<\/span>\n            <\/label>\n            <button class=\"voc-wm-submit\" type=\"button\" id=\"vocWmSubmit\">\n                <span id=\"vocWmSubmitLabel\">Send to my email<\/span>\n            <\/button>\n            <div class=\"voc-wm-status\" id=\"vocWmStatus\" role=\"status\" aria-live=\"polite\"><\/div>\n        <\/div>\n    <\/div>\n\n    <style>\n        .stone-modal {\n            display: none;\n            position: fixed;\n            inset: 0;\n            z-index: 1000;\n            align-items: center;\n            justify-content: center;\n        }\n        .stone-modal.show {\n            display: flex;\n        }\n        .stone-modal-overlay {\n            position: absolute;\n            inset: 0;\n            background: rgba(0,0,0,0.5);\n            backdrop-filter: blur(4px);\n        }\n        .stone-modal-content {\n            position: relative;\n            background: white;\n            border-radius: 20px;\n            width: 90%;\n            max-width: 780px;\n            max-height: 85vh;\n            overflow: hidden;\n            box-shadow: 0 25px 80px rgba(0,0,0,0.3);\n            animation: modalIn 0.3s ease;\n            display: flex;\n            flex-direction: column;\n        }\n        @keyframes modalIn {\n            from { opacity: 0; transform: scale(0.95) translateY(20px); }\n            to { opacity: 1; transform: scale(1) translateY(0); }\n        }\n        .stone-modal-header {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            padding: 20px 24px 16px;\n            border-bottom: 1px solid #eee;\n            flex-shrink: 0;\n        }\n        .stone-modal-header h3 {\n            font-family: 'Playfair Display', serif;\n            font-size: 22px;\n            font-weight: 500;\n            margin: 0;\n        }\n        .stone-modal-close {\n            width: 36px;\n            height: 36px;\n            border: none;\n            background: #f5f5f5;\n            border-radius: 50%;\n            font-size: 24px;\n            cursor: pointer;\n            color: #666;\n            transition: all 0.2s;\n        }\n        .stone-modal-close:hover { background: #eee; color: #333; }\n\n        \/* Filters *\/\n        .stone-modal-filters {\n            padding: 12px 24px 0;\n            flex-shrink: 0;\n        }\n        .stone-search-wrap {\n            position: relative;\n            margin-bottom: 12px;\n        }\n        .stone-search-wrap svg {\n            position: absolute;\n            left: 12px; top: 50%;\n            transform: translateY(-50%);\n            width: 16px; height: 16px;\n            color: #999;\n        }\n        .stone-search-wrap input {\n            width: 100%;\n            padding: 9px 12px 9px 36px;\n            border: 1.5px solid var(--border);\n            border-radius: 10px;\n            font-size: 14px;\n            font-family: 'DM Sans', sans-serif;\n            outline: none;\n            transition: border-color 0.2s;\n            background: var(--bg);\n        }\n        .stone-search-wrap input:focus { border-color: var(--primary); }\n\n        .stone-category-tabs {\n            display: flex;\n            gap: 6px;\n            flex-wrap: wrap;\n            padding-bottom: 12px;\n            border-bottom: 1px solid #f0f0f0;\n        }\n        .scat-btn {\n            padding: 5px 14px;\n            border: 1.5px solid var(--border);\n            border-radius: 20px;\n            background: white;\n            font-size: 12px;\n            font-family: 'DM Sans', sans-serif;\n            font-weight: 500;\n            color: var(--text-mid);\n            cursor: pointer;\n            transition: all 0.2s;\n        }\n        .scat-btn:hover { border-color: var(--primary); color: var(--primary); }\n        .scat-btn.active {\n            background: var(--primary);\n            border-color: var(--primary);\n            color: white;\n        }\n\n        \/* Grid *\/\n        .stone-modal-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));\n            gap: 14px;\n            padding: 20px 24px;\n            overflow-y: auto;\n            flex: 1;\n            min-height: 0;\n        }\n        .stone-modal-loading {\n            grid-column: 1 \/ -1;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: 12px;\n            padding: 60px 0;\n            color: var(--text-light);\n            font-size: 14px;\n        }\n        .stone-modal-spinner {\n            width: 32px; height: 32px;\n            border: 3px solid var(--border);\n            border-top-color: var(--primary);\n            border-radius: 50%;\n            animation: spin 0.8s linear infinite;\n        }\n        @keyframes spin { to { transform: rotate(360deg); } }\n        @keyframes fadeIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:none; } }\n            grid-column: 1 \/ -1;\n            text-align: center;\n            padding: 60px 0;\n            color: var(--text-light);\n            font-size: 14px;\n        }\n\n        .stone-option {\n            background: #fafafa;\n            border: 2px solid #eee;\n            border-radius: 16px;\n            padding: 0;\n            cursor: pointer;\n            transition: all 0.25s;\n            overflow: hidden;\n            display: flex;\n            flex-direction: column;\n        }\n        .stone-option:hover {\n            border-color: var(--primary);\n            transform: translateY(-2px);\n            box-shadow: 0 8px 25px rgba(69,189,176,0.15);\n        }\n        .stone-option.selected {\n            border-color: var(--primary);\n            background: var(--primary-light);\n        }\n        .stone-option-img {\n            width: 100%;\n            aspect-ratio: 1;\n            object-fit: cover;\n            background: linear-gradient(135deg, #f0f0f0, #e0e0e0);\n            display: block;\n        }\n        .stone-option-img-placeholder {\n            width: 100%;\n            aspect-ratio: 1;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 40px;\n        }\n        .stone-option-body {\n            padding: 12px;\n        }\n        .stone-option-name {\n            font-weight: 600;\n            font-size: 13px;\n            color: #333;\n            margin-bottom: 3px;\n            line-height: 1.3;\n            white-space: nowrap;\n            overflow: hidden;\n            text-overflow: ellipsis;\n        }\n        .stone-option-details {\n            font-size: 11px;\n            color: #888;\n            margin-bottom: 6px;\n            line-height: 1.4;\n        }\n        .stone-option-price {\n            font-weight: 700;\n            font-size: 14px;\n            color: var(--primary);\n        }\n        .stone-option-cert {\n            display: inline-block;\n            font-size: 10px;\n            background: var(--gold-light);\n            color: var(--gold);\n            border-radius: 4px;\n            padding: 1px 6px;\n            margin-top: 4px;\n            font-weight: 600;\n        }\n\n        \/* Pagination *\/\n        .stone-modal-pagination {\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 16px;\n            padding: 12px 24px;\n            border-top: 1px solid #eee;\n            flex-shrink: 0;\n        }\n        .spag-btn {\n            padding: 6px 16px;\n            border: 1.5px solid var(--border);\n            border-radius: 8px;\n            background: white;\n            font-size: 13px;\n            font-family: 'DM Sans', sans-serif;\n            cursor: pointer;\n            transition: all 0.2s;\n            color: var(--text);\n        }\n        .spag-btn:hover:not(:disabled) { border-color: var(--primary); color: var(--primary); }\n        .spag-btn:disabled { opacity: 0.4; cursor: default; }\n        #stonePageInfo { font-size: 13px; color: var(--text-light); min-width: 80px; text-align: center; }\n\n        \/* \u2550\u2550\u2550 Save by email \u2014 \u043b\u0438\u0434-\u043c\u0430\u0433\u043d\u0438\u0442, \u0422\u0417 \u0412\u043e\u043b\u043d\u0430 3 \u2550\u2550\u2550 *\/\n        .voc-save-row {\n            margin-top: 18px;\n            text-align: center;\n            display: none;\n        }\n        .preview-result.show ~ .angle-bar-wrapper ~ .voc-save-row,\n        .preview-result.show ~ * .voc-save-row,\n        .voc-save-row.visible {\n            display: block;\n        }\n        .voc-save-btn {\n            display: inline-flex;\n            align-items: center;\n            gap: 10px;\n            padding: 12px 22px;\n            background: rgba(255,255,255,0.06);\n            border: 1.5px solid rgba(255,255,255,0.18);\n            border-radius: 50px;\n            color: #fff;\n            font-family: inherit;\n            font-size: 14px;\n            font-weight: 500;\n            cursor: pointer;\n            transition: background 0.2s, border-color 0.2s, transform 0.2s;\n        }\n        .voc-save-btn:hover {\n            background: rgba(255,255,255,0.12);\n            border-color: rgba(255,255,255,0.35);\n            transform: translateY(-1px);\n        }\n        .voc-save-btn svg { flex-shrink: 0; }\n        .voc-save-hint {\n            margin-top: 8px;\n            font-size: 12px;\n            color: rgba(255,255,255,0.5);\n        }\n\n        \/* \u041c\u043e\u0434\u0430\u043b\u043a\u0430 *\/\n        .voc-wm-modal {\n            position: fixed;\n            inset: 0;\n            z-index: 99999;\n            background: rgba(8, 14, 18, 0.78);\n            backdrop-filter: blur(6px);\n            -webkit-backdrop-filter: blur(6px);\n            display: none;\n            align-items: center;\n            justify-content: center;\n            padding: 20px;\n            font-family: 'DM Sans', -apple-system, sans-serif;\n        }\n        .voc-wm-modal.open { display: flex; }\n        .voc-wm-card {\n            background: #14181C;\n            border: 1px solid rgba(255,255,255,0.08);\n            border-radius: 16px;\n            max-width: 440px;\n            width: 100%;\n            padding: 32px 28px 28px;\n            position: relative;\n            box-shadow: 0 30px 80px rgba(0,0,0,0.5);\n        }\n        .voc-wm-close {\n            position: absolute;\n            top: 14px;\n            right: 14px;\n            width: 32px;\n            height: 32px;\n            background: transparent;\n            border: none;\n            color: rgba(255,255,255,0.5);\n            font-size: 22px;\n            cursor: pointer;\n            border-radius: 50%;\n            transition: background 0.2s, color 0.2s;\n        }\n        .voc-wm-close:hover { background: rgba(255,255,255,0.08); color: #fff; }\n        .voc-wm-card h3 {\n            font-family: 'Playfair Display', Georgia, serif;\n            font-size: 22px;\n            color: #fff;\n            margin: 0 0 8px;\n            font-weight: 500;\n        }\n        .voc-wm-card .voc-wm-sub {\n            font-size: 14px;\n            color: rgba(255,255,255,0.6);\n            margin: 0 0 22px;\n            line-height: 1.5;\n        }\n        .voc-wm-field {\n            display: block;\n            width: 100%;\n            background: rgba(255,255,255,0.04);\n            border: 1.5px solid rgba(255,255,255,0.12);\n            border-radius: 10px;\n            padding: 13px 16px;\n            color: #fff;\n            font-size: 15px;\n            font-family: inherit;\n            box-sizing: border-box;\n            transition: border-color 0.2s;\n        }\n        .voc-wm-field:focus {\n            outline: none;\n            border-color: var(--primary, #45BDB0);\n        }\n        .voc-wm-field::placeholder { color: rgba(255,255,255,0.35); }\n        .voc-wm-consent {\n            display: flex;\n            align-items: flex-start;\n            gap: 10px;\n            margin: 14px 0 22px;\n            font-size: 13px;\n            color: rgba(255,255,255,0.7);\n            line-height: 1.5;\n            cursor: pointer;\n        }\n        .voc-wm-consent input[type=\"checkbox\"] {\n            width: 18px;\n            height: 18px;\n            flex-shrink: 0;\n            margin-top: 1px;\n            accent-color: var(--primary, #45BDB0);\n        }\n        .voc-wm-consent a { color: var(--primary, #45BDB0); text-decoration: underline; }\n        .voc-wm-submit {\n            width: 100%;\n            background: var(--primary, #45BDB0);\n            color: #14181C;\n            border: none;\n            border-radius: 10px;\n            padding: 14px;\n            font-size: 15px;\n            font-weight: 600;\n            font-family: inherit;\n            cursor: pointer;\n            transition: background 0.2s, opacity 0.2s;\n        }\n        .voc-wm-submit:hover:not(:disabled) { background: #5BCFC2; }\n        .voc-wm-submit:disabled { opacity: 0.5; cursor: not-allowed; }\n        .voc-wm-status {\n            margin-top: 14px;\n            font-size: 13px;\n            text-align: center;\n            min-height: 18px;\n            line-height: 1.5;\n        }\n        .voc-wm-status.error { color: #FF8A8A; }\n        .voc-wm-status.success { color: var(--primary, #45BDB0); }\n    <\/style>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 JAVASCRIPT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <script>\n        \/\/ \u2500\u2500\u2500 Utility: group button toggle \u2500\u2500\u2500\n        function initToggle(selector, callback) {\n            document.querySelectorAll(selector).forEach(btn => {\n                btn.addEventListener('click', () => {\n                    document.querySelectorAll(selector).forEach(b => b.classList.remove('active'));\n                    btn.classList.add('active');\n                    if (callback) callback(btn);\n                });\n            });\n        }\n\n        \/\/ \u2500\u2500\u2500 Type selection \u2500\u2500\u2500\n        initToggle('.type-btn', btn => {\n            \/\/ ring size removed\n            updatePreviewEmoji();\n        });\n\n        \/\/ \u2500\u2500\u2500 Gender, Ring Size \u2500\u2500\u2500\n        initToggle('.gender-btn');\n        initToggle('.size-btn');\n\n        \/\/ \u2500\u2500\u2500 Metal \u2500\u2500\u2500\n        initToggle('.metal-btn', btn => {\n            updateGradeVisibility();\n            updatePrices();\n            updateMetalColors();\n        });\n\n        \/\/ \u2500\u2500\u2500 Grade \u2500\u2500\u2500\n        initToggle('.grade-btn', () => updatePrices());\n\n        \/\/ \u2500\u2500\u2500 Style \u2500\u2500\u2500\n        initToggle('.style-btn');\n\n        \/\/ \u2500\u2500\u2500 Diamonds \u2500\u2500\u2500\n        initToggle('.diamond-btn', () => updatePrices());\n\n        \/\/ \u2500\u2500\u2500 Angle buttons \u2500\u2500\u2500\n        \/\/ \u041d\u0415 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c initToggle - \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0432 API Integration \u043d\u0438\u0436\u0435\n\n        \/\/ \u2500\u2500\u2500 Show\/hide ring-only sections \u2500\u2500\u2500\n        function updateRingSections() {}\n\n        \/\/ \u2500\u2500\u2500 Show\/hide grade section for platinum \u2500\u2500\u2500\n        function updateGradeVisibility() {\n            const metal = document.querySelector('.metal-btn.active')?.dataset.metal;\n            const gradeSection = document.getElementById('gradeSection');\n            gradeSection.classList.toggle('hidden', metal === 'platinum');\n        }\n\n        \/\/ \u2500\u2500\u2500 Preview emoji \u2192 now updates gem SVG colors \u2500\u2500\u2500\n        function updatePreviewEmoji() {\n            \/\/ No longer needed \u2014 gem updates via updateGemColors()\n        }\n\n        \/\/ \u2500\u2500\u2500 Gem SVG color palette \u2500\u2500\u2500\n        const gemColors = {\n            emerald:    ['#6FCF97','#2D9F6F','#1B7A4E'],\n            ruby:       ['#FF6B6B','#E53E3E','#9B2C2C'],\n            sapphire:   ['#63B3ED','#3182CE','#1E40AF'],\n            diamond:    ['#F7FAFC','#E2E8F0','#CBD5E0'],\n            amethyst:   ['#B794F4','#805AD5','#553C9A'],\n            topaz:      ['#FBD38D','#ED8936','#C05621'],\n            tourmaline: ['#4FD1C5','#38B2AC','#285E61'],\n            spinel:     ['#FC8181','#E53E3E','#822727'],\n        };\n\n        function updateGemColors() {\n            \/\/ For now, emerald is default \u2014 this will be dynamic when stone picker works\n            const colors = gemColors.emerald;\n            document.getElementById('gemColor1').setAttribute('stop-color', colors[0]);\n            document.getElementById('gemColor2').setAttribute('stop-color', colors[1]);\n            document.getElementById('gemColor3').setAttribute('stop-color', colors[2]);\n            \/\/ Also update the preview stone\n            if (document.getElementById('stoneC1')) {\n                document.getElementById('stoneC1').setAttribute('stop-color', colors[0]);\n                document.getElementById('stoneC2').setAttribute('stop-color', colors[2]);\n            }\n        }\n\n        \/\/ \u2500\u2500\u2500 Metal gradient update \u2500\u2500\u2500\n        const metalColors = {\n            'yellow-gold': ['#F5D76E','#D4A520','#AA8420'],\n            'white-gold':  ['#FAFAFA','#E0E0E0','#B8B8B8'],\n            'rose-gold':   ['#F0B8BC','#D4919A','#B87080'],\n            'platinum':    ['#E0E4E8','#C0C8D0','#98A4B0'],\n        };\n\n        function updateMetalColors() {\n            const metal = document.querySelector('.metal-btn.active')?.dataset.metal || 'yellow-gold';\n            const colors = metalColors[metal];\n            if (document.getElementById('metalC1')) {\n                document.getElementById('metalC1').setAttribute('stop-color', colors[0]);\n                document.getElementById('metalC2').setAttribute('stop-color', colors[1]);\n                document.getElementById('metalC3').setAttribute('stop-color', colors[2]);\n            }\n        }\n\n        \/\/ \u2500\u2500\u2500 Price calculator \u2500\u2500\u2500\n        function updatePrices() {\n            const metal    = document.querySelector('.metal-btn.active')?.dataset.metal;\n            const grade    = document.querySelector('.grade-btn.active')?.dataset.grade || '750';\n            const diamonds = document.querySelector('.diamond-btn.active')?.dataset.diamonds || 'none';\n\n            const metalPrices = {\n                'yellow-gold': { '750': 1850, '585': 1450 },\n                'white-gold':  { '750': 1950, '585': 1550 },\n                'rose-gold':   { '750': 1850, '585': 1450 },\n                'platinum':    { '950': 2400 }\n            };\n            const metalNames = {\n                'yellow-gold': 'Gold', 'white-gold': 'White Gold',\n                'rose-gold': 'Rose Gold', 'platinum': 'Platinum 950'\n            };\n            const diamondPrices = { 'none': 0, 'few': 650, 'many': 1800 };\n            const diamondLabels = { 'none': '', 'few': 'Diamonds (0.5ct)', 'many': 'Diamonds (1.5ct)' };\n\n            const metalGrade = metal === 'platinum' ? '950' : grade;\n            const metalCost  = metal ? (metalPrices[metal]?.[metalGrade] || 1850) : null;\n            const diamondCost = diamondPrices[diamonds] || 0;\n\n            const metalLabelEl = document.getElementById('metalLabel');\n            const metalPriceEl = document.getElementById('metalPrice');\n            if (metalLabelEl) metalLabelEl.textContent = metal\n                ? `${metalNames[metal] || metal}${metal === 'platinum' ? '' : ' ' + metalGrade} (~8g)`\n                : '\u2014';\n            if (metalPriceEl) metalPriceEl.textContent = metalCost ? `$${metalCost.toLocaleString()}` : '\u2014';\n\n            const diamondRow = document.getElementById('diamondRow');\n            if (diamondRow) {\n                if (!diamonds || diamonds === 'none') {\n                    diamondRow.style.display = 'none';\n                } else {\n                    diamondRow.style.display = 'flex';\n                    const dlabel = diamondRow.querySelector('.price-label');\n                    if (dlabel) dlabel.textContent = diamondLabels[diamonds];\n                    const dprice = document.getElementById('diamondsPrice');\n                    if (dprice) dprice.textContent = `$${diamondCost.toLocaleString()}`;\n                }\n            }\n\n            const stonePrice = VOC_API?.selectedStone?.price || 0;\n            const totalEl = document.getElementById('totalPrice');\n            if (totalEl) {\n                if (metalCost) {\n                    const total = stonePrice + metalCost + 800 + diamondCost;\n                    totalEl.textContent = `$${total.toLocaleString()}`;\n                } else {\n                    totalEl.textContent = '\u2014';\n                }\n            }\n        }\n\n        \/\/ \u2500\u2500\u2500 Generate button \u2500\u2500\u2500\n        document.getElementById('generateBtn').addEventListener('click', () => {\n            const loading = document.getElementById('loading');\n            const placeholder = document.getElementById('placeholder');\n            const result = document.getElementById('result');\n            const progressBar = document.getElementById('progressBar');\n            const angleBar = document.getElementById('angleBar');\n\n            \/\/ Reset\n            result.classList.remove('show');\n            angleBar.style.display = 'none';\n            loading.classList.add('show');\n            placeholder.style.display = 'none';\n            progressBar.style.width = '0%';\n\n            \/\/ Update steps\n            document.querySelectorAll('.step-item')[0].classList.add('completed');\n            document.querySelectorAll('.step-item')[0].classList.remove('active');\n            document.querySelectorAll('.step-item')[1].classList.add('active');\n            document.querySelectorAll('.step-connector')[0].classList.add('active');\n\n            \/\/ Animate progress bar\n            let progress = 0;\n            const interval = setInterval(() => {\n                progress += Math.random() * 15 + 5;\n                if (progress > 95) progress = 95;\n                progressBar.style.width = progress + '%';\n            }, 400);\n\n            \/\/ Complete after delay\n            setTimeout(() => {\n                clearInterval(interval);\n                progressBar.style.width = '100%';\n\n                setTimeout(() => {\n                    loading.classList.remove('show');\n                    result.classList.add('show');\n                    angleBar.style.display = 'flex';\n\n                    \/\/ Update step to completed\n                    document.querySelectorAll('.step-item')[1].classList.add('completed');\n                    document.querySelectorAll('.step-item')[1].classList.remove('active');\n                    document.querySelectorAll('.step-item')[2].classList.add('active');\n                    document.querySelectorAll('.step-connector')[1].classList.add('active');\n\n                    \/\/ Random design ID\n                    const id = 'VOC-2025-' + String(Math.floor(Math.random() * 99999)).padStart(5, '0');\n                    document.getElementById('designId').textContent = id;\n\n                    \/\/ \u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 \u00abSave by email\u00bb \u043f\u043e\u0441\u043b\u0435 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438\n                    const saveRow = document.getElementById('vocSaveRow');\n                    if (saveRow) saveRow.classList.add('visible');\n                }, 400);\n            }, 3000);\n        });\n\n        \/\/ \u2550\u2550\u2550 Watermark email modal \u2014 \u043b\u0438\u0434-\u043c\u0430\u0433\u043d\u0438\u0442, \u0422\u0417 \u0412\u043e\u043b\u043d\u0430 3 \u2550\u2550\u2550\n        (function() {\n            const isRu = window.location.pathname === '\/ru' || window.location.pathname.indexOf('\/ru\/') === 0;\n\n            \/\/ i18n \u0442\u0435\u043a\u0441\u0442\u043e\u0432 \u043a\u043d\u043e\u043f\u043a\u0438 \u0438 \u043c\u043e\u0434\u0430\u043b\u043a\u0438\n            const T = isRu ? {\n                btn:        '\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u0435\u0432\u044c\u044e \u043d\u0430 email',\n                hint:       '\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e, \u0441 \u0432\u043e\u0434\u044f\u043d\u044b\u043c \u0437\u043d\u0430\u043a\u043e\u043c \u2014 \u0434\u043b\u044f \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u044f. \u041d\u0438 \u043a \u0447\u0435\u043c\u0443 \u043d\u0435 \u043e\u0431\u044f\u0437\u044b\u0432\u0430\u0435\u0442.',\n                title:      '\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u0435\u0432\u044c\u044e \u043d\u0430 email',\n                sub:        '\u041c\u044b \u043f\u0440\u0438\u0448\u043b\u0451\u043c \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 \u043b\u0451\u0433\u043a\u0438\u043c \u0432\u043e\u0434\u044f\u043d\u044b\u043c \u0437\u043d\u0430\u043a\u043e\u043c \u2014 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u044f. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0441\u043f\u0430\u043c\u0430.',\n                placeholder:'\u0432\u0430\u0448@email.com',\n                consent:    '\u042f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e <a href=\"\/ru\/privacy-policy\/\" target=\"_blank\" rel=\"noopener\">\u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0443 \u043a\u043e\u043d\u0444\u0438\u0434\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438<\/a> \u0438 \u0441\u043e\u0433\u043b\u0430\u0441\u0435\u043d(\u043d\u0430) \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u0435\u0432\u044c\u044e \u043d\u0430 email.',\n                submit:     '\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043d\u0430 email',\n                sending:    '\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u2026',\n                badEmail:   '\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u043a\u0430\u0436\u0438\u0442\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 email',\n                needConsent:'\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0435, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430',\n                ok:         '\u0413\u043e\u0442\u043e\u0432\u043e! \u041f\u0440\u0435\u0432\u044c\u044e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e \u043d\u0430 \u0432\u0430\u0448 email. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0442\u0430\u043a\u0436\u0435 \u043f\u0430\u043f\u043a\u0443 \u00ab\u0421\u043f\u0430\u043c\u00bb.',\n                rateLimit:  '\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.',\n                fail:       '\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437 \u0438\u043b\u0438 \u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043d\u0430\u043c.'\n            } : {\n                btn:        'Save this preview by email',\n                hint:       'Free, with watermark \u2014 for inspiration. No commitment.',\n                title:      'Get this preview by email',\n                sub:        \"We'll send you the rendered image with a soft watermark \u2014 for your reference and inspiration. No spam, ever.\",\n                placeholder:'your@email.com',\n                consent:    'I agree to the <a href=\"\/privacy-policy\/\" target=\"_blank\" rel=\"noopener\">Privacy Policy<\/a> and consent to receiving the preview by email.',\n                submit:     'Send to my email',\n                sending:    'Sending\u2026',\n                badEmail:   'Please enter a valid email',\n                needConsent:'Please confirm the consent',\n                ok:         \"Done! The preview has been sent to your email. Please also check the Spam folder.\",\n                rateLimit:  'Too many requests. Please try again in a few minutes.',\n                fail:       'Could not send. Please try again or contact us.'\n            };\n\n            \/\/ \u041f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e\n            const set = (id, txt, asHtml) => {\n                const el = document.getElementById(id);\n                if (!el) return;\n                if (asHtml) el.innerHTML = txt; else el.textContent = txt;\n            };\n            set('vocSaveBtnLabel', T.btn);\n            set('vocSaveHint', T.hint);\n            set('vocWmTitle', T.title);\n            set('vocWmSub', T.sub);\n            set('vocWmConsentText', T.consent, true);\n            set('vocWmSubmitLabel', T.submit);\n            const emailField = document.getElementById('vocWmEmail');\n            if (emailField) emailField.placeholder = T.placeholder;\n\n            \/\/ \u041e\u0442\u043a\u0440\u044b\u0442\u044c \u043c\u043e\u0434\u0430\u043b\u043a\u0443\n            const modal      = document.getElementById('vocWmModal');\n            const openBtn    = document.getElementById('vocSaveBtn');\n            const closeBtn   = document.getElementById('vocWmClose');\n            const submitBtn  = document.getElementById('vocWmSubmit');\n            const statusEl   = document.getElementById('vocWmStatus');\n            const consentEl  = document.getElementById('vocWmConsent');\n\n            function openModal() {\n                if (!modal) return;\n                modal.classList.add('open');\n                statusEl.textContent = '';\n                statusEl.className = 'voc-wm-status';\n                setTimeout(() => { emailField && emailField.focus(); }, 50);\n            }\n            function closeModal() {\n                if (!modal) return;\n                modal.classList.remove('open');\n            }\n\n            if (openBtn) openBtn.addEventListener('click', openModal);\n            if (closeBtn) closeBtn.addEventListener('click', closeModal);\n            if (modal) {\n                modal.addEventListener('click', (e) => {\n                    if (e.target === modal) closeModal();\n                });\n            }\n            document.addEventListener('keydown', (e) => {\n                if (e.key === 'Escape' && modal && modal.classList.contains('open')) closeModal();\n            });\n\n            \/\/ \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c URL \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.\n            \/\/ \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 API \u043a\u043b\u0430\u0434\u0451\u0442 \u0435\u0433\u043e \u043d\u0430 VOC_API.lastGenerationImageUrl.\n            \/\/ \u0415\u0441\u043b\u0438 URL \u0435\u0449\u0451 \u043d\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u2014 \u0431\u0435\u0440\u0451\u043c fallback (\u043b\u043e\u0433\u043e\u0442\u0438\u043f\/\u043f\u0440\u0435\u0432\u044c\u044e), \u0447\u0442\u043e\u0431\u044b\n            \/\/ \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0438 \u043f\u0438\u0441\u044c\u043c\u043e \u0443\u0448\u043b\u043e (\u0441 \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c).\n            function getCurrentImageUrl() {\n                if (window.VOC_API && window.VOC_API.lastGenerationImageUrl) {\n                    return window.VOC_API.lastGenerationImageUrl;\n                }\n                \/\/ fallback \u043d\u0430 \u0445\u043e\u0441\u0442\u043e\u0432\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u2014 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043f\u0440\u0438\u043c\u0435\u0442 \u043b\u044e\u0431\u043e\u0439 URL \u0441 vocgems.com\n                return window.location.origin + '\/wp-content\/uploads\/voc-preview-fallback.jpg';\n            }\n\n            function validEmail(v) {\n                return \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(v);\n            }\n\n            if (submitBtn) submitBtn.addEventListener('click', async () => {\n                const email = (emailField.value || '').trim();\n                const consent = consentEl && consentEl.checked;\n\n                if (!validEmail(email)) {\n                    statusEl.textContent = T.badEmail;\n                    statusEl.className = 'voc-wm-status error';\n                    return;\n                }\n                if (!consent) {\n                    statusEl.textContent = T.needConsent;\n                    statusEl.className = 'voc-wm-status error';\n                    return;\n                }\n\n                submitBtn.disabled = true;\n                statusEl.textContent = T.sending;\n                statusEl.className = 'voc-wm-status';\n\n                const payload = {\n                    email:     email,\n                    image_url: getCurrentImageUrl(),\n                    stone_id:  (window.VOC_API && window.VOC_API.selectedStone && window.VOC_API.selectedStone.id) || null,\n                    source:    'constructor_v6',\n                    consent:   true,\n                    lang:      isRu ? 'ru' : 'en'\n                };\n\n                try {\n                    const res = await fetch('\/wp-json\/vocgems\/v1\/render-watermark', {\n                        method: 'POST',\n                        headers: { 'Content-Type': 'application\/json' },\n                        body: JSON.stringify(payload)\n                    });\n\n                    if (res.status === 429) {\n                        statusEl.textContent = T.rateLimit;\n                        statusEl.className = 'voc-wm-status error';\n                        submitBtn.disabled = false;\n                        return;\n                    }\n                    if (!res.ok) {\n                        statusEl.textContent = T.fail;\n                        statusEl.className = 'voc-wm-status error';\n                        submitBtn.disabled = false;\n                        return;\n                    }\n                    statusEl.textContent = T.ok;\n                    statusEl.className = 'voc-wm-status success';\n                    if (window.gtag) window.gtag('event', 'watermark_lead_submitted', {\n                        stone_id: payload.stone_id || ''\n                    });\n                    \/\/ \u0447\u0435\u0440\u0435\u0437 2.5\u0441 \u0437\u0430\u043a\u0440\u043e\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u043a\u0443\n                    setTimeout(closeModal, 2500);\n                } catch (e) {\n                    statusEl.textContent = T.fail;\n                    statusEl.className = 'voc-wm-status error';\n                    submitBtn.disabled = false;\n                }\n            });\n        })();\n\n        \/\/ Init \u2014 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\n        \/\/ ring size removed \/\/ \u0441\u043a\u0440\u044b\u0442\u044c ring-only \u0441\u0435\u043a\u0446\u0438\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e\n    <\/script>\n\n    <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 API INTEGRATION \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n    <script>\n        \/**\n         * VOC Gems AI Constructor \u2014 API Integration v6\n         * Stone picker: real WDK catalog via \/wp-json\/vocgems\/v1\/stones\n         *\/\n        const VOC_API = {\n            \/\/ \u2500\u2500 Backend (ComfyUI\/AI server) \u2500\u2500\n            \/\/ 26.06.2026 \u2014 baseUrl (http:\/\/127.0.0.1:8000) \u0423\u0411\u0420\u0410\u041d: \u044d\u0442\u043e \u0431\u044b\u043b \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439\n            \/\/ \u0430\u0434\u0440\u0435\u0441 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430, \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c. \u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u043a\u0430\u043a\n            \/\/ base64 data-URI \u0438\u0437 RunPod-output (\u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u043e\u043a\u0441\u0438). \u0414\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438\n            \/\/ imgSrc() \u043d\u0438\u0436\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0438 data-URI, \u0438 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u044b\u0439 URL, \u0438 \u043f\u0443\u0442\u044c \u2014\n            \/\/ \u043d\u043e \u0411\u0415\u0417 \u043f\u043e\u0434\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 localhost.\n            baseUrl: '',\n            pollInterval: 2000,\n            maxPollAttempts: 60,\n            currentGenerationId: null,\n            savedImageUrl: null,   \/\/ 26.06.2026 \u2014 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 url \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c\n            pollTimer: null,\n            generatedImages: null,\n\n            \/\/ \u2500\u2500 WDK Catalog \u2500\u2500\n            WDK_BASE: '\/wp-json\/vocgems\/v1\/stones',\n            stonePerPage: 24,\n            stonePage: 1,\n            stoneTotalPages: 1,\n            stoneCategory: '',\n            stoneSearch: '',\n            stoneSearchTimer: null,\n            stonesCache: {},   \/\/ cache[cacheKey] = {stones, total, total_pages}\n\n            selectedStone: null,\n\n            \/\/ \u2500\u2500\u2500 Gem color palette by category \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            gemColorMap: {\n                'emerald':     ['#6FCF97','#2D9F6F','#1B7A4E'],\n                'ruby':        ['#FF6B6B','#E53E3E','#9B2C2C'],\n                'sapphire':    ['#63B3ED','#3182CE','#1E40AF'],\n                'spinel':      ['#FC8181','#E53E3E','#822727'],\n                'alexandrite': ['#68D391','#9F7AEA','#805AD5'],\n                'tourmaline':  ['#4FD1C5','#38B2AC','#285E61'],\n                'aquamarine':  ['#7ECEF0','#3B9FCC','#1A6A99'],\n                'tanzanite':   ['#7B68EE','#4B4CC0','#2E2E8A'],\n                'tsavorite':   ['#56B870','#2D8B4A','#1A5C2E'],\n                'morganite':   ['#F4A8C0','#D4688A','#A03B5C'],\n                'paraiba':     ['#4FD1C5','#0D9488','#065F57'],\n            },\n\n            getGemColors(stone) {\n                const cat = (stone.category || '').toLowerCase();\n                for (const [key, val] of Object.entries(this.gemColorMap)) {\n                    if (cat.includes(key)) return val;\n                }\n                \/\/ Fallback \u2014 \u0443\u0433\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u043f\u043e \u0446\u0432\u0435\u0442\u0443\n                const color = (stone.color || stone.trade_color || '').toLowerCase();\n                if (color.includes('red') || color.includes('pink'))   return ['#FF8A80','#E53E3E','#9B2C2C'];\n                if (color.includes('blue'))  return ['#63B3ED','#3182CE','#1E40AF'];\n                if (color.includes('green')) return ['#6FCF97','#2D9F6F','#1B7A4E'];\n                if (color.includes('purple') || color.includes('violet')) return ['#B794F4','#805AD5','#553C9A'];\n                return ['#A0AEC0','#718096','#4A5568'];\n            },\n\n            \/\/ \u2500\u2500\u2500 Stone card display \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            selectStone(stone) {\n                this.selectedStone = stone;\n                this.updateStoneCard(stone);\n                this.updateGemColors(this.getGemColors(stone));\n                this.updatePriceBreakdown(stone);\n                this.hideStoneModal();\n            },\n\n            updateStoneCard(stone) {\n                const weight  = stone.weight  ? `${stone.weight} ct` : '';\n                const origin  = stone.location ? stone.location : '';\n                const clarity = stone.clarity  ? stone.clarity : '';\n                const cut     = stone.cut      ? stone.cut     : stone.cut_custom || '';\n                const dims    = stone.dimensions || '';\n                const treatment = stone.treatment || '';\n                const cert    = stone.certificate || '';\n                const priceType = (stone.price_type || '').toLowerCase();\n                const isRequest = priceType && priceType !== 'price';\n\n                document.querySelector('.stone-name').textContent = stone.title || stone.name || '\u2014';\n                document.querySelector('.stone-specs').innerHTML =\n                    [weight, origin, clarity, cut].filter(Boolean).join(' \u00b7 ')\n                    + (dims ? `<br>${dims}` : '')\n                    + (treatment ? ` \u00b7 ${treatment}` : '');\n\n                const priceEl = document.querySelector('.stone-price');\n                if (isRequest) {\n                    priceEl.textContent = 'Price on request';\n                } else if (stone.price) {\n                    priceEl.textContent = '$' + Number(stone.price).toLocaleString();\n                } else {\n                    priceEl.textContent = '\u2014';\n                }\n\n                const badgeEl = document.querySelector('.stone-badge');\n                if (badgeEl) badgeEl.textContent = cert ? `${cert} Certified` : 'Certified';\n\n                \/\/ Thumbnail\n                const visual = document.getElementById('stoneVisual');\n                if (visual && stone.thumbnail) {\n                    const existImg = visual.querySelector('img.stone-real-photo');\n                    if (existImg) {\n                        existImg.src = stone.thumbnail;\n                    } else {\n                        const img = document.createElement('img');\n                        img.className = 'stone-real-photo';\n                        img.src = stone.thumbnail;\n                        img.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:inherit;';\n                        visual.appendChild(img);\n                    }\n                }\n            },\n\n            updateGemColors(colors) {\n                document.getElementById('gemColor1')?.setAttribute('stop-color', colors[0]);\n                document.getElementById('gemColor2')?.setAttribute('stop-color', colors[1]);\n                document.getElementById('gemColor3')?.setAttribute('stop-color', colors[2]);\n                if (document.getElementById('stoneC1')) {\n                    document.getElementById('stoneC1').setAttribute('stop-color', colors[0]);\n                    document.getElementById('stoneC2').setAttribute('stop-color', colors[2]);\n                }\n            },\n\n            updatePriceBreakdown(stone) {\n                const gemLabel = document.querySelector('#gemstoneRow .price-label');\n                const gemPrice = document.getElementById('gemstonePrice');\n                const weight   = stone.weight ? `${stone.weight}ct` : '';\n                const name     = stone.title || stone.name || 'Gemstone';\n                if (gemLabel) gemLabel.textContent = `Gemstone (${name}${weight ? ' ' + weight : ''})`;\n\n                const priceType = (stone.price_type || '').toLowerCase();\n                const isRequest = priceType && priceType !== 'price';\n                if (gemPrice) gemPrice.textContent = isRequest ? 'On request' : (stone.price ? `$${Number(stone.price).toLocaleString()}` : '\u2014');\n                if (typeof updatePrices === 'function') updatePrices();\n            },\n\n            \/\/ \u2500\u2500\u2500 Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            showStoneModal() {\n                document.getElementById('stoneModal').classList.add('show');\n\n                \/\/ \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438\n                this.stoneCategory = '';\n                this.stoneSearch   = '';\n                this.stonePage     = 1;\n\n                \/\/ \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0432\u043a\u043b\u0430\u0434\u043a\u0443 \u043d\u0430 All\n                document.querySelectorAll('.scat-btn').forEach(b => b.classList.remove('active'));\n                const allBtn = document.querySelector('.scat-btn[data-cat=\"\"]');\n                if (allBtn) allBtn.classList.add('active');\n\n                \/\/ \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043f\u043e\u043b\u0435 \u043f\u043e\u0438\u0441\u043a\u0430\n                const searchInput = document.getElementById('stoneSearch');\n                if (searchInput) searchInput.value = '';\n\n                if (!this._modalInited) {\n                    this._modalInited = true;\n                    this._bindCategoryTabs();\n                }\n                this.loadPage(1);\n            },\n\n            hideStoneModal() {\n                document.getElementById('stoneModal').classList.remove('show');\n            },\n\n            _bindCategoryTabs() {\n                document.querySelectorAll('.scat-btn').forEach(btn => {\n                    btn.addEventListener('click', () => {\n                        document.querySelectorAll('.scat-btn').forEach(b => b.classList.remove('active'));\n                        btn.classList.add('active');\n                        this.stoneCategory = btn.dataset.cat || '';\n                        this.stonePage = 1;\n                        this.loadPage(1);\n                    });\n                });\n            },\n\n            onSearch(val) {\n                clearTimeout(this.stoneSearchTimer);\n                this.stoneSearchTimer = setTimeout(() => {\n                    this.stoneSearch = val.trim();\n                    this.stonePage = 1;\n                    this.loadPage(1);\n                }, 350);\n            },\n\n            async loadPage(page) {\n                this.stonePage = page;\n\n                \/\/ Build cache key\n                const cacheKey = `${this.stoneCategory}|${this.stoneSearch}|${page}`;\n                if (this.stonesCache[cacheKey]) {\n                    this.renderStones(this.stonesCache[cacheKey], page);\n                    return;\n                }\n\n                \/\/ Show loading\n                const grid = document.getElementById('stoneGrid');\n                grid.innerHTML = '<div class=\"stone-modal-loading\"><div class=\"stone-modal-spinner\"><\/div><span>Loading...<\/span><\/div>';\n\n                \/\/ Build URL\n                const params = new URLSearchParams({\n                    per_page: this.stonePerPage,\n                    page,\n                });\n                if (this.stoneCategory) params.set('category', this.stoneCategory);\n                if (this.stoneSearch)   params.set('search', this.stoneSearch);\n                \/\/ Exclude jewelry (category 128 = \"Jewelry\") \u2014 only loose stones\n                \/\/ The API filters by category title, so just don't pass jewelry category\n\n                try {\n                    const resp = await fetch(`${this.WDK_BASE}?${params.toString()}`);\n                    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n                    const data = await resp.json();\n\n                    this.stonesCache[cacheKey] = data;\n                    this.stoneTotalPages = data.total_pages || 1;\n                    this.renderStones(data, page);\n                } catch (err) {\n                    console.error('VOC_API: failed to load stones', err);\n                    \/\/ Fallback \u2014 \u0434\u0435\u043c\u043e-\u043a\u0430\u043c\u043d\u0438 \u043a\u043e\u0433\u0434\u0430 API \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d (\u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0444\u0430\u0439\u043b\u0430)\n                    const demoData = {\n                        stones: [\n                            { id:1, title:'Emerald Vivid Green', category:'Emerald', weight:0.46, location:'Pakistan', price:920, price_type:'price', cut:'Emerald Cut', clarity:'VS', certificate:'GRS', thumbnail:'' },\n                            { id:2, title:'Emerald Intense Green', category:'Emerald', weight:0.69, location:'Pakistan', price:620, price_type:'price', cut:'Emerald Cut', clarity:'VS', certificate:'GRS', thumbnail:'' },\n                            { id:3, title:'Ruby Pigeon Blood', category:'Ruby', weight:1.25, location:'Myanmar', price:8500, price_type:'price', cut:'Oval Cut', clarity:'VVS', certificate:'G\u00fcbelin', thumbnail:'' },\n                            { id:4, title:'Blue Sapphire Ceylon', category:'Sapphire', weight:3.10, location:'Sri Lanka', price:12000, price_type:'price', cut:'Oval Cut', clarity:'VVS', certificate:'GRS', thumbnail:'' },\n                            { id:5, title:'Red Spinel', category:'Spinel', weight:2.05, location:'Myanmar', price:6800, price_type:'price', cut:'Cushion Cut', clarity:'VS', certificate:'Lotus', thumbnail:'' },\n                            { id:6, title:'Alexandrite', category:'Alexandrite', weight:1.18, location:'Russia', price:28000, price_type:'price', cut:'Cushion Cut', clarity:'VS', certificate:'GRS', thumbnail:'' },\n                        ],\n                        total: 6, total_pages: 1, page: 1, per_page: 12\n                    };\n                    this.stonesCache[cacheKey] = demoData;\n                    this.renderStones(demoData, page);\n                    \/\/ \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\n                    const note = document.createElement('div');\n                    note.style.cssText = 'grid-column:1\/-1;text-align:center;font-size:12px;color:#aaa;padding-top:4px;';\n                    note.textContent = '\u26a0 Demo mode \u2014 upload to server to load real catalog';\n                    document.getElementById('stoneGrid').appendChild(note);\n                }\n            },\n\n            renderStones(data, page) {\n                const grid = document.getElementById('stoneGrid');\n                const stones = data.stones || [];\n\n                if (!stones.length) {\n                    grid.innerHTML = '<div class=\"stone-modal-empty\">No gemstones found.<\/div>';\n                    document.getElementById('stoneModalPagination').style.display = 'none';\n                    return;\n                }\n\n                grid.innerHTML = stones.map(stone => {\n                    const weight = stone.weight ? `${stone.weight} ct` : '';\n                    const origin = stone.location || '';\n                    const priceType = (stone.price_type || '').toLowerCase();\n                    const isRequest = priceType && priceType !== 'price';\n                    const priceText = isRequest ? 'On request' : (stone.price ? `$${Number(stone.price).toLocaleString()}` : '\u2014');\n                    const cert = stone.certificate || '';\n                    const isSelected = this.selectedStone?.id === stone.id;\n                    const thumbHtml = stone.thumbnail\n                        ? `<img decoding=\"async\" class=\"stone-option-img\" src=\"${stone.thumbnail}\" alt=\"${stone.title}\" loading=\"lazy\" onerror=\"this.style.display='none';this.nextElementSibling.style.display='flex'\">`\n                          + `<div class=\"stone-option-img-placeholder\" style=\"display:none\">\ud83d\udc8e<\/div>`\n                        : `<div class=\"stone-option-img-placeholder\">\ud83d\udc8e<\/div>`;\n\n                    return `\n                    <div class=\"stone-option${isSelected ? ' selected' : ''}\" data-stone-id=\"${stone.id}\">\n                        ${thumbHtml}\n                        <div class=\"stone-option-body\">\n                            <div class=\"stone-option-name\">${stone.title || '\u2014'}<\/div>\n                            <div class=\"stone-option-details\">${[weight, origin].filter(Boolean).join(' \u00b7 ')}<\/div>\n                            <div class=\"stone-option-price\">${priceText}<\/div>\n                            ${cert ? `<span class=\"stone-option-cert\">${cert}<\/span>` : ''}\n                        <\/div>\n                    <\/div>`;\n                }).join('');\n\n                \/\/ Click handlers\n                grid.querySelectorAll('.stone-option').forEach(opt => {\n                    opt.addEventListener('click', () => {\n                        const sid = parseInt(opt.dataset.stoneId);\n                        const stone = stones.find(s => s.id === sid);\n                        if (stone) this.selectStone(stone);\n                    });\n                });\n\n                \/\/ Pagination\n                const pagEl = document.getElementById('stoneModalPagination');\n                const totalPages = data.total_pages || 1;\n                pagEl.style.display = totalPages > 1 ? 'flex' : 'none';\n                document.getElementById('stonePageInfo').textContent = `Page ${page} \/ ${totalPages}`;\n                document.getElementById('stonePrevBtn').disabled = (page <= 1);\n                document.getElementById('stoneNextBtn').disabled = (page >= totalPages);\n            },\n\n            \/\/ \u2500\u2500\u2500 getParams (for AI generation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            getParams() {\n                const typeBtn   = document.querySelector('.type-btn.active');\n                const metalBtn  = document.querySelector('.metal-btn.active');\n                const styleBtn  = document.querySelector('.style-btn.active');\n                const diamondBtn = document.querySelector('.diamond-btn.active');\n                const wishesEl  = document.querySelector('.wishes-textarea');\n\n                const metalMap = {\n                    'yellow-gold': 'gold_750', 'white-gold': 'white_gold',\n                    'rose-gold': 'rose_gold',  'platinum': 'platinum'\n                };\n                const styleMap = {\n                    'classic':   'classic',\n                    'modern':    'modern',\n                    'vintage':   'vintage',\n                    'halo':      'halo',\n                    'solitaire': 'solitaire',\n                    'statement': 'statement',\n                    'nature':    'nature',\n                    'geometric': 'geometric'\n                };\n\n                const stone = this.selectedStone;\n                const stoneCat = (stone?.category || '').toLowerCase();\n                const wishes = wishesEl?.value?.trim() || '';\n\n                \/\/ \u2500\u2500\u2500 Reference image \u0434\u043b\u044f ControlNet \u2500\u2500\u2500\n                \/\/ \u0411\u0435\u0440\u0451\u043c \u0444\u043e\u0442\u043e \u043a\u0430\u043c\u043d\u044f \u0438\u0437 WDK \u2014 \u043e\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0430\n                \/\/ \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 (\u0444\u043e\u0440\u043c\u0430 \u043e\u0433\u0440\u0430\u043d\u043a\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 Canny).\n                const refImage = stone?.thumbnail\n                              || stone?.media?.[0]?.full\n                              || stone?.media?.[0]?.thumbnail\n                              || '';\n\n                return {\n                    stone_type:          stoneCat || 'gemstone',\n                    stone_carat:         stone?.weight || 1,\n                    stone_color:         stone?.trade_color || stone?.color || stone?.trade_color_manual || '',\n                    stone_origin:        stone?.location || '',\n                    stone_cut:           stone?.cut || stone?.cut_custom || '',\n                    stone_sku:           stone?.sku || '',\n                    stone_title:         stone?.title || '',\n                    jewelry_type:        typeBtn?.dataset.type || 'ring',\n                    metal:               metalMap[metalBtn?.dataset.metal] || 'gold_750',\n                    style:               styleMap[styleBtn?.dataset.style] || 'modern',\n                    with_diamonds:       diamondBtn?.dataset.diamonds !== 'none',\n                    wishes:              wishes,\n                    reference_image_url: refImage,\n                    \/\/ TEST: \u0432\u0435\u0440\u0441\u0438\u044f LoRA. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e v2 (\u043f\u0440\u043e\u0434). \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0430 v3:\n                    \/\/ \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430: localStorage.setItem('VOC_LORA','v3')\n                    \/\/ \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u0440\u043e\u0434:       localStorage.setItem('VOC_LORA','v2')\n                    lora_version:        (localStorage.getItem('VOC_LORA') || 'v2'),\n                };\n            },\n\n            \/\/ \u2500\u2500\u2500 AI Generation via RunPod \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            \/\/ 26.06.2026 \u2014 RunPod-\u043a\u043b\u044e\u0447 \u0423\u0411\u0420\u0410\u041d \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u0442\u043e\u0440\u0447\u0430\u043b \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0443 \u0432\u0441\u0435\u0445!).\n            \/\/ \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0438\u0434\u0451\u0442 \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u043e\u043a\u0441\u0438 \/wp-json\/vocgems\/v1\/generate,\n            \/\/ \u043a\u043b\u044e\u0447 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0411\u0440\u0430\u0443\u0437\u0435\u0440 \u043a\u043b\u044e\u0447\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0437\u043d\u0430\u0435\u0442.\n            GENERATE_ENDPOINT: '\/wp-json\/vocgems\/v1\/generate',\n            STATUS_ENDPOINT:   '\/wp-json\/vocgems\/v1\/generate-status',\n\n            async generate() {\n                const params = this.getParams();\n                this.savedImageUrl = null;   \/\/ \u0441\u0431\u0440\u043e\u0441 url \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438\n                console.log('\ud83d\ude80 Starting RunPod generation:', params);\n                console.log('\ud83e\uddec LoRA version:', params.lora_version);\n\n                try {\n                    const response = await fetch(this.GENERATE_ENDPOINT, {\n                        method: 'POST',\n                        credentials: 'same-origin',\n                        headers: {\n                            'Content-Type': 'application\/json',\n                            ...(window.VOC_NONCE ? { 'X-WP-Nonce': window.VOC_NONCE } : {})\n                        },\n                        body: JSON.stringify({ input: params })\n                    });\n                    const data = await response.json();\n                    console.log('Generation job submitted:', data);\n                    \/\/ \u041b\u0438\u043c\u0438\u0442 \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u043d \/ \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u043c\u043e\u043b\u043e\u0434 \u2014 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0451\u0442 4xx \u0441 usage.\n                    if (!response.ok) {\n                        if (response.status === 429 && data?.data?.usage) {\n                            this.generationUsage = data.data.usage;\n                            this.updateLimitBadge(data.data.usage);\n                            alert('Weekly generation limit reached. Try again later.');\n                        }\n                        console.error('Generate error:', data);\n                        return { success: false };\n                    }\n                    if (data.id) {\n                        this.currentGenerationId = data.id;\n                        if (data.usage) { this.generationUsage = data.usage; this.updateLimitBadge(data.usage); }\n                        this.startPolling();\n                        return { success: true, generation_id: data.id };\n                    } else {\n                        console.error('Generate error:', data);\n                        return { success: false };\n                    }\n                } catch (err) {\n                    console.error('Generate fetch error:', err);\n                    return { success: false };\n                }\n            },\n\n            \/\/ \u0414\u043e\u0441\u0442\u0430\u0442\u044c data-URL \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438\u0437 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0444\u043e\u0440\u043c output RunPod.\n            \/\/ \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c: output.image, output.images[0], output.message,\n            \/\/ \u043c\u0430\u0441\u0441\u0438\u0432 output[0].image, \u0433\u043e\u043b\u0443\u044e base64-\u0441\u0442\u0440\u043e\u043a\u0443. 11.06.2026\n            extractImage(output) {\n                if (!output) return null;\n                var toDataUrl = function(s) {\n                    if (typeof s !== 'string' || !s) return null;\n                    \/\/ \u0423\u0436\u0435 data-URL?\n                    if (s.indexOf('data:image') === 0) return s;\n                    \/\/ \u041f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 URL \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438?\n                    if (s.indexOf('http') === 0) return s;\n                    \/\/ \u0418\u043d\u0430\u0447\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u0433\u043e\u043b\u044b\u0439 base64 PNG\n                    return 'data:image\/png;base64,' + s;\n                };\n                \/\/ output \u2014 \u0441\u0442\u0440\u043e\u043a\u0430\n                if (typeof output === 'string') return toDataUrl(output);\n                \/\/ output \u2014 \u043c\u0430\u0441\u0441\u0438\u0432\n                if (Array.isArray(output)) {\n                    for (var i = 0; i < output.length; i++) {\n                        var got = this.extractImage(output[i]);\n                        if (got) return got;\n                    }\n                    return null;\n                }\n                \/\/ output \u2014 \u043e\u0431\u044a\u0435\u043a\u0442: \u043f\u0435\u0440\u0435\u0431\u0438\u0440\u0430\u0435\u043c \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u043a\u043b\u044e\u0447\u0438\n                var keys = ['image', 'images', 'message', 'img', 'base64', 'output', 'result'];\n                for (var k = 0; k < keys.length; k++) {\n                    var v = output[keys[k]];\n                    if (!v) continue;\n                    if (Array.isArray(v)) {\n                        var a = this.extractImage(v);\n                        if (a) return a;\n                    } else if (typeof v === 'string') {\n                        var u = toDataUrl(v);\n                        if (u) return u;\n                    } else if (typeof v === 'object') {\n                        var o = this.extractImage(v);\n                        if (o) return o;\n                    }\n                }\n                return null;\n            },\n\n            async checkStatus() {\n                if (!this.currentGenerationId) return null;\n                \/\/ \u0417\u0430\u043f\u0440\u043e\u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0441 \u0440\u0435\u0442\u0440\u0430\u044f\u043c\u0438: \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438\u043d\u043e\u0433\u0434\u0430\n                \/\/ \u0440\u0432\u0451\u0442\u0441\u044f (Failed to fetch \/ ERR_HTTP2_PING_FAILED). \u041d\u0435 \u0441\u0434\u0430\u0451\u043c\u0441\u044f \u0441\n                \/\/ \u043f\u0435\u0440\u0432\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u2014 \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0441 \u043f\u0430\u0443\u0437\u043e\u0439. 11.06.2026\n                \/\/ \u0417\u0430\u043f\u0440\u043e\u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0447\u0435\u0440\u0435\u0437 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u043e\u043a\u0441\u0438 (\u043a\u043b\u044e\u0447 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435).\n                \/\/ \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0438\u043d\u043e\u0433\u0434\u0430 \u0440\u0432\u0451\u0442\u0441\u044f \u2014 \u043d\u0435 \u0441\u0434\u0430\u0451\u043c\u0441\u044f \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438, \u0440\u0435\u0442\u0440\u0430\u0438\u043c.\n                var url = `${this.STATUS_ENDPOINT}\/${this.currentGenerationId}`;\n                var headers = window.VOC_NONCE ? { 'X-WP-Nonce': window.VOC_NONCE } : {};\n                var data = null;\n                for (var attempt = 0; attempt < 3; attempt++) {\n                    try {\n                        var response = await fetch(url, { headers: headers, credentials: 'same-origin' });\n                        data = await response.json();\n                        break; \/\/ \u0443\u0441\u043f\u0435\u0445 \u2014 \u0432\u044b\u0445\u043e\u0434\u0438\u043c \u0438\u0437 \u0440\u0435\u0442\u0440\u0430\u0435\u0432\n                    } catch (err) {\n                        console.warn('checkStatus network error (try ' + (attempt + 1) + '\/3):', err.message);\n                        if (attempt < 2) {\n                            \/\/ \u043a\u043e\u0440\u043e\u0442\u043a\u0430\u044f \u043f\u0430\u0443\u0437\u0430 \u043f\u0435\u0440\u0435\u0434 \u043f\u043e\u0432\u0442\u043e\u0440\u043e\u043c\n                            await new Promise(function(r) { setTimeout(r, 1200); });\n                        }\n                    }\n                }\n                if (!data) {\n                    \/\/ \u0412\u0441\u0435 \u0440\u0435\u0442\u0440\u0430\u0438 \u0443\u043f\u0430\u043b\u0438 \u2014 \u0441\u0435\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430, \u0432\u0435\u0440\u043d\u0451\u043c null (\u043f\u043e\u043b\u043b\u0438\u043d\u0433\n                    \/\/ \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u0442 \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0442\u0438\u043a\u0435), \u043d\u043e \u043d\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u044d\u0442\u043e \u043f\u0440\u043e\u0432\u0430\u043b\u043e\u043c \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438.\n                    return { status: 'network_error', progress: 50 };\n                }\n\n                console.log('RunPod status:', data.status, data);\n\n                if (data.status === 'COMPLETED') {\n                    var img = this.extractImage(data.output);\n                    \/\/ 26.06.2026 \u2014 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0433 \u0443\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u0444\u0430\u0439\u043b \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\n                    \/\/ \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 saved_image_url. \u0417\u0430\u043f\u043e\u043c\u043d\u0438\u043c \u0435\u0433\u043e \u2014 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u0434\u0438\u0437\u0430\u0439\u043d\u0430\n                    \/\/ \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0433\u043e (\u043d\u0430\u0434\u0451\u0436\u043d\u0435\u0435, \u0447\u0435\u043c base64 \u0438\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430).\n                    this.savedImageUrl = data.saved_image_url || null;\n                    if (img) {\n                        return { status: 'completed', progress: 100, images: [img], savedImageUrl: this.savedImageUrl };\n                    }\n                    \/\/ \u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0432 output \u043d\u0435\u0442, \u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0433 \u0435\u0451 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u0440\u0435\u0432\u044c\u044e-\u0444\u043e\u043b\u0431\u044d\u043a\n                    if (this.savedImageUrl) {\n                        return { status: 'completed', progress: 100, images: [this.savedImageUrl], savedImageUrl: this.savedImageUrl };\n                    }\n                    console.error('COMPLETED but no image found in output:', data.output);\n                    return { status: 'failed', error: 'No image in RunPod output' };\n                } else if (data.status === 'FAILED') {\n                    return { status: 'failed', error: data.error };\n                } else if (data.status === 'IN_PROGRESS' || data.status === 'IN_QUEUE') {\n                    return { status: 'processing', progress: 50 };\n                } else {\n                    return { status: 'queued', progress: 10 };\n                }\n            },\n\n            startPolling() {\n                let attempts = 0;\n                const maxAttempts = 180; \/\/ 6 \u043c\u0438\u043d\u0443\u0442 (cold start RunPod \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u0434\u043e 5)\n                let warmupNotified = false;\n                this.pollTimer = setInterval(async () => {\n                    attempts++;\n                    const status = await this.checkStatus();\n                    if (!status) return;\n                    console.log(`\ud83d\udcca ${status.status} - ${status.progress}% (attempt ${attempts}\/${maxAttempts})`);\n\n                    const bar = document.getElementById('progressBar');\n                    if (bar) bar.style.width = (status.progress || 10) + '%';\n\n                    \/\/ \u041f\u043e\u0441\u043b\u0435 30 \u0441\u0435\u043a\u0443\u043d\u0434 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u2014 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u043f\u0440\u043e\u0433\u0440\u0435\u0432\u0435 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\n                    if (!warmupNotified && attempts > 15 && status.status === 'queued') {\n                        warmupNotified = true;\n                        const loadingEl = document.getElementById('loading');\n                        if (loadingEl && !document.getElementById('warmupNote')) {\n                            const note = document.createElement('div');\n                            note.id = 'warmupNote';\n                            note.style.cssText = 'margin-top:12px;font-size:13px;color:#666;text-align:center;';\n                            note.textContent = 'Worker is warming up, this may take 2-3 minutes on first request...';\n                            loadingEl.appendChild(note);\n                        }\n                    }\n\n                    if (status.status === 'completed') {\n                        this.stopPolling();\n                        this.showResult(status.images);\n                    } else if (status.status === 'failed') {\n                        this.stopPolling();\n                        document.getElementById('loading')?.classList.remove('show');\n                        document.getElementById('warmupNote')?.remove();\n                        alert('Generation failed. Please try again.');\n                    } else if (attempts >= maxAttempts) {\n                        this.stopPolling();\n                        document.getElementById('loading')?.classList.remove('show');\n                        document.getElementById('warmupNote')?.remove();\n                        alert('Generation timeout after 6 minutes. The server may be overloaded. Please try again in a moment.');\n                    }\n                }, 2000);\n            },\n\n            stopPolling() {\n                if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; }\n            },\n\n            showResult(images) {\n                this.generatedImages = images;\n                document.getElementById('loading').classList.remove('show');\n                const result = document.getElementById('result');\n                result.classList.add('show');\n\n                const wrapper = result.querySelector('.preview-jewelry-render');\n                if (wrapper && images?.length > 0) {\n                    const src = this.imgSrc(images[0]);\n                    wrapper.innerHTML = `<img decoding=\"async\" src=\"${src}\" style=\"width:100%;height:100%;object-fit:contain;border-radius:16px;\" \/>`;\n                }\n\n                \/\/ Show price section and recalculate fresh\n                const priceSection = document.getElementById('priceSection');\n                if (priceSection) {\n                    priceSection.style.display = 'block';\n                    priceSection.style.animation = 'fadeIn 0.4s ease';\n                }\n                if (typeof updatePrices === 'function') updatePrices();\n                this.updatePriceBreakdown(this.selectedStone || {});\n\n                document.querySelectorAll('.step-item')[1].classList.add('completed');\n                document.querySelectorAll('.step-item')[1].classList.remove('active');\n                document.querySelectorAll('.step-item')[2].classList.add('active');\n                document.querySelectorAll('.step-connector')[1].classList.add('active');\n                document.getElementById('designId').textContent = this.currentGenerationId;\n\n                \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 WP \u043e\u0442 \u0438\u043c\u0435\u043d\u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\n                this.saveToWP(this.currentGenerationId, images[0] || null);\n            },\n\n            \/\/ \u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c src \u0434\u043b\u044f <img>: data-URI \u0438 http(s) \u2014 \u043a\u0430\u043a \u0435\u0441\u0442\u044c;\n            \/\/ \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u2014 \u043a\u0430\u043a \u0435\u0441\u0442\u044c (\u0411\u0415\u0417 localhost-\u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430). 26.06.2026\n            imgSrc(raw) {\n                if (!raw || typeof raw !== 'string') return '';\n                if (raw.indexOf('data:') === 0) return raw;       \/\/ base64\n                if (\/^https?:\\\/\\\/\/i.test(raw)) return raw;         \/\/ \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u044b\u0439 URL\n                return raw;                                         \/\/ \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a\u0430\u043a \u0435\u0441\u0442\u044c\n            },\n\n            \/\/ 26.06.2026 \u2014 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043a\u0430\u043a base64 data-URI \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f.\n            \/\/ raw \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c: data:image\/... (\u0432\u0435\u0440\u043d\u0451\u043c \u043a\u0430\u043a \u0435\u0441\u0442\u044c) \u0418\u041b\u0418 \u043f\u0443\u0442\u044c\/URL (\u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043c\n            \/\/ \u0438\u0437 \u0423\u0416\u0415 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e <img> \u0447\u0435\u0440\u0435\u0437 canvas). \u0415\u0441\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u2014\n            \/\/ \u0432\u0435\u0440\u043d\u0451\u043c null (\u0441\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u043f\u0438\u0448\u0435\u0442 \u0434\u0438\u0437\u0430\u0439\u043d \u0431\u0435\u0437 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u043d\u043e \u043d\u0435 \u0443\u043f\u0430\u0434\u0451\u0442).\n            async resolveImageDataUrl(raw) {\n                try {\n                    if (!raw) {\n                        \/\/ \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0435 \u043d\u0435\u0442 \u2014 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0432\u0437\u044f\u0442\u044c \u0438\u0437 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0432\u044c\u044e\n                        return this.captureRenderedImage();\n                    }\n                    if (typeof raw === 'string' && raw.indexOf('data:image') === 0) {\n                        return raw; \/\/ \u0443\u0436\u0435 base64 data-URI\n                    }\n                    \/\/ raw \u2014 \u043f\u0443\u0442\u044c\/URL (\u043d\u0435 data-URI). \u0411\u0435\u0440\u0451\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0438\u0437 DOM \u0438 \u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c.\n                    \/\/ \u0411\u0435\u0440\u0451\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0438\u0437 DOM (\u043e\u043d\u0430 \u0443\u0436\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e) \u0438 \u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c.\n                    return this.captureRenderedImage();\n                } catch (e) {\n                    console.warn('\u26a0\ufe0f resolveImageDataUrl failed:', e?.message);\n                    return null;\n                }\n            },\n\n            \/\/ \u0421\u043d\u044f\u0442\u044c base64 PNG \u0441 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043f\u0440\u0435\u0432\u044c\u044e \u0447\u0435\u0440\u0435\u0437 canvas.\n            captureRenderedImage() {\n                try {\n                    const img = document.querySelector('.preview-jewelry-render img');\n                    if (!img || !img.complete || !img.naturalWidth) return null;\n                    const canvas = document.createElement('canvas');\n                    canvas.width  = img.naturalWidth;\n                    canvas.height = img.naturalHeight;\n                    const ctx = canvas.getContext('2d');\n                    ctx.drawImage(img, 0, 0);\n                    \/\/ toDataURL \u0431\u0440\u043e\u0441\u0438\u0442 SecurityError, \u0435\u0441\u043b\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 cross-origin \u0431\u0435\u0437 CORS.\n                    return canvas.toDataURL('image\/png');\n                } catch (e) {\n                    console.warn('\u26a0\ufe0f captureRenderedImage failed (CORS?):', e?.message);\n                    return null;\n                }\n            },\n\n            \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 WP \u0411\u0414 \u2014 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441\u0430\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d, user_id \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c\n            async saveToWP(generationId, imageApiPath) {\n                \/\/ 23.06.2026 \u2014 \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u043c \u043f\u043e \u0437\u0430\u043f\u0435\u0447\u0451\u043d\u043d\u043e\u043c\u0443 VOC_LOGGED_IN (\u043a\u044d\u0448 Cloudflare\n                \/\/ \u043c\u043e\u0433 \u0437\u0430\u043f\u0435\u0447\u044c false). \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c, \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b \u043b\u043e\u0433\u0438\u043d \u0432 checkAccess\n                \/\/ (serverLoggedIn) \u0418\u041b\u0418 \u0437\u0430\u043f\u0435\u0447\u0451\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u0447\u0442\u043e \u044e\u0437\u0435\u0440 \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d.\n                \/\/ user_id \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435 \u0431\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 cookie-\u0441\u0435\u0441\u0441\u0438\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0430\u0436\u0435 \u0431\u0435\u0437 nonce\n                \/\/ \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u0440\u0438\u0432\u044f\u0436\u0435\u0442\u0441\u044f \u043a \u043d\u0443\u0436\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e (credentials:'same-origin').\n                if (!this.serverLoggedIn && !window.VOC_LOGGED_IN) {\n                    console.log('\u2139\ufe0f Guest \u2014 design not saved to personal history');\n                    return;\n                }\n                const stone  = this.selectedStone || {};\n                const params = this.getParams();\n\n                \/\/ 26.06.2026 \u2014 \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043b \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u0444\u0430\u0439\u043b \u043f\u0440\u0438 \u043e\u043f\u0440\u043e\u0441\u0435\n                \/\/ \u0441\u0442\u0430\u0442\u0443\u0441\u0430 (saved_image_url) \u2014 \u0448\u043b\u0451\u043c \u0435\u0433\u043e (\u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439, \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0439). \u0418\u043d\u0430\u0447\u0435 \u2014\n                \/\/ \u0434\u043e\u0441\u0442\u0430\u0451\u043c base64 \u0438\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 (\u0441\u0442\u0430\u0440\u044b\u0439 \u043f\u0443\u0442\u044c, \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438).\n                const imageUrl = this.savedImageUrl\n                    ? this.savedImageUrl\n                    : await this.resolveImageDataUrl(imageApiPath);\n\n                try {\n                    const saveHeaders = { 'Content-Type': 'application\/json' };\n                    \/\/ nonce \u0448\u043b\u0451\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043e\u043d \u0435\u0441\u0442\u044c; \u0438\u043d\u0430\u0447\u0435 \u0431\u044d\u043a\u0435\u043d\u0434 \u043e\u043f\u043e\u0437\u043d\u0430\u0435\u0442 \u044e\u0437\u0435\u0440\u0430\n                    \/\/ \u043f\u043e cookie-\u0441\u0435\u0441\u0441\u0438\u0438 (credentials:'same-origin'). 23.06.2026\n                    if (window.VOC_NONCE) {\n                        saveHeaders['X-WP-Nonce'] = window.VOC_NONCE;\n                    }\n                    const resp = await fetch('\/wp-json\/vocgems\/v1\/designs', {\n                        method: 'POST',\n                        credentials: 'same-origin',\n                        headers: saveHeaders,\n                        body: JSON.stringify({\n                            generation_id:   generationId,\n                            stone_sku:       stone.sku        || '',\n                            stone_title:     stone.title      || params.stone_title || '',\n                            stone_type:      params.stone_type,\n                            stone_carat:     params.stone_carat,\n                            stone_color:     params.stone_color,\n                            stone_origin:    params.stone_origin,\n                            stone_cut:       params.stone_cut,\n                            jewelry_type:    params.jewelry_type,\n                            metal:           params.metal,\n                            style:           params.style,\n                            with_diamonds:   params.with_diamonds,\n                            wishes:          params.wishes,\n                            image_url:       imageUrl,\n                            status:          'completed',\n                            completed_at:    new Date().toISOString().slice(0, 19).replace('T', ' '),\n                        }),\n                    });\n                    if (resp.ok) {\n                        console.log('\u2705 Design saved to history:', generationId);\n                        \/\/ \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0447\u0451\u0442\u0447\u0438\u043a \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0439 \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430\n                        try {\n                            const saveData = await resp.json();\n                            if (saveData.usage) {\n                                this.generationUsage = saveData.usage;\n                                this.updateLimitBadge(saveData.usage);\n                            }\n                        } catch(e) { \/* ignore parse error *\/ }\n                    } else if (resp.status === 429) {\n                        \/\/ \u041b\u0438\u043c\u0438\u0442 \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u043d\n                        console.warn('\u26a0\ufe0f Generation limit reached');\n                        try {\n                            const errData = await resp.json();\n                            if (errData.data?.usage) {\n                                this.generationUsage = errData.data.usage;\n                                this.updateLimitBadge(errData.data.usage);\n                            }\n                        } catch(e) {}\n                    } else {\n                        console.warn('\u26a0\ufe0f Failed to save design:', resp.status);\n                    }\n                } catch (err) {\n                    console.warn('\u26a0\ufe0f saveToWP error:', err.message);\n                }\n            },\n\n            showAngle(angle) {\n                if (!this.generatedImages) return;\n                const map = { front: 0, side: 1, top: 2, perspective: 3 };\n                const idx = map[angle] ?? 0;\n                const img = this.generatedImages[idx] || this.generatedImages[0];\n                const result = document.getElementById('result');\n                const wrapper = result?.querySelector('.preview-jewelry-render');\n                if (wrapper && img) {\n                    wrapper.innerHTML = `<img decoding=\"async\" src=\"${this.imgSrc(img)}\" style=\"width:100%;height:100%;object-fit:contain;border-radius:16px;\" crossorigin=\"anonymous\" \/>`;\n                }\n            },\n\n            \/\/ \u2500\u2500\u2500 Access & limits state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            accessAllowed: false,\n            generationUsage: null,\n            isGuestTrial: false,\n            serverLoggedIn: false,  \/\/ 23.06.2026 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f \u0432 checkAccess \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\n\n            \/\/ \u2500\u2500\u2500 Init \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            async init() {\n                console.log('\u2705 VOC API v6 \u2014 WDK catalog integration ready');\n                await this.refreshSession();   \/\/ 23.06.2026 \u0441\u0432\u0435\u0436\u0438\u0439 nonce\/\u043b\u043e\u0433\u0438\u043d, \u043c\u0438\u043d\u0443\u044f \u043a\u044d\u0448\n                await this.checkAccess();\n            },\n\n            \/\/ \u2500\u2500\u2500 \u0421\u0432\u0435\u0436\u0430\u044f \u0441\u0435\u0441\u0441\u0438\u044f (\u043e\u0431\u0445\u043e\u0434 \u043a\u044d\u0448\u0430 Cloudflare) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            \/\/ \u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0439\u0442\u0438 \u0438\u0437 \u043a\u044d\u0448\u0430 \u0441 \u043f\u0443\u0441\u0442\u044b\u043c nonce. \u0414\u0451\u0440\u0433\u0430\u0435\u043c \u0436\u0438\u0432\u043e\u0439\n            \/\/ \/session, \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c window.VOC_NONCE \u0438 VOC_LOGGED_IN \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438\n            \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438, \u0447\u0442\u043e\u0431\u044b REST \u043e\u043f\u043e\u0437\u043d\u0430\u043b \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u044e\u0437\u0435\u0440\u0430. 23.06.2026\n            async refreshSession() {\n                try {\n                    const resp = await fetch('\/wp-json\/vocgems\/v1\/session', {\n                        credentials: 'same-origin',\n                        cache: 'no-store',\n                    });\n                    if (!resp.ok) return;\n                    const s = await resp.json();\n                    window.VOC_LOGGED_IN = !!s.logged_in;\n                    window.VOC_NONCE     = s.nonce || '';\n                    if (s.logged_in) {\n                        this.serverLoggedIn = true;\n                        if (s.user_id && window.VOC_ACCOUNT) { window.VOC_ACCOUNT.userId = s.user_id; }\n                        console.log('\ud83d\udd11 Session refreshed: logged in');\n                    } else {\n                        console.log('\ud83d\udd11 Session refreshed: guest');\n                    }\n                    \/\/ 23.06.2026 \u2014 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0448\u0430\u043f\u043a\u0443 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430\n                    if (typeof window.vocUpdateHeaderAuth === 'function') {\n                        window.vocUpdateHeaderAuth(!!s.logged_in);\n                    }\n                } catch (e) {\n                    console.warn('\u26a0\ufe0f Session refresh failed:', e.message);\n                }\n            },\n\n            \/\/ \u2500\u2500\u2500 Check access via API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            async checkAccess() {\n                \/\/ 23.06.2026 \u2014 \u041e\u0411\u0425\u041e\u0414 \u041a\u042d\u0428\u0410 CLOUDFLARE.\n                \/\/ \u0420\u0430\u043d\u044c\u0448\u0435 \u0437\u0434\u0435\u0441\u044c \u0431\u044b\u043b \u0440\u0430\u043d\u043d\u0438\u0439 \u0432\u044b\u0445\u043e\u0434 \u0432 \u0433\u043e\u0441\u0442\u0435\u0432\u043e\u0439 \u0440\u0435\u0436\u0438\u043c \u043f\u043e window.VOC_LOGGED_IN.\n                \/\/ \u041d\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/ai-constructor\/ \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0437 \u043a\u044d\u0448\u0430 Cloudflare,\n                \/\/ \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0434\u043b\u044f \u0413\u041e\u0421\u0422\u042f \u2014 \u0442\u043e\u0433\u0434\u0430 VOC_LOGGED_IN \u0437\u0430\u043f\u0435\u0447\u0451\u043d \u043a\u0430\u043a false,\n                \/\/ \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u044e\u0437\u0435\u0440 \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d. \u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438\u0441\u044c \u0431\u0435\u0437\n                \/\/ user_id \u0438 \u0441\u0447\u0451\u0442\u0447\u0438\u043a \u0432\u0438\u0441\u0435\u043b 20\/20. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u041d\u0415 \u0434\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0437\u0430\u043f\u0435\u0447\u0451\u043d\u043d\u043e\u043c\u0443\n                \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e, \u0430 \u0412\u0421\u0415\u0413\u0414\u0410 \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440: \u0437\u0430\u043f\u0440\u043e\u0441 account-access \u0438\u0434\u0451\u0442\n                \/\/ \u0441 cookie \u0441\u0435\u0441\u0441\u0438\u0438 \u0438 \u043c\u0438\u043d\u0443\u0435\u0442 \u043a\u044d\u0448 \u2014 \u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0434\u0443.\n                \/\/ Cookie \u0448\u043b\u0451\u043c \u0447\u0435\u0440\u0435\u0437 credentials:'same-origin'. nonce \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c,\n                \/\/ \u0435\u0441\u043b\u0438 \u043e\u043d \u0435\u0441\u0442\u044c (\u043d\u043e \u0438 \u0431\u0435\u0437 \u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u043f\u043e\u0437\u043d\u0430\u0435\u0442 \u044e\u0437\u0435\u0440\u0430 \u043f\u043e cookie).\n                try {\n                    const headers = {};\n                    if (window.VOC_NONCE) {\n                        headers['X-WP-Nonce'] = window.VOC_NONCE;\n                    }\n                    const resp = await fetch('\/wp-json\/vocgems\/v1\/account-access?feature=constructor', {\n                        headers,\n                        credentials: 'same-origin',\n                    });\n                    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n                    const data = await resp.json();\n\n                    if (!data.logged_in) {\n                        \/\/ \u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b: \u044d\u0442\u043e \u0433\u043e\u0441\u0442\u044c \u2014 \u043f\u0440\u043e\u0431\u043d\u0430\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f\n                        if (this.guestFreeUsed()) {\n                            this.showAccessGate('guest_used_free');\n                        } else {\n                            this.accessAllowed = true;\n                            this.isGuestTrial = true;\n                            this.generationUsage = { remaining: 1, limit: 1 };\n                            this.updateLimitBadge(this.generationUsage);\n                        }\n                        return;\n                    }\n\n                    if (!data.allowed) {\n                        this.showAccessGate('account_too_new', data.account_age);\n                        return;\n                    }\n\n                    \/\/ \u0417\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d \u0438 \u0434\u043e\u0441\u0442\u0443\u043f \u0435\u0441\u0442\u044c \u2014 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043b\u0438\u043c\u0438\u0442 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\n                    this.accessAllowed = true;\n                    this.serverLoggedIn = true;  \/\/ 23.06.2026 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b \u043b\u043e\u0433\u0438\u043d (\u043c\u0438\u043d\u0443\u044f \u043a\u044d\u0448)\n                    if (data.generation_usage) {\n                        this.generationUsage = data.generation_usage;\n                        this.updateLimitBadge(data.generation_usage);\n                    }\n                } catch (err) {\n                    console.warn('\u26a0\ufe0f Access check failed:', err.message);\n                    \/\/ \u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u2014 \u043e\u0442\u043a\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u0441\u044f \u043d\u0430 \u0437\u0430\u043f\u0435\u0447\u0451\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n                    if (window.VOC_LOGGED_IN) {\n                        this.accessAllowed = true;\n                    } else if (this.guestFreeUsed()) {\n                        this.showAccessGate('guest_used_free');\n                    } else {\n                        this.accessAllowed = true;\n                        this.isGuestTrial = true;\n                        this.generationUsage = { remaining: 1, limit: 1 };\n                        this.updateLimitBadge(this.generationUsage);\n                    }\n                }\n            },\n\n            \/\/ \u041f\u0440\u043e\u0431\u043d\u0430\u044f \u0433\u043e\u0441\u0442\u0435\u0432\u0430\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0430?\n            guestFreeUsed() {\n                try { return localStorage.getItem('voc_guest_gen_used') === '1'; }\n                catch (e) { return false; }\n            },\n            markGuestFreeUsed() {\n                try { localStorage.setItem('voc_guest_gen_used', '1'); } catch (e) {}\n            },\n\n            showAccessGate(reason, ageData) {\n                const gate = document.getElementById('accessGate');\n                const card = document.getElementById('accessGateCard');\n                if (!gate || !card) return;\n\n                if (reason === 'not_logged_in' || reason === 'guest_used_free') {\n                    const T = VOC_I18N || { t: k => null };\n                    const isUsed = reason === 'guest_used_free';\n                    const title = isUsed\n                        ? (T.t('gate_used_title') || 'Loved your first design?')\n                        : (T.t('gate_signin_title') || 'Sign In Required');\n                    const text = isUsed\n                        ? (T.t('gate_used_text') || 'Your free design is ready! Create a free account to keep generating \u2014 you get up to 20 designs every week.')\n                        : (T.t('gate_signin_text') || 'Create an account or sign in to use the AI Jewelry Constructor and generate unique designs for your gemstones.');\n                    card.innerHTML = `\n                        <div class=\"access-gate-icon\">\n                            <svg viewBox=\"0 0 24 24\"><path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"\/><circle cx=\"12\" cy=\"7\" r=\"4\"\/><\/svg>\n                        <\/div>\n                        <h2 class=\"access-gate-title\">${title}<\/h2>\n                        <p class=\"access-gate-text\">${text}<\/p>\n                        <div class=\"access-gate-actions\">\n                            <a href=\"\/dash\/?action=register\" class=\"access-gate-btn primary\">${T.t('gate_register_btn') || 'Create Account'}<\/a>\n                            <a href=\"\/dash\/\" class=\"access-gate-btn secondary\">${T.t('gate_signin_btn') || 'Sign In'}<\/a>\n                        <\/div>`;\n                } else if (reason === 'account_too_new') {\n                    const T = VOC_I18N || { t: k => null };\n                    const daysLeft = ageData?.days_left || '?';\n                    const daysWord = daysLeft === 1 ? (T.t('gate_day_remaining') || 'day remaining') : (T.t('gate_days_remaining') || 'days remaining');\n                    card.innerHTML = `\n                        <div class=\"access-gate-icon\">\n                            <svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><polyline points=\"12 6 12 12 16 14\"\/><\/svg>\n                        <\/div>\n                        <h2 class=\"access-gate-title\">${T.t('gate_new_title') || 'Almost There'}<\/h2>\n                        <p class=\"access-gate-text\">${T.t('gate_new_text') || 'The AI Constructor is available to members with accounts at least <strong>7 days old<\/strong>.'}<\/p>\n                        <div class=\"access-gate-days\">\n                            <strong>${daysLeft}<\/strong> ${daysWord}\n                        <\/div>`;\n                }\n\n                gate.classList.add('show');\n            },\n\n            \/\/ \u2500\u2500\u2500 Update limit badge near Generate button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            updateLimitBadge(usage) {\n                const badge = document.getElementById('genLimitBadge');\n                const remainEl = document.getElementById('genRemaining');\n                const limitEl  = document.getElementById('genLimit');\n                const genBtn   = document.getElementById('generateBtn');\n                if (!badge || !remainEl || !usage) return;\n\n                remainEl.textContent = usage.remaining;\n                if (limitEl) limitEl.textContent = usage.limit;\n                badge.style.display = 'flex';\n\n                \/\/ Visual states\n                badge.classList.remove('low', 'exhausted');\n                if (usage.remaining <= 0) {\n                    badge.classList.add('exhausted');\n                    if (genBtn) genBtn.disabled = true;\n                } else if (usage.remaining <= 5) {\n                    badge.classList.add('low');\n                }\n            }\n        };\n\n        \/\/ \u2500\u2500 Override generate button \u2500\u2500\n        document.getElementById('generateBtn').addEventListener('click', async (e) => {\n            e.stopImmediatePropagation();\n\n            \/\/ \u2500\u2500 Access check \u2500\u2500\n            if (!VOC_API.accessAllowed) {\n                VOC_API.checkAccess();\n                return;\n            }\n\n            \/\/ \u2500\u2500 Limit check \u2500\u2500\n            if (VOC_API.generationUsage && VOC_API.generationUsage.remaining <= 0) {\n                \/\/ \u0413\u043e\u0441\u0442\u044c \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u043b \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u0443\u044e \u043f\u0440\u043e\u0431\u0443 \u2192 \u0433\u0435\u0439\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438.\n                if (VOC_API.isGuestTrial || (typeof window.VOC_LOGGED_IN !== 'undefined' && !window.VOC_LOGGED_IN)) {\n                    VOC_API.showAccessGate('guest_used_free');\n                    return;\n                }\n                \/\/ \u0417\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u043d\u044b\u0439 \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u043b \u043d\u0435\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043b\u0438\u043c\u0438\u0442.\n                alert('You have reached the weekly generation limit (' + VOC_API.generationUsage.limit + ' per week). Please try again later.');\n                return;\n            }\n\n            const loading     = document.getElementById('loading');\n            const placeholder = document.getElementById('placeholder');\n            const result      = document.getElementById('result');\n            const angleBar    = document.getElementById('angleBar');\n\n            result.classList.remove('show');\n            angleBar.style.display = 'none';\n            loading.classList.add('show');\n            placeholder.style.display = 'none';\n            document.getElementById('progressBar').style.width = '0%';\n            \/\/ \u0421\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0446\u0435\u043d\u0443 \u0434\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438\n            const priceSection = document.getElementById('priceSection');\n            if (priceSection) priceSection.style.display = 'none';\n\n            document.querySelectorAll('.step-item')[0].classList.add('completed');\n            document.querySelectorAll('.step-item')[0].classList.remove('active');\n            document.querySelectorAll('.step-item')[1].classList.add('active');\n            document.querySelectorAll('.step-connector')[0].classList.add('active');\n\n            try {\n                await VOC_API.generate();\n                \/\/ \u0413\u043e\u0441\u0442\u0435\u0432\u0430\u044f \u043f\u0440\u043e\u0431\u0430 \u0438\u0437\u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432\u0430\u043d\u0430: \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u043c \u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0435.\n                if (VOC_API.isGuestTrial) {\n                    VOC_API.markGuestFreeUsed();\n                    VOC_API.generationUsage = { remaining: 0, limit: 1 };\n                    VOC_API.updateLimitBadge(VOC_API.generationUsage);\n                }\n            } catch (err) {\n                console.error(err);\n                alert('Error: ' + err.message);\n                loading.classList.remove('show');\n                placeholder.style.display = 'block';\n            }\n        }, true);\n\n        \/\/ \u2500\u2500 Angle buttons \u2500\u2500\n        document.querySelectorAll('.angle-btn').forEach(btn => {\n            btn.addEventListener('click', (e) => {\n                e.preventDefault(); e.stopPropagation();\n                if (VOC_API.generatedImages) VOC_API.showAngle(btn.dataset.angle);\n            });\n        });\n\n        \/\/ \u2500\u2500 Stone modal open \u2500\u2500\n        document.getElementById('chooseStoneBtn')\n            ?.addEventListener('click', () => VOC_API.showStoneModal());\n\n        \/\/ \u2500\u2500 Search handler \u2500\u2500\n        document.getElementById('stoneSearch')\n            ?.addEventListener('input', (e) => VOC_API.onSearch(e.target.value));\n\n        \/\/ \u2500\u2500 Pagination \u2500\u2500\n        document.getElementById('stonePrevBtn')\n            ?.addEventListener('click', () => VOC_API.loadPage(VOC_API.stonePage - 1));\n        document.getElementById('stoneNextBtn')\n            ?.addEventListener('click', () => VOC_API.loadPage(VOC_API.stonePage + 1));\n\n        \/\/ \u2500\u2500 Modal close \u2500\u2500\n        document.querySelector('.stone-modal-close')\n            ?.addEventListener('click', () => VOC_API.hideStoneModal());\n\n        \/\/ \u2500\u2500 i18n system \u2500\u2500\n        const VOC_I18N = {\n            currentLang: 'en',\n\n            translations: {\n                \/\/ \u2500\u2500\u2500 Header \/ Nav \u2500\u2500\u2500\n                menu:           { en: 'Menu',               ru: '\u041c\u0435\u043d\u044e' },\n                nav_home:       { en: 'Home',               ru: '\u0413\u043b\u0430\u0432\u043d\u0430\u044f' },\n                nav_gemstones:  { en: 'Loose Gemstones',    ru: '\u0414\u0440\u0430\u0433\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438' },\n                nav_jewelry:    { en: 'Fine Jewelry',       ru: '\u042e\u0432\u0435\u043b\u0438\u0440\u043d\u044b\u0435 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u044f' },\n                nav_auction:    { en: 'Auction',            ru: '\u0410\u0443\u043a\u0446\u0438\u043e\u043d' },\n                nav_constructor:{ en: 'Constructor',        ru: '\u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440' },\n                nav_contact:    { en: 'Contact',            ru: '\u041a\u043e\u043d\u0442\u0430\u043a\u0442\u044b' },\n                nav_news:       { en: 'News',               ru: '\u041d\u043e\u0432\u043e\u0441\u0442\u0438' },\n                nav_about:      { en: 'About',              ru: '\u041e \u043d\u0430\u0441' },\n                nav_about_us:   { en: 'About Us',           ru: '\u041e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438' },\n                nav_payment:    { en: 'Payment & Delivery', ru: '\u041e\u043f\u043b\u0430\u0442\u0430 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0430' },\n                nav_security:   { en: 'Security',           ru: '\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c' },\n                nav_guide:      { en: 'Gemstone Guide',     ru: '\u0413\u0438\u0434 \u043f\u043e \u043a\u0430\u043c\u043d\u044f\u043c' },\n                nav_terms:      { en: 'Terms & Conditions', ru: '\u0423\u0441\u043b\u043e\u0432\u0438\u044f' },\n                nav_account:    { en: 'Account',            ru: '\u0410\u043a\u043a\u0430\u0443\u043d\u0442' },\n                nav_dashboard:  { en: 'My Account',         ru: '\u041c\u043e\u0439 \u0430\u043a\u043a\u0430\u0443\u043d\u0442' },\n                nav_selection:  { en: 'My Cart',            ru: '\u041c\u043e\u044f \u043a\u043e\u0440\u0437\u0438\u043d\u0430' },\n                nav_login:      { en: 'Log In',             ru: '\u0412\u043e\u0439\u0442\u0438' },\n                nav_signup:     { en: 'Sign Up',            ru: '\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f' },\n                aside_collections: { en: 'Collections',     ru: '\u041a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438' },\n                aside_title:    { en: 'Rare gemstones,<br>certified & verified', ru: '\u0420\u0435\u0434\u043a\u0438\u0435 \u043a\u0430\u043c\u043d\u0438,<br>\u0441\u0435\u0440\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435' },\n                aside_cta:      { en: 'Explore catalog \u2192',  ru: '\u0421\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u2192' },\n\n                \/\/ \u2500\u2500\u2500 Hero & Steps \u2500\u2500\u2500\n                hero_title:     { en: 'Design Your Jewelry',    ru: '\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0432\u043e\u0451 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u0435' },\n                hero_subtitle:  { en: 'Set your parameters \u2014 AI will generate a unique design just for you', ru: '\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u2014 \u0418\u0418 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u0437\u0430\u0439\u043d \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0432\u0430\u0441' },\n                step_params:    { en: 'Parameters',     ru: '\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b' },\n                step_gen:       { en: 'Generation',     ru: '\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f' },\n                step_order:     { en: 'Order',          ru: '\u0417\u0430\u043a\u0430\u0437' },\n\n                \/\/ \u2500\u2500\u2500 Left panel \u2500\u2500\u2500\n                panel_params:   { en: 'Jewelry Parameters',     ru: '\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u044f' },\n                lbl_gemstone:   { en: 'Selected Gemstone',      ru: '\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c' },\n                btn_choose_gem: { en: 'Choose Gemstone',        ru: '\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u043a\u0430\u043c\u0435\u043d\u044c' },\n                no_stone:       { en: 'No gemstone selected',   ru: '\u041a\u0430\u043c\u0435\u043d\u044c \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d' },\n                no_stone_hint:  { en: 'Choose a gemstone from the catalog to continue', ru: '\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u0430\u043c\u0435\u043d\u044c \u0438\u0437 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c' },\n                lbl_type:       { en: 'Jewelry Type',           ru: '\u0422\u0438\u043f \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u044f' },\n                type_ring:      { en: 'Ring',           ru: '\u041a\u043e\u043b\u044c\u0446\u043e' },\n                type_earrings:  { en: 'Earrings',       ru: '\u0421\u0435\u0440\u044c\u0433\u0438' },\n                type_pendant:   { en: 'Pendant',        ru: '\u041f\u043e\u0434\u0432\u0435\u0441\u043a\u0430' },\n                type_bracelet:  { en: 'Bracelet',       ru: '\u0411\u0440\u0430\u0441\u043b\u0435\u0442' },\n                type_brooch:    { en: 'Brooch',         ru: '\u0411\u0440\u043e\u0448\u044c' },\n                type_necklace:  { en: 'Necklace',       ru: '\u041a\u043e\u043b\u044c\u0435' },\n                lbl_metal:      { en: 'Metal',          ru: '\u041c\u0435\u0442\u0430\u043b\u043b' },\n                metal_yellow:   { en: 'Yellow Gold',    ru: '\u0416\u0451\u043b\u0442\u043e\u0435 \u0437\u043e\u043b\u043e\u0442\u043e' },\n                metal_white:    { en: 'White Gold',     ru: '\u0411\u0435\u043b\u043e\u0435 \u0437\u043e\u043b\u043e\u0442\u043e' },\n                metal_rose:     { en: 'Rose Gold',      ru: '\u0420\u043e\u0437\u043e\u0432\u043e\u0435 \u0437\u043e\u043b\u043e\u0442\u043e' },\n                metal_plat:     { en: 'Platinum 950',   ru: '\u041f\u043b\u0430\u0442\u0438\u043d\u0430 950' },\n                lbl_grade:      { en: 'Gold Purity',    ru: '\u041f\u0440\u043e\u0431\u0430 \u0437\u043e\u043b\u043e\u0442\u0430' },\n                lbl_style:      { en: 'Design Style',   ru: '\u0421\u0442\u0438\u043b\u044c \u0434\u0438\u0437\u0430\u0439\u043d\u0430' },\n\n                \/\/ \u2500\u2500\u2500 Styles \u2500\u2500\u2500\n                style_modern:       { en: 'Modern',         ru: '\u041c\u043e\u0434\u0435\u0440\u043d' },\n                style_modern_d:     { en: 'Clean lines',    ru: '\u0427\u0438\u0441\u0442\u044b\u0435 \u043b\u0438\u043d\u0438\u0438' },\n                style_minimalist:   { en: 'Minimalist',     ru: '\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0437\u043c' },\n                style_minimalist_d: { en: 'Simple forms',   ru: '\u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u0444\u043e\u0440\u043c\u044b' },\n                style_classic:      { en: 'Classic',        ru: '\u041a\u043b\u0430\u0441\u0441\u0438\u043a\u0430' },\n                style_classic_d:    { en: 'Timeless elegance', ru: '\u0412\u0435\u0447\u043d\u0430\u044f \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c' },\n                style_solitaire:    { en: 'Solitaire',      ru: '\u0421\u043e\u043b\u0438\u0442\u0435\u0440' },\n                style_solitaire_d:  { en: 'Single stone',   ru: '\u041e\u0434\u0438\u043d \u043a\u0430\u043c\u0435\u043d\u044c' },\n                style_pave:         { en: 'Pav\u00e9',           ru: '\u041f\u0430\u0432\u0435' },\n                style_pave_d:       { en: 'Stone cascade',  ru: '\u041a\u0430\u0441\u043a\u0430\u0434 \u043a\u0430\u043c\u043d\u0435\u0439' },\n                style_halo:         { en: 'Halo',           ru: '\u0413\u0430\u043b\u043e' },\n                style_halo_d:       { en: 'Diamond halo',   ru: '\u041e\u0440\u0435\u043e\u043b \u0431\u0440\u0438\u043b\u043b\u0438\u0430\u043d\u0442\u043e\u0432' },\n                style_artdeco:      { en: 'Art Deco',       ru: '\u0410\u0440-\u0434\u0435\u043a\u043e' },\n                style_artdeco_d:    { en: '1920s, geometry', ru: '1920-\u0435, \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044f' },\n                style_artnouveau:   { en: 'Art Nouveau',    ru: '\u0410\u0440-\u043d\u0443\u0432\u043e' },\n                style_artnouveau_d: { en: 'Organic forms',  ru: '\u041e\u0440\u0433\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0444\u043e\u0440\u043c\u044b' },\n                style_victorian:    { en: 'Victorian',      ru: '\u0412\u0438\u043a\u0442\u043e\u0440\u0438\u0430\u043d\u0441\u043a\u0438\u0439' },\n                style_victorian_d:  { en: '19th century romance', ru: '\u0420\u043e\u043c\u0430\u043d\u0442\u0438\u043a\u0430 XIX \u0432\u0435\u043a\u0430' },\n                style_edwardian:    { en: 'Edwardian',      ru: '\u042d\u0434\u0432\u0430\u0440\u0434\u0438\u0430\u043d\u0441\u043a\u0438\u0439' },\n                style_edwardian_d:  { en: 'Lace & filigree', ru: '\u041a\u0440\u0443\u0436\u0435\u0432\u043e \u0438 \u0444\u0438\u043b\u0438\u0433\u0440\u0430\u043d\u044c' },\n                style_baroque:      { en: 'Baroque',        ru: '\u0411\u0430\u0440\u043e\u043a\u043a\u043e' },\n                style_baroque_d:    { en: 'Opulent scrolls', ru: '\u0420\u043e\u0441\u043a\u043e\u0448\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0442\u043a\u0438' },\n                style_renaissance:  { en: 'Renaissance',    ru: '\u0420\u0435\u043d\u0435\u0441\u0441\u0430\u043d\u0441' },\n                style_renaissance_d:{ en: 'Revival era',    ru: '\u042d\u043f\u043e\u0445\u0430 \u0432\u043e\u0437\u0440\u043e\u0436\u0434\u0435\u043d\u0438\u044f' },\n                style_retro:        { en: 'Retro',          ru: '\u0420\u0435\u0442\u0440\u043e' },\n                style_retro_d:      { en: '1940s\u201350s style', ru: '\u0421\u0442\u0438\u043b\u044c 1940\u201350-\u0445' },\n                style_midcentury:   { en: 'Mid-Century',    ru: '\u041c\u0438\u0434-\u0441\u0435\u043d\u0447\u0443\u0440\u0438' },\n                style_midcentury_d: { en: '1950s\u201360s modern', ru: '\u041c\u043e\u0434\u0435\u0440\u043d 1950\u201360-\u0445' },\n                style_industrial:   { en: 'Industrial',     ru: '\u0418\u043d\u0434\u0430\u0441\u0442\u0440\u0438\u0430\u043b' },\n                style_industrial_d: { en: 'Metal & gears',  ru: '\u041c\u0435\u0442\u0430\u043b\u043b \u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u044b' },\n                style_vintage:      { en: 'Vintage',        ru: '\u0412\u0438\u043d\u0442\u0430\u0436' },\n                style_vintage_d:    { en: 'Antique charm',  ru: '\u041e\u0447\u0430\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u0438\u043d\u044b' },\n                style_romantic:     { en: 'Romantic',        ru: '\u0420\u043e\u043c\u0430\u043d\u0442\u0438\u043a\u0430' },\n                style_romantic_d:   { en: 'Delicate grace', ru: '\u0423\u0442\u043e\u043d\u0447\u0451\u043d\u043d\u0430\u044f \u0433\u0440\u0430\u0446\u0438\u044f' },\n                style_floral:       { en: 'Floral',         ru: '\u0424\u043b\u043e\u0440\u0438\u0441\u0442\u0438\u043a\u0430' },\n                style_floral_d:     { en: 'Flower motifs',  ru: '\u0426\u0432\u0435\u0442\u043e\u0447\u043d\u044b\u0435 \u043c\u043e\u0442\u0438\u0432\u044b' },\n                style_nature:       { en: 'Nature',         ru: '\u041f\u0440\u0438\u0440\u043e\u0434\u0430' },\n                style_nature_d:     { en: 'Leaves & branches', ru: '\u041b\u0438\u0441\u0442\u044c\u044f \u0438 \u0432\u0435\u0442\u0432\u0438' },\n                style_bohemian:     { en: 'Bohemian',       ru: '\u0411\u043e\u0445\u043e' },\n                style_bohemian_d:   { en: 'Free spirit',    ru: '\u0421\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0439 \u0434\u0443\u0445' },\n                style_geometric:    { en: 'Geometric',      ru: '\u0413\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044f' },\n                style_geometric_d:  { en: 'Sharp forms',    ru: '\u0427\u0451\u0442\u043a\u0438\u0435 \u0444\u043e\u0440\u043c\u044b' },\n                style_angular:      { en: 'Angular',        ru: '\u0423\u0433\u043b\u043e\u0432\u0430\u0442\u044b\u0439' },\n                style_angular_d:    { en: 'Sharp edges',    ru: '\u041e\u0441\u0442\u0440\u044b\u0435 \u0433\u0440\u0430\u043d\u0438' },\n                style_symmetrical:  { en: 'Symmetry',       ru: '\u0421\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u044f' },\n                style_symmetrical_d:{ en: 'Balanced forms', ru: '\u0411\u0430\u043b\u0430\u043d\u0441 \u0444\u043e\u0440\u043c' },\n\n                \/\/ \u2500\u2500\u2500 Diamonds \u2500\u2500\u2500\n                lbl_diamonds:   { en: 'Accent Diamonds',    ru: '\u0410\u043a\u0446\u0435\u043d\u0442\u043d\u044b\u0435 \u0431\u0440\u0438\u043b\u043b\u0438\u0430\u043d\u0442\u044b' },\n                dia_none:       { en: 'None',       ru: '\u041d\u0435\u0442' },\n                dia_many:       { en: 'Many',       ru: '\u041c\u043d\u043e\u0433\u043e' },\n\n                \/\/ \u2500\u2500\u2500 Wishes \u2500\u2500\u2500\n                lbl_wishes:     { en: 'Your Wishes',        ru: '\u0412\u0430\u0448\u0438 \u043f\u043e\u0436\u0435\u043b\u0430\u043d\u0438\u044f' },\n\n                \/\/ \u2500\u2500\u2500 Center panel \u2500\u2500\u2500\n                ph_title:       { en: 'Your jewelry will appear here',      ru: '\u0412\u0430\u0448\u0435 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u0435 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0437\u0434\u0435\u0441\u044c' },\n                ph_hint:        { en: 'Set parameters and click \"Generate Design\"', ru: '\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u00ab\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d\u00bb' },\n                load_text:      { en: 'AI is creating your design',         ru: '\u0418\u0418 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0432\u0430\u0448 \u0434\u0438\u0437\u0430\u0439\u043d' },\n                load_sub:       { en: 'This usually takes 10\u201315 seconds',   ru: '\u041e\u0431\u044b\u0447\u043d\u043e \u044d\u0442\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 10\u201315 \u0441\u0435\u043a\u0443\u043d\u0434' },\n                angle_front:    { en: 'Front',      ru: '\u0421\u043f\u0435\u0440\u0435\u0434\u0438' },\n                angle_side:     { en: 'Side',       ru: '\u0421\u0431\u043e\u043a\u0443' },\n\n                \/\/ \u2500\u2500\u2500 Generate \u2500\u2500\u2500\n                btn_generate:   { en: 'Generate Design',    ru: '\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d' },\n                gen_week:       { en: 'generations this week', ru: '\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0439 \u043d\u0430 \u044d\u0442\u043e\u0439 \u043d\u0435\u0434\u0435\u043b\u0435' },\n                gen_hint:       { en: 'AI will generate a unique design based on your selection', ru: '\u0418\u0418 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u0437\u0430\u0439\u043d \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0432\u0430\u0448\u0435\u0433\u043e \u0432\u044b\u0431\u043e\u0440\u0430' },\n\n                \/\/ \u2500\u2500\u2500 Price panel \u2500\u2500\u2500\n                price_title:    { en: 'Price Estimate',     ru: '\u041e\u0446\u0435\u043d\u043a\u0430 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u0438' },\n                price_breakdown:{ en: 'Price Breakdown',    ru: '\u0420\u0430\u0441\u0447\u0451\u0442 \u0446\u0435\u043d\u044b' },\n                price_gem:      { en: 'Gemstone',           ru: '\u041a\u0430\u043c\u0435\u043d\u044c' },\n                price_craft:    { en: 'Craftsmanship',      ru: '\u0420\u0430\u0431\u043e\u0442\u0430 \u043c\u0430\u0441\u0442\u0435\u0440\u0430' },\n                cert_note:      { en: 'Gemstone authenticity certificate', ru: '\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u043c\u043d\u044f' },\n\n                \/\/ \u2500\u2500\u2500 Stone modal \u2500\u2500\u2500\n                modal_title:    { en: 'Select Gemstone',    ru: '\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u0430\u043c\u0435\u043d\u044c' },\n\n                \/\/ \u2500\u2500\u2500 Access gate \u2500\u2500\u2500\n                gate_signin_title: { en: 'Sign In Required',    ru: '\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f' },\n                gate_used_title:   { en: 'Loved your first design?', ru: '\u041f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u0439 \u0434\u0438\u0437\u0430\u0439\u043d?' },\n                gate_used_text:    { en: 'Your free design is ready! Create a free account to keep generating \u2014 up to 20 designs every week.', ru: '\u0412\u0430\u0448 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0434\u0438\u0437\u0430\u0439\u043d \u0433\u043e\u0442\u043e\u0432! \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0430\u043a\u043a\u0430\u0443\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u2014 \u0434\u043e 20 \u0434\u0438\u0437\u0430\u0439\u043d\u043e\u0432 \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e.' },\n                gate_signin_text:  { en: 'Create an account or sign in to use the AI Jewelry Constructor and generate unique designs for your gemstones.', ru: '\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0438\u043b\u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c AI-\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0438 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0438\u0437\u0430\u0439\u043d\u044b \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u043a\u0430\u043c\u043d\u0435\u0439.' },\n                gate_signin_btn:   { en: 'Sign In',             ru: '\u0412\u043e\u0439\u0442\u0438' },\n                gate_register_btn: { en: 'Create Account',      ru: '\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442' },\n                gate_new_title:    { en: 'Almost There',         ru: '\u041f\u043e\u0447\u0442\u0438 \u0433\u043e\u0442\u043e\u0432\u043e' },\n                gate_new_text:     { en: 'The AI Constructor is available to members with accounts at least <strong>7 days old<\/strong>. This helps us ensure the best experience for our community.', ru: 'AI-\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u0441 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430\u043c\u0438 \u0441\u0442\u0430\u0440\u0448\u0435 <strong>7 \u0434\u043d\u0435\u0439<\/strong>. \u042d\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043d\u0430\u043c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u043b\u0443\u0447\u0448\u0438\u0439 \u043e\u043f\u044b\u0442 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0430.' },\n                gate_days_remaining: { en: 'days remaining',     ru: '\u0434\u043d\u0435\u0439 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c' },\n                gate_day_remaining:  { en: 'day remaining',      ru: '\u0434\u0435\u043d\u044c \u043e\u0441\u0442\u0430\u043b\u0441\u044f' },\n            },\n\n            t(key) {\n                const entry = this.translations[key];\n                if (!entry) return null;\n                return entry[this.currentLang] || entry.en;\n            },\n\n            detectLang() {\n                \/\/ 1. URL path check (\/ru\/)\n                const path = window.location.pathname;\n                if (path.startsWith('\/ru\/') || path === '\/ru') return 'ru';\n                \/\/ 2. sessionStorage (voc_lang)\n                const stored = sessionStorage.getItem('voc_lang');\n                if (stored === 'ru') return 'ru';\n                return 'en';\n            },\n\n            apply(lang) {\n                this.currentLang = lang || this.detectLang();\n                document.getElementById('vocHtmlRoot').lang = this.currentLang === 'ru' ? 'ru' : 'en';\n\n                \/\/ Update all data-i18n elements\n                document.querySelectorAll('[data-i18n]').forEach(el => {\n                    const key = el.getAttribute('data-i18n');\n                    const val = this.t(key);\n                    if (val) el.innerHTML = val;\n                });\n\n                \/\/ Update language switcher\n                const enBtn = document.getElementById('langEN');\n                const ruBtn = document.getElementById('langRU');\n                if (enBtn && ruBtn) {\n                    enBtn.classList.toggle('active', this.currentLang === 'en');\n                    ruBtn.classList.toggle('active', this.currentLang === 'ru');\n                    \/\/ Build correct hrefs\n                    const path = window.location.pathname;\n                    if (this.currentLang === 'ru') {\n                        enBtn.href = path.replace('\/ru\/', '\/').replace('\/ru', '\/');\n                        ruBtn.href = path;\n                    } else {\n                        ruBtn.href = '\/ru' + path;\n                        enBtn.href = path;\n                    }\n                }\n\n                \/\/ Store in session\n                sessionStorage.setItem('voc_lang', this.currentLang);\n            }\n        };\n\n        \/\/ Apply translations immediately\n        VOC_I18N.apply();\n\n        \/\/ \u2500\u2500 \u0411\u043b\u043e\u043a 9.2: stone_id \u0438\u0437 URL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        (function() {\n            var params = new URLSearchParams(window.location.search);\n            var stoneId = params.get('stone_id');\n            var isRu = (document.documentElement.lang || '').toLowerCase().startsWith('ru')\n                    || window.location.pathname.indexOf('\/ru\/') === 0;\n\n            \/\/ i18n \u0434\u043b\u044f \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\n            var catalogLink = document.getElementById('linkBrowseCatalog');\n            if (catalogLink && isRu) catalogLink.textContent = '\u0418\u043b\u0438 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430\u0448\u0438 \u043a\u0430\u043c\u043d\u0438 \u2192';\n\n            if (!stoneId) return;\n\n            \/\/ \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u044f \u0434\u043b\u044f \u0431\u0430\u043d\u043d\u0435\u0440\u0430\n            fetch('\/wp-json\/vocgems\/v1\/stones\/' + stoneId)\n                .then(function(r) { return r.json(); })\n                .then(function(stone) {\n                    if (!stone || !stone.title) return;\n                    var banner = document.getElementById('stoneFromCardBanner');\n                    var text   = document.getElementById('stoneFromCardText');\n                    var link   = document.getElementById('stoneFromCardLink');\n                    if (!banner) return;\n\n                    var stoneInfo = stone.title + (stone.weight ? ', ' + stone.weight + ' ct' : '') + (stone.location ? ', ' + stone.location : '');\n                    text.textContent = isRu\n                        ? '\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c: ' + stoneInfo\n                        : 'You selected: ' + stoneInfo;\n                    if (link) link.textContent = isRu ? '\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0441 \u044d\u0442\u0438\u043c \u043a\u0430\u043c\u043d\u0435\u043c \u2192' : 'Continue with this stone \u2192';\n                    banner.style.display = 'block';\n\n                    \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 window \u0434\u043b\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n                    window._vocSelectedStoneId = stoneId;\n                    window._vocSelectedStone = stone;\n                })\n                .catch(function() {});\n        })();\n\n        \/\/ \u2500\u2500 \u0411\u043b\u043e\u043a 10: \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 Generate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        var genBtn = document.getElementById('generateBtn');\n        if (genBtn) {\n            genBtn.addEventListener('click', function() {\n                if (window.gtag) window.gtag('event', 'request_submitted', {\n                    stone_id: window._vocSelectedStoneId || ''\n                });\n            });\n        }\n\n        \/\/ \u2500\u2500 Init \u2500\u2500\n        VOC_API.init();\n    <\/script>\n\n    <!-- \u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u00ab\u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u044b\u00bb \u0443\u0434\u0430\u043b\u0435\u043d\u0430 11.06.2026 \u2014 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0440\u0430\u0437\u043c\u043e\u0440\u043e\u0436\u0435\u043d. -->\n\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>VOC Gems \u2014 AI Jewelry Constructor Menu EN | RU 0 01Home 02Loose Gemstones 03Fine Jewelry 04Auction 05Constructor 06Contact 07News 08About\u2193 About Us Payment &#038; Delivery Security FAQ Gemstone Guide Terms &#038; Conditions 09Account\u2193 Dashboard Your Selection Log In Sign Up Account Collections Rare gemstones,certified &#038; verified Explore catalog \u2192 \u2715 Design Your Jewelry Set [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"inline_featured_image":false,"footnotes":""},"class_list":["post-14909","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Jewelry Constructor | Build Your Design | VOC Gems<\/title>\n<meta name=\"description\" content=\"Pick a certified gemstone, choose metal and setting, and see your custom jewelry rendered by AI. VOC Gems constructor.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/vocgems.com\/ru\/\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440-\u0438\u0438\/\" \/>\n<meta property=\"og:locale\" content=\"ru_RU\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Jewelry Constructor | Build Your Design | VOC Gems\" \/>\n<meta property=\"og:description\" content=\"Pick a certified gemstone, choose metal and setting, and see your custom jewelry rendered by AI. VOC Gems constructor.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/vocgems.com\/ru\/\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440-\u0438\u0438\/\" \/>\n<meta property=\"og:site_name\" content=\"VOC Gems\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-13T14:38:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f\" \/>\n\t<meta name=\"twitter:data1\" content=\"2 \u043c\u0438\u043d\u0443\u0442\u044b\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/vocgems.com\/ai-constructor\/\",\"url\":\"https:\/\/vocgems.com\/ai-constructor\/\",\"name\":\"Jewelry Constructor | Build Your Design | VOC Gems\",\"isPartOf\":{\"@id\":\"https:\/\/vocgems.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/vocgems.com\/ai-constructor\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/vocgems.com\/ai-constructor\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png\",\"datePublished\":\"2026-04-10T13:22:36+00:00\",\"dateModified\":\"2026-07-13T14:38:07+00:00\",\"description\":\"Pick a certified gemstone, choose metal and setting, and see your custom jewelry rendered by AI. VOC Gems constructor.\",\"breadcrumb\":{\"@id\":\"https:\/\/vocgems.com\/ai-constructor\/#breadcrumb\"},\"inLanguage\":\"ru-RU\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/vocgems.com\/ai-constructor\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ru-RU\",\"@id\":\"https:\/\/vocgems.com\/ai-constructor\/#primaryimage\",\"url\":\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png\",\"contentUrl\":\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/vocgems.com\/ai-constructor\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\",\"item\":\"https:\/\/vocgems.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ai-constructor\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/vocgems.com\/#website\",\"url\":\"https:\/\/vocgems.com\/\",\"name\":\"VOC Gems\",\"description\":\"Gemstones shop\",\"publisher\":{\"@id\":\"https:\/\/vocgems.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/vocgems.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ru-RU\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/vocgems.com\/#organization\",\"name\":\"VOC Gems\",\"url\":\"https:\/\/vocgems.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ru-RU\",\"@id\":\"https:\/\/vocgems.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/02\/cropped-Green_logo-Photoroom-2.png\",\"contentUrl\":\"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/02\/cropped-Green_logo-Photoroom-2.png\",\"width\":848,\"height\":417,\"caption\":\"VOC Gems\"},\"image\":{\"@id\":\"https:\/\/vocgems.com\/#\/schema\/logo\/image\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u0439 \u2014 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d | VOC Gems","description":"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c, \u043c\u0435\u0442\u0430\u043b\u043b \u0438 \u043e\u043f\u0440\u0430\u0432\u0443 \u2014 \u0418\u0418 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0432\u0430\u0448\u0435 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u0435 \u0432 \u0444\u043e\u0442\u043e\u0440\u0435\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0435.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/vocgems.com\/ru\/\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440-\u0438\u0438\/","og_locale":"ru_RU","og_type":"article","og_title":"Jewelry Constructor | Build Your Design | VOC Gems","og_description":"Pick a certified gemstone, choose metal and setting, and see your custom jewelry rendered by AI. VOC Gems constructor.","og_url":"https:\/\/vocgems.com\/ru\/\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440-\u0438\u0438\/","og_site_name":"VOC Gems","article_modified_time":"2026-07-13T14:38:07+00:00","og_image":[{"url":"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png","type":"","width":"","height":""}],"twitter_card":"summary_large_image","twitter_misc":{"\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f":"2 \u043c\u0438\u043d\u0443\u0442\u044b"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/vocgems.com\/ai-constructor\/","url":"https:\/\/vocgems.com\/ai-constructor\/","name":"\u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u0439 \u2014 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d | VOC Gems","isPartOf":{"@id":"https:\/\/vocgems.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/vocgems.com\/ai-constructor\/#primaryimage"},"image":{"@id":"https:\/\/vocgems.com\/ai-constructor\/#primaryimage"},"thumbnailUrl":"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png","datePublished":"2026-04-10T13:22:36+00:00","dateModified":"2026-07-13T14:38:07+00:00","description":"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c, \u043c\u0435\u0442\u0430\u043b\u043b \u0438 \u043e\u043f\u0440\u0430\u0432\u0443 \u2014 \u0418\u0418 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0432\u0430\u0448\u0435 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u0435 \u0432 \u0444\u043e\u0442\u043e\u0440\u0435\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0435.","breadcrumb":{"@id":"https:\/\/vocgems.com\/ai-constructor\/#breadcrumb"},"inLanguage":"ru-RU","potentialAction":[{"@type":"ReadAction","target":["https:\/\/vocgems.com\/ai-constructor\/"]}]},{"@type":"ImageObject","inLanguage":"ru-RU","@id":"https:\/\/vocgems.com\/ai-constructor\/#primaryimage","url":"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png","contentUrl":"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/06\/LOGO-EMERALD-TM-PNG-2-scaled.png"},{"@type":"BreadcrumbList","@id":"https:\/\/vocgems.com\/ai-constructor\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430","item":"https:\/\/vocgems.com\/"},{"@type":"ListItem","position":2,"name":"ai-constructor"}]},{"@type":"WebSite","@id":"https:\/\/vocgems.com\/#website","url":"https:\/\/vocgems.com\/","name":"VOC Gems","description":"\u041c\u0430\u0433\u0430\u0437\u0438\u043d \u0434\u0440\u0430\u0433\u043e\u0446\u0435\u043d\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439","publisher":{"@id":"https:\/\/vocgems.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/vocgems.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ru-RU"},{"@type":"Organization","@id":"https:\/\/vocgems.com\/#organization","name":"VOC Gems","url":"https:\/\/vocgems.com\/","logo":{"@type":"ImageObject","inLanguage":"ru-RU","@id":"https:\/\/vocgems.com\/#\/schema\/logo\/image\/","url":"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/02\/cropped-Green_logo-Photoroom-2.png","contentUrl":"https:\/\/vocgems.com\/wp-content\/uploads\/2026\/02\/cropped-Green_logo-Photoroom-2.png","width":848,"height":417,"caption":"VOC Gems"},"image":{"@id":"https:\/\/vocgems.com\/#\/schema\/logo\/image\/"}}]}},"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/pages\/14909","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/comments?post=14909"}],"version-history":[{"count":3,"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/pages\/14909\/revisions"}],"predecessor-version":[{"id":15880,"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/pages\/14909\/revisions\/15880"}],"wp:attachment":[{"href":"https:\/\/vocgems.com\/ru\/wp-json\/wp\/v2\/media?parent=14909"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}