Pytml

Pytml is a 15KB lightweight PyScript alternative — run Python in HTML with one script tag.

git clone https://github.com/notamitgamer/Pytml.git
· rebuilt hourlyOpen on GitHub
root / index.html

index.html (44637B)


      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4   <meta charset="UTF-8">
      5   <meta name="viewport" content="width=device-width, initial-scale=1.0">
      6 
      7   <!-- ========== PRIMARY SEO ========== -->
      8   <title>Pytml – Python + HTML in the Browser</title>
      9   <meta name="description" content="Pytml 2.5 is a zero-config JavaScript bridge for Python + HTML, powered by Pyodide 0.314.0.7. Use normal Python, interact with HTML, and build demos and educational pages without a backend.">
     10   <meta name="keywords" content="Pytml, Python in browser, Python HTML, Pyodide, DOM, educational coding, static site Python, micropip">
     11   <meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large">
     12   <meta name="author" content="Aswanth R (NodeX-AR)">
     13   <link rel="canonical" href="https://pytml.js.org/">
     14   <link rel="sitemap" type="application/xml" title="Sitemap" href="https://pytml.js.org/sitemap.xml" />
     15   <meta name="google-site-verification" content="ri10nA5CuSxwYGnZIdeVOJl2eh8FayQ2ydsNIrGJTo8" />
     16 
     17   <!-- ========== FAVICON ========== -->
     18   <link rel="icon" href="/favicon.ico" sizes="any">
     19   <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
     20   <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
     21   <link rel="icon" type="image/png" href="/favicon-48x48.png" sizes="48x48">
     22   <link rel="manifest" href="/site.webmanifest">
     23   <link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
     24   <link rel="icon" type="image/png" href="/android-chrome-512x512.png" sizes="512x512">
     25   <link rel="apple-touch-icon" href="/apple-touch-icon.png">
     26   <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3b82f6">
     27   <link rel="icon" href="/favicon.svg" type="image/svg+xml">
     28 
     29   <!-- ========== OPEN GRAPH ========== -->
     30   <meta property="og:type" content="website">
     31   <meta property="og:title" content="Pytml – Python + HTML in the Browser (vs PyScript & Flask)">
     32   <meta property="og:description" content="Run Python in HTML with zero config. Compare with PyScript & Flask – 15KB, no server, instant setup. Honest, fast, lazy.">
     33   <meta property="og:url" content="https://pytml.js.org/">
     34   <meta property="og:image" content="https://pytml.js.org/preview.png">
     35   <meta property="og:image:width" content="1200">
     36   <meta property="og:image:height" content="630">
     37   <meta property="og:site_name" content="NodeX-AR">
     38   <meta property="og:locale" content="en_US">
     39   <meta property="og:updated_time" content="2026-06-18T10:00:00+00:00">
     40 
     41   <!-- ========== TWITTER CARD ========== -->
     42   <meta name="twitter:card" content="summary_large_image">
     43   <meta name="twitter:title" content="Pytml – Python + HTML in the Browser (vs PyScript & Flask)">
     44   <meta name="twitter:description" content="Zero‑config Python in HTML – compare vs PyScript & Flask. 15KB, faster, simpler, no backend.">
     45   <meta name="twitter:image" content="https://pytml.js.org/preview.png">
     46 
     47   <!-- ========== ENHANCED SCHEMA.ORG WITH ZENODO CITATION ========== -->
     48 <script type="application/ld+json">
     49 {
     50   "@context": "https://schema.org",
     51   "@graph": [
     52     {
     53       "@type": "WebApplication",
     54       "@id": "https://pytml.js.org/#webapp",
     55       "mainEntityOfPage": {
     56         "@type": "WebPage",
     57         "@id": "https://pytml.js.org/"
     58       },
     59       "name": "Pytml",
     60       "alternateName": "Python in HTML Bridge",
     61       "headline": "Pytml 2.5 – Python + HTML in the Browser",
     62       "description": "Pytml 2.5 is a zero-config JavaScript bridge that runs Python in HTML using Pyodide and WebAssembly. It adds DOM interaction, browser input, numbered inline UI controls, events and micropip-backed package support.",
     63       "keywords": ["Pytml", "PyScript alternative", "Flask alternative", "Python in Browser", "Zero Config Python"],
     64       "applicationCategory": "DeveloperApplication",
     65       "softwareVersion": "2.5.1",
     66       "datePublished": "2026-06-04",
     67       "dateModified": "2026-10-02",
     68       "license": "https://github.com/NodeX-AR/Pytml/blob/main/LICENSE",
     69       "url": "https://pytml.js.org/",
     70       "author": {
     71         "@type": "Person",
     72         "@id": "https://a-r.is-a.dev/#person",
     73         "name": "Aswanth R",
     74         "alternateName": "NodeX-AR",
     75         "url": "https://a-r.is-a.dev/",
     76         "sameAs": [
     77           "https://www.wikidata.org/wiki/Q140290370",
     78           "https://orcid.org/0009-0007-1619-374X",
     79           "https://github.com/NodeX-AR",
     80           "https://www.linkedin.com/in/nodex-ar"
     81         ]
     82       },
     83       "citation": {
     84         "@type": "ScholarlyArticle",
     85         "@id": "https://doi.org/10.5281/zenodo.20786716",
     86         "name": "Pytml: Running Python in the Browser Using Pyodide",
     87         "description": "Academic paper describing Pytml, a JavaScript library that uses Pyodide to run Python in web browsers.",
     88         "author": {
     89           "@type": "Person",
     90           "@id": "https://a-r.is-a.dev/#person",
     91           "name": "Aswanth R",
     92           "url": "https://a-r.is-a.dev/"
     93         },
     94         "datePublished": "2026-06-21",
     95         "publisher": {
     96           "@type": "Organization",
     97           "name": "Zenodo"
     98         },
     99         "url": "https://doi.org/10.5281/zenodo.20786716"
    100       },
    101       "offers": {
    102         "@type": "Offer",
    103         "price": "0",
    104         "priceCurrency": "USD",
    105         "availability": "https://schema.org/InStock"
    106       },
    107       "sameAs": [
    108         "https://github.com/NodeX-AR/Pytml",
    109         "https://dev.to/nodex-ar/pytml-running-python-in-the-browser-without-a-backend-1nn4",
    110         "https://devpost.com/software/pytml",
    111         "https://www.npmjs.com/package/pytml"
    112       ]
    113     },
    114     {
    115       "@type": "Person",
    116       "@id": "https://a-r.is-a.dev/#person",
    117       "name": "Aswanth R",
    118       "alternateName": "NodeX-AR",
    119       "description": "Ethical hacker and Python developer. Creator of Pytml — a zero-config JavaScript library that runs Python in the browser.",
    120       "url": "https://a-r.is-a.dev/",
    121       "sameAs": [
    122         "https://www.wikidata.org/wiki/Q140290370",
    123         "https://orcid.org/0009-0007-1619-374X",
    124         "https://github.com/NodeX-AR",
    125         "https://www.linkedin.com/in/nodex-ar"
    126       ],
    127       "citation": {
    128         "@type": "ScholarlyArticle",
    129         "@id": "https://doi.org/10.5281/zenodo.20786716",
    130         "name": "Pytml: Running Python in the Browser Using Pyodide",
    131         "datePublished": "2026-06-21",
    132         "publisher": "Zenodo",
    133         "url": "https://doi.org/10.5281/zenodo.20786716"
    134       }
    135     },
    136     {
    137       "@type": "ScholarlyArticle",
    138       "@id": "https://doi.org/10.5281/zenodo.20786716",
    139       "name": "Pytml: Running Python in the Browser Using Pyodide",
    140       "description": "Academic paper describing Pytml, a JavaScript library that uses Pyodide to run Python in web browsers.",
    141       "author": {
    142         "@type": "Person",
    143         "@id": "https://a-r.is-a.dev/#person",
    144         "name": "Aswanth R",
    145         "url": "https://a-r.is-a.dev/"
    146       },
    147       "datePublished": "2026-06-21",
    148       "publisher": {
    149         "@type": "Organization",
    150         "name": "Zenodo"
    151       },
    152       "url": "https://doi.org/10.5281/zenodo.20786716",
    153       "isPartOf": {
    154         "@type": "CreativeWork",
    155         "name": "Zenodo",
    156         "url": "https://zenodo.org/"
    157       }
    158     },
    159     {
    160       "@type": "BreadcrumbList",
    161       "itemListElement": [
    162         {
    163           "@type": "ListItem",
    164           "position": 1,
    165           "name": "Home",
    166           "item": "https://pytml.js.org/"
    167         },
    168         {
    169           "@type": "ListItem",
    170           "position": 2,
    171           "name": "Pytml vs PyScript & Flask"
    172         }
    173       ]
    174     },
    175     {
    176       "@type": "FAQPage",
    177       "mainEntity": [
    178         {
    179           "@type": "Question",
    180           "name": "What is the actual bundle size of Pytml?",
    181           "acceptedAnswer": {
    182             "@type": "Answer",
    183             "text": "Pytml loads its JavaScript bridge immediately and loads Pyodide 0.314.0.7 from the configured CDN during startup. The exact transfer size varies with the browser and cache state."
    184           }
    185         },
    186         {
    187           "@type": "Question",
    188           "name": "Can I use Pytml with React, Vue, or other JS frameworks?",
    189           "acceptedAnswer": {
    190             "@type": "Answer",
    191             "text": "Yes. Pytml is framework-agnostic. You can drop the script tag in any HTML page, including those generated by React or Vue. It scans the DOM for <py> tags after the framework renders."
    192           }
    193         },
    194         {
    195           "@type": "Question",
    196           "name": "What Python packages are supported?",
    197           "acceptedAnswer": {
    198             "@type": "Answer",
    199             "text": "Pytml uses Pyodide's package environment and micropip workflow. Packages available in the Pyodide distribution, including NumPy, pandas and Matplotlib, can be used when compatible with the browser runtime."
    200           }
    201         },
    202         {
    203           "@type": "Question",
    204           "name": "How does Pytml handle errors and debugging?",
    205           "acceptedAnswer": {
    206             "@type": "Answer",
    207             "text": "Python tracebacks are captured and rendered directly in the browser terminal box. Syntax errors, runtime exceptions, and import errors all appear with line numbers and stack traces, just like a local Python REPL."
    208           }
    209         },
    210         {
    211           "@type": "Question",
    212           "name": "Is there any server-side component or data sent to a backend?",
    213           "acceptedAnswer": {
    214             "@type": "Answer",
    215             "text": "No. Everything runs client-side in WebAssembly. No Python code, input, or output ever leaves your browser. This makes Pytml ideal for privacy-sensitive demos and educational tools."
    216           }
    217         },
    218         {
    219           "@type": "Question",
    220           "name": "What are the performance limitations?",
    221           "acceptedAnswer": {
    222             "@type": "Answer",
    223             "text": "Pytml runs Python in a WebAssembly sandbox. It's roughly 2-5x slower than native CPython for CPU-bound tasks, and memory is limited to the browser's tab allocation (usually 2-4GB). Heavy number crunching may be slower than server-side Python, but for demos, data viz, and scripting, it's perfectly fine."
    224           }
    225         }
    226       ]
    227     }
    228   ]
    229 }
    230 </script>
    231 
    232   <!-- ========== FONTS ========== -->
    233   <link rel="preconnect" href="https://fonts.googleapis.com">
    234   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    235   <link href="https://fonts.googleapis.com/css2?family=Inter:opsz@14..32&family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
    236 
    237   <!-- ========== STYLES (YOUR ORIGINAL – UNCHANGED, except minor mobile fixes below) ========== -->
    238   <script src="https://cdn.tailwindcss.com"></script>
    239   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    240 
    241   <style>
    242     :root {
    243       --bg: #050505;
    244       --accent: #3b82f6;
    245       --accent-dark: #2563eb;
    246       --accent-glow: rgba(59, 130, 246, 0.2);
    247       --border: rgba(255,255,255,0.08);
    248       --text: #e5e5e5;
    249     }
    250     * { margin: 0; padding: 0; box-sizing: border-box; }
    251     body {
    252       background: var(--bg);
    253       color: var(--text);
    254       font-family: 'Inter', system-ui, sans-serif;
    255       overflow-x: hidden;
    256       -webkit-tap-highlight-color: transparent;
    257     }
    258     h1, h2, h3 { font-family: 'Space Grotesk', sans-serif; }
    259     .mono { font-family: 'JetBrains Mono', monospace; }
    260     .text-accent { color: var(--accent); }
    261     .border-accent { border-color: var(--accent); }
    262     .font-heading { font-family: 'Space Grotesk', sans-serif; }
    263 
    264     .bg-noise {
    265       position: fixed;
    266       inset: 0;
    267       pointer-events: none;
    268       z-index: 9998;
    269       opacity: 0.03;
    270       background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
    271     }
    272 
    273     .btn-term {
    274       display: inline-flex;
    275       align-items: center;
    276       gap: 0.5rem;
    277       padding: 0.7rem 1.3rem;
    278       background: #0c0c0c;
    279       border: 1px solid #27272a;
    280       color: #b0b0b0;
    281       font-family: 'JetBrains Mono', monospace;
    282       font-size: 0.85rem;
    283       text-transform: uppercase;
    284       transition: all 0.2s;
    285       text-decoration: none;
    286       touch-action: manipulation;
    287       cursor: pointer;
    288       min-height: 48px;
    289       line-height: 1.2;
    290     }
    291     .btn-term::before { content: "> "; margin-right: 4px; color: var(--accent); }
    292     .btn-term:hover { background: var(--accent); color: #050505; border-color: var(--accent); transform: translateX(4px); }
    293     .btn-term:active { transform: scale(0.97); }
    294 
    295     .card {
    296       background: rgba(8,8,8,0.7);
    297       backdrop-filter: blur(2px);
    298       border: 1px solid #1e1e1e;
    299       border-radius: 0.5rem;
    300       padding: 1.5rem;
    301       transition: border 0.25s, transform 0.25s;
    302       height: 100%;
    303     }
    304     .card:hover { border-color: var(--accent); transform: translateY(-4px); background: rgba(59,130,246,0.02); }
    305 
    306     .code-box {
    307       background: #0b0b0b;
    308       border-left: 3px solid var(--accent);
    309       padding: 1rem;
    310       font-family: 'JetBrains Mono', monospace;
    311       font-size: 0.85rem;
    312       overflow-x: auto;
    313       white-space: pre-wrap;
    314       word-break: break-word;
    315       color: #c0c0c0;
    316       border-radius: 0 0.5rem 0.5rem 0;
    317       max-width: 100%;
    318       tab-size: 2;
    319       line-height: 1.6;
    320     }
    321 
    322     .fade-up { opacity: 0; transform: translateY(30px); transition: all 0.7s cubic-bezier(0.2, 0.9, 0.4, 1); }
    323     .fade-up.visible { opacity: 1; transform: translateY(0); }
    324 
    325     .logo-box {
    326       display: inline-flex;
    327       align-items: center;
    328       gap: 0.75rem;
    329       background: #0c0c0c;
    330       border: 2px solid var(--accent);
    331       padding: 0.4rem 1rem 0.4rem 0.75rem;
    332       border-radius: 0.25rem;
    333       box-shadow: 0 0 20px var(--accent-glow);
    334       flex-wrap: nowrap;
    335       max-width: 100%;
    336     }
    337     .logo-box img {
    338       width: 42px;
    339       height: 42px;
    340       display: block;
    341       flex-shrink: 0;
    342     }
    343     .logo-box .text {
    344       font-family: 'JetBrains Mono', monospace;
    345       font-size: 2rem;
    346       font-weight: 700;
    347       color: var(--accent);
    348       letter-spacing: 0.08em;
    349       line-height: 1.1;
    350       white-space: nowrap;
    351     }
    352     .logo-box .text small {
    353       font-size: 0.6rem;
    354       color: #5A627A;
    355       display: block;
    356       letter-spacing: 0.2em;
    357       margin-top: 0.05rem;
    358       white-space: nowrap;
    359     }
    360 
    361     .badge-term {
    362       display: inline-block;
    363       background: #0c0c0c;
    364       border: 1px solid #27272a;
    365       padding: 0.2rem 0.8rem;
    366       font-family: 'JetBrains Mono', monospace;
    367       font-size: 0.7rem;
    368       color: var(--text);
    369       border-radius: 999px;
    370     }
    371     .badge-term i { color: var(--accent); }
    372 
    373     .skill-tag {
    374       background: #0f0f0f;
    375       border: 1px solid #262626;
    376       padding: 0.25rem 0.8rem;
    377       font-size: 0.75rem;
    378       font-family: monospace;
    379       border-radius: 999px;
    380       transition: all 0.2s;
    381       white-space: nowrap;
    382     }
    383     .skill-tag:hover { border-color: var(--accent); color: var(--accent); }
    384 
    385     .footer-link { color: #5A627A; text-decoration: none; transition: color 0.2s; }
    386     .footer-link:hover { color: var(--accent); text-decoration: underline; }
    387 
    388     .marquee {
    389       overflow: hidden;
    390       mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
    391       -webkit-mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
    392     }
    393     .marquee-track {
    394       display: flex;
    395       gap: 2rem;
    396       animation: marqueeScroll 25s linear infinite;
    397       width: max-content;
    398     }
    399     @keyframes marqueeScroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
    400 
    401     ::-webkit-scrollbar { width: 6px; }
    402     ::-webkit-scrollbar-track { background: #0a0a0a; }
    403     ::-webkit-scrollbar-thumb { background: #1f1f1f; border-radius: 0; }
    404     ::-webkit-scrollbar-thumb:hover { background: var(--accent); }
    405 
    406     .pulse-ring {
    407       animation: pulse 2s ease-in-out infinite;
    408     }
    409     @keyframes pulse {
    410       0% { transform: scale(1); opacity: 0.15; }
    411       50% { transform: scale(1.1); opacity: 0.35; }
    412       100% { transform: scale(1); opacity: 0.15; }
    413     }
    414 
    415     /* ===== YOUR MOBILE‑FIRST TWEAKS (ENHANCED FOR BETTER CENTERING & SCROLL) ===== */
    416     @media (max-width: 640px) {
    417       .logo-box { padding: 0.3rem 0.7rem 0.3rem 0.5rem; gap: 0.5rem; }
    418       .logo-box img { width: 32px; height: 32px; }
    419       .logo-box .text { font-size: 1.5rem; letter-spacing: 0.05em; }
    420       .logo-box .text small { font-size: 0.5rem; }
    421       .btn-term { font-size: 0.7rem; padding: 0.5rem 0.9rem; min-height: 44px; gap: 0.35rem; }
    422       .btn-term::before { content: ">"; margin-right: 2px; }
    423       .card { padding: 1rem; }
    424       .code-box { font-size: 0.75rem; padding: 0.75rem; }
    425       .hero-img-wrap img { width: 120px; height: 120px; }
    426       .hero-img-wrap .pulse-ring { width: 160px; height: 160px; }
    427       .marquee-track { gap: 1rem; }
    428       .skill-tag { font-size: 0.65rem; padding: 0.2rem 0.6rem; }
    429 
    430       /* FIX: Comparison table scroll on mobile */
    431       .compare-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    432       .compare-table { min-width: 420px; }
    433     }
    434     @media (max-width: 480px) {
    435       .logo-box .text { font-size: 1.25rem; }
    436       .logo-box img { width: 28px; height: 28px; }
    437       .logo-box .text small { font-size: 0.45rem; }
    438       .btn-term { font-size: 0.65rem; padding: 0.4rem 0.7rem; min-height: 40px; }
    439       .hero-img-wrap img { width: 100px; height: 100px; }
    440       .hero-img-wrap .pulse-ring { width: 140px; height: 140px; }
    441       .code-box { font-size: 0.65rem; padding: 0.5rem; }
    442       .badge-term { font-size: 0.6rem; padding: 0.15rem 0.5rem; }
    443       .card h3 { font-size: 1rem; }
    444       .skill-tag { font-size: 0.55rem; padding: 0.15rem 0.5rem; }
    445       .marquee-track { gap: 0.75rem; }
    446     }
    447     @media (max-width: 380px) {
    448       .logo-box .text { font-size: 1rem; }
    449       .logo-box img { width: 24px; height: 24px; }
    450       .logo-box .text small { font-size: 0.4rem; }
    451       .btn-term { font-size: 0.55rem; padding: 0.3rem 0.6rem; min-height: 36px; }
    452       .hero-img-wrap img { width: 80px; height: 80px; }
    453       .hero-img-wrap .pulse-ring { width: 110px; height: 110px; }
    454       .code-box { font-size: 0.6rem; padding: 0.4rem; }
    455       /* Tighter padding on ultra-small screens */
    456       section { padding-left: 0.75rem !important; padding-right: 0.75rem !important; }
    457       .compare-table { min-width: 360px; font-size: 0.55rem; }
    458       .compare-table th, .compare-table td { padding: 0.3rem 0.2rem; }
    459     }
    460     @media (prefers-reduced-motion: reduce) {
    461       .fade-up { opacity: 1; transform: none; transition: none; }
    462       .marquee-track { animation: none; }
    463       .pulse-ring { animation: none; }
    464       .btn-term:hover { transform: none; }
    465       .card:hover { transform: none; }
    466     }
    467 
    468     /* ===== COMPARISON TABLE ===== */
    469     .compare-table {
    470       width: 100%;
    471       border-collapse: collapse;
    472       font-size: 0.8rem;
    473       text-align: left;
    474     }
    475     .compare-table th,
    476     .compare-table td {
    477       padding: 0.75rem 0.5rem;
    478       border-bottom: 1px solid #1e1e1e;
    479     }
    480     .compare-table th {
    481       color: var(--accent);
    482       font-family: 'JetBrains Mono', monospace;
    483       font-weight: 600;
    484       text-transform: uppercase;
    485       letter-spacing: 0.05em;
    486       font-size: 0.7rem;
    487     }
    488     .compare-table td {
    489       color: #b0b0b0;
    490     }
    491     .compare-table .highlight {
    492       color: var(--text);
    493       font-weight: 500;
    494     }
    495     .compare-table .check { color: var(--accent); }
    496     .compare-table .cross { color: #555; }
    497     @media (max-width: 640px) {
    498       .compare-table th,
    499       .compare-table td {
    500         padding: 0.5rem 0.3rem;
    501         font-size: 0.65rem;
    502       }
    503       .compare-table th { font-size: 0.6rem; }
    504     }
    505   </style>
    506 </head>
    507 <body>
    508 
    509   <div class="bg-noise"></div>
    510 
    511   <!-- ===== HEADER WITH LOGO (ONLY ONE – KEPT THIS) ===== -->
    512   <header class="relative z-10 border-b border-white/5 py-4 px-4 sm:px-6">
    513     <div class="max-w-6xl mx-auto flex items-center justify-between flex-wrap gap-4">
    514       <div class="logo-box">
    515         <img src="/android-chrome-192x192.png" alt="Pytml logo" width="42" height="42" loading="lazy">
    516         <div class="text">
    517           PYTML
    518           <small>python → html</small>
    519         </div>
    520       </div>
    521       <nav aria-label="Primary navigation">
    522         <ul class="flex flex-wrap gap-4 text-sm mono text-gray-400">
    523           <li><a href="#compare" class="hover:text-accent transition">Comparison</a></li>
    524           <li><a href="#features" class="hover:text-accent transition">Features</a></li>
    525           <li><a href="#code" class="hover:text-accent transition">Code</a></li>
    526           <li><a href="#faq" class="hover:text-accent transition">FAQ</a></li>
    527           <li><a href="demo.html" class="hover:text-accent transition">Demos</a></li>
    528           <li><a href="https://github.com/NodeX-AR/Pytml" target="_blank" rel="noopener noreferrer" class="hover:text-accent transition">GitHub</a></li>
    529         </ul>
    530       </nav>
    531     </div>
    532   </header>
    533 
    534   <!-- ===== MAIN CONTENT ===== -->
    535   <main class="relative z-10">
    536 
    537     <!-- ===== HERO (FIXED CENTERING) ===== -->
    538     <section class="min-h-screen flex items-center justify-center px-4 py-12 sm:px-6 sm:py-20">
    539       <div class="max-w-6xl mx-auto w-full">
    540         <div class="flex flex-col md:flex-row items-center gap-6 md:gap-12">
    541           <div class="flex-1 text-center md:text-left">
    542             <div class="fade-up mt-3 md:mt-4">
    543               <h1 class="text-base sm:text-xl md:text-2xl text-gray-300 mono">$> Run Python in your browser – no backend</h1>
    544             </div>
    545             <div class="fade-up mt-2">
    546               <span class="badge-term"><i class="fas fa-bolt text-accent mr-1"></i> Python + HTML with Pyodide</span>
    547             </div>
    548             <div class="fade-up mt-3">
    549               <p class="text-gray-400 max-w-lg mx-auto md:mx-0 leading-relaxed text-sm sm:text-base">
    550                 A zero-config <strong>Python + HTML runtime</strong> that lets you embed <strong>Python code</strong> directly in your <strong>HTML</strong>.
    551                 Handles <code class="text-accent">print()</code>, <code class="text-accent">input()</code>, and errors – live, in the browser.
    552                 <span class="block mt-1 text-xs text-gray-500">No server. No heavy frameworks. Just <code class="text-accent">&lt;py&gt;</code> and go.</span>
    553               </p>
    554             </div>
    555             <div class="fade-up mt-5 flex flex-wrap gap-2 sm:gap-3 justify-center md:justify-start">
    556               <a href="demo.html" target="_blank" class="btn-term"><i class="fas fa-play"></i> Live Demo</a>
    557               <a href="#compare" class="btn-term"><i class="fas fa-balance-scale"></i> Compare</a>
    558               <a href="#code" class="btn-term"><i class="fas fa-code"></i> See Code</a>
    559               <a href="https://github.com/NodeX-AR/Pytml" target="_blank" class="btn-term"><i class="fab fa-github"></i> GitHub</a>
    560             </div>
    561             <div class="fade-up mt-4 flex flex-wrap gap-2 justify-center md:justify-start">
    562               <span class="badge-term"><i class="fas fa-cubes mr-1"></i> Pytml 2.5</span>
    563             </div>
    564           </div>
    565           <!-- FIXED: added items-center and mx-auto to ensure dead-center on mobile -->
    566           <div class="flex-1 flex justify-center items-center hero-img-wrap">
    567             <div class="relative w-40 h-40 sm:w-60 sm:h-60 md:w-72 md:h-72 flex items-center justify-center mx-auto">
    568               <div class="absolute inset-0 rounded-full border border-[var(--accent)] opacity-20 pulse-ring"></div>
    569               <img src="/android-chrome-192x192.png" alt="Pytml" class="w-28 h-28 sm:w-40 sm:h-40 md:w-56 md:h-56 object-contain" style="filter: drop-shadow(0 0 30px rgba(59,130,246,0.2));" loading="lazy">
    570             </div>
    571           </div>
    572         </div>
    573       </div>
    574     </section>
    575 
    576     <!-- ===== WHAT IS PYTML? (unchanged) ===== -->
    577     <section class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    578       <div class="max-w-5xl mx-auto">
    579         <div class="fade-up text-center">
    580           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent">//</span> The Lazy Bridge</h2>
    581         </div>
    582         <div class="grid sm:grid-cols-2 gap-6 sm:gap-8 mt-8">
    583           <div class="fade-up space-y-4 text-gray-300 leading-relaxed text-sm sm:text-base">
    584             <p><strong>Pytml</strong> is a minimal <strong>JavaScript library</strong> that lets you write <span class="text-accent font-bold">Python inside your HTML</span> – no server, no setup.</p>
    585             <p>It wraps <span class="text-accent">Pyodide</span> (Python compiled to <strong>WebAssembly</strong>) and connects Python to HTML, including <code class="text-accent">print()</code>, browser-backed <code class="text-accent">input()</code>, DOM access, events and numbered inline controls.</p>
    586             <p>Built by <span class="text-accent">Aswanth R (NodeX‑AR)</span> – a Class 12 student who wanted a easy and fast way to show <strong>Python projects</strong> live on the web.</p>
    587           </div>
    588           <div class="fade-up card">
    589             <h3 class="text-base sm:text-lg font-bold font-heading mb-2">Why it exists</h3>
    590             <ul class="space-y-2 text-gray-400 text-xs sm:text-sm mono">
    591               <li><span class="text-accent">➜</span> No Flask, no Django, no server</li>
    592               <li><span class="text-accent">➜</span> Just <span class="text-white">&lt;py&gt;code&lt;/py&gt;</span> or <span class="text-white">&lt;py1&gt;...&lt;/py1&gt;</span></li>
    593               <li><span class="text-accent">➜</span> Works offline after first load</li>
    594               <li><span class="text-accent">➜</span> Lightweight JavaScript bridge</li>
    595             </ul>
    596           </div>
    597         </div>
    598       </div>
    599     </section>
    600 
    601     <!-- ===== HONEST COMPARISON (FIXED MOBILE SCROLL) ===== -->
    602     <section id="compare" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    603       <div class="max-w-5xl mx-auto">
    604         <div class="fade-up text-center">
    605           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent"></span>Comparison</h2>
    606           <p class="text-gray-400 max-w-2xl mx-auto text-sm sm:text-base">Just real trade‑offs. Pytml is <strong>not</strong> a Flask replacement for backends, and it's <strong>not</strong> as feature‑heavy as PyScript. But for <strong>static pages, demos, and education</strong>, it wins on speed, size, and simplicity.</p>
    607         </div>
    608         <div class="fade-up mt-8 card overflow-x-auto compare-table-wrap">
    609           <table class="compare-table min-w-[420px]">
    610             <thead>
    611               <tr>
    612                 <th>Feature</th>
    613                 <th>Pytml</th>
    614                 <th>PyScript</th>
    615                 <th>Flask</th>
    616               </tr>
    617             </thead>
    618             <tbody>
    619               <tr>
    620                 <td class="highlight">Setup time</td>
    621                 <td><span class="check">✔</span> 1 min (1 tag)</td>
    622                 <td><span class="cross">~</span> 5 min (config)</td>
    623                 <td><span class="cross">~</span> 10+ min (env)</td>
    624               </tr>
    625               <tr>
    626                 <td class="highlight">Server required</td>
    627                 <td><span class="check">✔</span> No (static)</td>
    628                 <td><span class="check">✔</span> No (static)</td>
    629                 <td><span class="cross">✘</span> Yes (backend)</td>
    630               </tr>
    631               <tr>
    632                 <td class="highlight">Wrapper size</td>
    633                 <td><span class="check">✔</span> <strong>~15 KB</strong></td>
    634                 <td><span class="cross">~</span> Larger bundle</td>
    635                 <td><span class="cross">—</span> N/A</td>
    636               </tr>
    637               <tr>
    638                 <td class="highlight">Pyodide load</td>
    639                 <td><span class="check">✔</span> Lazy</td>
    640                 <td><span class="cross">✘</span> Eager</td>
    641                 <td><span class="cross">—</span> N/A</td>
    642               </tr>
    643               <tr>
    644                 <td class="highlight">Configuration</td>
    645                 <td><span class="check">✔</span> Zero (<code>&lt;py&gt;</code>)</td>
    646                 <td><span class="cross">~</span> <code>&lt;py-config&gt;</code></td>
    647                 <td><span class="cross">~</span> Full structure</td>
    648               </tr>
    649               <tr>
    650                 <td class="highlight">Best for</td>
    651                 <td><span class="check">✔</span> Demos, education</td>
    652                 <td><span class="check">✔</span> Complex browser apps</td>
    653                 <td><span class="check">✔</span> Full‑stack apps</td>
    654               </tr>
    655             </tbody>
    656           </table>
    657           <p class="text-xs text-gray-500 mt-4 text-center">* Pyodide (~6MB) loaded from CDN – same for both. Pytml adds a small JavaScript bridge around Pyodide.</p>
    658         </div>
    659       </div>
    660     </section>
    661 
    662     <!-- ===== HOW IT WORKS (unchanged) ===== -->
    663     <section id="code" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    664       <div class="max-w-5xl mx-auto">
    665         <div class="fade-up text-center">
    666           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-6"><span class="text-accent">#</span> How It Works</h2>
    667         </div>
    668         <div class="grid sm:grid-cols-2 gap-6 sm:gap-8">
    669           <div class="fade-up space-y-3 text-gray-300 leading-relaxed mono text-xs sm:text-sm">
    670             <div class="flex gap-3"><span class="text-accent font-bold">1.</span> Loads <strong>Pyodide</strong> (WebAssembly) from CDN</div>
    671             <div class="flex gap-3"><span class="text-accent font-bold">2.</span> Connects <code>print()</code>, <code>input()</code> and the DOM</div>
    672             <div class="flex gap-3"><span class="text-accent font-bold">3.</span> Finds <code>&lt;py&gt;</code>, <code>&lt;py1&gt;</code>, <code>&lt;py2&gt;</code> and other Python blocks</div>
    673             <div class="flex gap-3"><span class="text-accent font-bold">4.</span> Runs Python, catches output and errors</div>
    674             <div class="flex gap-3"><span class="text-accent font-bold">5.</span> Displays everything in a styled terminal</div>
    675           </div>
    676           <div class="fade-up code-box">
    677 &lt;!DOCTYPE html&gt;
    678 &lt;html&gt;
    679   &lt;head&gt;
    680     &lt;script src="https://pytml.vercel.app/pytml.js"&gt;&lt;/script&gt;
    681   &lt;/head&gt;
    682   &lt;body&gt;
    683     &lt;py1&gt;
    684 &lt;btn1&gt;Hello&lt;/btn1&gt;
    685 
    686 def hello(event):
    687     print("Hello from Python!")
    688 
    689 btn1.on("click", hello)
    690     &lt;/py1&gt;
    691   &lt;/body&gt;
    692 &lt;/html&gt;
    693           </div>
    694         </div>
    695       </div>
    696     </section>
    697 
    698     <!-- ===== FEATURES (your original 4 cards) ===== -->
    699     <section id="features" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    700       <div class="max-w-6xl mx-auto">
    701         <div class="fade-up text-center">
    702           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-6"><span class="text-accent"></span> Features</h2>
    703         </div>
    704         <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
    705           <div class="fade-up card text-center"><i class="fas fa-bolt text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Zero config</p><p class="text-xs text-gray-500">Just a script tag</p></div>
    706           <div class="fade-up card text-center"><i class="fas fa-terminal text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Real I/O</p><p class="text-xs text-gray-500">print() and input() work</p></div>
    707           <div class="fade-up card text-center"><i class="fas fa-mobile-alt text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Any device</p><p class="text-xs text-gray-500">Works on phones too</p></div>
    708           <div class="fade-up card text-center"><i class="fas fa-code text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Open source</p><p class="text-xs text-gray-500">Apache 2.0</p></div>
    709         </div>
    710       </div>
    711     </section>
    712 
    713     <!-- ===== USE CASES ===== -->
    714     <section class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    715       <div class="max-w-6xl mx-auto">
    716         <div class="fade-up text-center">
    717           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent"></span> When to Use Pytml</h2>
    718           <p class="text-gray-400 max-w-2xl mx-auto mb-8 text-sm sm:text-base">Be honest – Pytml shines here, not everywhere.</p>
    719         </div>
    720         <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
    721           <div class="fade-up card">
    722             <h3 class="text-sm font-bold">Education & Tutorials</h3>
    723             <p class="text-xs text-gray-400 mt-1">Embed live Python in courseware. Students run code without installing anything – instant feedback.</p>
    724           </div>
    725           <div class="fade-up card">
    726             <h3 class="text-sm font-bold">Technical Blogs</h3>
    727             <p class="text-xs text-gray-400 mt-1">Interactive code snippets – readers tweak and run Python right in the article. No server costs.</p>
    728           </div>
    729           <div class="fade-up card">
    730             <h3 class="text-sm font-bold">Data Science Demos</h3>
    731             <p class="text-xs text-gray-400 mt-1">Show visualisations or data processing with Python, all client‑side. Perfect for portfolio projects.</p>
    732           </div>
    733           <div class="fade-up card">
    734             <h3 class="text-sm font-bold">Rapid Prototyping</h3>
    735             <p class="text-xs text-gray-400 mt-1">Test Python logic in the browser without spinning up a Flask app. Great for hackathons.</p>
    736           </div>
    737           <div class="fade-up card sm:col-span-2 text-center border-accent/20">
    738             <p class="text-xs text-gray-400"><span class="text-accent font-bold">Not for:</span> Heavy backend processing, database operations, or production APIs – use Flask for that. Pytml is client‑side only.</p>
    739           </div>
    740         </div>
    741       </div>
    742     </section>
    743 
    744     <!-- ===== TECH STACK MARQUEE (unchanged) ===== -->
    745     <section class="py-12 sm:py-16 px-4 sm:px-6 border-t border-white/5">
    746       <div class="max-w-6xl mx-auto">
    747         <div class="fade-up text-center">
    748           <h2 class="text-xl sm:text-2xl font-bold font-heading mb-4"><span class="text-accent">🧩</span> Built With</h2>
    749         </div>
    750         <div class="marquee">
    751           <div class="marquee-track">
    752             <span class="skill-tag"><i class="fab fa-python"></i> Python</span>
    753             <span class="skill-tag"><i class="fab fa-js"></i> JavaScript</span>
    754             <span class="skill-tag"><i class="fas fa-globe"></i> WebAssembly</span>
    755             <span class="skill-tag"><i class="fas fa-database"></i> Pyodide</span>
    756             <span class="skill-tag"><i class="fas fa-code"></i> ES6</span>
    757             <span class="skill-tag"><i class="fab fa-html5"></i> HTML5</span>
    758             <span class="skill-tag"><i class="fas fa-terminal"></i> Bash</span>
    759             <span class="skill-tag"><i class="fab fa-github"></i> GitHub</span>
    760             <span class="skill-tag"><i class="fas fa-cloud"></i> Vercel</span>
    761             <span class="skill-tag"><i class="fas fa-shield-alt"></i> Apache 2.0</span>
    762             <span class="skill-tag"><i class="fab fa-python"></i> Python</span>
    763             <span class="skill-tag"><i class="fab fa-js"></i> JavaScript</span>
    764             <span class="skill-tag"><i class="fas fa-globe"></i> WebAssembly</span>
    765             <span class="skill-tag"><i class="fas fa-database"></i> Pyodide</span>
    766             <span class="skill-tag"><i class="fas fa-code"></i> ES6</span>
    767             <span class="skill-tag"><i class="fab fa-html5"></i> HTML5</span>
    768             <span class="skill-tag"><i class="fas fa-terminal"></i> Bash</span>
    769             <span class="skill-tag"><i class="fab fa-github"></i> GitHub</span>
    770             <span class="skill-tag"><i class="fas fa-cloud"></i> Vercel</span>
    771             <span class="skill-tag"><i class="fas fa-shield-alt"></i> Apache 2.0</span>
    772           </div>
    773         </div>
    774       </div>
    775     </section>
    776 
    777     <!-- ===== UPDATED FAQ (actually useful) ===== -->
    778     <section id="faq" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    779       <div class="max-w-4xl mx-auto">
    780         <div class="fade-up text-center">
    781           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-6"><span class="text-accent"></span> Frequently Asked Questions</h2>
    782         </div>
    783         <div class="space-y-4">
    784           <div class="fade-up card">
    785             <h3 class="text-sm font-bold">What is the actual bundle size of Pytml?</h3>
    786             <p class="text-xs text-gray-400 mt-1">The Pytml wrapper itself is <strong>~15KB</strong> minified. The Pyodide runtime (~6MB) is loaded lazily from CDN only when you actually run Python code, so initial page load isn't blocked.</p>
    787           </div>
    788           <div class="fade-up card">
    789             <h3 class="text-sm font-bold">Can I use Pytml with React, Vue, or other JS frameworks?</h3>
    790             <p class="text-xs text-gray-400 mt-1">Yes. Pytml is framework-agnostic. You can drop the script tag in any HTML page, including those generated by React or Vue. It scans the DOM for <code>&lt;py&gt;</code> tags after the framework renders.</p>
    791           </div>
    792           <div class="fade-up card">
    793             <h3 class="text-sm font-bold">What Python packages are supported?</h3>
    794             <p class="text-xs text-gray-400 mt-1">Any pure-Python package available on Pyodide's repository – including NumPy, Pandas, Matplotlib, SciPy, and scikit-learn. C-extensions are not supported unless compiled to WebAssembly.</p>
    795           </div>
    796           <div class="fade-up card">
    797             <h3 class="text-sm font-bold">How does Pytml handle errors and debugging?</h3>
    798             <p class="text-xs text-gray-400 mt-1">Python tracebacks are captured and rendered directly in the browser terminal box. Syntax errors, runtime exceptions, and import errors all appear with line numbers and stack traces, just like a local Python REPL.</p>
    799           </div>
    800           <div class="fade-up card">
    801             <h3 class="text-sm font-bold">Is there any server-side component or data sent to a backend?</h3>
    802             <p class="text-xs text-gray-400 mt-1">No. Everything runs client-side in WebAssembly. No Python code, input, or output ever leaves your browser. This makes Pytml ideal for privacy-sensitive demos and educational tools.</p>
    803           </div>
    804           <div class="fade-up card">
    805             <h3 class="text-sm font-bold">What are the performance limitations?</h3>
    806             <p class="text-xs text-gray-400 mt-1">Pytml runs Python in a WebAssembly sandbox. It's roughly <strong>2-5x slower</strong> than native CPython for CPU-bound tasks, and memory is limited to the browser's tab allocation (usually 2-4GB). Heavy number crunching may be slower than server-side Python, but for demos, data viz, and scripting, it's perfectly fine.</p>
    807           </div>
    808         </div>
    809       </div>
    810     </section>
    811 
    812     <!-- ===== TESTIMONIALS – REMOVED (no fake reviews) ===== -->
    813 
    814     <!-- ===== LIVE DEMO (unchanged) ===== -->
    815     <section id="demo" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5">
    816       <div class="max-w-4xl mx-auto text-center">
    817         <div class="fade-up">
    818           <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent">[</span> Try It <span class="text-accent">]</span></h2>
    819         </div>
    820         <div class="fade-up">
    821           <p class="text-gray-300 max-w-xl mx-auto mb-6 sm:mb-8 text-sm sm:text-base">See Pytml in action – run <strong>Python code</strong> live in your browser.</p>
    822         </div>
    823         <div class="fade-up flex flex-wrap justify-center gap-2 sm:gap-3 md:gap-5">
    824           <a href="demo.html" target="_blank" class="btn-term"><i class="fas fa-globe"></i> Live Demo</a>
    825           <a href="https://github.com/NodeX-AR/Pytml" target="_blank" class="btn-term"><i class="fab fa-github"></i> GitHub Repo</a>
    826           <a href="https://dev.to/nodex-ar/pytml-running-python-in-the-browser-without-a-backend-1nn4" target="_blank" class="btn-term"><i class="fas fa-pen"></i> DEV Article</a>
    827           <a href="https://devpost.com/software/pytml" target="_blank" class="btn-term"><i class="fas fa-trophy"></i> Devpost</a>
    828         </div>
    829         <div class="fade-up mt-8 sm:mt-10 text-xs sm:text-sm text-gray-500 mono">
    830           Made from <span class="text-accent">Laziness</span> of 
    831           <a href="https://a-r.is-a.dev" target="_blank" class="text-accent hover:underline">Aswanth R (NodeX-AR)</a>
    832         </div>
    833       </div>
    834     </section>
    835   </main>
    836 
    837   <!-- ===== FOOTER ===== -->
    838   <footer class="py-6 sm:py-8 px-4 sm:px-6 border-t border-white/5 text-center text-xs text-gray-600">
    839     <p>
    840       <a href="https://a-r.is-a.dev" target="_blank" class="footer-link">Aswanth R (NodeX-AR)</a> &nbsp;·&nbsp; 
    841       <a href="https://github.com/NodeX-AR/Pytml" target="_blank" class="footer-link">GitHub</a> &nbsp;·&nbsp; 
    842       <a href="demo.html" target="_blank" class="footer-link">Live Demo</a> &nbsp;·&nbsp;
    843       <a href="https://github.com/NodeX-AR/Pytml/blob/main/README.md" target="_blank" class="footer-link">Docs</a>
    844     </p>
    845     <p class="mt-1 text-gray-700">Open-source under Apache 2.0 &nbsp;·&nbsp; 2026</p>
    846   </footer>
    847 
    848   <!-- ========== SCRIPTS (unchanged) ========== -->
    849   <script>
    850     const observer = new IntersectionObserver((entries) => {
    851       entries.forEach(entry => { if (entry.isIntersecting) entry.target.classList.add('visible'); });
    852     }, { threshold: 0.1 });
    853     document.querySelectorAll('.fade-up').forEach(el => observer.observe(el));
    854 
    855     document.querySelector('a[href="#code"]')?.addEventListener('click', (e) => {
    856       e.preventDefault();
    857       document.getElementById('code').scrollIntoView({ behavior: 'smooth' });
    858     });
    859 
    860     document.querySelectorAll('nav a[href^="#"]').forEach(link => {
    861       link.addEventListener('click', (e) => {
    862         const target = document.querySelector(link.getAttribute('href'));
    863         if (target) {
    864           e.preventDefault();
    865           target.scrollIntoView({ behavior: 'smooth' });
    866         }
    867       });
    868     });
    869   </script>
    870 <!-- ===== FLOATING CHAT BUTTON ===== -->
    871 <style>
    872   .chat-float {
    873     position: fixed;
    874     bottom: 28px;
    875     right: 28px;
    876     z-index: 9999;
    877     display: flex;
    878     flex-direction: column;
    879     align-items: flex-end;
    880     gap: 10px;
    881   }
    882 
    883   /* Speech bubble */
    884   .chat-bubble {
    885     background: #0c0c0c;
    886     border: 1px solid #1e1e1e;
    887     border-radius: 16px 16px 4px 16px;
    888     padding: 12px 18px;
    889     max-width: 200px;
    890     color: #e5e5e5;
    891     font-size: 0.85rem;
    892     line-height: 1.5;
    893     box-shadow: 0 8px 24px rgba(0,0,0,0.6);
    894     opacity: 0;
    895     transform: translateY(10px) scale(0.95);
    896     animation: bubbleIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.6s forwards;
    897     font-family: 'Inter', system-ui, sans-serif;
    898   }
    899   .chat-bubble .greeting {
    900     font-weight: 600;
    901     color: #3b82f6;
    902   }
    903   .chat-bubble .sub {
    904     color: #8a8a8a;
    905     font-size: 0.75rem;
    906   }
    907 
    908   @keyframes bubbleIn {
    909     to {
    910       opacity: 1;
    911       transform: translateY(0) scale(1);
    912     }
    913   }
    914 
    915   /* Circular button */
    916   .chat-btn {
    917     display: flex;
    918     align-items: center;
    919     justify-content: center;
    920     width: 60px;
    921     height: 60px;
    922     border-radius: 50%;
    923     background: #3b82f6;
    924     border: none;
    925     box-shadow: 0 4px 20px rgba(59, 130, 246, 0.4);
    926     cursor: pointer;
    927     transition: transform 0.2s, box-shadow 0.2s;
    928     text-decoration: none;
    929     color: white;
    930     font-size: 28px;
    931   }
    932   .chat-btn:hover {
    933     transform: scale(1.05);
    934     box-shadow: 0 6px 28px rgba(59, 130, 246, 0.6);
    935   }
    936   .chat-btn:active {
    937     transform: scale(0.95);
    938   }
    939 
    940   /* Hide bubble on small screens if needed */
    941   @media (max-width: 480px) {
    942     .chat-bubble {
    943       max-width: 160px;
    944       font-size: 0.75rem;
    945       padding: 10px 14px;
    946     }
    947     .chat-btn {
    948       width: 52px;
    949       height: 52px;
    950       font-size: 24px;
    951     }
    952   }
    953 </style>
    954 
    955 <div class="chat-float">
    956   <!-- Speech bubble -->
    957   <div class="chat-bubble">
    958     <div class="greeting">Hello,</div>
    959     <div>How can I help you?</div>
    960     <div class="sub">💬 Ask about Pytml</div>
    961   </div>
    962 
    963   <!-- Button linking to /bot -->
    964   <a href="https://pytml.js.org/bot" class="chat-btn" aria-label="Open chat support">
    965     💬
    966   </a>
    967 </div>
    968 </body>
    969 </html>