Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Colorize — Bring Photos to Life</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,300;1,400&family=Outfit:wght@300;400;500;600&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --black: #ffffff; | |
| --dark: #f8f8f8; | |
| --surface: #f2f2f2; | |
| --border: #e0e0e0; | |
| --muted: #888888; | |
| --silver: #666666; | |
| --light: #1a1a1a; | |
| --amber: #ff6b35; | |
| --amber-dim:#ff8c5a; | |
| --amber-glow: rgba(255, 107, 53, 0.1); | |
| --red: #e74c3c; | |
| } | |
| *, *::before, *::after { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| html, body { | |
| height: 100%; | |
| } | |
| body { | |
| font-family: 'Outfit', sans-serif; | |
| background: linear-gradient(135deg, #ffffff 0%, #f9f5f0 100%); | |
| color: var(--light); | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 24px; | |
| overflow-x: hidden; | |
| } | |
| /* Grain overlay */ | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.01'/%3E%3C/svg%3E"); | |
| pointer-events: none; | |
| z-index: 0; | |
| opacity: 0.3; | |
| } | |
| /* Ambient amber glow behind card */ | |
| body::after { | |
| content: ''; | |
| position: fixed; | |
| width: 900px; | |
| height: 900px; | |
| background: radial-gradient(ellipse, rgba(255, 107, 53, 0.08) 0%, transparent 70%); | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| .page-wrapper { | |
| position: relative; | |
| z-index: 1; | |
| width: 100%; | |
| max-width: 520px; | |
| animation: fadeUp 0.7s cubic-bezier(0.22, 1, 0.36, 1) both; | |
| } | |
| @keyframes fadeUp { | |
| from { opacity: 0; transform: translateY(32px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| /* ── Header ── */ | |
| .header { | |
| text-align: center; | |
| margin-bottom: 44px; | |
| } | |
| .eyebrow { | |
| font-family: 'Outfit', sans-serif; | |
| font-size: 0.7rem; | |
| font-weight: 500; | |
| letter-spacing: 0.22em; | |
| text-transform: uppercase; | |
| color: var(--amber); | |
| margin-bottom: 14px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 10px; | |
| } | |
| .eyebrow::before, | |
| .eyebrow::after { | |
| content: ''; | |
| display: inline-block; | |
| width: 32px; | |
| height: 1px; | |
| background: var(--amber); | |
| } | |
| .title { | |
| font-family: 'Cormorant Garamond', serif; | |
| font-size: 3.6rem; | |
| font-weight: 300; | |
| line-height: 1; | |
| letter-spacing: -0.02em; | |
| color: var(--light); | |
| margin-bottom: 10px; | |
| } | |
| .title em { | |
| font-style: italic; | |
| color: var(--amber); | |
| } | |
| .subtitle { | |
| font-size: 0.875rem; | |
| font-weight: 300; | |
| color: var(--silver); | |
| letter-spacing: 0.02em; | |
| } | |
| /* ── Card ── */ | |
| .card { | |
| background: var(--dark); | |
| border: 1px solid var(--border); | |
| border-radius: 4px; | |
| padding: 40px; | |
| position: relative; | |
| overflow: hidden; | |
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); | |
| } | |
| .card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--amber), transparent); | |
| } | |
| /* ── Error ── */ | |
| .error { | |
| background: rgba(231, 76, 60, 0.08); | |
| border: 1px solid rgba(231, 76, 60, 0.2); | |
| border-left: 3px solid var(--red); | |
| color: #d63031; | |
| padding: 14px 18px; | |
| border-radius: 3px; | |
| margin-bottom: 28px; | |
| font-size: 0.875rem; | |
| animation: shake 0.4s ease; | |
| } | |
| @keyframes shake { | |
| 0%, 100% { transform: translateX(0); } | |
| 20% { transform: translateX(-6px); } | |
| 60% { transform: translateX(6px); } | |
| } | |
| /* ── Upload Zone ── */ | |
| .drop-label { | |
| font-family: 'Outfit', sans-serif; | |
| font-size: 0.7rem; | |
| font-weight: 500; | |
| letter-spacing: 0.16em; | |
| text-transform: uppercase; | |
| color: var(--silver); | |
| display: block; | |
| margin-bottom: 16px; | |
| } | |
| .drop-zone { | |
| position: relative; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 14px; | |
| width: 100%; | |
| padding: 52px 24px; | |
| border: 2px dashed var(--border); | |
| border-radius: 3px; | |
| background: var(--surface); | |
| cursor: pointer; | |
| transition: border-color 0.25s, background 0.25s, box-shadow 0.25s; | |
| margin-bottom: 10px; | |
| } | |
| .drop-zone:hover { | |
| border-color: var(--amber); | |
| background: rgba(255, 107, 53, 0.04); | |
| box-shadow: 0 0 40px var(--amber-glow); | |
| } | |
| .drop-zone.dragover { | |
| border-color: var(--amber); | |
| background: rgba(255, 107, 53, 0.08); | |
| box-shadow: 0 0 60px rgba(255, 107, 53, 0.2); | |
| } | |
| .drop-icon { | |
| width: 44px; | |
| height: 44px; | |
| color: var(--amber); | |
| transition: color 0.25s, transform 0.25s; | |
| } | |
| .drop-zone:hover .drop-icon { | |
| color: var(--amber); | |
| transform: translateY(-2px); | |
| } | |
| .drop-text-main { | |
| font-family: 'Cormorant Garamond', serif; | |
| font-size: 1.25rem; | |
| font-weight: 400; | |
| color: var(--light); | |
| letter-spacing: 0.01em; | |
| } | |
| .drop-text-sub { | |
| font-size: 0.78rem; | |
| color: var(--muted); | |
| letter-spacing: 0.04em; | |
| } | |
| #file { | |
| display: none; | |
| } | |
| /* ── File name pill ── */ | |
| .file-name { | |
| display: none; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 9px 14px; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 3px; | |
| font-size: 0.8rem; | |
| color: var(--muted); | |
| margin-bottom: 28px; | |
| animation: fadeIn 0.3s ease; | |
| } | |
| .file-name.show { | |
| display: flex; | |
| } | |
| .file-name svg { | |
| color: var(--amber); | |
| flex-shrink: 0; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(-4px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| /* ── Submit ── */ | |
| .submit-btn { | |
| width: 100%; | |
| padding: 16px; | |
| font-family: 'Outfit', sans-serif; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| letter-spacing: 0.14em; | |
| text-transform: uppercase; | |
| color: white; | |
| background: var(--amber); | |
| border: none; | |
| border-radius: 3px; | |
| cursor: pointer; | |
| transition: background 0.2s, box-shadow 0.2s, transform 0.15s; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .submit-btn::after { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.2) 50%, transparent 100%); | |
| transform: translateX(-100%); | |
| transition: transform 0.5s; | |
| } | |
| .submit-btn:hover { | |
| background: #ff5522; | |
| box-shadow: 0 4px 24px rgba(255, 107, 53, 0.35); | |
| transform: translateY(-1px); | |
| } | |
| .submit-btn:hover::after { | |
| transform: translateX(100%); | |
| } | |
| .submit-btn:active { | |
| transform: translateY(0); | |
| box-shadow: none; | |
| } | |
| /* ── Footer tag ── */ | |
| .footer-tag { | |
| text-align: center; | |
| margin-top: 28px; | |
| font-size: 0.72rem; | |
| color: var(--muted); | |
| letter-spacing: 0.06em; | |
| } | |
| .footer-tag span { | |
| color: var(--amber); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="page-wrapper"> | |
| <div class="header"> | |
| <div class="eyebrow">AI Photo Lab</div> | |
| <h1 class="title">Colo<em>rize</em></h1> | |
| <p class="subtitle">Restore life to black & white photographs</p> | |
| </div> | |
| <div class="card"> | |
| {% if error %} | |
| <div class="error">⚠ {{ error }}</div> | |
| {% endif %} | |
| <form method="post" enctype="multipart/form-data" id="uploadForm"> | |
| <label class="drop-label">Upload Image</label> | |
| <label for="file" class="drop-zone" id="dropZone"> | |
| <svg class="drop-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.2"> | |
| <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" /> | |
| </svg> | |
| <span class="drop-text-main">Drop your photos here</span> | |
| <span class="drop-text-sub">or click to browse · JPG, PNG, BMP · Multiple files</span> | |
| </label> | |
| <input type="file" id="file" name="files" accept="image/*" multiple required> | |
| <div class="file-name" id="fileName"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> | |
| <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" /> | |
| </svg> | |
| <span id="fileNameText"></span> | |
| </div> | |
| <button class="submit-btn" type="submit">Colorize Now</button> | |
| </form> | |
| </div> | |
| <p class="footer-tag">Powered by <span>AI colorization</span> · Results in seconds</p> | |
| </div> | |
| <script> | |
| const fileInput = document.getElementById('file'); | |
| const dropZone = document.getElementById('dropZone'); | |
| const fileNameEl = document.getElementById('fileName'); | |
| const fileNameTx = document.getElementById('fileNameText'); | |
| fileInput.addEventListener('change', function () { | |
| if (this.files.length > 0) { | |
| const fileCount = this.files.length; | |
| fileNameTx.textContent = fileCount + ' file' + (fileCount > 1 ? 's' : '') + ' selected'; | |
| fileNameEl.classList.add('show'); | |
| } | |
| }); | |
| dropZone.addEventListener('dragover', function (e) { | |
| e.preventDefault(); | |
| dropZone.classList.add('dragover'); | |
| }); | |
| dropZone.addEventListener('dragleave', function (e) { | |
| e.preventDefault(); | |
| dropZone.classList.remove('dragover'); | |
| }); | |
| dropZone.addEventListener('drop', function (e) { | |
| e.preventDefault(); | |
| dropZone.classList.remove('dragover'); | |
| const files = e.dataTransfer.files; | |
| fileInput.files = files; | |
| if (files.length > 0) { | |
| const fileCount = files.length; | |
| fileNameTx.textContent = fileCount + ' file' + (fileCount > 1 ? 's' : '') + ' selected'; | |
| fileNameEl.classList.add('show'); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |